-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Build lib files in two passes, so we can have a module that can be im…
…ported via npm as well as a normal js script
- Loading branch information
Showing
5 changed files
with
243 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
var __defProp = Object.defineProperty; | ||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: !0, configurable: !0, writable: !0, value }) : obj[key] = value; | ||
var __publicField = (obj, key, value) => (__defNormalProp(obj, typeof key != "symbol" ? key + "" : key, value), value); | ||
class Booster { | ||
constructor(element = "", options = {}) { | ||
__publicField(this, "mounted", !1); | ||
__publicField(this, "elm", null); | ||
__publicField(this, "target", null); | ||
__publicField(this, "_state", {}); | ||
this._options = options || {}, element && (this.elm = element); | ||
} | ||
get options() { | ||
return this._options; | ||
} | ||
set options(defaults) { | ||
let options = {}; | ||
if (this.elm) { | ||
let mount = document.querySelector(this.elm); | ||
if (mount) { | ||
let optionsFromAttribute = mount.dataset.options; | ||
optionsFromAttribute && (options = JSON.parse(optionsFromAttribute)), mount = null; | ||
} | ||
} | ||
this._options = { | ||
...this._options, | ||
...defaults, | ||
...options | ||
}; | ||
} | ||
mount() { | ||
} | ||
unmount() { | ||
} | ||
refresh() { | ||
this.unmount(), this.mount(); | ||
} | ||
get state() { | ||
return console.warn("You should not get state manually. Use getState() instead."), this._state; | ||
} | ||
set state(state) { | ||
console.warn("You should not change state manually. Use setState() instead."), this._state = state; | ||
} | ||
setState(scope = "local", changes) { | ||
let stateChanges = {}, stateRef = this._state; | ||
scope === "global" ? stateRef = Booster._globalState : scope === "component" && (Booster._globalState.hasOwnProperty(this.constructor.name) || (Booster._globalState[this.constructor.name] = {}), stateRef = Booster._globalState[this.constructor.name]), Object.keys(changes).forEach((key) => { | ||
Array.isArray(changes[key]) ? stateRef[key] != null && Array.isArray(stateRef[key]) && stateRef[key].length === changes[key].length ? changes[key].some((item, index) => stateRef[key][index] !== item ? (stateChanges[key] = changes[key], stateRef[key] = stateChanges[key], !0) : !1) : (stateChanges[key] = changes[key], stateRef[key] = stateChanges[key]) : typeof changes[key] == "object" ? (stateRef[key] != null && typeof stateRef[key] == "object" ? (stateChanges[key] = {}, Object.keys(changes[key]).forEach((subkey) => { | ||
stateRef[key][subkey] !== changes[key][subkey] && (stateChanges[key][subkey] = changes[key][subkey]); | ||
})) : stateChanges[key] = changes[key], stateRef[key] = { | ||
...stateRef[key], | ||
...stateChanges[key] | ||
}) : stateRef !== changes[key] && (stateChanges[key] = changes[key], stateRef[key] = changes[key]); | ||
}), Object.keys(stateChanges).forEach((key) => { | ||
Array.isArray(changes[key]) ? stateChanges[key].length === 0 && delete stateChanges[key] : typeof changes[key] == "object" && Object.keys(stateChanges[key]).length === 0 && delete stateChanges[key]; | ||
}), stateRef = null, this.stateChange(stateChanges); | ||
} | ||
stateChange(changes) { | ||
} | ||
getState(scope = "local", defaults = {}) { | ||
let stateRef = this._state; | ||
return scope === "global" ? stateRef = Booster._globalState : scope === "component" && (Booster._globalState.hasOwnProperty(this.constructor.name) ? stateRef = Booster._globalState[this.constructor.name] : stateRef = {}), { | ||
...defaults, | ||
...stateRef | ||
}; | ||
} | ||
destroyState(scope = "local") { | ||
scope === "global" ? Booster._globalState = {} : scope === "component" ? Booster._globalState.hasOwnProperty(this.constructor.name) && (Booster._globalState[this.constructor.name] = {}) : this._state = {}; | ||
} | ||
css(urls) { | ||
return Promise.all(urls.map(this._loadCSS)); | ||
} | ||
_loadCSS(href) { | ||
return new Promise((resolve) => { | ||
if (Booster._sheets.includes(href)) | ||
return resolve(); | ||
Booster._sheets.push(href); | ||
let link = document.createElement("link"); | ||
link.type = "text/css", link.rel = "stylesheet", link.onload = resolve, link.setAttribute("href", href), document.head.appendChild(link); | ||
}); | ||
} | ||
} | ||
Object.defineProperty(Booster, "_sheets", { | ||
value: [], | ||
writable: !0 | ||
}); | ||
Object.defineProperty(Booster, "_globalState", { | ||
value: {}, | ||
writable: !0 | ||
}); | ||
const event = (requirement) => new Promise((resolve) => { | ||
let topic; | ||
if (requirement.indexOf("(") !== -1) { | ||
const topicStart = requirement.indexOf("(") + 1; | ||
topic = requirement.slice(topicStart, -1); | ||
} | ||
topic ? document.body.addEventListener(topic, () => { | ||
resolve(); | ||
}, { once: !0 }) : resolve(); | ||
}), idle = () => new Promise((resolve) => { | ||
"requestIdleCallback" in window ? window.requestIdleCallback(resolve) : setTimeout(resolve, 200); | ||
}), media = (requirement) => new Promise((resolve) => { | ||
const queryStart = requirement.indexOf("("), query = requirement.slice(queryStart), mediaQuery = window.matchMedia(query); | ||
mediaQuery.matches ? resolve() : mediaQuery.addEventListener("change", resolve, { once: !0 }); | ||
}), visible = (selector = null, requirement) => selector ? new Promise((resolve) => { | ||
let rootMargin = "0px 0px 0px 0px"; | ||
if (requirement.indexOf("(") !== -1) { | ||
const rootMarginStart = requirement.indexOf("(") + 1; | ||
rootMargin = requirement.slice(rootMarginStart, -1); | ||
} | ||
const observer = new IntersectionObserver((entries) => { | ||
entries[0].isIntersecting && (observer.disconnect(), resolve()); | ||
}, { rootMargin }); | ||
let elm = document.querySelector(selector); | ||
elm ? observer.observe(elm) : resolve(); | ||
}) : Promise.resolve(!0); | ||
function loadStrategies(strategy, selector) { | ||
let promises = []; | ||
if (strategy) { | ||
let requirements = strategy.split("|").map((requirement) => requirement.trim()).filter((requirement) => requirement !== "immediate").filter((requirement) => requirement !== "eager"); | ||
for (let requirement of requirements) { | ||
if (requirement.startsWith("event")) { | ||
promises.push( | ||
event(requirement) | ||
); | ||
continue; | ||
} | ||
if (requirement === "idle") { | ||
promises.push( | ||
idle() | ||
); | ||
continue; | ||
} | ||
if (requirement.startsWith("media")) { | ||
promises.push( | ||
media(requirement) | ||
); | ||
continue; | ||
} | ||
requirement.startsWith("visible") && promises.push( | ||
visible(selector, requirement) | ||
); | ||
} | ||
} | ||
return promises; | ||
} | ||
class BoosterFactory extends Booster { | ||
constructor() { | ||
super(); | ||
__publicField(this, "loaded", []); | ||
__publicField(this, "config", {}); | ||
this.config = { | ||
origin: location.origin, | ||
basePath: "scripts/boosts" | ||
}; | ||
let configMeta = document.querySelector('meta[name="booster-config"]') ?? null; | ||
configMeta && (this.config = { | ||
...this.config, | ||
...JSON.parse(configMeta.content) | ||
}), this.config.basePath = this.config.basePath.replace(/^\/|\/$/g, ""), this.mount(); | ||
} | ||
mount() { | ||
let targetId = htmx.config.currentTargetId ?? "main", target = document.getElementById(targetId); | ||
if (target) { | ||
let components = target.querySelectorAll("[data-booster]"); | ||
for (let el of components) | ||
this.lazyload(el); | ||
target = null, components = null; | ||
} | ||
} | ||
unmount() { | ||
let targetId = htmx.config.currentTargetId ?? "main", target = document.getElementById(targetId); | ||
if (target) { | ||
for (let i = this.loaded.length - 1; i >= 0; i--) { | ||
let inTarget = target.querySelector(this.loaded[i].selector), inDocument = document.querySelector(this.loaded[i].selector); | ||
(inTarget || !inDocument) && (this.loaded[i].instance.unmount(), this.loaded.splice(i, 1)); | ||
} | ||
target = null; | ||
} | ||
} | ||
/** | ||
* Import a component on demand, optionally using a loading strategy | ||
* | ||
* @param el | ||
*/ | ||
lazyload(el) { | ||
let component = el.dataset.booster, version = el.dataset.version ?? "1", strategy = el.dataset.load ?? null, selector = el.getAttribute("id") ? "#" + el.getAttribute("id") : '[data-booster="' + component + '"]', promises = loadStrategies(strategy, selector); | ||
Promise.all(promises).then(() => { | ||
import( | ||
/* @vite-ignore */ | ||
`${this.config.origin}/${this.config.basePath}/${component}.js?v=${version}` | ||
).then( | ||
(lazyComponent) => { | ||
let instance = new lazyComponent.default(selector); | ||
instance.mounted = !0, this.loaded.push({ | ||
name: component, | ||
selector, | ||
instance | ||
}); | ||
} | ||
); | ||
}); | ||
} | ||
} | ||
export { | ||
Booster, | ||
BoosterFactory, | ||
loadStrategies | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/** | ||
* Booster Pack extension - module exports | ||
* | ||
* @author Mark Croxton, Hallmark Design | ||
*/ | ||
|
||
import Booster from '../booster.js'; | ||
import BoosterFactory from '../boosterFactory.js'; | ||
import { loadStrategies } from '../loadStrategies.js'; | ||
export { Booster, BoosterFactory, loadStrategies }; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { defineConfig } from 'vite'; | ||
|
||
export default defineConfig(({}) => { | ||
return { | ||
esbuild: { | ||
minifyIdentifiers: false | ||
}, | ||
build: { | ||
lib: { | ||
entry: { | ||
"booster-pack": "./lib/ext/booster-pack.mjs" | ||
}, | ||
formats: ["es"], | ||
fileName: (format, name) => `${name}.min.mjs` | ||
}, | ||
minify: true | ||
} | ||
} | ||
}); |