beercss 3.2.10 → 3.2.12

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