@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
JavaScript
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
};