UNPKG

@base-framework/ui

Version:

This is a UI package that adds components and atoms that use Tailwind CSS and a theme based on Shadcn.

653 lines (652 loc) 18.6 kB
var y = Object.defineProperty; var S = (i, t, n) => t in i ? y(i, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : i[t] = n; var g = (i, t, n) => S(i, typeof t != "symbol" ? t + "" : t, n); import { DateTime as f, base as l } from "@base-framework/base"; const h = (i, t) => (typeof i == "string" && (i = [i]), Array.isArray(i) ? (i.push(t), i) : { ...i, callBack: t }), x = (i, t = "") => i ?? t, z = { /** * Formats a number with commas. * * @param {string|number|object|array} watcher * @param {string|null} defaultValue - Value if original is null or undefined. * @returns {object|array} */ number(i, t = null) { return h(i, (e) => { if (!isNaN(e)) { const s = /\B(?=(\d{3})+(?!\d))/g; return e.toString().replace(s, ","); } return t || ""; }); }, /** * Formats a boolean value as a yes/no string. * * @param {string|number|object|array} watcher * @param {string} yes - Text for true values. * @param {string} no - Text for false values. * @returns {object|array} */ yesno(i, t = "Yes", n = "No") { return h(i, (s) => s ? t : n); }, /** * Formats a value as money with two decimals. * * @param {string|number|object|array} watcher * @param {string} currency - Currency symbol. * @param {*} defaultValue - Value if original is invalid. * @returns {object|array} */ money(i, t = "$", n = null) { return h(i, (s) => { const o = parseFloat(s); if (isNaN(o)) return n || ""; const r = /\B(?=(\d{3})+(?!\d))/g; return t + o.toFixed(2).toString().replace(r, ","); }); }, /** * Formats a value as a US phone number (10 digits). * * @param {string|object|array} watcher * @param {*} defaultValue - Value if original is invalid. * @returns {object|array} */ phone(i, t = null) { return h(i, (e) => { e = e || ""; const s = String(e.toString()).replace(/\D/g, ""); return s.length === 10 ? "(" + s.slice(0, 3) + ") " + s.slice(3, 6) + "-" + s.slice(6) : e || t; }); }, /** * Formats a value as an integer (rounds down). * * @param {string|number|object|array} watcher * @param {*} defaultValue - Value if original is invalid. * @returns {object|array} */ integer(i, t = null) { return h(i, (e) => { e = x(e, t); const s = parseInt(e, 10); return isNaN(s) ? t : s.toString(); }); }, /** * Formats a date value to a standard date format. * * @param {string|number|object|array} watcher * @param {*} defaultValue - Value if original is invalid. * @returns {object|array} */ date(i, t = null) { return h(i, (e) => e ? f.format("standard", e) : t || ""); }, /** * Formats a date and time value to a standard date and time format. * * @param {string|number|object|array} watcher * @param {*} defaultValue - Value if original is invalid. * @returns {object|array} */ dateTime(i, t = null) { return h(i, (e) => e ? f.format("standard", e) + " " + f.formatTime(e, 12) : t || ""); }, /** * Formats a time value to a standard time format. * * @param {string|number|object|array} watcher * @param {*} defaultValue - Value if original is invalid. * @returns {object|array} */ time(i, t = null) { return h(i, (e) => e ? f.formatTime(e, 12) : t || ""); }, /** * Formats a value with a default value if null or undefined. * * @param {string|number|object|array} watcher * @param {string|null} defaultValue - Value if original is null or undefined. * @returns {object|array} */ default(i, t = null) { return h(i, (e) => x(e, t)); } }; class v { /** * Creates an instance of ElementScaler. * * @constructor * @param {HTMLElement} element - The DOM element to scale. */ constructor(t) { this.element = t, this.elementBoundingBox = null, this.container = t.parentNode, this.containerSize = { width: 0, height: 0, top: 0, left: 0 }, this.setup(); } /** * Initializes the scaling behavior by removing margins * and triggering a resize check. * * @returns {void} */ setup() { this.removeMargin(), this.resize(); } /** * Removes all margin from the element (top/right/bottom/left). * * @returns {void} */ removeMargin() { this.element.style.margin = "0px 0px 0px 0px"; } /** * Gets the current bounding box of the element. * * @returns {DOMRect|null} The bounding box or null if not set. */ getSize() { return this.elementBoundingBox; } /** * Calculates and caches the bounding client rect for the element. * * @returns {void} */ setBoundingBox() { this.elementBoundingBox = this.element.getBoundingClientRect(); } /** * Applies translation and scaling (width/height) to the element. * * @param {number} x - The horizontal position (left). * @param {number} y - The vertical position (top). * @param {number} scale - Scale factor (e.g., 1.0 = 100%, 0.5 = 50%). * @returns {{width: number, height: number}} The new width and height after scaling. */ transform(t, n, e) { const s = this.element, o = s.style; o.top = n + "px", o.left = t + "px"; const r = s.naturalWidth * e, c = s.naturalHeight * e; return o.width = r + "px", o.height = c + "px", { width: r, height: c }; } /** * Updates internal bounding boxes for both the element and its container. * * @returns {void} */ resize() { this.setBoundingBox(), this.containerSize = this.container.getBoundingClientRect(); } } class D { /** * Creates an EventController instance. * * @constructor * @param {object} parent - The parent object (ImageScaler) that handles actions. * @param {HTMLElement} container - The DOM element to attach events to. */ constructor(t, n) { /** * Tracks and measures distances between touches for pinch gestures. */ g(this, "pinchTracker", { /** @type {number|null} */ previousDistance: null, /** @type {number|null} */ currentDistance: null, /** * Calculates Euclidean distance between two points. * * @param {number} x1 * @param {number} y1 * @param {number} x2 * @param {number} y2 * @returns {number} */ distance(t, n, e, s) { return Math.sqrt( (t - e) * (t - e) + (n - s) * (n - s) ); }, /** * If currentDistance is set, store it as previousDistance. * * @returns {void} */ setPreviousDistance() { this.currentDistance !== null && (this.previousDistance = this.currentDistance); }, /** * Sets the current distance between two touch points. * * @param {object} touch1 * @param {object} touch2 * @returns {void} */ setCurrentDistance(t, n) { this.currentDistance = this.distance(t.x, t.y, n.x, n.y); }, /** * Updates currentDistance and keeps track of the previous distance. * * @param {object} touch1 * @param {object} touch2 * @returns {number} The updated current distance. */ updateCurrentDistance(t, n) { return this.setPreviousDistance(), this.setCurrentDistance(t, n), this.currentDistance; }, /** * Determines the scale direction (zoom in/out) based on distance changes. * * @param {object} touch1 * @param {object} touch2 * @returns {number} 1 for zoom in, -1 for zoom out, 0 if below threshold. */ getScale(t, n) { let e = 0; const s = this.updateCurrentDistance(t, n), o = this.previousDistance; return o === null || Math.abs(s - o) < 2 || (s > o ? e = 1 : s < o && (e = -1)), e; }, /** * Resets the distance measurements. * * @returns {void} */ reset() { this.previousDistance = null, this.currentDistance = null; } }); this.parent = t, this.container = n, this.pointer = { x: 0, y: 0, status: "up" }, this.setup(); } /** * Initializes event setup. * * @returns {void} */ setup() { this.setupEvents(); } /** * Removes all event listeners. * * @returns {void} */ remove() { this.removeEvents(); } /** * Creates and binds all required event listeners. * * @returns {void} */ setupEvents() { const t = this.container, n = l.bind(this, this.pointerMove), e = l.bind(this, this.pointerUp), s = l.bind(this, this.pointerDown), o = l.bind(this, this.wheel), r = l.bind(this, this.resize); this.addEvents = function() { l.on(["mousemove", "touchmove"], t, n), l.on(["mouseup", "mouseout", "touchend", "touchcancel"], t, e), l.on(["mousedown", "touchstart"], t, s), l.onMouseWheel(o, t, !0), l.on("resize", globalThis, r); }, this.addEvents(), this.removeEvents = function() { l.off(["mousemove", "touchmove"], t, n), l.off(["mouseup", "mouseout", "touchend", "touchcancel"], t, e), l.off(["mousedown", "touchstart"], t, s), l.offMouseWheel(o, t), l.off("resize", globalThis, r); }; } /** * Handles mouse wheel or pinch events. * * @param {number} delta - The wheel direction (positive or negative). * @param {Event} e - The associated event. * @returns {void} */ wheel(t, n) { this.parent.callAction("pinch", n, t); } /** * Extracts the position from mouse or touch events and updates `this.pointer`. * * @param {Event} e - The event object. * @returns {void} */ getEventPosition(t) { let n, e; const s = t.touches; if (s && s.length) { const o = s[0]; n = o.clientX, e = o.clientY; } else n = t.clientX, e = t.clientY; this.pointer.x = n, this.pointer.y = e; } /** * Called when the pointer goes down (mouse/touch). * * @param {Event} e - The associated event. * @returns {void} */ pointerDown(t) { this.getEventPosition(t), this.pointer.status = "down", this.isGesture(t) === !1 && this.parent.callAction("pointerDown", t); } /** * Called when the pointer is released. * * @param {Event} e - The associated event. * @returns {void} */ pointerUp(t) { this.pointer.status = "up", this.parent.callAction("pointerUp", t), this.pinchTracker.reset(); } /** * Handles window resize actions. * * @returns {void} */ resize() { this.parent.resize(); } /** * Extracts all touches from the event object. * * @param {Event} e - The touch event. * @returns {Array<object>} Array of touch points. */ getTouches(t) { const n = [], e = t.touches; if (e && e.length) for (let s = 0; s < e.length; s++) n.push(e[s]); return n; } /** * Calculates the midpoint (center) between two sets of coordinates. * * @param {number} x1 * @param {number} y1 * @param {number} x2 * @param {number} y2 * @returns {{x: number, y: number}} The center coordinates. */ getCenter(t, n, e, s) { return { x: (t + e) / 2, y: (n + s) / 2 }; } /** * Attempts a pinch gesture if two touches are present. * * @param {Event} e - The touch event. * @returns {void} */ pinch(t) { const n = this.getTouches(t); if (n.length === 2) { this.pointer.status = "down"; const e = n[0], s = n[1], o = this.getPosition(e.clientX, e.clientY), r = this.getPosition(s.clientX, s.clientY); this.centerMousePinch(e, s); const c = this.pinchTracker.getScale(o, r); this.parent.callAction("pinch", t, c); } } /** * Creates a coordinate object from x/y. * * @param {number} eX * @param {number} eY * @returns {{x: number, y: number}} */ getPosition(t, n) { return { x: parseInt(String(t)), y: parseInt(String(n)) }; } /** * Moves pointer coordinates to the midpoint of two touches for pinch usage. * * @param {Touch} touch1 * @param {Touch} touch2 * @returns {void} */ centerMousePinch(t, n) { const e = this.getCenter( t.clientX, t.clientY, n.clientX, n.clientY ), s = this.pointer; s.x = e.x, s.y = e.y; } /** * Called on a rotate gesture (currently not used). * * @param {Event} e - The associated event. * @returns {void} */ rotate(t) { this.pointer.status = "down", this.parent.callAction("rotate", t); } /** * Checks if the event is a multi-touch gesture. If yes, performs pinch logic. * * @param {Event} e - The event object. * @returns {boolean} True if it was a gesture (pinch); false otherwise. */ isGesture(t) { let n = !1; const e = t.touches; return e && e.length > 1 && (t.preventDefault(), this.pinch(t), n = !0), n; } /** * Called when the pointer moves (mouse/touch) but might also detect pinch. * * @param {Event} e - The associated event. * @returns {void} */ pointerMove(t) { this.getEventPosition(t), this.isGesture(t) === !1 && this.parent.callAction("pointerMove", t); } } class E { /** * Creates an instance of ImageScaler. * * @constructor * @param {HTMLImageElement} element - The image element to scale. */ constructor(t) { /** @type {number} Minimum allowed scale factor. */ g(this, "minScale", 0.2); this.elementScaler = new v(t), this.scale = this.getImageScale(t), this.panning = !1, this.events = null, this.start = { x: 0, y: 0 }, this.delta = { x: 0, y: 0 }, this.setup(); } /** * Initializes event handling and centers the image. * * @returns {void} */ setup() { this.setupEvents(), this.center(); } /** * Removes all event bindings. * * @returns {void} */ remove() { this.events.remove(); } /** * Invokes a method on this class by name, passing event/data. * * @param {string} action - The method name to call. * @param {Event} e - The associated event object. * @param {*} [data] - Additional data passed to the method. * @returns {void} */ callAction(t, n, e) { this[t](n, e); } /** * Sets up the event controller for the image. * * @returns {void} */ setupEvents() { this.events = new D(this, this.elementScaler.element); } /** * Calculates an initial scale based on the element's offsetWidth vs. naturalWidth. * * @param {HTMLImageElement} element - The image element. * @returns {number} The initial scale factor. */ getImageScale(t) { return t.offsetWidth / t.naturalWidth; } /** * Gets the offset position of the pointer, adjusted by scale and delta. * * @param {Event} e - The associated event object. * @returns {{x: number, y: number}} The pointer offset without scale. */ getOffset(t) { const n = this.scale, e = this.delta, s = this.getPointerPosition(); return { x: (s.x - e.x) / n, y: (s.y - e.y) / n }; } /** * Transforms the element (x, y, scale) and then re-centers it if needed. * * @param {number} x - The new left offset. * @param {number} y - The new top offset. * @param {number} scale - The scale factor. * @returns {void} */ scaleElement(t, n, e) { const s = this.elementScaler.transform(t, n, e); this.center(s.width, s.height); } /** * Attempts to center the scaled element within its container, respecting boundaries. * * @param {number} [width] - Width of the scaled element. * @param {number} [height] - Height of the scaled element. * @returns {void} */ center(t, n) { const e = this.elementScaler, s = e.containerSize, o = e.elementBoundingBox, r = this.delta; t = t || o.width, n = n || o.height; let c, a; const p = s.width; if (t < p) c = p / 2 - t / 2, c = c > 0 ? c : 0; else { c = r.x; const u = t + r.x; u < p && (c = u + (p - u) - t), r.x > 0 && (c = 0); } const d = s.height; if (n < d) a = d / 2 - n / 2, a = a > 0 ? a : 0; else { a = r.y; const u = n + r.y; u < d && (a = u + (d - u) - n), r.y > 0 && (a = 0); } const m = e.element.style; m.left = c + "px", m.top = a + "px", this.delta = { x: c, y: a }; } /** * Updates the current scale (zoom) value based on scroll delta. * * @param {number} delta - Positive = zoom in, negative = zoom out. * @returns {number} The updated scale factor. */ updateScale(t) { let n = this.scale; return t !== 0 && (n = t > 0 ? this.scale *= 1.05 : this.scale /= 1.05), n <= this.minScale && (this.scale = this.minScale), this.scale; } /** * Returns the pointer position relative to the container. * * @returns {{x: number, y: number}} The pointer coordinates. */ getPointerPosition() { const t = this.elementScaler.containerSize, n = this.events.pointer; return { x: n.x - t.left, y: n.y - t.top }; } /** * Called when the user presses down on the pointer (mouse/touch). * * @param {Event} e - The associated event object. * @returns {void} */ pointerDown(t) { const n = this.delta, e = this.getPointerPosition(); this.start = { x: e.x - n.x, y: e.y - n.y }, this.panning = !0; } /** * Called when the user moves the pointer (mouse/touch). * * @param {Event} e - The associated event object. * @returns {void} */ pointerMove(t) { if (t.preventDefault(), !this.panning) return; const n = this.getPointerPosition(), e = this.delta, s = this.start; e.x = n.x - s.x, e.y = n.y - s.y, this.scaleElement(e.x, e.y, this.scale); } /** * Called when the user releases the pointer (mouse/touch). * * @param {Event} e - The associated event object. * @returns {void} */ pointerUp(t) { this.panning = !1; } /** * Recalculates container/element bounding sizes, e.g., on window resize. * * @returns {void} */ resize() { this.elementScaler.resize(); } /** * Called on pinch gesture (usually from a wheel or multi-touch). * * @param {Event} e - The associated event. * @param {number} delta - Positive = zoom in, negative = zoom out. * @returns {void} */ pinch(t, n) { const e = this.getOffset(t); t.preventDefault(); const s = this.updateScale(n), o = this.getPointerPosition(), r = this.delta; r.x = o.x - e.x * s, r.y = o.y - e.y * s, this.scaleElement(r.x, r.y, s); } } export { z as F, E as I, h as c };