beercss 3.2.11 → 3.2.13

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/src/cdn/beer.ts CHANGED
@@ -1,478 +1,475 @@
1
- export default (() => {
2
- const _window: Window | any = globalThis;
3
- let _timeoutToast: ReturnType<typeof setTimeout>;
4
- let _timeoutMutation: ReturnType<typeof setTimeout>;
5
- let _mutation: MutationObserver | null;
6
- const _lastTheme: IBeerCssTheme = {
7
- light: "",
8
- dark: "",
9
- };
10
- const _emptyNodeList = [] as unknown as NodeListOf<Element>;
1
+ let _timeoutToast: ReturnType<typeof setTimeout>;
2
+ let _timeoutMutation: ReturnType<typeof setTimeout>;
3
+ let _mutation: MutationObserver | null;
4
+ const _lastTheme: IBeerCssTheme = {
5
+ light: "",
6
+ dark: "",
7
+ };
8
+ const _emptyNodeList = [] as unknown as NodeListOf<Element>;
9
+
10
+ async function wait (milliseconds: number): Promise<Function> {
11
+ return await new Promise((resolve: Function) => setTimeout(resolve, milliseconds));
12
+ }
13
+
14
+ function guid (): string {
15
+ return "fxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c: string) => {
16
+ const r = (Math.random() * 16) | 0;
17
+ const v = c === "x" ? r : (r & 0x3) | 0x8;
18
+ return v.toString(16);
19
+ });
20
+ }
21
+
22
+ function query (selector: string | Element | null, element?: Element | null): Element | null {
23
+ try {
24
+ return (typeof selector === "string")
25
+ ? (element ?? document).querySelector(selector)
26
+ : selector;
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
32
+ function queryAll (selector: string | NodeListOf<Element> | null, element?: Element | null): NodeListOf<Element> {
33
+ try {
34
+ return (typeof selector === "string")
35
+ ? (element ?? document).querySelectorAll(selector)
36
+ : selector ?? _emptyNodeList;
37
+ } catch {
38
+ return _emptyNodeList;
39
+ }
40
+ }
41
+
42
+ function hasQuery (selector: string | Element | null, element?: Element | null): boolean {
43
+ return !!(query(selector, element));
44
+ }
45
+
46
+ function hasClass (element: Element | null, name: string): boolean {
47
+ return element?.classList?.contains(name) ?? false;
48
+ }
49
+
50
+ function hasTag (element: Element | null, name: string): boolean {
51
+ return element?.tagName?.toLowerCase() === name;
52
+ }
53
+
54
+ function hasType (element: HTMLInputElement | null, name: string): boolean {
55
+ return element?.type?.toLowerCase() === name;
56
+ }
57
+
58
+ function addClass (element: Element | null, name: string): void {
59
+ element?.classList?.add(name);
60
+ }
61
+
62
+ function removeClass (element: Element | null, name: string): void {
63
+ element?.classList?.remove(name);
64
+ }
65
+
66
+ function on (element: Element | null, name: string, callback: any): void {
67
+ element?.addEventListener(name, callback, true);
68
+ }
69
+
70
+ function off (element: Element | null, name: string, callback: any): void {
71
+ element?.removeEventListener(name, callback, true);
72
+ }
73
+
74
+ function insertBefore (newElement: Element, element: Element | null): void {
75
+ element?.parentNode?.insertBefore(newElement, element);
76
+ }
77
+
78
+ function prev (element: Element): Element | null {
79
+ return element?.previousElementSibling;
80
+ }
81
+
82
+ function next (element: Element): Element | null {
83
+ return element?.nextElementSibling;
84
+ }
85
+
86
+ function parent (element: Element): Element | null {
87
+ return element?.parentElement;
88
+ }
89
+
90
+ function create (htmlAttributesAsJson: any): HTMLElement {
91
+ const element = document.createElement("div");
92
+ for (const key in htmlAttributesAsJson) element.setAttribute(key, htmlAttributesAsJson[key]);
93
+ return element;
94
+ }
95
+
96
+ function updateInput (target: Element): void {
97
+ const input = target as HTMLInputElement;
98
+ if (hasType(input, "number") && !input.value) input.value = "";
99
+
100
+ const parentTarget = parent(target);
101
+ const label = query("label", parentTarget) as HTMLLabelElement;
102
+ const isBorder = hasClass(parentTarget, "border") && !hasClass(parentTarget, "fill");
103
+ const toActive = document.activeElement === target || input.value || hasQuery("[selected]", input) || hasType(input, "date") || hasType(input, "time") || hasType(input, "datetime-local");
104
+
105
+ if (toActive) {
106
+ if (isBorder) addClass(input, "active");
107
+ addClass(label, "active");
108
+ } else {
109
+ if (isBorder) removeClass(input, "active");
110
+ removeClass(label, "active");
111
+ }
112
+
113
+ if (target.getAttribute("data-ui")) void open(target, null);
114
+ }
115
+
116
+ function onClickElement (e: Event): void {
117
+ void open(e.currentTarget as HTMLElement, null, null, e);
118
+ }
119
+
120
+ function onClickLabel (e: Event): void {
121
+ const target = e.currentTarget as Element;
122
+ const parentTarget = parent(target);
123
+ const input = query("input:not([type=file], [type=checkbox], [type=radio]), select, textarea", parentTarget) as HTMLElement;
124
+ if (input) input.focus();
125
+ }
126
+
127
+ function onFocusInput (e: Event): void {
128
+ const target = e.currentTarget as Element;
129
+ updateInput(target);
130
+ }
131
+
132
+ function onBlurInput (e: Event): void {
133
+ const target = e.currentTarget as Element;
134
+ updateInput(target);
135
+ }
136
+
137
+ function onClickDocument (e: Event): void {
138
+ off(document.body, "click", onClickDocument);
139
+ const target = e.target as Element;
140
+ const menus = queryAll("menu.active");
141
+ menus.forEach((x: Element) => menu(target, x, e));
142
+ }
143
+
144
+ function onClickToast (e: Event): void {
145
+ const target = e.currentTarget as Element;
146
+ removeClass(target, "active");
147
+
148
+ if (_timeoutToast) clearTimeout(_timeoutToast);
149
+ }
150
+
151
+ function onChangeFile (e: Event): void {
152
+ const target = e.currentTarget as HTMLInputElement;
153
+ updateFile(target);
154
+ }
155
+
156
+ function onKeydownFile (e: KeyboardEvent): void {
157
+ const target = e.currentTarget as HTMLInputElement;
158
+ updateFile(target, e);
159
+ }
160
+
161
+ function onInputRange (e: Event): void {
162
+ const target = e.currentTarget as HTMLInputElement;
163
+ updateRange(target);
164
+ }
165
+
166
+ function onMutation (): void {
167
+ if (_timeoutMutation) clearTimeout(_timeoutMutation);
168
+ _timeoutMutation = setTimeout(() => { void ui(); }, 180);
169
+ }
170
+
171
+ function updateFile (target: Element, e?: KeyboardEvent): void {
172
+ if (e) {
173
+ if (e.key !== "Enter") return;
11
174
 
12
- async function wait (milliseconds: number): Promise<Function> {
13
- return await new Promise((resolve: Function) => setTimeout(resolve, milliseconds));
14
- }
175
+ const target = e.currentTarget as Element;
176
+ const nextTarget = next(target) as HTMLInputElement;
177
+ if (!hasType(nextTarget, "file")) return;
178
+ return nextTarget.click();
179
+ }
180
+
181
+ const currentTarget = target as HTMLInputElement;
182
+ const previousTarget = prev(target) as HTMLInputElement;
183
+ if (!hasType(previousTarget, "text")) return;
184
+ previousTarget.value = currentTarget.files ? Array.from(currentTarget.files).map((x) => x.name).join(", ") : "";
185
+ previousTarget.readOnly = true;
186
+ previousTarget.addEventListener("keydown", onKeydownFile);
187
+ updateInput(previousTarget);
188
+ }
189
+
190
+ function updateRange (target: Element): void {
191
+ const parentTarget = parent(target) as HTMLElement;
192
+ const bar = query("span", parentTarget) as HTMLElement;
193
+ const inputs = queryAll("input", parentTarget) as NodeListOf<HTMLInputElement>;
194
+ const tooltip = query(".tooltip", parentTarget) as HTMLElement;
195
+ if (!inputs.length || !bar) return;
196
+
197
+ const percents: Array<number> = [];
198
+ const values: Array<number> = [];
199
+ for (let i = 0; i < inputs.length; i++) {
200
+ const min = parseFloat(inputs[i].min || "0");
201
+ const max = parseFloat(inputs[i].max || "100");
202
+ const value = parseFloat(inputs[i].value || "0");
203
+ const percent = (value - min) * 100 / (max - min);
204
+ percents.push(percent);
205
+ values.push(value);
206
+ }
207
+
208
+ if (tooltip && tooltip.textContent !== values.join()) tooltip.innerHTML = values.join();
209
+
210
+ let percent = percents[0];
211
+ let left = 0;
212
+ let right = 100 - left - percent;
213
+ if (inputs.length > 1) {
214
+ percent = Math.abs(percents[1] - percents[0]);
215
+ left = percents[1] > percents[0] ? percents[0] : percents[1];
216
+ right = 100 - left - percent;
217
+ }
218
+
219
+ bar.style.left = `${left}%`;
220
+ bar.style.right = `${right}%`;
221
+ }
222
+
223
+ async function open (from: Element, to: Element | null, options?: any, e?: Event): Promise<void> {
224
+ if (!to) {
225
+ to = query(from.getAttribute("data-ui"));
226
+ if (!to) return;
227
+ }
228
+
229
+ if (hasTag(to, "dialog")) return await dialog(from, to);
230
+ if (hasTag(to, "menu")) return menu(from, to, e);
231
+ if (hasClass(to, "toast")) return toast(from, to, options);
232
+ if (hasClass(to, "page")) return page(from, to);
233
+ if (hasClass(to, "progress")) return progress(to, options);
234
+
235
+ tab(from);
236
+
237
+ if (hasClass(to, "active")) return removeClass(to, "active");
238
+
239
+ addClass(to, "active");
240
+ }
241
+
242
+ function tab (from: Element): void {
243
+ const container = parent(from);
244
+ if (!hasClass(container, "tabs")) return;
245
+ const tabs = queryAll("a", container);
246
+ tabs.forEach((x: Element) => removeClass(x, "active"));
247
+ addClass(from, "active");
248
+ }
249
+
250
+ function page (from: Element, to: Element): void {
251
+ tab(from);
252
+ const container = parent(to);
253
+ if (container) for (let i = 0; i < container.children.length; i++) if (hasClass(container.children[i], "page")) removeClass(container.children[i], "active");
254
+ addClass(to, "active");
255
+ }
256
+
257
+ function menu (from: Element, to: Element, e?: Event): any {
258
+ on(document.body, "click", onClickDocument);
259
+ e?.stopPropagation();
260
+ tab(from);
261
+
262
+ if (hasClass(to, "active")) {
263
+ if (!e) return removeClass(to, "active");
264
+
265
+ const trustedFrom = e.target as Element;
266
+ const trustedTo = query(trustedFrom.getAttribute("data-ui") ?? "");
267
+ const trustedMenu = trustedFrom.closest("menu");
268
+ const trustedActive = !query("menu", trustedFrom.closest("[data-ui]") ?? undefined);
269
+
270
+ if (trustedTo && trustedTo !== trustedMenu) return menu(trustedFrom, trustedTo);
271
+ if (!trustedTo && !trustedActive && trustedMenu) return false;
272
+ return removeClass(to, "active");
273
+ }
274
+
275
+ const menus = queryAll("menu.active");
276
+ menus.forEach((x: Element) => removeClass(x, "active"));
277
+ addClass(to, "active");
278
+ }
279
+
280
+ async function dialog (from: Element, to: Element): Promise<void> {
281
+ (document.activeElement as HTMLElement)?.blur();
282
+ tab(from);
283
+
284
+ let overlay = prev(to) as HTMLElement;
285
+ const target = to as HTMLDialogElement;
286
+ const isActive = hasClass(to, "active") || target.open;
287
+ const isModal = hasClass(to, "modal");
288
+ const container = parent(to);
289
+ const isNav = hasTag(container, "nav");
290
+
291
+ if (!hasClass(overlay, "overlay")) {
292
+ overlay = create({ class: "overlay" });
293
+ insertBefore(overlay, to);
294
+ await wait(90);
295
+ }
296
+
297
+ overlay.onclick = () => {
298
+ if (isModal) return;
299
+
300
+ removeClass(from, "active");
301
+ removeClass(to, "active");
302
+ removeClass(overlay, "active");
303
+ target.close();
304
+ };
15
305
 
16
- function guid (): string {
17
- return "fxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c: string) => {
18
- const r = (Math.random() * 16) | 0;
19
- const v = c === "x" ? r : (r & 0x3) | 0x8;
20
- return v.toString(16);
306
+ if (isNav) {
307
+ const elements = queryAll("dialog, a, .overlay", container);
308
+ elements.forEach((x: any) => {
309
+ removeClass(x, "active");
310
+ if (x.open) x.close();
21
311
  });
22
312
  }
23
313
 
24
- function query (selector: string | Element | null, element?: Element | null): Element | null {
25
- try {
26
- return (typeof selector === "string")
27
- ? (element ?? document).querySelector(selector)
28
- : selector;
29
- } catch {
30
- return null;
31
- }
32
- }
33
-
34
- function queryAll (selector: string | NodeListOf<Element> | null, element?: Element | null): NodeListOf<Element> {
35
- try {
36
- return (typeof selector === "string")
37
- ? (element ?? document).querySelectorAll(selector)
38
- : selector ?? _emptyNodeList;
39
- } catch {
40
- return _emptyNodeList;
41
- }
42
- }
43
-
44
- function hasQuery (selector: string | Element | null, element?: Element | null): boolean {
45
- return !!(query(selector, element));
46
- }
47
-
48
- function hasClass (element: Element | null, name: string): boolean {
49
- return element?.classList?.contains(name) ?? false;
50
- }
51
-
52
- function hasTag (element: Element | null, name: string): boolean {
53
- return element?.tagName?.toLowerCase() === name;
54
- }
55
-
56
- function hasType (element: HTMLInputElement | null, name: string): boolean {
57
- return element?.type?.toLowerCase() === name;
58
- }
59
-
60
- function addClass (element: Element | null, name: string): void {
61
- element?.classList?.add(name);
62
- }
63
-
64
- function removeClass (element: Element | null, name: string): void {
65
- element?.classList?.remove(name);
66
- }
67
-
68
- function on (element: Element | null, name: string, callback: any): void {
69
- element?.addEventListener(name, callback, true);
70
- }
71
-
72
- function off (element: Element | null, name: string, callback: any): void {
73
- element?.removeEventListener(name, callback, true);
74
- }
75
-
76
- function insertBefore (newElement: Element, element: Element | null): void {
77
- element?.parentNode?.insertBefore(newElement, element);
78
- }
79
-
80
- function prev (element: Element): Element | null {
81
- return element?.previousElementSibling;
82
- }
83
-
84
- function next (element: Element): Element | null {
85
- return element?.nextElementSibling;
86
- }
87
-
88
- function parent (element: Element): Element | null {
89
- return element?.parentElement;
90
- }
91
-
92
- function create (htmlAttributesAsJson: any): HTMLElement {
93
- const element = document.createElement("div");
94
- for (const key in htmlAttributesAsJson) element.setAttribute(key, htmlAttributesAsJson[key]);
95
- return element;
96
- }
97
-
98
- function updateInput (target: Element): void {
99
- const input = target as HTMLInputElement;
100
- if (hasType(input, "number") && !input.value) input.value = "";
101
-
102
- const parentTarget = parent(target);
103
- const label = query("label", parentTarget) as HTMLLabelElement;
104
- const isBorder = hasClass(parentTarget, "border") && !hasClass(parentTarget, "fill");
105
- const toActive = document.activeElement === target || input.value || hasQuery("[selected]", input) || hasType(input, "date") || hasType(input, "time") || hasType(input, "datetime-local");
106
-
107
- if (toActive) {
108
- if (isBorder) addClass(input, "active");
109
- addClass(label, "active");
110
- } else {
111
- if (isBorder) removeClass(input, "active");
112
- removeClass(label, "active");
113
- }
114
-
115
- if (target.getAttribute("data-ui")) void open(target, null);
116
- }
117
-
118
- function onClickElement (e: Event): void {
119
- void open(e.currentTarget as HTMLElement, null, null, e);
120
- }
121
-
122
- function onClickLabel (e: Event): void {
123
- const target = e.currentTarget as Element;
124
- const parentTarget = parent(target);
125
- const input = query("input:not([type=file], [type=checkbox], [type=radio]), select, textarea", parentTarget) as HTMLElement;
126
- if (input) input.focus();
127
- }
128
-
129
- function onFocusInput (e: Event): void {
130
- const target = e.currentTarget as Element;
131
- updateInput(target);
132
- }
314
+ if (isActive) {
315
+ removeClass(from, "active");
316
+ removeClass(overlay, "active");
317
+ removeClass(to, "active");
318
+ target.close();
319
+ } else {
320
+ if (!hasTag(from, "button") && !hasClass(from, "button") && !hasClass(from, "chip")) addClass(from, "active");
321
+ addClass(overlay, "active");
322
+ addClass(to, "active");
133
323
 
134
- function onBlurInput (e: Event): void {
135
- const target = e.currentTarget as Element;
136
- updateInput(target);
324
+ if (isModal) target.showModal();
325
+ else target.show();
137
326
  }
327
+ }
138
328
 
139
- function onClickDocument (e: Event): void {
140
- off(document.body, "click", onClickDocument);
141
- const target = e.target as Element;
142
- const menus = queryAll("menu.active");
143
- menus.forEach((x: Element) => menu(target, x, e));
144
- }
329
+ function toast (from: Element, to: Element, milliseconds?: number): void {
330
+ tab(from);
145
331
 
146
- function onClickToast (e: Event): void {
147
- const target = e.currentTarget as Element;
148
- removeClass(target, "active");
332
+ const elements = queryAll(".toast.active");
333
+ elements.forEach((x: Element) => removeClass(x, "active"));
334
+ addClass(to, "active");
335
+ on(to, "click", onClickToast);
149
336
 
150
- if (_timeoutToast) clearTimeout(_timeoutToast);
151
- }
337
+ if (_timeoutToast) clearTimeout(_timeoutToast);
152
338
 
153
- function onChangeFile (e: Event): void {
154
- const target = e.currentTarget as HTMLInputElement;
155
- updateFile(target);
156
- }
339
+ if (milliseconds === -1) return;
157
340
 
158
- function onKeydownFile (e: KeyboardEvent): void {
159
- const target = e.currentTarget as HTMLInputElement;
160
- updateFile(target, e);
161
- }
341
+ _timeoutToast = setTimeout(() => {
342
+ removeClass(to, "active");
343
+ }, milliseconds ?? 6000);
344
+ }
162
345
 
163
- function onInputRange (e: Event): void {
164
- const target = e.currentTarget as HTMLInputElement;
165
- updateRange(target);
166
- }
346
+ function progress (to: Element, percentage: number): void {
347
+ const element = to as HTMLElement;
167
348
 
168
- function onMutation (): void {
169
- if (_timeoutMutation) clearTimeout(_timeoutMutation);
170
- _timeoutMutation = setTimeout(() => { void ui(); }, 180);
349
+ if (hasClass(element, "left")) {
350
+ element.style.clipPath = `polygon(0% 0%, 0% 100%, ${percentage}% 100%, ${percentage}% 0%)`;
351
+ return;
171
352
  }
172
353
 
173
- function updateFile (target: Element, e?: KeyboardEvent): void {
174
- if (e) {
175
- if (e.key !== "Enter") return;
176
-
177
- const target = e.currentTarget as Element;
178
- const nextTarget = next(target) as HTMLInputElement;
179
- if (!hasType(nextTarget, "file")) return;
180
- return nextTarget.click();
181
- }
182
-
183
- const currentTarget = target as HTMLInputElement;
184
- const previousTarget = prev(target) as HTMLInputElement;
185
- if (!hasType(previousTarget, "text")) return;
186
- previousTarget.value = currentTarget.files ? Array.from(currentTarget.files).map((x) => x.name).join(", ") : "";
187
- previousTarget.readOnly = true;
188
- previousTarget.addEventListener("keydown", onKeydownFile);
189
- updateInput(previousTarget);
354
+ if (hasClass(element, "top")) {
355
+ element.style.clipPath = `polygon(0% 0%, 100% 0%, 100% ${percentage}%, 0% ${percentage}%)`;
356
+ return;
190
357
  }
191
358
 
192
- function updateRange (target: Element): void {
193
- const parentTarget = parent(target) as HTMLElement;
194
- const bar = query("span", parentTarget) as HTMLElement;
195
- const inputs = queryAll("input", parentTarget) as NodeListOf<HTMLInputElement>;
196
- const tooltip = query(".tooltip", parentTarget) as HTMLElement;
197
- if (!inputs.length || !bar) return;
198
-
199
- const percents: Array<number> = [];
200
- const values: Array<number> = [];
201
- for (let i = 0; i < inputs.length; i++) {
202
- const min = parseFloat(inputs[i].min || "0");
203
- const max = parseFloat(inputs[i].max || "100");
204
- const value = parseFloat(inputs[i].value || "0");
205
- const percent = (value - min) * 100 / (max - min);
206
- percents.push(percent);
207
- values.push(value);
208
- }
209
-
210
- if (tooltip && tooltip.textContent !== values.join()) tooltip.innerHTML = values.join();
211
-
212
- let percent = percents[0];
213
- let left = 0;
214
- let right = 100 - left - percent;
215
- if (inputs.length > 1) {
216
- percent = Math.abs(percents[1] - percents[0]);
217
- left = percents[1] > percents[0] ? percents[0] : percents[1];
218
- right = 100 - left - percent;
219
- }
220
-
221
- bar.style.left = `${left}%`;
222
- bar.style.right = `${right}%`;
359
+ if (hasClass(element, "right")) {
360
+ element.style.clipPath = `polygon(100% 0%, 100% 100%, ${100 - percentage}% 100%, ${100 - percentage}% 0%)`;
361
+ return;
223
362
  }
224
363
 
225
- async function open (from: Element, to: Element | null, options?: any, e?: Event): Promise<void> {
226
- if (!to) {
227
- to = query(from.getAttribute("data-ui"));
228
- if (!to) return;
229
- }
364
+ if (hasClass(element, "bottom")) element.style.clipPath = `polygon(0% 100%, 100% 100%, 100% ${100 - percentage}%, 0% ${100 - percentage}%)`;
365
+ }
230
366
 
231
- if (hasTag(to, "dialog")) return await dialog(from, to);
232
- if (hasTag(to, "menu")) return menu(from, to, e);
233
- if (hasClass(to, "toast")) return toast(from, to, options);
234
- if (hasClass(to, "page")) return page(from, to);
235
- if (hasClass(to, "progress")) return progress(to, options);
367
+ function lastTheme (): IBeerCssTheme {
368
+ if (_lastTheme.light && _lastTheme.dark) return _lastTheme;
236
369
 
237
- tab(from);
370
+ const light = document.createElement("body");
371
+ light.className = "light";
372
+ document.body.appendChild(light);
238
373
 
239
- if (hasClass(to, "active")) return removeClass(to, "active");
374
+ const dark = document.createElement("body");
375
+ dark.className = "dark";
376
+ document.body.appendChild(dark);
240
377
 
241
- addClass(to, "active");
242
- }
243
-
244
- function tab (from: Element): void {
245
- const container = parent(from);
246
- if (!hasClass(container, "tabs")) return;
247
- const tabs = queryAll("a", container);
248
- tabs.forEach((x: Element) => removeClass(x, "active"));
249
- addClass(from, "active");
250
- }
251
-
252
- function page (from: Element, to: Element): void {
253
- tab(from);
254
- const container = parent(to);
255
- if (container) for (let i = 0; i < container.children.length; i++) if (hasClass(container.children[i], "page")) removeClass(container.children[i], "active");
256
- addClass(to, "active");
378
+ const fromLight = getComputedStyle(light);
379
+ const fromDark = getComputedStyle(dark);
380
+ const variables = ["--primary", "--on-primary", "--primary-container", "--on-primary-container", "--secondary", "--on-secondary", "--secondary-container", "--on-secondary-container", "--tertiary", "--on-tertiary", "--tertiary-container", "--on-tertiary-container", "--error", "--on-error", "--error-container", "--on-error-container", "--background", "--on-background", "--surface", "--on-surface", "--outline", "--surface-variant", "--on-surface-variant", "--inverse-surface", "--inverse-on-surface", "--inverse-primary", "--inverse-on-primary"];
381
+ for (let i = 0; i < variables.length; i++) {
382
+ _lastTheme.light += variables[i] + ":" + fromLight.getPropertyValue(variables[i]) + ";";
383
+ _lastTheme.dark += variables[i] + ":" + fromDark.getPropertyValue(variables[i]) + ";";
257
384
  }
258
385
 
259
- function menu (from: Element, to: Element, e?: Event): any {
260
- on(document.body, "click", onClickDocument);
261
- e?.stopPropagation();
262
- tab(from);
386
+ document.body.removeChild(light);
387
+ document.body.removeChild(dark);
388
+ return _lastTheme;
389
+ }
263
390
 
264
- if (hasClass(to, "active")) {
265
- if (!e) return removeClass(to, "active");
391
+ function theme (source?: IBeerCssTheme | any): IBeerCssTheme | Promise<IBeerCssTheme> {
392
+ if (!source || !(globalThis as any).materialDynamicColors) return lastTheme();
266
393
 
267
- const trustedFrom = e.target as Element;
268
- const trustedTo = query(trustedFrom.getAttribute("data-ui") ?? "");
269
- const trustedMenu = trustedFrom.closest("menu");
270
- const trustedActive = !query("menu", trustedFrom.closest("[data-ui]") ?? undefined);
271
-
272
- if (trustedTo && trustedTo !== trustedMenu) return menu(trustedFrom, trustedTo);
273
- if (!trustedTo && !trustedActive && trustedMenu) return false;
274
- return removeClass(to, "active");
275
- }
276
-
277
- const menus = queryAll("menu.active");
278
- menus.forEach((x: Element) => removeClass(x, "active"));
279
- addClass(to, "active");
394
+ const mode = /dark/i.test(document.body.className) ? "dark" : "light";
395
+ if (source.light && source.dark) {
396
+ _lastTheme.light = source.light;
397
+ _lastTheme.dark = source.dark;
398
+ document.body.setAttribute("style", source[mode]);
399
+ return source;
280
400
  }
281
401
 
282
- async function dialog (from: Element, to: Element): Promise<void> {
283
- (document.activeElement as HTMLElement)?.blur();
284
- tab(from);
285
-
286
- let overlay = prev(to) as HTMLElement;
287
- const target = to as HTMLDialogElement;
288
- const isActive = hasClass(to, "active") || target.open;
289
- const isModal = hasClass(to, "modal");
290
- const container = parent(to);
291
- const isNav = hasTag(container, "nav");
292
-
293
- if (!hasClass(overlay, "overlay")) {
294
- overlay = create({ class: "overlay" });
295
- insertBefore(overlay, to);
296
- await wait(90);
297
- }
298
-
299
- overlay.onclick = () => {
300
- if (isModal) return;
301
-
302
- removeClass(from, "active");
303
- removeClass(to, "active");
304
- removeClass(overlay, "active");
305
- target.close();
402
+ return (globalThis as any).materialDynamicColors(source).then((theme: IBeerCssTheme) => {
403
+ const toCss = (data: any) => {
404
+ let style = "";
405
+ for (const i in data) {
406
+ const kebabCase = i.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2").toLowerCase();
407
+ const value: string = data[i];
408
+ style += "--" + kebabCase + ":" + value + ";";
409
+ }
410
+ return style;
306
411
  };
307
412
 
308
- if (isNav) {
309
- const elements = queryAll("dialog, a, .overlay", container);
310
- elements.forEach((x: any) => {
311
- removeClass(x, "active");
312
- if (x.open) x.close();
313
- });
314
- }
315
-
316
- if (isActive) {
317
- removeClass(from, "active");
318
- removeClass(overlay, "active");
319
- removeClass(to, "active");
320
- target.close();
321
- } else {
322
- if (!hasTag(from, "button") && !hasClass(from, "button") && !hasClass(from, "chip")) addClass(from, "active");
323
- addClass(overlay, "active");
324
- addClass(to, "active");
325
-
326
- if (isModal) target.showModal();
327
- else target.show();
328
- }
329
- }
330
-
331
- function toast (from: Element, to: Element, milliseconds?: number): void {
332
- tab(from);
333
-
334
- const elements = queryAll(".toast.active");
335
- elements.forEach((x: Element) => removeClass(x, "active"));
336
- addClass(to, "active");
337
- on(to, "click", onClickToast);
338
-
339
- if (_timeoutToast) clearTimeout(_timeoutToast);
340
-
341
- if (milliseconds === -1) return;
342
-
343
- _timeoutToast = setTimeout(() => {
344
- removeClass(to, "active");
345
- }, milliseconds ?? 6000);
346
- }
347
-
348
- function progress (to: Element, percentage: number): void {
349
- const element = to as HTMLElement;
350
-
351
- if (hasClass(element, "left")) {
352
- element.style.clipPath = `polygon(0% 0%, 0% 100%, ${percentage}% 100%, ${percentage}% 0%)`;
353
- return;
354
- }
355
-
356
- if (hasClass(element, "top")) {
357
- element.style.clipPath = `polygon(0% 0%, 100% 0%, 100% ${percentage}%, 0% ${percentage}%)`;
358
- return;
359
- }
360
-
361
- if (hasClass(element, "right")) {
362
- element.style.clipPath = `polygon(100% 0%, 100% 100%, ${100 - percentage}% 100%, ${100 - percentage}% 0%)`;
363
- return;
364
- }
365
-
366
- if (hasClass(element, "bottom")) element.style.clipPath = `polygon(0% 100%, 100% 100%, 100% ${100 - percentage}%, 0% ${100 - percentage}%)`;
367
- }
368
-
369
- function lastTheme (): IBeerCssTheme {
370
- if (_lastTheme.light && _lastTheme.dark) return _lastTheme;
371
-
372
- const light = document.createElement("body");
373
- light.className = "light";
374
- document.body.appendChild(light);
375
-
376
- const dark = document.createElement("body");
377
- dark.className = "dark";
378
- document.body.appendChild(dark);
379
-
380
- const fromLight = getComputedStyle(light);
381
- const fromDark = getComputedStyle(dark);
382
- const variables = ["--primary", "--on-primary", "--primary-container", "--on-primary-container", "--secondary", "--on-secondary", "--secondary-container", "--on-secondary-container", "--tertiary", "--on-tertiary", "--tertiary-container", "--on-tertiary-container", "--error", "--on-error", "--error-container", "--on-error-container", "--background", "--on-background", "--surface", "--on-surface", "--outline", "--surface-variant", "--on-surface-variant", "--inverse-surface", "--inverse-on-surface", "--inverse-primary", "--inverse-on-primary"];
383
- for (let i = 0; i < variables.length; i++) {
384
- _lastTheme.light += variables[i] + ":" + fromLight.getPropertyValue(variables[i]) + ";";
385
- _lastTheme.dark += variables[i] + ":" + fromDark.getPropertyValue(variables[i]) + ";";
386
- }
387
-
388
- document.body.removeChild(light);
389
- document.body.removeChild(dark);
413
+ _lastTheme.light = toCss(theme.light);
414
+ _lastTheme.dark = toCss(theme.dark);
415
+ document.body.setAttribute("style", _lastTheme[mode]);
390
416
  return _lastTheme;
391
- }
392
-
393
- function theme (source?: IBeerCssTheme | any): IBeerCssTheme | Promise<IBeerCssTheme> {
394
- if (!source || !_window.materialDynamicColors) return lastTheme();
395
-
396
- const mode = /dark/i.test(document.body.className) ? "dark" : "light";
397
- if (source.light && source.dark) {
398
- _lastTheme.light = source.light;
399
- _lastTheme.dark = source.dark;
400
- document.body.setAttribute("style", source[mode]);
401
- return source;
402
- }
403
-
404
- return _window.materialDynamicColors(source).then((theme: IBeerCssTheme) => {
405
- const toCss = (data: any) => {
406
- let style = "";
407
- for (const i in data) {
408
- const kebabCase = i.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, "$1-$2").toLowerCase();
409
- const value: string = data[i];
410
- style += "--" + kebabCase + ":" + value + ";";
411
- }
412
- return style;
413
- };
414
-
415
- _lastTheme.light = toCss(theme.light);
416
- _lastTheme.dark = toCss(theme.dark);
417
- document.body.setAttribute("style", _lastTheme[mode]);
418
- return _lastTheme;
419
- });
420
- }
421
-
422
- function mode (value: string | any): string {
423
- if (!value) return /dark/i.test(document.body.className) ? "dark" : "light";
424
- document.body.classList.remove("light", "dark");
425
- document.body.classList.add(value);
426
- const lastThemeStyle = value === "light" ? _lastTheme.light : _lastTheme.dark;
427
- if (_window.materialDynamicColors) document.body.setAttribute("style", lastThemeStyle);
428
- return value;
429
- }
430
-
431
- function setup (): void {
432
- if (_mutation) return;
433
- _mutation = new MutationObserver(onMutation);
434
- _mutation.observe(document.body, { childList: true, subtree: true });
435
- onMutation();
436
- }
437
-
438
- function ui (selector?: string | Element, options?: string | number | IBeerCssTheme): string | IBeerCssTheme | Promise<IBeerCssTheme> | undefined {
439
- if (selector) {
440
- if (selector === "setup") return setup() as undefined;
441
- if (selector === "guid") return guid();
442
- if (selector === "mode") return mode(options);
443
- if (selector === "theme") return theme(options);
444
-
445
- const to = query(selector);
446
- if (!to) return;
447
- void open(to, to, options);
448
- }
449
-
450
- const elements = queryAll("[data-ui]");
451
- elements.forEach((x: Element) => on(x, "click", onClickElement));
452
-
453
- const labels = queryAll(".field > label");
454
- labels.forEach((x: Element) => on(x, "click", onClickLabel));
455
-
456
- const inputs = queryAll(".field > input:not([type=file]), .field > select, .field > textarea");
457
- inputs.forEach((x: Element) => {
458
- on(x, "focus", onFocusInput);
459
- on(x, "blur", onBlurInput);
460
- updateInput(x);
461
- });
462
- const files = queryAll(".field > input[type=file]");
463
- files.forEach((x: Element) => {
464
- on(x, "change", onChangeFile);
465
- updateFile(x);
466
- });
467
- const ranges = queryAll(".slider > input[type=range]");
468
- ranges.forEach((x: Element) => {
469
- on(x, "input", onInputRange);
470
- updateRange(x);
471
- });
472
- }
473
-
474
- if (_window.addEventListener) _window.addEventListener("load", async () => await ui("setup"));
475
- _window.beercss = ui;
476
- _window.ui = ui;
477
- return _window.ui;
478
- })();
417
+ });
418
+ }
419
+
420
+ function mode (value: string | any): string {
421
+ if (!value) return /dark/i.test(document.body.className) ? "dark" : "light";
422
+ document.body.classList.remove("light", "dark");
423
+ document.body.classList.add(value);
424
+ const lastThemeStyle = value === "light" ? _lastTheme.light : _lastTheme.dark;
425
+ if ((globalThis as any).materialDynamicColors) document.body.setAttribute("style", lastThemeStyle);
426
+ return value;
427
+ }
428
+
429
+ function setup (): void {
430
+ if (_mutation) return;
431
+ _mutation = new MutationObserver(onMutation);
432
+ _mutation.observe(document.body, { childList: true, subtree: true });
433
+ onMutation();
434
+ }
435
+
436
+ function ui (selector?: string | Element, options?: string | number | IBeerCssTheme): string | IBeerCssTheme | Promise<IBeerCssTheme> | undefined {
437
+ if (selector) {
438
+ if (selector === "setup") return setup() as undefined;
439
+ if (selector === "guid") return guid();
440
+ if (selector === "mode") return mode(options);
441
+ if (selector === "theme") return theme(options);
442
+
443
+ const to = query(selector);
444
+ if (!to) return;
445
+ void open(to, to, options);
446
+ }
447
+
448
+ const elements = queryAll("[data-ui]");
449
+ elements.forEach((x: Element) => on(x, "click", onClickElement));
450
+
451
+ const labels = queryAll(".field > label");
452
+ labels.forEach((x: Element) => on(x, "click", onClickLabel));
453
+
454
+ const inputs = queryAll(".field > input:not([type=file]), .field > select, .field > textarea");
455
+ inputs.forEach((x: Element) => {
456
+ on(x, "focus", onFocusInput);
457
+ on(x, "blur", onBlurInput);
458
+ updateInput(x);
459
+ });
460
+ const files = queryAll(".field > input[type=file]");
461
+ files.forEach((x: Element) => {
462
+ on(x, "change", onChangeFile);
463
+ updateFile(x);
464
+ });
465
+ const ranges = queryAll(".slider > input[type=range]");
466
+ ranges.forEach((x: Element) => {
467
+ on(x, "input", onInputRange);
468
+ updateRange(x);
469
+ });
470
+ }
471
+
472
+ if ((globalThis as any).addEventListener) (globalThis as any).addEventListener("load", async () => await ui("setup"));
473
+ (globalThis as any).beercss = ui;
474
+ (globalThis as any).ui = ui;
475
+ export default ui;