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/README.md +9 -9
- package/dist/cdn/beer.min.css +1 -1
- package/dist/cdn/beer.min.js +4 -1
- package/package.json +2 -2
- package/src/cdn/beer.ts +442 -445
- package/src/cdn/elements/fields.css +15 -15
- package/src/cdn/helpers/forms.css +1 -1
package/src/cdn/beer.ts
CHANGED
|
@@ -1,478 +1,475 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
13
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
updateInput(target);
|
|
324
|
+
if (isModal) target.showModal();
|
|
325
|
+
else target.show();
|
|
137
326
|
}
|
|
327
|
+
}
|
|
138
328
|
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
151
|
-
}
|
|
337
|
+
if (_timeoutToast) clearTimeout(_timeoutToast);
|
|
152
338
|
|
|
153
|
-
|
|
154
|
-
const target = e.currentTarget as HTMLInputElement;
|
|
155
|
-
updateFile(target);
|
|
156
|
-
}
|
|
339
|
+
if (milliseconds === -1) return;
|
|
157
340
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
341
|
+
_timeoutToast = setTimeout(() => {
|
|
342
|
+
removeClass(to, "active");
|
|
343
|
+
}, milliseconds ?? 6000);
|
|
344
|
+
}
|
|
162
345
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
updateRange(target);
|
|
166
|
-
}
|
|
346
|
+
function progress (to: Element, percentage: number): void {
|
|
347
|
+
const element = to as HTMLElement;
|
|
167
348
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
226
|
-
|
|
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
|
-
|
|
232
|
-
|
|
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
|
-
|
|
370
|
+
const light = document.createElement("body");
|
|
371
|
+
light.className = "light";
|
|
372
|
+
document.body.appendChild(light);
|
|
238
373
|
|
|
239
|
-
|
|
374
|
+
const dark = document.createElement("body");
|
|
375
|
+
dark.className = "dark";
|
|
376
|
+
document.body.appendChild(dark);
|
|
240
377
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
386
|
+
document.body.removeChild(light);
|
|
387
|
+
document.body.removeChild(dark);
|
|
388
|
+
return _lastTheme;
|
|
389
|
+
}
|
|
263
390
|
|
|
264
|
-
|
|
265
|
-
|
|
391
|
+
function theme (source?: IBeerCssTheme | any): IBeerCssTheme | Promise<IBeerCssTheme> {
|
|
392
|
+
if (!source || !(globalThis as any).materialDynamicColors) return lastTheme();
|
|
266
393
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
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;
|