diff --git a/ignite-html-refresh.js b/ignite-html-refresh.js new file mode 100644 index 0000000..62e704b --- /dev/null +++ b/ignite-html-refresh.js @@ -0,0 +1,134 @@ +import { IgniteElement } from "../ignite-html/ignite-element.js"; +import { IgniteTemplate, slot } from "../ignite-html/ignite-template.js"; + +/** + * Adds a pull to refresh functionality to an IgniteTemplate. This template must have an overflow set for this to work. + * @param {Function} refreshCallback A callback function that will be invoked when the pull to refresh is activated. + * @param {IgniteTemplate} placeholderElement An optional element to display indicating a pull to refresh is supported. + * @param {Number} maxPull Optional amount in pixels required to be pulled down before a refresh is triggered. + * @returns {IgniteTemplate} This ignite template. + */ +IgniteTemplate.prototype.refresh = function(refreshCallback, placeholderElement = null, maxPull = 125) { + this.refreshCallback = refreshCallback; + this.refreshPlaceholderElement = placeholderElement; + this.refreshTouchStartY = 0; + + if (placeholderElement && !(placeholderElement instanceof IgniteTemplate)) { + throw "placeholderElement must be an IgniteTemplate."; + } + + this.refreshTouchStartListener = (event) => { + this.refreshTouchStartY = event.touches[0].clientY; + this.refreshTouchStartTop = this.element.scrollTop; + + //Save the default overflow value if we don't have it yet. + if (!this.refreshTouchOverflow) { + this.refreshTouchOverflow = window.getComputedStyle(this.element).getPropertyValue("overflow-y"); + } + + this.element.addEventListener("touchmove", this.refreshTouchMoveListener); + this.element.addEventListener("touchend", this.refreshTouchEndListener); + }; + + this.refreshTouchMoveListener = (event) => { + var touch = event.touches[0]; + + var diff = touch.clientY - (this.refreshTouchStartY + this.refreshTouchStartTop + 15); + + if (diff < maxPull) { + if (this.refreshPlaceholderElement instanceof IgniteTemplate) { + this.refreshPlaceholderElement.element.style.setProperty("min-height", `${Math.max(diff, 0)}px`); + this.refreshPlaceholderElement.element.style.setProperty("height", `${Math.max(diff, 0)}px`); + } else { + this.refreshPlaceholderElement.style.setProperty("min-height", `${Math.max(diff, 0)}px`); + this.refreshPlaceholderElement.style.setProperty("height", `${Math.max(diff, 0)}px`); + } + } else { + if (this.refreshPlaceholderElement instanceof IgniteTemplate) { + this.refreshPlaceholderElement.element.style.setProperty("min-height", "0px"); + this.refreshPlaceholderElement.element.style.setProperty("height", "0px"); + } else { + this.refreshPlaceholderElement.style.setProperty("min-height", "0px"); + this.refreshPlaceholderElement.style.setProperty("height", "0px"); + } + + this.element.removeEventListener("touchmove", this.refreshTouchMoveListener); + this.element.removeEventListener("touchend", this.refreshTouchEndListener); + this.element.style.removeProperty("overflow-y"); + this.element.style.setProperty("overflow-y", this.refreshTouchOverflow, "important"); + + this.refreshCallback(); + + return; + } + + if (diff > 0 && this.element.style["overflow-y"] != "hidden") { + this.element.style.setProperty("overflow-y", "hidden", "important"); + } + }; + + this.refreshTouchEndListener = (event) => { + if (this.refreshPlaceholderElement instanceof IgniteTemplate) { + this.refreshPlaceholderElement.element.style.setProperty("min-height", "0px"); + this.refreshPlaceholderElement.element.style.setProperty("height", "0px"); + } else { + this.refreshPlaceholderElement.style.setProperty("min-height", "0px"); + this.refreshPlaceholderElement.style.setProperty("height", "0px"); + } + + this.element.removeEventListener("touchmove", this.refreshTouchMoveListener); + this.element.removeEventListener("touchend", this.refreshTouchEndListener); + this.element.style.removeProperty("overflow-y"); + this.element.style.setProperty("overflow-y", this.refreshTouchOverflow, "important"); + }; + + this._constructors.push((parent, sibling) => { + if (this.refreshCallback) { + if (this.refreshPlaceholderElement) { + this.refreshPlaceholderElement.construct(this.element, null); + this.refreshPlaceholderElement.element.style.setProperty("min-height", "0px"); + this.refreshPlaceholderElement.element.style.setProperty("height", "0px"); + this.element.prepend(this.refreshPlaceholderElement.element); + } else { + this.refreshPlaceholderElement = this.element.querySelector("refresh-placeholder"); + + if (!this.refreshPlaceholderElement) { + this.refreshPlaceholderElement = document.createElement("div"); + this.refreshPlaceholderElement.classList.add("ignite-html"); + this.refreshPlaceholderElement.classList.add("refresh-placeholder"); + this.refreshPlaceholderElement.style.setProperty("min-height", "0px"); + this.refreshPlaceholderElement.style.setProperty("height", "0px"); + this.element.prepend(this.refreshPlaceholderElement); + } else { + this.refreshPlaceholderElement.style.setProperty("min-height", "0px"); + this.refreshPlaceholderElement.style.setProperty("height", "0px"); + } + } + + this.element.addEventListener("touchstart", this.refreshTouchStartListener); + } + }); + + return this; +}; + +/** + * A placeholder element that can be used to customize the refresh placeholder location. + */ +class RefreshPlaceholder extends IgniteElement { + render() { + return this.template.child(new slot(this)); + } +} + +class RefreshPlaceholderTemplate extends IgniteTemplate { + constructor(...children) { + super("refresh-placeholder", children); + } +} + +IgniteHtml.register("refresh-placeholder", RefreshPlaceholder); + +export { + RefreshPlaceholderTemplate as RefreshPlaceholder +} \ No newline at end of file