1 | export const last = (v = []) => v[v.length - 1]
|
2 |
|
3 | export const isWhitespace = (node) => {
|
4 | return node.nodeType === node.TEXT_NODE && node.nodeValue.trim() === ""
|
5 | }
|
6 |
|
7 | export const walk = (node, callback, deep = true) => {
|
8 | if (!node) return
|
9 | if (!isWhitespace(node)) {
|
10 | let v = callback(node)
|
11 | if (v === false) return
|
12 | if (v?.nodeName) return walk(v, callback, deep)
|
13 | }
|
14 | if (deep) walk(node.firstChild, callback, deep)
|
15 | walk(node.nextSibling, callback, deep)
|
16 | }
|
17 |
|
18 | const transformBrackets = (str = "") => {
|
19 | let parts = str.split(/(\[[^\]]+\])/).filter((v) => v)
|
20 | return parts.reduce((a, part) => {
|
21 | let v = part.charAt(0) === "[" ? "." + part.replace(/\./g, ":") : part
|
22 | return a + v
|
23 | }, "")
|
24 | }
|
25 |
|
26 | const getTarget = (path, target) => {
|
27 | let parts = transformBrackets(path)
|
28 | .split(".")
|
29 | .map((k) => {
|
30 | if (k.charAt(0) === "[") {
|
31 | let p = k.slice(1, -1).replace(/:/g, ".")
|
32 | return getValueAtPath(p, target)
|
33 | } else {
|
34 | return k
|
35 | }
|
36 | })
|
37 |
|
38 | let t =
|
39 | parts.slice(0, -1).reduce((o, k) => {
|
40 | return o && o[k]
|
41 | }, target) || target
|
42 | return [t, last(parts)]
|
43 | }
|
44 |
|
45 | export const getValueAtPath = (path, target) => {
|
46 | let [a, b] = getTarget(path, target)
|
47 | let v = a?.[b]
|
48 | if (typeof v === "function") return v.bind(a)
|
49 | return v
|
50 | }
|
51 |
|
52 | export const fragmentFromTemplate = (v) => {
|
53 | if (typeof v === "string") {
|
54 | let tpl = document.createElement("template")
|
55 | tpl.innerHTML = v.trim()
|
56 | return tpl.content
|
57 | }
|
58 | if (v.nodeName === "TEMPLATE") return v.cloneNode(true).content
|
59 | if (v.nodeName === "defs") return v.firstElementChild.cloneNode(true)
|
60 | }
|
61 |
|
62 | export const debounce = (fn) => {
|
63 | let wait = false
|
64 | let invoke = false
|
65 | return () => {
|
66 | if (wait) {
|
67 | invoke = true
|
68 | } else {
|
69 | wait = true
|
70 | fn()
|
71 | requestAnimationFrame(() => {
|
72 | if (invoke) fn()
|
73 | wait = false
|
74 | })
|
75 | }
|
76 | }
|
77 | }
|
78 |
|
79 | export const isPrimitive = (v) => v === null || typeof v !== "object"
|
80 |
|
81 | export const typeOf = (v) =>
|
82 | Object.prototype.toString.call(v).match(/\s(.+[^\]])/)[1]
|
83 |
|
84 | export const pascalToKebab = (string) =>
|
85 | string.replace(/[\w]([A-Z])/g, function (m) {
|
86 | return m[0] + "-" + m[1].toLowerCase()
|
87 | })
|
88 |
|
89 | export const kebabToPascal = (string) =>
|
90 | string.replace(/[\w]-([\w])/g, function (m) {
|
91 | return m[0] + m[2].toUpperCase()
|
92 | })
|
93 |
|
94 | export const applyAttribute = (node, name, value) => {
|
95 | name = pascalToKebab(name)
|
96 |
|
97 | if (typeof value === "boolean") {
|
98 | if (name.startsWith("aria-")) {
|
99 | value = "" + value
|
100 | } else if (value) {
|
101 | value = ""
|
102 | }
|
103 | }
|
104 |
|
105 | if (typeof value === "string" || typeof value === "number") {
|
106 | node.setAttribute(name, value)
|
107 | } else {
|
108 | node.removeAttribute(name)
|
109 | }
|
110 | }
|
111 |
|
112 | export const attributeToProp = (k, v) => {
|
113 | let name = kebabToPascal(k)
|
114 | if (v === "") v = true
|
115 | if (k.startsWith("aria-")) {
|
116 | if (v === "true") v = true
|
117 | if (v === "false") v = false
|
118 | }
|
119 | return {
|
120 | name,
|
121 | value: v,
|
122 | }
|
123 | }
|