@zeyos/zx 4.1.0 → 4.2.0
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/CHANGELOG.md +61 -0
- package/DESIGN-SYSTEM.md +33 -3
- package/README.md +17 -5
- package/dist/types/components/app-icon/app-icon.d.ts +8 -1
- package/dist/types/components/aurora/aurora.d.ts +72 -0
- package/dist/types/components/calendar/calendar-model.d.ts +202 -0
- package/dist/types/components/calendar/calendar.d.ts +500 -0
- package/dist/types/components/card-view/card-view.d.ts +303 -0
- package/dist/types/components/card-view/record-card.d.ts +242 -0
- package/dist/types/components/kanban-view/kanban-view.d.ts +572 -0
- package/dist/types/components/table/table.d.ts +120 -31
- package/dist/types/components/table-view/table-view.d.ts +192 -0
- package/dist/types/components/view/record-view.d.ts +464 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/zeyos/calendar.d.ts +91 -0
- package/dist/types/zeyos/icons.d.ts +19 -3
- package/dist/types/zeyos/index.d.ts +4 -0
- package/dist/types/zeyos/legacy-saved-views.d.ts +66 -0
- package/dist/types/zeyos/saved-views.d.ts +331 -0
- package/dist/types/zeyos/view.d.ts +305 -0
- package/dist/zx-zeyos.esm.js +4627 -1460
- package/dist/zx-zeyos.esm.min.js +1 -1
- package/dist/zx.css +2492 -410
- package/dist/zx.esm.js +4891 -115
- package/dist/zx.esm.min.js +3 -3
- package/dist/zx.global.js +4891 -115
- package/dist/zx.global.min.js +3 -3
- package/dist/zx.min.css +1 -1
- package/docs/api.json +1702 -191
- package/docs/llms.md +207 -20
- package/docs/llms.txt +16 -6
- package/package.json +1 -1
- package/src/components/app-icon/app-icon.css +140 -30
- package/src/components/app-icon/app-icon.js +32 -5
- package/src/components/app-rail/app-rail.css +11 -2
- package/src/components/app-sidebar/app-sidebar.css +11 -2
- package/src/components/aurora/aurora.css +143 -0
- package/src/components/aurora/aurora.js +146 -0
- package/src/components/avatar/avatar.css +1 -1
- package/src/components/button/button.css +34 -2
- package/src/components/calendar/calendar-model.js +402 -0
- package/src/components/calendar/calendar.css +567 -0
- package/src/components/calendar/calendar.js +1524 -0
- package/src/components/card/card.css +25 -3
- package/src/components/card-view/card-view.css +389 -0
- package/src/components/card-view/card-view.js +527 -0
- package/src/components/card-view/record-card.js +392 -0
- package/src/components/context-menu/context-menu.css +2 -2
- package/src/components/date-picker/date-picker.css +15 -3
- package/src/components/datebox/date-range-box.css +15 -2
- package/src/components/datebox/datebox.css +16 -3
- package/src/components/dropdown/dropdown.css +15 -3
- package/src/components/kanban-view/kanban-view.css +216 -0
- package/src/components/kanban-view/kanban-view.js +1228 -0
- package/src/components/launcher/launcher.css +18 -5
- package/src/components/master-panel/master-panel.css +14 -3
- package/src/components/menu-button/menu-button.css +2 -2
- package/src/components/message/message.css +15 -10
- package/src/components/modal/modal.css +15 -3
- package/src/components/navigation-bar/navigation-bar.css +17 -5
- package/src/components/panel/panel.css +10 -2
- package/src/components/select/select.css +20 -4
- package/src/components/sheet/sheet.css +4 -1
- package/src/components/table/table.css +80 -4
- package/src/components/table/table.js +322 -37
- package/src/components/table-view/table-view.css +18 -0
- package/src/components/table-view/table-view.js +347 -0
- package/src/components/tag-picker/tag-picker.css +15 -3
- package/src/components/tooltip/tooltip.css +15 -3
- package/src/components/view/record-view.css +114 -0
- package/src/components/view/record-view.js +770 -0
- package/src/index.js +15 -0
- package/src/zeyos/calendar.js +115 -0
- package/src/zeyos/icons.js +15 -8
- package/src/zeyos/index.js +13 -0
- package/src/zeyos/launcher.js +2 -2
- package/src/zeyos/legacy-saved-views.js +155 -0
- package/src/zeyos/saved-views.js +815 -0
- package/src/zeyos/table.js +3 -1
- package/src/zeyos/view.js +543 -0
- package/styles/icons.css +3 -10
- package/styles/tokens/semantic.css +74 -2
- package/styles/zx.css +6 -0
|
@@ -12,12 +12,13 @@ import { isCssColor } from '../../core/util.js';
|
|
|
12
12
|
* @property {string|null} [label=null] Accessible image label; null makes the icon decorative.
|
|
13
13
|
* @property {string|number|null} [badge=null] Optional corner badge.
|
|
14
14
|
* @property {boolean} [selected=false] Whether the icon represents the active application.
|
|
15
|
+
* @property {'tile'|'circle'} [shape='tile'] Identity vessel geometry.
|
|
15
16
|
* @property {false|'subtle'|'strong'} [glass='subtle'] CSS-first glass treatment.
|
|
16
17
|
* @property {string|string[]} [class] Additional class names.
|
|
17
18
|
*/
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
|
-
* Stable application-identity
|
|
21
|
+
* Stable application-identity surface with progressive CSS glass treatment.
|
|
21
22
|
* @extends {Component<AppIconOptions>}
|
|
22
23
|
*/
|
|
23
24
|
export class AppIcon extends Component {
|
|
@@ -32,6 +33,7 @@ export class AppIcon extends Component {
|
|
|
32
33
|
label: null,
|
|
33
34
|
badge: null,
|
|
34
35
|
selected: false,
|
|
36
|
+
shape: 'tile',
|
|
35
37
|
glass: 'subtle',
|
|
36
38
|
class: []
|
|
37
39
|
};
|
|
@@ -42,6 +44,7 @@ export class AppIcon extends Component {
|
|
|
42
44
|
const root = /** @type {HTMLElement} */ (this.el ?? h('span'));
|
|
43
45
|
this.el = root;
|
|
44
46
|
this._snapshot = this._createdRoot ? null : snapshotTarget(root);
|
|
47
|
+
this._interactiveRoot = root.matches('a[href], button, input, select, textarea, [role="button"], [role="link"]');
|
|
45
48
|
this._state = { ...this.options };
|
|
46
49
|
this._appliedClasses = [];
|
|
47
50
|
this._sync();
|
|
@@ -78,10 +81,20 @@ export class AppIcon extends Component {
|
|
|
78
81
|
if (isCssColor(state.color)) this.el.style.setProperty('--zx-app-icon-color', String(state.color));
|
|
79
82
|
else this.el.style.removeProperty('--zx-app-icon-color');
|
|
80
83
|
this.el.dataset.glass = normalizeGlass(state.glass);
|
|
84
|
+
this.el.dataset.shape = normalizeShape(state.shape);
|
|
81
85
|
this.el.toggleAttribute('data-selected', Boolean(state.selected));
|
|
82
86
|
|
|
83
87
|
const label = state.label == null ? '' : String(state.label).trim();
|
|
84
|
-
if (
|
|
88
|
+
if (this._interactiveRoot) {
|
|
89
|
+
const originalRole = originalAttribute(this._snapshot, 'role');
|
|
90
|
+
const originalLabel = originalAttribute(this._snapshot, 'aria-label');
|
|
91
|
+
if (originalRole === null) this.el.removeAttribute('role');
|
|
92
|
+
else this.el.setAttribute('role', originalRole);
|
|
93
|
+
if (label) this.el.setAttribute('aria-label', label);
|
|
94
|
+
else if (originalLabel === null) this.el.removeAttribute('aria-label');
|
|
95
|
+
else this.el.setAttribute('aria-label', originalLabel);
|
|
96
|
+
this.el.removeAttribute('aria-hidden');
|
|
97
|
+
} else if (label) {
|
|
85
98
|
this.el.setAttribute('role', 'img');
|
|
86
99
|
this.el.setAttribute('aria-label', label);
|
|
87
100
|
this.el.removeAttribute('aria-hidden');
|
|
@@ -95,11 +108,15 @@ export class AppIcon extends Component {
|
|
|
95
108
|
const badge = state.badge == null || state.badge === '' ? null : h('span', {
|
|
96
109
|
class: 'zx-app-icon__badge', ariaHidden: 'true'
|
|
97
110
|
}, String(state.badge));
|
|
98
|
-
|
|
111
|
+
const surface = h('span', { class: 'zx-app-icon__surface', ariaHidden: 'true' },
|
|
99
112
|
h('span', { class: 'zx-app-icon__shine', ariaHidden: 'true' }),
|
|
100
|
-
h('span', { class: 'zx-app-icon__glyph', ariaHidden: 'true' }, visual)
|
|
101
|
-
badge
|
|
113
|
+
h('span', { class: 'zx-app-icon__glyph', ariaHidden: 'true' }, visual)
|
|
102
114
|
);
|
|
115
|
+
const children = [surface];
|
|
116
|
+
// Native replaceChildren() stringifies nullish arguments. Only append a badge when it exists,
|
|
117
|
+
// otherwise the icon gets a visible "null" text node that can also disturb optical centring.
|
|
118
|
+
if (badge) children.push(badge);
|
|
119
|
+
this.el.replaceChildren(...children);
|
|
103
120
|
}
|
|
104
121
|
}
|
|
105
122
|
|
|
@@ -132,3 +149,13 @@ function normalizeGlass(value) {
|
|
|
132
149
|
if (value === false || value === 'none') return 'none';
|
|
133
150
|
return value === 'strong' ? 'strong' : 'subtle';
|
|
134
151
|
}
|
|
152
|
+
|
|
153
|
+
/** @param {unknown} value @returns {'tile'|'circle'} */
|
|
154
|
+
function normalizeShape(value) {
|
|
155
|
+
return value === 'circle' ? 'circle' : 'tile';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** @param {{attributes: [string, string][]}|null} snapshot @param {string} name @returns {string|null} */
|
|
159
|
+
function originalAttribute(snapshot, name) {
|
|
160
|
+
return snapshot?.attributes.find(([attribute]) => attribute === name)?.[1] ?? null;
|
|
161
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
.zx-app-rail {
|
|
2
2
|
display: flex;
|
|
3
3
|
min-inline-size: 0;
|
|
4
|
-
border: 1px solid var(--zx-color-border);
|
|
5
|
-
background: var(--zx-color-
|
|
4
|
+
border: 1px solid var(--zx-color-glass-border);
|
|
5
|
+
background: var(--zx-color-glass-chrome);
|
|
6
|
+
box-shadow: var(--zx-glass-chrome-shadow);
|
|
6
7
|
color: var(--zx-color-text);
|
|
7
8
|
}
|
|
8
9
|
|
|
@@ -105,3 +106,11 @@
|
|
|
105
106
|
.zx-app-rail__flyout-item-label { grid-column: 2; }
|
|
106
107
|
.zx-app-rail__flyout-item-badge { grid-column: 3; color: var(--zx-color-text-muted); font-size: var(--zx-text-xs); }
|
|
107
108
|
.zx-app-rail__flyout-item-disclosure { display: inline-flex; grid-column: 4; color: var(--zx-color-text-muted); }
|
|
109
|
+
|
|
110
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
111
|
+
.zx-app-rail {
|
|
112
|
+
border-color: var(--zx-color-border);
|
|
113
|
+
background: var(--zx-color-bg-surface);
|
|
114
|
+
box-shadow: none;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
.zx-app-sidebar {
|
|
2
2
|
display: flex;
|
|
3
3
|
min-block-size: 0;
|
|
4
|
-
border: 1px solid var(--zx-color-border);
|
|
5
|
-
background: var(--zx-color-
|
|
4
|
+
border: 1px solid var(--zx-color-glass-border);
|
|
5
|
+
background: var(--zx-color-glass-chrome);
|
|
6
|
+
box-shadow: var(--zx-glass-chrome-shadow);
|
|
6
7
|
color: var(--zx-color-text);
|
|
7
8
|
}
|
|
8
9
|
|
|
@@ -78,3 +79,11 @@
|
|
|
78
79
|
}
|
|
79
80
|
.zx-app-sidebar__footer[hidden] { display: none; }
|
|
80
81
|
.zx-app-sidebar__rail-header { display: grid; gap: var(--zx-space-1); }
|
|
82
|
+
|
|
83
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
84
|
+
.zx-app-sidebar {
|
|
85
|
+
border-color: var(--zx-color-border);
|
|
86
|
+
background: var(--zx-color-bg-surface);
|
|
87
|
+
box-shadow: none;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
.zx-aurora {
|
|
2
|
+
--zx-aurora-color-1: var(--zx-color-accent);
|
|
3
|
+
--zx-aurora-color-2: var(--zx-color-info);
|
|
4
|
+
--zx-aurora-color-3: var(--zx-color-success);
|
|
5
|
+
--zx-aurora-color-4: var(--zx-color-warning);
|
|
6
|
+
--zx-aurora-core: 7%;
|
|
7
|
+
--zx-aurora-wash: 4%;
|
|
8
|
+
--zx-aurora-faint: 2%;
|
|
9
|
+
--zx-aurora-core-1: color-mix(in srgb, var(--zx-aurora-color-1) var(--zx-aurora-core), transparent);
|
|
10
|
+
--zx-aurora-core-2: color-mix(in srgb, var(--zx-aurora-color-2) var(--zx-aurora-core), transparent);
|
|
11
|
+
--zx-aurora-core-3: color-mix(in srgb, var(--zx-aurora-color-3) var(--zx-aurora-core), transparent);
|
|
12
|
+
--zx-aurora-core-4: color-mix(in srgb, var(--zx-aurora-color-4) var(--zx-aurora-core), transparent);
|
|
13
|
+
--zx-aurora-wash-1: color-mix(in srgb, var(--zx-aurora-color-1) var(--zx-aurora-wash), transparent);
|
|
14
|
+
--zx-aurora-wash-2: color-mix(in srgb, var(--zx-aurora-color-2) var(--zx-aurora-wash), transparent);
|
|
15
|
+
--zx-aurora-wash-3: color-mix(in srgb, var(--zx-aurora-color-3) var(--zx-aurora-wash), transparent);
|
|
16
|
+
--zx-aurora-wash-4: color-mix(in srgb, var(--zx-aurora-color-4) var(--zx-aurora-wash), transparent);
|
|
17
|
+
--zx-aurora-faint-1: color-mix(in srgb, var(--zx-aurora-color-1) var(--zx-aurora-faint), transparent);
|
|
18
|
+
--zx-aurora-faint-2: color-mix(in srgb, var(--zx-aurora-color-2) var(--zx-aurora-faint), transparent);
|
|
19
|
+
--zx-aurora-faint-3: color-mix(in srgb, var(--zx-aurora-color-3) var(--zx-aurora-faint), transparent);
|
|
20
|
+
--zx-aurora-faint-4: color-mix(in srgb, var(--zx-aurora-color-4) var(--zx-aurora-faint), transparent);
|
|
21
|
+
position: relative;
|
|
22
|
+
isolation: isolate;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.zx-aurora[data-intensity="balanced"] {
|
|
26
|
+
--zx-aurora-core: 11%;
|
|
27
|
+
--zx-aurora-wash: 6.5%;
|
|
28
|
+
--zx-aurora-faint: 3%;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.zx-aurora[data-intensity="vivid"] {
|
|
32
|
+
--zx-aurora-core: 16%;
|
|
33
|
+
--zx-aurora-wash: 9%;
|
|
34
|
+
--zx-aurora-faint: 4.5%;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
:is([data-zx-theme="dark"] .zx-aurora, .zx-aurora[data-zx-theme="dark"]) {
|
|
38
|
+
--zx-aurora-core: 12%;
|
|
39
|
+
--zx-aurora-wash: 7%;
|
|
40
|
+
--zx-aurora-faint: 3%;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
:is([data-zx-theme="dark"] .zx-aurora, .zx-aurora[data-zx-theme="dark"])[data-intensity="balanced"] {
|
|
44
|
+
--zx-aurora-core: 17%;
|
|
45
|
+
--zx-aurora-wash: 10%;
|
|
46
|
+
--zx-aurora-faint: 5%;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
:is([data-zx-theme="dark"] .zx-aurora, .zx-aurora[data-zx-theme="dark"])[data-intensity="vivid"] {
|
|
50
|
+
--zx-aurora-core: 24%;
|
|
51
|
+
--zx-aurora-wash: 14%;
|
|
52
|
+
--zx-aurora-faint: 7%;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.zx-aurora::before {
|
|
56
|
+
content: "";
|
|
57
|
+
position: absolute;
|
|
58
|
+
z-index: -1;
|
|
59
|
+
inset: 0;
|
|
60
|
+
border-radius: inherit;
|
|
61
|
+
background-repeat: no-repeat;
|
|
62
|
+
pointer-events: none;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.zx-aurora[data-preset="source"]::before {
|
|
66
|
+
background-image:
|
|
67
|
+
radial-gradient(94% 78% at 0% 0%, var(--zx-aurora-core-1) 0%, var(--zx-aurora-wash-1) 34%, transparent 72%),
|
|
68
|
+
radial-gradient(68% 54% at 72% 0%, var(--zx-aurora-wash-2) 0%, var(--zx-aurora-faint-2) 42%, transparent 74%),
|
|
69
|
+
radial-gradient(62% 74% at 100% 82%, var(--zx-aurora-wash-3) 0%, transparent 68%),
|
|
70
|
+
radial-gradient(58% 42% at 38% 100%, var(--zx-aurora-faint-4) 0%, transparent 72%);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.zx-aurora[data-preset="confluence"]::before {
|
|
74
|
+
background-image:
|
|
75
|
+
radial-gradient(108% 84% at 0% 0%, var(--zx-aurora-core-1) 0%, var(--zx-aurora-wash-1) 34%, transparent 70%),
|
|
76
|
+
radial-gradient(92% 72% at 100% 0%, var(--zx-aurora-core-2) 0%, var(--zx-aurora-wash-2) 36%, transparent 72%),
|
|
77
|
+
radial-gradient(78% 62% at 0% 100%, var(--zx-aurora-wash-3) 0%, transparent 70%),
|
|
78
|
+
radial-gradient(72% 64% at 100% 100%, var(--zx-aurora-wash-4) 0%, transparent 72%);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.zx-aurora[data-preset="horizon"]::before {
|
|
82
|
+
background-image:
|
|
83
|
+
radial-gradient(72% 28% at 8% 19%, var(--zx-aurora-core-1) 0%, var(--zx-aurora-wash-1) 48%, transparent 78%),
|
|
84
|
+
radial-gradient(68% 24% at 42% 23%, var(--zx-aurora-core-2) 0%, var(--zx-aurora-wash-2) 44%, transparent 78%),
|
|
85
|
+
radial-gradient(72% 26% at 76% 18%, var(--zx-aurora-core-3) 0%, var(--zx-aurora-wash-3) 46%, transparent 80%),
|
|
86
|
+
radial-gradient(54% 22% at 100% 26%, var(--zx-aurora-wash-4) 0%, transparent 78%);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.zx-aurora[data-preset="diagonal"]::before {
|
|
90
|
+
background-image:
|
|
91
|
+
linear-gradient(126deg, var(--zx-aurora-core-1) 0%, var(--zx-aurora-wash-1) 18%, transparent 44%),
|
|
92
|
+
linear-gradient(126deg, transparent 18%, var(--zx-aurora-wash-2) 42%, transparent 63%),
|
|
93
|
+
linear-gradient(126deg, transparent 44%, var(--zx-aurora-wash-3) 67%, transparent 84%),
|
|
94
|
+
linear-gradient(126deg, transparent 68%, var(--zx-aurora-wash-4) 90%, transparent 100%);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.zx-aurora[data-preset="edge"]::before {
|
|
98
|
+
background-image:
|
|
99
|
+
radial-gradient(38% 108% at 0% 18%, var(--zx-aurora-core-1) 0%, var(--zx-aurora-faint-1) 52%, transparent 78%),
|
|
100
|
+
radial-gradient(72% 34% at 72% 0%, var(--zx-aurora-wash-2) 0%, transparent 74%),
|
|
101
|
+
radial-gradient(38% 106% at 100% 84%, var(--zx-aurora-wash-3) 0%, transparent 74%),
|
|
102
|
+
radial-gradient(76% 32% at 32% 100%, var(--zx-aurora-wash-4) 0%, transparent 76%);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.zx-aurora[data-preset="curtain"]::before {
|
|
106
|
+
background-image:
|
|
107
|
+
radial-gradient(28% 112% at 10% 0%, var(--zx-aurora-core-1) 0%, var(--zx-aurora-faint-1) 52%, transparent 78%),
|
|
108
|
+
radial-gradient(26% 104% at 38% 0%, var(--zx-aurora-core-2) 0%, var(--zx-aurora-faint-2) 48%, transparent 76%),
|
|
109
|
+
radial-gradient(28% 110% at 67% 0%, var(--zx-aurora-core-3) 0%, var(--zx-aurora-faint-3) 50%, transparent 78%),
|
|
110
|
+
radial-gradient(26% 106% at 92% 0%, var(--zx-aurora-core-4) 0%, var(--zx-aurora-faint-4) 48%, transparent 76%);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@media (prefers-color-scheme: dark) {
|
|
114
|
+
:is([data-zx-theme="auto"] .zx-aurora, .zx-aurora[data-zx-theme="auto"]) {
|
|
115
|
+
--zx-aurora-core: 12%;
|
|
116
|
+
--zx-aurora-wash: 7%;
|
|
117
|
+
--zx-aurora-faint: 3%;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
:is([data-zx-theme="auto"] .zx-aurora, .zx-aurora[data-zx-theme="auto"])[data-intensity="balanced"] {
|
|
121
|
+
--zx-aurora-core: 17%;
|
|
122
|
+
--zx-aurora-wash: 10%;
|
|
123
|
+
--zx-aurora-faint: 5%;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
:is([data-zx-theme="auto"] .zx-aurora, .zx-aurora[data-zx-theme="auto"])[data-intensity="vivid"] {
|
|
127
|
+
--zx-aurora-core: 24%;
|
|
128
|
+
--zx-aurora-wash: 14%;
|
|
129
|
+
--zx-aurora-faint: 7%;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@media (prefers-reduced-transparency: reduce), (forced-colors: active) {
|
|
134
|
+
.zx-aurora::before {
|
|
135
|
+
display: none;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@media (prefers-contrast: more) {
|
|
140
|
+
.zx-aurora::before {
|
|
141
|
+
opacity: .35;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { Component } from '../../core/component.js';
|
|
2
|
+
import { h, restoreTarget, snapshotTarget } from '../../core/dom.js';
|
|
3
|
+
import { isCssColor } from '../../core/util.js';
|
|
4
|
+
|
|
5
|
+
const PRESETS = Object.freeze(['source', 'confluence', 'horizon', 'diagonal', 'edge', 'curtain']);
|
|
6
|
+
const INTENSITIES = Object.freeze(['subtle', 'balanced', 'vivid']);
|
|
7
|
+
const COLOR_PROPERTIES = Object.freeze([
|
|
8
|
+
'--zx-aurora-color-1', '--zx-aurora-color-2',
|
|
9
|
+
'--zx-aurora-color-3', '--zx-aurora-color-4'
|
|
10
|
+
]);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {Object} AuroraOptions
|
|
14
|
+
* @property {'source'|'confluence'|'horizon'|'diagonal'|'edge'|'curtain'} [preset='source']
|
|
15
|
+
* Geometry of the ambient light field.
|
|
16
|
+
* @property {string[]} [colors=[]] Zero to four concrete CSS colours. An empty list uses semantic
|
|
17
|
+
* Zx colours; shorter palettes repeat across the four gradient fields.
|
|
18
|
+
* @property {'subtle'|'balanced'|'vivid'} [intensity='subtle'] Strength of the light field.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Decorative multicolour ambient light for an existing application surface.
|
|
23
|
+
* Aurora owns no application content or interaction: it enhances the target in place and renders
|
|
24
|
+
* its pointer-transparent light field behind the target's existing children.
|
|
25
|
+
* @extends {Component<AuroraOptions>}
|
|
26
|
+
*/
|
|
27
|
+
export class Aurora extends Component {
|
|
28
|
+
static cssName = 'aurora';
|
|
29
|
+
|
|
30
|
+
/** @type {Readonly<AuroraOptions>} */
|
|
31
|
+
static defaults = {
|
|
32
|
+
preset: 'source',
|
|
33
|
+
colors: [],
|
|
34
|
+
intensity: 'subtle'
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Creates or enhances an Aurora surface.
|
|
39
|
+
* @param {Element|string|null} [target=null] Existing surface, selector, or null.
|
|
40
|
+
* @param {AuroraOptions} [options={}] Aurora options.
|
|
41
|
+
*/
|
|
42
|
+
constructor(target = null, options = {}) {
|
|
43
|
+
super(target, options);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** @returns {HTMLElement} */
|
|
47
|
+
render() {
|
|
48
|
+
const created = !this.el;
|
|
49
|
+
const root = /** @type {HTMLElement} */ (this.el ?? h('div'));
|
|
50
|
+
this.el = root;
|
|
51
|
+
this._createdRoot = created;
|
|
52
|
+
this._original = created ? null : snapshotTarget(root);
|
|
53
|
+
this._cleaned = false;
|
|
54
|
+
this._initialColors = COLOR_PROPERTIES.map((property) => ({
|
|
55
|
+
property,
|
|
56
|
+
value: root.style.getPropertyValue(property),
|
|
57
|
+
priority: root.style.getPropertyPriority(property)
|
|
58
|
+
}));
|
|
59
|
+
this._colors = [];
|
|
60
|
+
|
|
61
|
+
this.setPreset(this.options.preset);
|
|
62
|
+
this.setIntensity(this.options.intensity);
|
|
63
|
+
this.setColors(this.options.colors);
|
|
64
|
+
return root;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Sets the light-field geometry without replacing surface content. @param {string} preset @returns {this} */
|
|
68
|
+
setPreset(preset) {
|
|
69
|
+
this.el.dataset.preset = normalizeAuroraPreset(preset);
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Sets zero to four concrete colours without replacing surface content. @param {string[]|null} colors @returns {this} */
|
|
74
|
+
setColors(colors) {
|
|
75
|
+
const palette = normalizeAuroraColors(colors);
|
|
76
|
+
const fields = expandAuroraColors(palette);
|
|
77
|
+
this._colors = [...palette];
|
|
78
|
+
this.el.dataset.colors = palette.length === 0 ? 'semantic' : String(palette.length);
|
|
79
|
+
|
|
80
|
+
if (fields.length === 0) {
|
|
81
|
+
for (const initial of this._initialColors) {
|
|
82
|
+
if (initial.value) this.el.style.setProperty(initial.property, initial.value, initial.priority);
|
|
83
|
+
else this.el.style.removeProperty(initial.property);
|
|
84
|
+
}
|
|
85
|
+
return this;
|
|
86
|
+
}
|
|
87
|
+
for (let index = 0; index < COLOR_PROPERTIES.length; index += 1) {
|
|
88
|
+
this.el.style.setProperty(COLOR_PROPERTIES[index], fields[index]);
|
|
89
|
+
}
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Sets the light-field strength without replacing surface content. @param {string} intensity @returns {this} */
|
|
94
|
+
setIntensity(intensity) {
|
|
95
|
+
this.el.dataset.intensity = normalizeAuroraIntensity(intensity);
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Restores an enhanced target exactly, or removes an owned surface. @returns {void} */
|
|
100
|
+
destroy() {
|
|
101
|
+
if (this._cleaned) return;
|
|
102
|
+
this._cleaned = true;
|
|
103
|
+
super.destroy();
|
|
104
|
+
if (!this._createdRoot) restoreTarget(this.el, this._original);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** @param {unknown} value @returns {'source'|'confluence'|'horizon'|'diagonal'|'edge'|'curtain'} */
|
|
109
|
+
export function normalizeAuroraPreset(value) {
|
|
110
|
+
const preset = String(value ?? '').trim().toLowerCase();
|
|
111
|
+
if (!PRESETS.includes(preset)) throw new TypeError(`Unknown Aurora preset: ${value}`);
|
|
112
|
+
return /** @type {'source'|'confluence'|'horizon'|'diagonal'|'edge'|'curtain'} */ (preset);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** @param {unknown} value @returns {'subtle'|'balanced'|'vivid'} */
|
|
116
|
+
export function normalizeAuroraIntensity(value) {
|
|
117
|
+
const intensity = String(value ?? '').trim().toLowerCase();
|
|
118
|
+
if (!INTENSITIES.includes(intensity)) throw new TypeError(`Unknown Aurora intensity: ${value}`);
|
|
119
|
+
return /** @type {'subtle'|'balanced'|'vivid'} */ (intensity);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** @param {unknown} value @returns {string[]} */
|
|
123
|
+
export function normalizeAuroraColors(value) {
|
|
124
|
+
if (value == null) return [];
|
|
125
|
+
if (!Array.isArray(value)) throw new TypeError('Aurora colors must be an array');
|
|
126
|
+
if (value.length > COLOR_PROPERTIES.length) {
|
|
127
|
+
throw new TypeError('Aurora accepts at most four colors');
|
|
128
|
+
}
|
|
129
|
+
return value.map((color) => {
|
|
130
|
+
if (!isCssColor(color)) throw new TypeError(`Invalid Aurora color: ${color}`);
|
|
131
|
+
return color.trim();
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Repeats a one-to-three-colour palette deterministically across four gradient fields.
|
|
137
|
+
* @param {string[]} colors
|
|
138
|
+
* @returns {string[]}
|
|
139
|
+
*/
|
|
140
|
+
export function expandAuroraColors(colors) {
|
|
141
|
+
if (colors.length === 0) return [];
|
|
142
|
+
if (colors.length === 1) return [colors[0], colors[0], colors[0], colors[0]];
|
|
143
|
+
if (colors.length === 2) return [colors[0], colors[1], colors[0], colors[1]];
|
|
144
|
+
if (colors.length === 3) return [colors[0], colors[1], colors[2], colors[1]];
|
|
145
|
+
return [...colors];
|
|
146
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
min-block-size: var(--zx-control-height);
|
|
7
7
|
border: 1px solid var(--zx-color-border-control);
|
|
8
8
|
border-radius: var(--zx-button-radius, var(--zx-control-radius));
|
|
9
|
-
background: var(--zx-color-
|
|
9
|
+
background: var(--zx-color-glass-control);
|
|
10
10
|
color: var(--zx-color-text);
|
|
11
11
|
padding-inline: var(--zx-control-pad-x);
|
|
12
12
|
font: inherit;
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
font-weight: var(--zx-control-font-weight);
|
|
15
15
|
line-height: 1;
|
|
16
16
|
cursor: pointer;
|
|
17
|
+
-webkit-backdrop-filter: var(--zx-glass-filter-control);
|
|
18
|
+
backdrop-filter: var(--zx-glass-filter-control);
|
|
19
|
+
box-shadow: var(--zx-glass-control-shadow);
|
|
17
20
|
transition: color var(--zx-dur-fast) var(--zx-ease),
|
|
18
21
|
background-color var(--zx-dur-fast) var(--zx-ease),
|
|
19
22
|
border-color var(--zx-dur-fast) var(--zx-ease),
|
|
@@ -21,7 +24,7 @@
|
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
.zx-btn:hover:not(:disabled) {
|
|
24
|
-
background: var(--zx-color-
|
|
27
|
+
background: var(--zx-color-glass-control-hover);
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
.zx-btn:active:not(:disabled) {
|
|
@@ -66,6 +69,9 @@
|
|
|
66
69
|
.zx-btn[data-kind="ghost"] {
|
|
67
70
|
border-color: transparent;
|
|
68
71
|
background: none;
|
|
72
|
+
-webkit-backdrop-filter: none;
|
|
73
|
+
backdrop-filter: none;
|
|
74
|
+
box-shadow: none;
|
|
69
75
|
}
|
|
70
76
|
|
|
71
77
|
.zx-btn[data-kind="ghost"]:hover:not(:disabled) {
|
|
@@ -101,3 +107,29 @@
|
|
|
101
107
|
.zx-btn-group > .zx-btn:focus-visible {
|
|
102
108
|
z-index: 1;
|
|
103
109
|
}
|
|
110
|
+
|
|
111
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
112
|
+
.zx-btn {
|
|
113
|
+
background: var(--zx-color-bg-control);
|
|
114
|
+
-webkit-backdrop-filter: none;
|
|
115
|
+
backdrop-filter: none;
|
|
116
|
+
box-shadow: none;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@media (forced-colors: active) {
|
|
121
|
+
.zx-btn {
|
|
122
|
+
border-color: ButtonText;
|
|
123
|
+
background: ButtonFace;
|
|
124
|
+
color: ButtonText;
|
|
125
|
+
box-shadow: none;
|
|
126
|
+
-webkit-backdrop-filter: none;
|
|
127
|
+
backdrop-filter: none;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.zx-btn[data-kind="primary"] {
|
|
131
|
+
border-color: Highlight;
|
|
132
|
+
background: Highlight;
|
|
133
|
+
color: HighlightText;
|
|
134
|
+
}
|
|
135
|
+
}
|