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/README.md +9 -9
- package/dist/cdn/beer.min.css +1 -1
- package/dist/cdn/beer.min.js +4 -1
- package/package.json +5 -5
- package/src/cdn/beer.ts +442 -460
- package/src/cdn/elements/badges.css +1 -5
- package/src/cdn/elements/buttons.css +5 -6
- package/src/cdn/elements/chips.css +1 -1
- package/src/cdn/elements/dialogs.css +0 -1
- package/src/cdn/elements/fields.css +62 -59
- package/src/cdn/elements/layouts.css +0 -1
- package/src/cdn/elements/media.css +16 -18
- package/src/cdn/elements/menus.css +0 -2
- package/src/cdn/elements/navigations.css +0 -1
- package/src/cdn/elements/tables.css +0 -1
- package/src/cdn/elements/tabs.css +0 -5
- package/src/cdn/helpers/forms.css +30 -16
- package/src/cdn/helpers/reset.css +0 -4
- package/src/cdn/helpers/responsive.css +0 -1
package/src/cdn/beer.ts
CHANGED
|
@@ -1,493 +1,475 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
141
|
-
if (
|
|
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
|
-
|
|
145
|
-
const
|
|
146
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
-
|
|
155
|
-
|
|
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
|
-
|
|
162
|
-
|
|
163
|
-
removeClass(target, "active");
|
|
329
|
+
function toast (from: Element, to: Element, milliseconds?: number): void {
|
|
330
|
+
tab(from);
|
|
164
331
|
|
|
165
|
-
|
|
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
|
-
|
|
169
|
-
const target = e.currentTarget as HTMLInputElement;
|
|
170
|
-
updateFile(target);
|
|
171
|
-
}
|
|
337
|
+
if (_timeoutToast) clearTimeout(_timeoutToast);
|
|
172
338
|
|
|
173
|
-
|
|
174
|
-
const target = e.currentTarget as HTMLInputElement;
|
|
175
|
-
updateFile(target, e);
|
|
176
|
-
}
|
|
339
|
+
if (milliseconds === -1) return;
|
|
177
340
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
341
|
+
_timeoutToast = setTimeout(() => {
|
|
342
|
+
removeClass(to, "active");
|
|
343
|
+
}, milliseconds ?? 6000);
|
|
344
|
+
}
|
|
182
345
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
_timeoutMutation = setTimeout(() => { void ui(); }, 180);
|
|
186
|
-
}
|
|
346
|
+
function progress (to: Element, percentage: number): void {
|
|
347
|
+
const element = to as HTMLElement;
|
|
187
348
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
-
|
|
256
|
-
|
|
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
|
-
|
|
260
|
-
|
|
367
|
+
function lastTheme (): IBeerCssTheme {
|
|
368
|
+
if (_lastTheme.light && _lastTheme.dark) return _lastTheme;
|
|
261
369
|
|
|
262
|
-
|
|
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
|
-
|
|
269
|
-
|
|
374
|
+
const dark = document.createElement("body");
|
|
375
|
+
dark.className = "dark";
|
|
376
|
+
document.body.appendChild(dark);
|
|
270
377
|
|
|
271
|
-
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
386
|
+
document.body.removeChild(light);
|
|
387
|
+
document.body.removeChild(dark);
|
|
388
|
+
return _lastTheme;
|
|
389
|
+
}
|
|
278
390
|
|
|
279
|
-
|
|
280
|
-
|
|
391
|
+
function theme (source?: IBeerCssTheme | any): IBeerCssTheme | Promise<IBeerCssTheme> {
|
|
392
|
+
if (!source || !(globalThis as any).materialDynamicColors) return lastTheme();
|
|
281
393
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
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
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
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
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
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;
|