@withl5e/l5e 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +24 -0
- package/dist/action.js +10 -0
- package/dist/action.js.map +1 -0
- package/dist/client-D67hK4Yy.js +9 -0
- package/dist/client-D67hK4Yy.js.map +1 -0
- package/dist/entry-server-Ckh6zfgm.js +258 -0
- package/dist/entry-server-Ckh6zfgm.js.map +1 -0
- package/dist/entry-server.js +12 -0
- package/dist/entry-server.js.map +1 -0
- package/dist/generateMetadata-C5QsMS-H.js +144 -0
- package/dist/generateMetadata-C5QsMS-H.js.map +1 -0
- package/dist/index-BIt7MJT9.js +163 -0
- package/dist/index-BIt7MJT9.js.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/island/client.js +5 -0
- package/dist/island/client.js.map +1 -0
- package/dist/island/runtime.js +98 -0
- package/dist/island/runtime.js.map +1 -0
- package/dist/island.js +39 -0
- package/dist/island.js.map +1 -0
- package/dist/jsx-runtime-C2Vw67N2.js +256 -0
- package/dist/jsx-runtime-C2Vw67N2.js.map +1 -0
- package/dist/jsx-runtime.js +26 -0
- package/dist/jsx-runtime.js.map +1 -0
- package/dist/middleware.js +9 -0
- package/dist/middleware.js.map +1 -0
- package/dist/seo.js +7 -0
- package/dist/seo.js.map +1 -0
- package/dist/server.js +489 -0
- package/dist/server.js.map +1 -0
- package/dist/swap/server.js +15 -0
- package/dist/swap/server.js.map +1 -0
- package/dist/swap.js +121 -0
- package/dist/swap.js.map +1 -0
- package/dist/tooltip.js +129 -0
- package/dist/tooltip.js.map +1 -0
- package/dist/vite-plugin.js +381 -0
- package/dist/vite-plugin.js.map +1 -0
- package/index.ts +1 -0
- package/package.json +129 -0
- package/src/action/define-action.ts +8 -0
- package/src/action/index.ts +2 -0
- package/src/action/types.ts +21 -0
- package/src/core/bundler.ts +275 -0
- package/src/core/const.ts +2 -0
- package/src/core/entry-server.d.ts +1 -0
- package/src/core/entry-server.ts +381 -0
- package/src/core/exceptions.ts +80 -0
- package/src/core/head-priority.ts +15 -0
- package/src/core/index.ts +40 -0
- package/src/core/jsx-runtime.ts +325 -0
- package/src/core/jsx-types.d.ts +548 -0
- package/src/core/render.ts +181 -0
- package/src/core/request.ts +31 -0
- package/src/core/server.ts +740 -0
- package/src/core/vite-plugin.ts +779 -0
- package/src/island/ClientIsland.ts +71 -0
- package/src/island/client.ts +3 -0
- package/src/island/index.ts +3 -0
- package/src/island/runtime.ts +149 -0
- package/src/island/strategy-registry.ts +10 -0
- package/src/island/types.ts +28 -0
- package/src/middleware/defineMiddleware.ts +5 -0
- package/src/middleware/index.ts +133 -0
- package/src/middleware/sequence.ts +105 -0
- package/src/middleware/types.ts +28 -0
- package/src/seo/generateMetadata.tsx +559 -0
- package/src/seo/index.ts +10 -0
- package/src/seo/mergeMetadata.ts +200 -0
- package/src/seo/types.ts +316 -0
- package/src/swap/SwapResponse.tsx +16 -0
- package/src/swap/create-swap.ts +121 -0
- package/src/swap/index.ts +8 -0
- package/src/swap/parse.ts +12 -0
- package/src/swap/server.ts +1 -0
- package/src/swap/swap.ts +57 -0
- package/src/swap/types.ts +47 -0
- package/src/swap/utils.ts +7 -0
- package/src/tooltip/index.ts +2 -0
- package/src/tooltip/tooltip-loader.ts +108 -0
- package/src/tooltip/tooltip-runtime.ts +173 -0
- package/types.d.ts +14 -0
package/dist/swap.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
function b(e, n) {
|
|
2
|
+
const a = document.createElement("template");
|
|
3
|
+
a.innerHTML = e;
|
|
4
|
+
const s = a.content;
|
|
5
|
+
return n ? Array.from(s.querySelectorAll(n)) : s;
|
|
6
|
+
}
|
|
7
|
+
function S(e, n, a) {
|
|
8
|
+
switch (a) {
|
|
9
|
+
case "innerHTML":
|
|
10
|
+
e.innerHTML = "";
|
|
11
|
+
const s = [];
|
|
12
|
+
for (const r of n) {
|
|
13
|
+
const i = e.appendChild(r);
|
|
14
|
+
s.push(i);
|
|
15
|
+
}
|
|
16
|
+
return s;
|
|
17
|
+
case "outerHTML": {
|
|
18
|
+
const r = e.parentNode;
|
|
19
|
+
if (!r) return [];
|
|
20
|
+
const i = [];
|
|
21
|
+
for (const c of n)
|
|
22
|
+
r.insertBefore(c, e), i.push(c);
|
|
23
|
+
return r.removeChild(e), i;
|
|
24
|
+
}
|
|
25
|
+
case "beforebegin": {
|
|
26
|
+
const r = e.parentNode;
|
|
27
|
+
return r ? n.map((i) => r.insertBefore(i, e)) : [];
|
|
28
|
+
}
|
|
29
|
+
case "afterbegin": {
|
|
30
|
+
const r = e.firstChild;
|
|
31
|
+
return n.map((i) => e.insertBefore(i, r));
|
|
32
|
+
}
|
|
33
|
+
case "beforeend":
|
|
34
|
+
return n.map((r) => e.appendChild(r));
|
|
35
|
+
case "afterend": {
|
|
36
|
+
const r = e.parentNode;
|
|
37
|
+
if (!r) return [];
|
|
38
|
+
const i = e.nextSibling;
|
|
39
|
+
return n.map((c) => r.insertBefore(c, i));
|
|
40
|
+
}
|
|
41
|
+
case "delete":
|
|
42
|
+
return e.remove(), [];
|
|
43
|
+
case "none":
|
|
44
|
+
return [];
|
|
45
|
+
default:
|
|
46
|
+
return [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function N(e, n) {
|
|
50
|
+
let a;
|
|
51
|
+
return ((...s) => {
|
|
52
|
+
clearTimeout(a), a = setTimeout(() => e(...s), n);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
const w = "l5e-request";
|
|
56
|
+
function A(e) {
|
|
57
|
+
const n = e.tagName.toLowerCase();
|
|
58
|
+
return n === "form" ? "submit" : n === "input" || n === "textarea" || n === "select" ? "change" : "click";
|
|
59
|
+
}
|
|
60
|
+
function x(e) {
|
|
61
|
+
let n = !1;
|
|
62
|
+
const a = e.swap ?? "innerHTML", s = e.trigger ? Array.from(document.querySelectorAll(e.trigger)) : [], r = document.querySelector(e.target), i = s[0] ?? null, c = e.event ?? (i ? A(i) : "click"), H = c === "submit", l = e.loading, y = l?.class ?? w;
|
|
63
|
+
function M(o) {
|
|
64
|
+
if (!l) return () => {
|
|
65
|
+
};
|
|
66
|
+
const t = l.target ? document.querySelector(l.target) : o;
|
|
67
|
+
if (!t) return () => {
|
|
68
|
+
};
|
|
69
|
+
const u = l.disabled ?? t === o, m = t.innerHTML, g = t.disabled ?? !1;
|
|
70
|
+
return t.classList.add(y), l.html && (t.innerHTML = l.html), u && "disabled" in t && (t.disabled = !0), () => {
|
|
71
|
+
t.classList.remove(y), l.html && t !== r && (t.innerHTML = m), u && "disabled" in t && (t.disabled = g);
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
async function E(o) {
|
|
75
|
+
if (n || !r) return;
|
|
76
|
+
const t = o instanceof HTMLElement ? o : i, u = {
|
|
77
|
+
triggerElement: t,
|
|
78
|
+
triggerDataset: t?.dataset ?? {}
|
|
79
|
+
}, m = typeof o == "string" ? o : void 0;
|
|
80
|
+
if (e.onBefore?.(u) === !1) return;
|
|
81
|
+
const g = M(t);
|
|
82
|
+
try {
|
|
83
|
+
let f, h;
|
|
84
|
+
if (e.action)
|
|
85
|
+
h = await e.action(u), f = await h.text();
|
|
86
|
+
else if (m !== void 0)
|
|
87
|
+
f = m;
|
|
88
|
+
else
|
|
89
|
+
return;
|
|
90
|
+
let d;
|
|
91
|
+
e.select ? d = b(f, e.select) : d = Array.from(b(f).childNodes);
|
|
92
|
+
const L = { root: b(f).firstElementChild, response: h };
|
|
93
|
+
if (e.onNodes) {
|
|
94
|
+
const v = e.onNodes(d, L);
|
|
95
|
+
Array.isArray(v) && (d = v);
|
|
96
|
+
}
|
|
97
|
+
const p = S(r, d, a);
|
|
98
|
+
e.onSwap?.(p, L), e.onAfter?.(p, L, u);
|
|
99
|
+
} catch (f) {
|
|
100
|
+
e.onError?.(f, f.status);
|
|
101
|
+
} finally {
|
|
102
|
+
g();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function T(o) {
|
|
106
|
+
H && o.preventDefault(), E(o.currentTarget ?? void 0);
|
|
107
|
+
}
|
|
108
|
+
return s.forEach((o) => o.addEventListener(c, T)), {
|
|
109
|
+
exec: E,
|
|
110
|
+
destroy() {
|
|
111
|
+
n = !0, s.forEach((o) => o.removeEventListener(c, T));
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export {
|
|
116
|
+
x as createSwap,
|
|
117
|
+
N as debounce,
|
|
118
|
+
b as parseHTML,
|
|
119
|
+
S as swap
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=swap.js.map
|
package/dist/swap.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swap.js","sources":["../src/swap/parse.ts","../src/swap/swap.ts","../src/swap/utils.ts","../src/swap/create-swap.ts"],"sourcesContent":["export function parseHTML(html: string): DocumentFragment;\nexport function parseHTML(html: string, selector: string): Element[];\nexport function parseHTML(html: string, selector?: string): DocumentFragment | Element[] {\n const tpl = document.createElement('template');\n tpl.innerHTML = html;\n const fragment = tpl.content;\n\n if (selector) {\n return Array.from(fragment.querySelectorAll(selector));\n }\n return fragment;\n}\n","import type { SwapMode } from './types';\n\nexport function swap(target: Element, nodes: Node[], mode: SwapMode): Node[] {\n switch (mode) {\n case 'innerHTML':\n target.innerHTML = '';\n const innerInserted: Node[] = [];\n for (const n of nodes) {\n const added = target.appendChild(n);\n innerInserted.push(added);\n }\n return innerInserted;\n\n case 'outerHTML': {\n const parent = target.parentNode;\n if (!parent) return [];\n const outerInserted: Node[] = [];\n for (const n of nodes) {\n parent.insertBefore(n, target);\n outerInserted.push(n);\n }\n parent.removeChild(target);\n return outerInserted;\n }\n\n case 'beforebegin': {\n const parent = target.parentNode;\n if (!parent) return [];\n return nodes.map((n) => parent.insertBefore(n, target));\n }\n\n case 'afterbegin': {\n const first = target.firstChild;\n return nodes.map((n) => target.insertBefore(n, first));\n }\n\n case 'beforeend':\n return nodes.map((n) => target.appendChild(n));\n\n case 'afterend': {\n const parent = target.parentNode;\n if (!parent) return [];\n const ref = target.nextSibling;\n return nodes.map((n) => parent.insertBefore(n, ref));\n }\n\n case 'delete':\n target.remove();\n return [];\n\n case 'none':\n return [];\n\n default:\n return [];\n }\n}\n","export function debounce<T extends (...args: any[]) => void>(fn: T, ms: number): T {\n let timer: ReturnType<typeof setTimeout>;\n return ((...args: any[]) => {\n clearTimeout(timer);\n timer = setTimeout(() => fn(...args), ms);\n }) as unknown as T;\n}\n","import { parseHTML } from './parse';\nimport { swap } from './swap';\nimport type { SwapOptions, SwapInstance, SwapMeta, SwapContext } from './types';\n\nconst REQUEST_CLASS = 'l5e-request';\n\nfunction getNaturalEvent(el: Element): string {\n const tag = el.tagName.toLowerCase();\n if (tag === 'form') return 'submit';\n if (tag === 'input' || tag === 'textarea' || tag === 'select') return 'change';\n return 'click';\n}\n\nexport function createSwap(opts: SwapOptions): SwapInstance {\n let destroyed = false;\n const swapMode = opts.swap ?? 'innerHTML';\n\n const triggerEls = opts.trigger\n ? Array.from(document.querySelectorAll<HTMLElement>(opts.trigger))\n : [];\n\n const targetEl = document.querySelector<HTMLElement>(opts.target);\n\n const firstTrigger = triggerEls[0] ?? null;\n const eventName = opts.event ?? (firstTrigger ? getNaturalEvent(firstTrigger) : 'click');\n const shouldPreventDefault = eventName === 'submit';\n\n const loadingCfg = opts.loading;\n const loadingClass = loadingCfg?.class ?? REQUEST_CLASS;\n\n function applyLoading(currentTrigger: HTMLElement | null): () => void {\n if (!loadingCfg) return () => {};\n const el = loadingCfg.target\n ? document.querySelector<HTMLElement>(loadingCfg.target)\n : currentTrigger;\n if (!el) return () => {};\n\n const shouldDisable = loadingCfg.disabled ?? el === currentTrigger;\n const originalHtml = el.innerHTML;\n const originalDisabled = (el as HTMLButtonElement).disabled ?? false;\n\n el.classList.add(loadingClass);\n if (loadingCfg.html) el.innerHTML = loadingCfg.html;\n if (shouldDisable && 'disabled' in el) (el as any).disabled = true;\n\n return () => {\n el.classList.remove(loadingClass);\n if (loadingCfg.html && el !== targetEl) el.innerHTML = originalHtml;\n if (shouldDisable && 'disabled' in el) (el as any).disabled = originalDisabled;\n };\n }\n\n async function exec(input?: HTMLElement | string): Promise<void> {\n if (destroyed) return;\n\n if (!targetEl) return;\n\n const ctxEl = input instanceof HTMLElement ? input : firstTrigger;\n const ctx: SwapContext = {\n triggerElement: ctxEl,\n triggerDataset: ctxEl?.dataset ?? ({} as DOMStringMap),\n };\n const htmlInput = typeof input === 'string' ? input : undefined;\n\n if (opts.onBefore?.(ctx) === false) return;\n\n const restoreLoading = applyLoading(ctxEl);\n\n try {\n let html: string;\n let res: Response | undefined;\n if (opts.action) {\n res = await opts.action(ctx);\n html = await res.text();\n } else if (htmlInput !== undefined) {\n html = htmlInput;\n } else {\n return;\n }\n\n let nodes: Node[];\n if (opts.select) {\n nodes = parseHTML(html, opts.select) as Node[];\n } else {\n nodes = Array.from(parseHTML(html).childNodes);\n }\n\n const root = parseHTML(html).firstElementChild as HTMLElement | null;\n const meta: SwapMeta = { root, response: res };\n\n if (opts.onNodes) {\n const t = opts.onNodes(nodes, meta);\n if (Array.isArray(t)) nodes = t;\n }\n\n const inserted = swap(targetEl, nodes, swapMode);\n\n opts.onSwap?.(inserted, meta);\n opts.onAfter?.(inserted, meta, ctx);\n } catch (err: any) {\n opts.onError?.(err, err.status);\n } finally {\n restoreLoading();\n }\n }\n\n function handleTrigger(e: Event) {\n if (shouldPreventDefault) e.preventDefault();\n exec((e.currentTarget as HTMLElement) ?? undefined);\n }\n\n triggerEls.forEach((el) => el.addEventListener(eventName, handleTrigger));\n\n return {\n exec,\n destroy() {\n destroyed = true;\n triggerEls.forEach((el) => el.removeEventListener(eventName, handleTrigger));\n },\n };\n}\n"],"names":["parseHTML","html","selector","tpl","fragment","swap","target","nodes","mode","innerInserted","n","added","parent","outerInserted","first","ref","debounce","fn","ms","timer","args","REQUEST_CLASS","getNaturalEvent","el","tag","createSwap","opts","destroyed","swapMode","triggerEls","targetEl","firstTrigger","eventName","shouldPreventDefault","loadingCfg","loadingClass","applyLoading","currentTrigger","shouldDisable","originalHtml","originalDisabled","exec","input","ctxEl","ctx","htmlInput","restoreLoading","res","meta","t","inserted","err","handleTrigger","e"],"mappings":"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"}
|
package/dist/tooltip.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { autoUpdate as u, computePosition as f, offset as h, flip as v, shift as b, hide as y } from "@floating-ui/dom";
|
|
2
|
+
function w() {
|
|
3
|
+
const n = navigator.userAgent.toLowerCase(), e = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini|mobile|tablet/i.test(n), t = window.matchMedia("(max-width: 768px)").matches;
|
|
4
|
+
return e || t;
|
|
5
|
+
}
|
|
6
|
+
function T() {
|
|
7
|
+
const n = document.querySelectorAll("[data-tooltip-id]");
|
|
8
|
+
if (n.length === 0) return;
|
|
9
|
+
const e = w();
|
|
10
|
+
n.forEach((t) => {
|
|
11
|
+
t.hasAttribute("data-tooltip-initialized") || (t.setAttribute("data-tooltip-initialized", "true"), e ? t.addEventListener("click", async (o) => {
|
|
12
|
+
o.preventDefault();
|
|
13
|
+
const { showTooltipMobile: i } = await Promise.resolve().then(() => m);
|
|
14
|
+
i(t);
|
|
15
|
+
}) : t.addEventListener(
|
|
16
|
+
"pointerenter",
|
|
17
|
+
async () => {
|
|
18
|
+
const { showTooltip: o } = await Promise.resolve().then(() => m);
|
|
19
|
+
o(t);
|
|
20
|
+
},
|
|
21
|
+
{ passive: !0 }
|
|
22
|
+
));
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
function M() {
|
|
26
|
+
if (window.__tooltipObserver) return;
|
|
27
|
+
const n = new MutationObserver((e) => {
|
|
28
|
+
let t = !1;
|
|
29
|
+
e.forEach((o) => {
|
|
30
|
+
o.type === "childList" && o.addedNodes.length > 0 && o.addedNodes.forEach((i) => {
|
|
31
|
+
if (i.nodeType === Node.ELEMENT_NODE) {
|
|
32
|
+
const l = i;
|
|
33
|
+
(l.hasAttribute("data-tooltip-id") || l.querySelector("[data-tooltip-id]")) && (t = !0);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}), t && T();
|
|
37
|
+
});
|
|
38
|
+
n.observe(document.body, {
|
|
39
|
+
childList: !0,
|
|
40
|
+
subtree: !0
|
|
41
|
+
}), window.__tooltipObserver = n;
|
|
42
|
+
}
|
|
43
|
+
async function L(n) {
|
|
44
|
+
if (!n.matches(":hover")) return;
|
|
45
|
+
const e = document.createElement("div");
|
|
46
|
+
e.className = "tp", e.innerHTML = '<div class="tp-loading">Loading...</div>', document.body.append(e);
|
|
47
|
+
let t = null;
|
|
48
|
+
const o = () => {
|
|
49
|
+
t && (t(), t = null), document.body.contains(e) && e.remove();
|
|
50
|
+
};
|
|
51
|
+
if (n.addEventListener("pointerleave", o), !n.matches(":hover")) {
|
|
52
|
+
n.removeEventListener("pointerleave", o), e.remove();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
t = u(n, e, () => p(n, e));
|
|
56
|
+
try {
|
|
57
|
+
const { tooltipId: i, tooltipType: l } = n.dataset, c = await fetch(`/tooltip/${l}/${i}`, {
|
|
58
|
+
headers: { Accept: "text/html" }
|
|
59
|
+
}).then((a) => a.text());
|
|
60
|
+
if (!document.body.contains(e)) return;
|
|
61
|
+
e.innerHTML = c, await p(n, e);
|
|
62
|
+
} catch {
|
|
63
|
+
document.body.contains(e) && (e.innerHTML = '<div class="tp-error">Không thể tải tooltip</div>');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function E(n) {
|
|
67
|
+
const e = document.createElement("div");
|
|
68
|
+
e.className = "tp-overlay";
|
|
69
|
+
const t = document.createElement("div");
|
|
70
|
+
t.className = "tp-mobile", t.innerHTML = '<div class="tp-loading">Loading...</div>', e.append(t), document.body.append(e), document.body.style.overflow = "hidden";
|
|
71
|
+
const o = () => {
|
|
72
|
+
e.remove(), document.body.style.overflow = "";
|
|
73
|
+
};
|
|
74
|
+
e.addEventListener("click", (i) => {
|
|
75
|
+
i.target === e && o();
|
|
76
|
+
});
|
|
77
|
+
try {
|
|
78
|
+
const { tooltipId: i, tooltipType: l } = n.dataset, c = await fetch(`/tooltip/${l}/${i}`, {
|
|
79
|
+
headers: { Accept: "text/html" }
|
|
80
|
+
}).then((r) => r.text());
|
|
81
|
+
t.innerHTML = "";
|
|
82
|
+
const a = document.createElement("button");
|
|
83
|
+
a.className = "tp-mobile-close", a.innerHTML = "✕", a.addEventListener("click", o), t.append(a);
|
|
84
|
+
const d = document.createElement("div");
|
|
85
|
+
d.className = "tp-mobile-content", d.innerHTML = c, t.append(d);
|
|
86
|
+
const s = n.dataset.href;
|
|
87
|
+
if (s) {
|
|
88
|
+
const r = document.createElement("a");
|
|
89
|
+
r.className = "tp-mobile-link", r.href = s, r.textContent = "Xem chi tiết", t.append(r);
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
t.innerHTML = '<div class="tp-error">Không thể tải tooltip</div>';
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function g() {
|
|
96
|
+
return {
|
|
97
|
+
name: "centerFallback",
|
|
98
|
+
fn({ rects: n }) {
|
|
99
|
+
const e = window.innerHeight, t = 8;
|
|
100
|
+
return n.floating.height <= e * 0.6 ? {} : {
|
|
101
|
+
y: window.scrollY + Math.max(t, (e - n.floating.height) / 2),
|
|
102
|
+
data: { centered: !0, maxHeight: e - t * 2 }
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function p(n, e) {
|
|
108
|
+
const o = n.dataset.tooltipPlacement ?? "left";
|
|
109
|
+
return f(n, e, {
|
|
110
|
+
placement: o,
|
|
111
|
+
middleware: [h(6), v(), b({ padding: 5 }), g(), y()]
|
|
112
|
+
}).then(({ x: i, y: l, middlewareData: c }) => {
|
|
113
|
+
Object.assign(e.style, { left: `${i}px`, top: `${l}px` }), e.style.visibility = c.hide?.referenceHidden ? "hidden" : "visible";
|
|
114
|
+
const a = c.centerFallback;
|
|
115
|
+
a?.centered ? (e.style.maxHeight = `${a.maxHeight}px`, e.style.overflowY = "auto") : (e.style.maxHeight = "", e.style.overflowY = "");
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
const m = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
119
|
+
__proto__: null,
|
|
120
|
+
showTooltip: L,
|
|
121
|
+
showTooltipMobile: E
|
|
122
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
123
|
+
export {
|
|
124
|
+
T as initTooltips,
|
|
125
|
+
M as setupTooltipObserver,
|
|
126
|
+
L as showTooltip,
|
|
127
|
+
E as showTooltipMobile
|
|
128
|
+
};
|
|
129
|
+
//# sourceMappingURL=tooltip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tooltip.js","sources":["../src/tooltip/tooltip-loader.ts","../src/tooltip/tooltip-runtime.ts"],"sourcesContent":["// tooltip-loader.ts\n// File này chỉ làm nhiệm vụ wake up tooltip khi cần thiết\n\n// Định nghĩa kiểu cho tooltip host element\ntype TooltipHost = HTMLElement & {\n dataset: {\n tooltipId: string;\n tooltipType?: string;\n };\n};\n\n/**\n * Kiểm tra xem thiết bị hiện tại có phải là thiết bị di động hay không\n * @returns true nếu là thiết bị di động\n */\nfunction isMobileDevice(): boolean {\n // Kiểm tra User Agent\n const userAgent = navigator.userAgent.toLowerCase();\n const isMobile =\n /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini|mobile|tablet/i.test(userAgent);\n\n // Kiểm tra thêm kích thước màn hình (dưới 768px thường được coi là thiết bị di động)\n const isTouchScreen = window.matchMedia('(max-width: 768px)').matches;\n\n return isMobile || isTouchScreen;\n}\n\n/**\n * Hàm khởi tạo tooltip, chỉ load runtime khi cần thiết\n */\nexport function initTooltips(): void {\n const tooltipTriggers = document.querySelectorAll<TooltipHost>('[data-tooltip-id]');\n if (tooltipTriggers.length === 0) return;\n\n const mobile = isMobileDevice();\n\n tooltipTriggers.forEach((trigger) => {\n if (trigger.hasAttribute('data-tooltip-initialized')) return;\n trigger.setAttribute('data-tooltip-initialized', 'true');\n\n if (mobile) {\n trigger.addEventListener('click', async (e) => {\n e.preventDefault();\n const { showTooltipMobile } = await import('./tooltip-runtime');\n showTooltipMobile(trigger);\n });\n } else {\n trigger.addEventListener(\n 'pointerenter',\n async () => {\n const { showTooltip } = await import('./tooltip-runtime');\n showTooltip(trigger);\n },\n { passive: true },\n );\n }\n });\n}\n\n// Thêm một MutationObserver để theo dõi các phần tử DOM mới (cho React components)\nexport function setupTooltipObserver(): void {\n // Kiểm tra xem đã có observer chưa\n if (window.__tooltipObserver) return;\n\n // Observer theo dõi khi có phần tử mới được thêm vào DOM\n const observer = new MutationObserver((mutations) => {\n let shouldInit = false;\n\n mutations.forEach((mutation) => {\n if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {\n // Kiểm tra xem có phần tử tooltip nào mới được thêm vào không\n mutation.addedNodes.forEach((node) => {\n if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as HTMLElement;\n // Kiểm tra nếu phần tử có data-tooltip-id hoặc chứa phần tử con có data-tooltip-id\n if (\n element.hasAttribute('data-tooltip-id') ||\n element.querySelector('[data-tooltip-id]')\n ) {\n shouldInit = true;\n }\n }\n });\n }\n });\n\n // Chỉ gọi initTooltips nếu có phát hiện tooltip mới\n if (shouldInit) {\n initTooltips();\n }\n });\n\n // Bắt đầu theo dõi toàn bộ DOM\n observer.observe(document.body, {\n childList: true,\n subtree: true,\n });\n\n // Lưu observer vào window để tránh tạo nhiều observer\n window.__tooltipObserver = observer;\n}\n\n// Typescript declaration cho window object\ndeclare global {\n interface Window {\n __tooltipObserver?: MutationObserver;\n }\n}\n","// tooltip-runtime.ts\n// Logic đầy đủ cho tooltip, chỉ được tải khi cần thiết\n\nimport type { Middleware, Placement } from '@floating-ui/dom';\nimport { autoUpdate, computePosition, flip, hide, offset, shift } from '@floating-ui/dom';\n\ntype TooltipHost = HTMLElement & {\n dataset: {\n tooltipId: string;\n tooltipType?: string;\n tooltipPlacement?: Placement;\n href?: string;\n };\n};\n\nexport async function showTooltip(host: TooltipHost): Promise<void> {\n /*\n * Trong lúc chờ dynamic import, con trỏ có thể đã rời khỏi phần tử host.\n * Nếu vậy, việc tạo tooltip là không cần thiết và sẽ không có sự kiện\n * `pointerleave` nào được kích hoạt để ẩn tooltip.\n */\n if (!host.matches(':hover')) return;\n\n const tip = document.createElement('div');\n tip.className = 'tp';\n tip.innerHTML = '<div class=\"tp-loading\">Loading...</div>';\n document.body.append(tip);\n\n // --- Cleanup ---\n let cleanupAutoUpdate: (() => void) | null = null;\n\n const cleanup = () => {\n if (cleanupAutoUpdate) {\n cleanupAutoUpdate();\n cleanupAutoUpdate = null;\n }\n if (document.body.contains(tip)) tip.remove();\n };\n\n host.addEventListener('pointerleave', cleanup);\n\n // Con trỏ đã rời trước khi listener được gắn\n if (!host.matches(':hover')) {\n host.removeEventListener('pointerleave', cleanup);\n tip.remove();\n return;\n }\n\n // --- Position: autoUpdate thay thế manual scroll/resize ---\n cleanupAutoUpdate = autoUpdate(host, tip, () => position(host, tip));\n\n // --- Fetch nội dung từ server ---\n try {\n const { tooltipId: id, tooltipType: type } = host.dataset;\n const html = await fetch(`/tooltip/${type}/${id}`, {\n headers: { Accept: 'text/html' },\n }).then((r) => r.text());\n\n if (!document.body.contains(tip)) return;\n\n tip.innerHTML = html;\n await position(host, tip);\n } catch {\n if (document.body.contains(tip)) {\n tip.innerHTML = '<div class=\"tp-error\">Không thể tải tooltip</div>';\n }\n }\n}\n\n/**\n * Mobile: hiển thị tooltip dạng fullscreen popup.\n * Nếu host có data-href, thêm nút \"Xem chi tiết\" dẫn đến link đó.\n */\nexport async function showTooltipMobile(host: TooltipHost): Promise<void> {\n const overlay = document.createElement('div');\n overlay.className = 'tp-overlay';\n\n const popup = document.createElement('div');\n popup.className = 'tp-mobile';\n popup.innerHTML = '<div class=\"tp-loading\">Loading...</div>';\n\n overlay.append(popup);\n document.body.append(overlay);\n document.body.style.overflow = 'hidden';\n\n const cleanup = () => {\n overlay.remove();\n document.body.style.overflow = '';\n };\n\n // Đóng khi bấm vào overlay (bên ngoài popup)\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) cleanup();\n });\n\n try {\n const { tooltipId: id, tooltipType: type } = host.dataset;\n const html = await fetch(`/tooltip/${type}/${id}`, {\n headers: { Accept: 'text/html' },\n }).then((r) => r.text());\n\n popup.innerHTML = '';\n\n // Nút đóng\n const closeBtn = document.createElement('button');\n closeBtn.className = 'tp-mobile-close';\n closeBtn.innerHTML = '✕';\n closeBtn.addEventListener('click', cleanup);\n popup.append(closeBtn);\n\n // Nội dung\n const content = document.createElement('div');\n content.className = 'tp-mobile-content';\n content.innerHTML = html;\n popup.append(content);\n\n // Nút \"Xem chi tiết\" nếu có data-href\n const href = host.dataset.href;\n if (href) {\n const link = document.createElement('a');\n link.className = 'tp-mobile-link';\n link.href = href;\n link.textContent = 'Xem chi tiết';\n popup.append(link);\n }\n } catch {\n popup.innerHTML = '<div class=\"tp-error\">Không thể tải tooltip</div>';\n }\n}\n\n/**\n * Khi tooltip cao hơn 60% viewport, bỏ qua placement trên/dưới host\n * và căn giữa theo chiều dọc màn hình + giới hạn maxHeight để scroll.\n */\nfunction centerFallback(): Middleware {\n return {\n name: 'centerFallback',\n fn({ rects }) {\n const vh = window.innerHeight;\n const padding = 8;\n\n if (rects.floating.height <= vh * 0.6) return {};\n\n return {\n y: window.scrollY + Math.max(padding, (vh - rects.floating.height) / 2),\n data: { centered: true, maxHeight: vh - padding * 2 },\n };\n },\n };\n}\n\nfunction position(host: TooltipHost, tip: HTMLDivElement): Promise<void> {\n const preferredPlacement = host.dataset.tooltipPlacement;\n const placement = preferredPlacement ?? 'left';\n\n return computePosition(host, tip, {\n placement,\n middleware: [offset(6), flip(), shift({ padding: 5 }), centerFallback(), hide()],\n }).then(({ x, y, middlewareData }) => {\n Object.assign(tip.style, { left: `${x}px`, top: `${y}px` });\n\n tip.style.visibility = middlewareData.hide?.referenceHidden ? 'hidden' : 'visible';\n\n const center = middlewareData.centerFallback as { centered?: boolean; maxHeight?: number };\n if (center?.centered) {\n tip.style.maxHeight = `${center.maxHeight}px`;\n tip.style.overflowY = 'auto';\n } else {\n tip.style.maxHeight = '';\n tip.style.overflowY = '';\n }\n });\n}\n"],"names":["isMobileDevice","userAgent","isMobile","isTouchScreen","initTooltips","tooltipTriggers","mobile","trigger","e","showTooltipMobile","tooltipRuntime","showTooltip","setupTooltipObserver","observer","mutations","shouldInit","mutation","node","element","host","tip","cleanupAutoUpdate","cleanup","autoUpdate","position","id","type","html","r","overlay","popup","closeBtn","content","href","link","centerFallback","rects","vh","padding","placement","computePosition","offset","flip","shift","hide","x","y","middlewareData","center"],"mappings":"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"}
|