@jxsuite/ui 0.0.0 → 0.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/components/jx-accordion-item.json +281 -0
- package/components/jx-accordion.json +33 -0
- package/components/jx-action-button.json +268 -34
- package/components/jx-action-group.json +131 -0
- package/components/jx-button.json +141 -26
- package/components/jx-checkbox.json +222 -0
- package/components/jx-color-area.json +263 -0
- package/components/jx-color-field.json +565 -0
- package/components/jx-color-slider.json +216 -0
- package/components/jx-combobox.json +551 -0
- package/components/jx-dialog.json +416 -0
- package/components/jx-divider.json +52 -0
- package/components/jx-dot.json +38 -0
- package/components/jx-field.json +162 -0
- package/components/jx-kbd.json +24 -0
- package/components/jx-listbox.json +76 -0
- package/components/jx-menu-item.json +6 -13
- package/components/jx-menu.json +45 -6
- package/components/jx-number-field.json +441 -0
- package/components/jx-option.json +350 -0
- package/components/jx-popover.json +227 -0
- package/components/jx-select.json +637 -0
- package/components/jx-spinner.json +129 -0
- package/components/jx-split.json +221 -0
- package/components/jx-swatch-group.json +107 -0
- package/components/jx-swatch.json +262 -0
- package/components/jx-switch.json +331 -0
- package/components/jx-tab-panel.json +58 -0
- package/components/jx-tab.json +296 -0
- package/components/jx-tabs.json +124 -0
- package/components/jx-textfield.json +645 -0
- package/components/jx-toast-host.json +121 -0
- package/components/jx-toast.json +255 -0
- package/components/jx-toolbar.json +81 -0
- package/components/jx-tooltip.json +207 -0
- package/components/jx-tree-item.json +325 -0
- package/components/jx-tree.json +202 -0
- package/icons/list.json +4 -0
- package/icons/manifest.json +12 -0
- package/package.json +27 -5
- package/project.json +164 -153
- package/src/behaviors/action-group.ts +255 -0
- package/src/behaviors/color-area.ts +303 -0
- package/src/behaviors/color-field.ts +465 -0
- package/src/behaviors/color-slider.ts +154 -0
- package/src/behaviors/combobox.ts +474 -0
- package/src/behaviors/dialog.ts +137 -0
- package/src/behaviors/listbox.ts +118 -0
- package/src/behaviors/menu.ts +80 -48
- package/src/behaviors/number-field.ts +161 -0
- package/src/behaviors/popover.ts +399 -0
- package/src/behaviors/select.ts +154 -0
- package/src/behaviors/split.ts +478 -0
- package/src/behaviors/swatch-group.ts +297 -0
- package/src/behaviors/tabs.ts +319 -0
- package/src/behaviors/textfield.ts +164 -0
- package/src/behaviors/toast-host.ts +241 -0
- package/src/behaviors/toast.ts +261 -0
- package/src/behaviors/toolbar.ts +413 -0
- package/src/behaviors/tooltip.ts +719 -0
- package/src/behaviors/tree.ts +830 -0
- package/src/color.ts +433 -0
- package/src/documents.ts +91 -1
- package/src/index.ts +41 -1
- package/src/loaders.ts +47 -0
- package/src/testing/anchor-support.ts +28 -0
- package/src/testing/popover-shim.ts +39 -6
- package/src/theme.ts +30 -6
- package/stylebook/jx-accordion-item.json +195 -0
- package/stylebook/jx-accordion.json +220 -0
- package/stylebook/jx-action-button.json +191 -23
- package/stylebook/jx-action-group.json +335 -0
- package/stylebook/jx-button.json +129 -26
- package/stylebook/jx-checkbox.json +245 -0
- package/stylebook/jx-color-area.json +272 -0
- package/stylebook/jx-color-field.json +261 -0
- package/stylebook/jx-color-slider.json +220 -0
- package/stylebook/jx-combobox.json +332 -0
- package/stylebook/jx-dialog.json +186 -0
- package/stylebook/jx-divider.json +125 -0
- package/stylebook/jx-dot.json +92 -0
- package/stylebook/jx-field.json +313 -0
- package/stylebook/jx-icon.json +4 -0
- package/stylebook/jx-kbd.json +90 -0
- package/stylebook/jx-listbox.json +337 -0
- package/stylebook/jx-menu.json +0 -1
- package/stylebook/jx-number-field.json +203 -0
- package/stylebook/jx-option.json +285 -0
- package/stylebook/jx-popover.json +123 -0
- package/stylebook/jx-select.json +407 -0
- package/stylebook/jx-spinner.json +111 -0
- package/stylebook/jx-split.json +170 -0
- package/stylebook/jx-swatch-group.json +175 -0
- package/stylebook/jx-swatch.json +249 -0
- package/stylebook/jx-switch.json +240 -0
- package/stylebook/jx-tab-panel.json +147 -0
- package/stylebook/jx-tab.json +201 -0
- package/stylebook/jx-tabs.json +278 -0
- package/stylebook/jx-textfield.json +309 -0
- package/stylebook/jx-toast-host.json +157 -0
- package/stylebook/jx-toast.json +266 -0
- package/stylebook/jx-toolbar.json +435 -0
- package/stylebook/jx-tooltip.json +162 -0
- package/stylebook/jx-tree-item.json +258 -0
- package/stylebook/jx-tree.json +429 -0
package/src/color.ts
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The colour maths the kit's colour elements are made of: sRGB, HSV, OKLCH, and the two WCAG
|
|
3
|
+
* numbers that decide whether a mark drawn on a colour can be seen.
|
|
4
|
+
*
|
|
5
|
+
* It is a MODULE rather than a document because none of it is expressible in the closed operator
|
|
6
|
+
* set: a cube root, an `atan2`, a piecewise transfer function and a regular expression are all
|
|
7
|
+
* arithmetic a `$expression` cannot say, and the elements that need them need them per keystroke.
|
|
8
|
+
* Nothing here touches the DOM, reads an element or holds state — it is the pure half of the colour
|
|
9
|
+
* family, and the three behaviour sidecars beside it are the half that may focus, measure and
|
|
10
|
+
* dispatch.
|
|
11
|
+
*
|
|
12
|
+
* THE MODEL EACH SPACE IS FOR.
|
|
13
|
+
*
|
|
14
|
+
* `Hsv` is the geometry of the 2D area and nothing else: `jx-color-area` is a saturation/brightness
|
|
15
|
+
* square under one hue, which is what that square's two axes ARE, so the element's props are its
|
|
16
|
+
* coordinates and no conversion happens while a pointer drags. `Rgb` is what a value is stored and
|
|
17
|
+
* transported as, because `#rrggbbaa` is what a colour input, a stylesheet and a project file all
|
|
18
|
+
* read. `Oklch` is what a colour is JUDGED in — a perceptual space, so a readout says something
|
|
19
|
+
* true about lightness — and it is the format `jx-color-field` writes when a host asks for one.
|
|
20
|
+
*
|
|
21
|
+
* ALPHA IS ALWAYS 0-1 and always present. A colour with no alpha is opaque, never `undefined`: an
|
|
22
|
+
* optional channel would make every caller write `?? 1` and one of them would forget, and the
|
|
23
|
+
* failure mode of forgetting is an invisible swatch.
|
|
24
|
+
*
|
|
25
|
+
* @docs extending/ui-kit
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/** An sRGB colour. `r`, `g` and `b` are 0-255 and need not be integers; `a` is 0-1. */
|
|
29
|
+
export interface Rgb {
|
|
30
|
+
r: number;
|
|
31
|
+
g: number;
|
|
32
|
+
b: number;
|
|
33
|
+
a: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A colour as the 2D area's own coordinates: hue 0-360, saturation and value 0-100, alpha 0-1. */
|
|
37
|
+
export interface Hsv {
|
|
38
|
+
h: number;
|
|
39
|
+
s: number;
|
|
40
|
+
v: number;
|
|
41
|
+
a: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** A colour in OKLCH: lightness 0-1, chroma 0 upward, hue 0-360 degrees, alpha 0-1. */
|
|
45
|
+
export interface Oklch {
|
|
46
|
+
l: number;
|
|
47
|
+
c: number;
|
|
48
|
+
h: number;
|
|
49
|
+
a: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* `value` held inside `[min, max]`. `NaN` resolves to `min`, because every caller is converting a
|
|
54
|
+
* reader's text or a measured coordinate and both can be nothing at all.
|
|
55
|
+
*
|
|
56
|
+
* @param value The number to hold.
|
|
57
|
+
* @param min The lower bound.
|
|
58
|
+
* @param max The upper bound.
|
|
59
|
+
* @returns The bounded number.
|
|
60
|
+
*/
|
|
61
|
+
export function clamp(value: number, min: number, max: number): number {
|
|
62
|
+
if (!Number.isFinite(value)) {
|
|
63
|
+
return min;
|
|
64
|
+
}
|
|
65
|
+
return value < min ? min : value > max ? max : value;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* `value` snapped to the grid `step` describes, measured from `min` and held inside `[min, max]`.
|
|
70
|
+
*
|
|
71
|
+
* The area's pointer path is why this exists: a drag produces a continuous coordinate and the two
|
|
72
|
+
* ranges behind it hold a stepped one, so without the snap the thumb and the announced value
|
|
73
|
+
* disagree by up to a step for the whole of a drag.
|
|
74
|
+
*
|
|
75
|
+
* @param value The number to snap.
|
|
76
|
+
* @param step The grid, measured from `min`; a non-positive step snaps nothing.
|
|
77
|
+
* @param min The lower bound and the grid's origin.
|
|
78
|
+
* @param max The upper bound.
|
|
79
|
+
* @returns The snapped, bounded number.
|
|
80
|
+
*/
|
|
81
|
+
export function snapToStep(value: number, step: number, min: number, max: number): number {
|
|
82
|
+
const held = clamp(value, min, max);
|
|
83
|
+
if (!Number.isFinite(step) || step <= 0) {
|
|
84
|
+
return held;
|
|
85
|
+
}
|
|
86
|
+
let snapped = min + Math.round((held - min) / step) * step;
|
|
87
|
+
/* HTML's own step algorithm, and the reason this is not a plain round: when the nearest rung is
|
|
88
|
+
ABOVE `max` the value falls back to the rung below it rather than to `max` itself, because
|
|
89
|
+
`max` need not be on the grid. 0 to 100 in fortieths rounds 100 up to 120, and a native range
|
|
90
|
+
holding the same three attributes reports 80. Without this the element and the two `<input
|
|
91
|
+
type="range">`s inside it would disagree about the value at the top of every uneven track. */
|
|
92
|
+
if (snapped > max) {
|
|
93
|
+
snapped -= step;
|
|
94
|
+
}
|
|
95
|
+
return clamp(Number(snapped.toFixed(6)), min, max);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Two hex digits for one 0-255 channel. */
|
|
99
|
+
function hexPair(channel: number): string {
|
|
100
|
+
return Math.round(clamp(channel, 0, 255))
|
|
101
|
+
.toString(16)
|
|
102
|
+
.padStart(2, "0");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* A `#rgb`, `#rgba`, `#rrggbb` or `#rrggbbaa` string as a colour, or `null` when it is none of
|
|
107
|
+
* those.
|
|
108
|
+
*
|
|
109
|
+
* The leading `#` is optional, because a reader retyping a value into a text field drops it about
|
|
110
|
+
* as often as they keep it, and refusing `3b82f6` would be pedantry rather than validation.
|
|
111
|
+
*
|
|
112
|
+
* @param text The text to read.
|
|
113
|
+
* @returns The colour, or null.
|
|
114
|
+
*/
|
|
115
|
+
export function parseHex(text: string): Rgb | null {
|
|
116
|
+
const digits = text.trim().replace(/^#/, "");
|
|
117
|
+
if (!/^[0-9a-f]+$/i.test(digits)) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const wide = digits.length === 6 || digits.length === 8;
|
|
121
|
+
const short = digits.length === 3 || digits.length === 4;
|
|
122
|
+
if (!wide && !short) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const size = wide ? 2 : 1;
|
|
126
|
+
const at = (index: number): number => {
|
|
127
|
+
const part = digits.slice(index * size, index * size + size);
|
|
128
|
+
return Number.parseInt(size === 1 ? part + part : part, 16);
|
|
129
|
+
};
|
|
130
|
+
const hasAlpha = digits.length === 4 || digits.length === 8;
|
|
131
|
+
return { a: hasAlpha ? at(3) / 255 : 1, b: at(2), g: at(1), r: at(0) };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* One component of a CSS colour function, as a number. A percentage is scaled by `full`.
|
|
136
|
+
*
|
|
137
|
+
* @param token The token as written.
|
|
138
|
+
* @param full What 100% means for this component.
|
|
139
|
+
* @returns The number, or NaN when the token is not one.
|
|
140
|
+
*/
|
|
141
|
+
function component(token: string, full: number): number {
|
|
142
|
+
/* `Number` rather than `parseFloat`, and the percent sign taken off by hand: `parseFloat` reads
|
|
143
|
+
as far as it understands and stops, so it turns "12abc" into 12 and a reader's typo into a
|
|
144
|
+
colour. `Number` refuses the whole token, which is the answer a parser owes its caller. */
|
|
145
|
+
if (token.endsWith("%")) {
|
|
146
|
+
return (Number(token.slice(0, -1)) / 100) * full;
|
|
147
|
+
}
|
|
148
|
+
return Number(token);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** The arguments of `name(...)`, split on commas, whitespace and the alpha solidus. */
|
|
152
|
+
function argsOf(text: string, name: string): string[] | null {
|
|
153
|
+
const match = new RegExp(`^${name}a?\\(([^)]*)\\)$`, "i").exec(text.trim());
|
|
154
|
+
if (!match) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
return match[1]!
|
|
158
|
+
.trim()
|
|
159
|
+
.split(/[\s,/]+/)
|
|
160
|
+
.filter(Boolean);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* A colour written as hex, `rgb()`/`rgba()` or `oklch()`, or `null` when the text says none of
|
|
165
|
+
* those.
|
|
166
|
+
*
|
|
167
|
+
* These three and no more, deliberately. Hex is what the element stores, `rgb()` is what
|
|
168
|
+
* `getComputedStyle` hands back for anything a host already had, and `oklch()` is the format the
|
|
169
|
+
* field itself writes — so the set is "what this element can produce", which is exactly the set a
|
|
170
|
+
* reader can paste back into it and get the same colour. A named colour or a `color()` is a
|
|
171
|
+
* question for the platform, and the honest answer to "did the platform understand it" is a
|
|
172
|
+
* computed style, not a table copied into a kit.
|
|
173
|
+
*
|
|
174
|
+
* @param text The text to read.
|
|
175
|
+
* @returns The colour, or null.
|
|
176
|
+
*/
|
|
177
|
+
export function parseColor(text: string): Rgb | null {
|
|
178
|
+
const trimmed = text.trim();
|
|
179
|
+
const rgb = argsOf(trimmed, "rgb");
|
|
180
|
+
if (rgb && rgb.length >= 3) {
|
|
181
|
+
const parts = [component(rgb[0]!, 255), component(rgb[1]!, 255), component(rgb[2]!, 255)];
|
|
182
|
+
const alpha = rgb.length > 3 ? component(rgb[3]!, 1) : 1;
|
|
183
|
+
if (parts.some((n) => Number.isNaN(n)) || Number.isNaN(alpha)) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
a: clamp(alpha, 0, 1),
|
|
188
|
+
b: clamp(parts[2]!, 0, 255),
|
|
189
|
+
g: clamp(parts[1]!, 0, 255),
|
|
190
|
+
r: clamp(parts[0]!, 0, 255),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
const lch = argsOf(trimmed, "oklch");
|
|
194
|
+
if (lch && lch.length >= 3) {
|
|
195
|
+
const l = component(lch[0]!, 1);
|
|
196
|
+
const c = component(lch[1]!, 0.4);
|
|
197
|
+
const h = Number(lch[2]!.replace(/deg$/i, ""));
|
|
198
|
+
const alpha = lch.length > 3 ? component(lch[3]!, 1) : 1;
|
|
199
|
+
if ([l, c, h, alpha].some((n) => Number.isNaN(n))) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
return oklchToSrgb({ a: clamp(alpha, 0, 1), c, h, l });
|
|
203
|
+
}
|
|
204
|
+
return parseHex(trimmed);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The `#rrggbb` or `#rrggbbaa` spelling of a colour. The alpha pair is written only when the colour
|
|
209
|
+
* is not opaque, so an opaque value round-trips through a native `<input type="color">` — which
|
|
210
|
+
* accepts the six-digit form and nothing else — without growing two digits it cannot read.
|
|
211
|
+
*
|
|
212
|
+
* @param rgb The colour.
|
|
213
|
+
* @returns The hex string, lowercase, with its leading `#`.
|
|
214
|
+
*/
|
|
215
|
+
export function formatHex(rgb: Rgb): string {
|
|
216
|
+
const base = `#${hexPair(rgb.r)}${hexPair(rgb.g)}${hexPair(rgb.b)}`;
|
|
217
|
+
return rgb.a >= 1 ? base : `${base}${hexPair(clamp(rgb.a, 0, 1) * 255)}`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* A colour as the area's own coordinates.
|
|
222
|
+
*
|
|
223
|
+
* A grey has no hue to report, and this returns 0 for it rather than something arbitrary — but the
|
|
224
|
+
* element never asks: `jx-color-field` keeps the hue it was last given while the reader drags the
|
|
225
|
+
* saturation to zero, because a hue slider that snapped back to red every time a colour went grey
|
|
226
|
+
* would be unusable.
|
|
227
|
+
*
|
|
228
|
+
* @param rgb The colour.
|
|
229
|
+
* @returns Its hue, saturation, value and alpha.
|
|
230
|
+
*/
|
|
231
|
+
export function rgbToHsv(rgb: Rgb): Hsv {
|
|
232
|
+
const r = clamp(rgb.r, 0, 255) / 255;
|
|
233
|
+
const g = clamp(rgb.g, 0, 255) / 255;
|
|
234
|
+
const b = clamp(rgb.b, 0, 255) / 255;
|
|
235
|
+
const max = Math.max(r, g, b);
|
|
236
|
+
const min = Math.min(r, g, b);
|
|
237
|
+
const span = max - min;
|
|
238
|
+
let h = 0;
|
|
239
|
+
if (span !== 0) {
|
|
240
|
+
if (max === r) {
|
|
241
|
+
h = 60 * (((g - b) / span) % 6);
|
|
242
|
+
} else if (max === g) {
|
|
243
|
+
h = 60 * ((b - r) / span + 2);
|
|
244
|
+
} else {
|
|
245
|
+
h = 60 * ((r - g) / span + 4);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
a: clamp(rgb.a, 0, 1),
|
|
250
|
+
h: (h + 360) % 360,
|
|
251
|
+
s: max === 0 ? 0 : (span / max) * 100,
|
|
252
|
+
v: max * 100,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* The colour at a point of the area, under a hue.
|
|
258
|
+
*
|
|
259
|
+
* @param hsv The coordinates.
|
|
260
|
+
* @returns The sRGB colour.
|
|
261
|
+
*/
|
|
262
|
+
export function hsvToRgb(hsv: Hsv): Rgb {
|
|
263
|
+
const h = ((hsv.h % 360) + 360) % 360;
|
|
264
|
+
const s = clamp(hsv.s, 0, 100) / 100;
|
|
265
|
+
const v = clamp(hsv.v, 0, 100) / 100;
|
|
266
|
+
const chroma = v * s;
|
|
267
|
+
const x = chroma * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
268
|
+
const m = v - chroma;
|
|
269
|
+
const sector = Math.floor(h / 60) % 6;
|
|
270
|
+
const table: [number, number, number][] = [
|
|
271
|
+
[chroma, x, 0],
|
|
272
|
+
[x, chroma, 0],
|
|
273
|
+
[0, chroma, x],
|
|
274
|
+
[0, x, chroma],
|
|
275
|
+
[x, 0, chroma],
|
|
276
|
+
[chroma, 0, x],
|
|
277
|
+
];
|
|
278
|
+
const [r, g, b] = table[sector]!;
|
|
279
|
+
return {
|
|
280
|
+
a: clamp(hsv.a, 0, 1),
|
|
281
|
+
b: (b + m) * 255,
|
|
282
|
+
g: (g + m) * 255,
|
|
283
|
+
r: (r + m) * 255,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** One sRGB channel, 0-1, off its transfer function and onto light. */
|
|
288
|
+
function linearize(channel: number): number {
|
|
289
|
+
return channel <= 0.04045 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** One linear-light channel back onto sRGB's transfer function. */
|
|
293
|
+
function delinearize(channel: number): number {
|
|
294
|
+
return channel <= 0.0031308 ? channel * 12.92 : 1.055 * channel ** (1 / 2.4) - 0.055;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* A colour in OKLCH: what the readout says and what a host asking for a perceptual value gets.
|
|
299
|
+
*
|
|
300
|
+
* The matrices are Björn Ottosson's, which CSS Color 4 §10 adopts verbatim, so this and a browser's
|
|
301
|
+
* own `oklch()` agree to within float noise.
|
|
302
|
+
*
|
|
303
|
+
* @param rgb The colour.
|
|
304
|
+
* @returns Its lightness, chroma, hue and alpha.
|
|
305
|
+
*/
|
|
306
|
+
export function srgbToOklch(rgb: Rgb): Oklch {
|
|
307
|
+
const r = linearize(clamp(rgb.r, 0, 255) / 255);
|
|
308
|
+
const g = linearize(clamp(rgb.g, 0, 255) / 255);
|
|
309
|
+
const b = linearize(clamp(rgb.b, 0, 255) / 255);
|
|
310
|
+
const lCone = Math.cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
|
|
311
|
+
const mCone = Math.cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
|
|
312
|
+
const sCone = Math.cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
|
|
313
|
+
const l = 0.2104542553 * lCone + 0.793617785 * mCone - 0.0040720468 * sCone;
|
|
314
|
+
const a = 1.9779984951 * lCone - 2.428592205 * mCone + 0.4505937099 * sCone;
|
|
315
|
+
const bAxis = 0.0259040371 * lCone + 0.7827717662 * mCone - 0.808675766 * sCone;
|
|
316
|
+
const chroma = Math.hypot(a, bAxis);
|
|
317
|
+
return {
|
|
318
|
+
a: clamp(rgb.a, 0, 1),
|
|
319
|
+
c: chroma,
|
|
320
|
+
h: chroma < 1e-6 ? 0 : ((Math.atan2(bAxis, a) * 180) / Math.PI + 360) % 360,
|
|
321
|
+
l,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* An OKLCH colour as sRGB, CLAMPED into the gamut per channel.
|
|
327
|
+
*
|
|
328
|
+
* Per-channel clamping is not gamut mapping and is not pretending to be: a chroma sRGB cannot hold
|
|
329
|
+
* comes back as the nearest thing sRGB can draw, with its hue shifted. That is the honest answer
|
|
330
|
+
* for this kit — the only OKLCH values it converts are ones it computed FROM an sRGB colour a
|
|
331
|
+
* moment earlier, so the round trip is exact and the clamp is unreachable on every path the
|
|
332
|
+
* elements take. It exists for `parseColor`, where a reader may type any three numbers at all.
|
|
333
|
+
*
|
|
334
|
+
* @param color The OKLCH colour.
|
|
335
|
+
* @returns The sRGB colour.
|
|
336
|
+
*/
|
|
337
|
+
export function oklchToSrgb(color: Oklch): Rgb {
|
|
338
|
+
const hue = (((color.h % 360) + 360) % 360) * (Math.PI / 180);
|
|
339
|
+
const a = color.c * Math.cos(hue);
|
|
340
|
+
const bAxis = color.c * Math.sin(hue);
|
|
341
|
+
const lCone = (color.l + 0.3963377774 * a + 0.2158037573 * bAxis) ** 3;
|
|
342
|
+
const mCone = (color.l - 0.1055613458 * a - 0.0638541728 * bAxis) ** 3;
|
|
343
|
+
const sCone = (color.l - 0.0894841775 * a - 1.291485548 * bAxis) ** 3;
|
|
344
|
+
const r = 4.0767416621 * lCone - 3.3077115913 * mCone + 0.2309699292 * sCone;
|
|
345
|
+
const g = -1.2684380046 * lCone + 2.6097574011 * mCone - 0.3413193965 * sCone;
|
|
346
|
+
const b = -0.0041960863 * lCone - 0.7034186147 * mCone + 1.707614701 * sCone;
|
|
347
|
+
return {
|
|
348
|
+
a: clamp(color.a, 0, 1),
|
|
349
|
+
b: clamp(delinearize(b) * 255, 0, 255),
|
|
350
|
+
g: clamp(delinearize(g) * 255, 0, 255),
|
|
351
|
+
r: clamp(delinearize(r) * 255, 0, 255),
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* The `oklch()` spelling of a colour, rounded to what a reader can act on: lightness as a
|
|
357
|
+
* percentage to one decimal, chroma to three, hue to one.
|
|
358
|
+
*
|
|
359
|
+
* @param color The OKLCH colour.
|
|
360
|
+
* @returns The CSS function, with its alpha only when the colour is not opaque.
|
|
361
|
+
*/
|
|
362
|
+
export function formatOklch(color: Oklch): string {
|
|
363
|
+
const body = `${(color.l * 100).toFixed(1)}% ${color.c.toFixed(3)} ${color.h.toFixed(1)}`;
|
|
364
|
+
return color.a >= 1 ? `oklch(${body})` : `oklch(${body} / ${Number(color.a.toFixed(3))})`;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* WCAG 2.2's relative luminance, which is the sRGB definition and not OKLCH's lightness. The two
|
|
369
|
+
* disagree, and the contrast criteria are written against this one.
|
|
370
|
+
*
|
|
371
|
+
* @param rgb The colour.
|
|
372
|
+
* @returns Its relative luminance, 0 to 1.
|
|
373
|
+
*/
|
|
374
|
+
export function relativeLuminance(rgb: Rgb): number {
|
|
375
|
+
const r = linearize(clamp(rgb.r, 0, 255) / 255);
|
|
376
|
+
const g = linearize(clamp(rgb.g, 0, 255) / 255);
|
|
377
|
+
const b = linearize(clamp(rgb.b, 0, 255) / 255);
|
|
378
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* The contrast ratio between two colours, 1 to 21, as SC 1.4.3 and 1.4.11 define it.
|
|
383
|
+
*
|
|
384
|
+
* Alpha is not composited: a ratio between two translucent colours is not defined without knowing
|
|
385
|
+
* what is behind them, and the kit's one caller passes opaque colours.
|
|
386
|
+
*
|
|
387
|
+
* @param one A colour.
|
|
388
|
+
* @param two The other colour.
|
|
389
|
+
* @returns The ratio, with the lighter colour on top whichever order they arrived in.
|
|
390
|
+
*/
|
|
391
|
+
export function contrastRatio(one: Rgb, two: Rgb): number {
|
|
392
|
+
const a = relativeLuminance(one);
|
|
393
|
+
const b = relativeLuminance(two);
|
|
394
|
+
return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/** Pure black, for the contrast comparison below. */
|
|
398
|
+
const BLACK: Rgb = { a: 1, b: 0, g: 0, r: 0 };
|
|
399
|
+
/** Pure white, likewise. */
|
|
400
|
+
const WHITE: Rgb = { a: 1, b: 255, g: 255, r: 255 };
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Which ink a mark drawn ON this colour should use, decided by measuring both rather than by a
|
|
404
|
+
* luminance threshold somebody once wrote down.
|
|
405
|
+
*
|
|
406
|
+
* This is SC 1.4.11 for the one mark the kit draws over a colour it does not choose: the tick on a
|
|
407
|
+
* selected swatch. A fixed ink fails against half the gamut, and a threshold is a guess at the
|
|
408
|
+
* answer this computes.
|
|
409
|
+
*
|
|
410
|
+
* @param rgb The colour the mark is drawn on.
|
|
411
|
+
* @returns `"black"` or `"white"`, whichever has the greater ratio against it.
|
|
412
|
+
*/
|
|
413
|
+
export function preferredInk(rgb: Rgb): "black" | "white" {
|
|
414
|
+
return contrastRatio(rgb, BLACK) >= contrastRatio(rgb, WHITE) ? "black" : "white";
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* The ink a mark or a boundary drawn ON a colour should use, as a word a document can put straight
|
|
419
|
+
* into a custom property, or `""` when the colour is not one this module can read.
|
|
420
|
+
*
|
|
421
|
+
* This is the `$src` door onto {@link preferredInk}: `jx-swatch` reaches it from its own document,
|
|
422
|
+
* so a swatch works out its own tick and its own ring from its own `color` with no host, no group
|
|
423
|
+
* and no sidecar writing into it. The empty answer is deliberate and is not a failure — a swatch
|
|
424
|
+
* coloured `var(--jx-accent)` or `rebeccapurple` is a colour only the browser can resolve, and the
|
|
425
|
+
* element falls back to the kit's border token rather than guessing at black.
|
|
426
|
+
*
|
|
427
|
+
* @param color The colour, as it was written.
|
|
428
|
+
* @returns `"black"`, `"white"`, or `""`.
|
|
429
|
+
*/
|
|
430
|
+
export function inkOn(color: unknown): string {
|
|
431
|
+
const parsed = typeof color === "string" ? parseColor(color) : null;
|
|
432
|
+
return parsed ? preferredInk({ ...parsed, a: 1 }) : "";
|
|
433
|
+
}
|
package/src/documents.ts
CHANGED
|
@@ -3,21 +3,111 @@
|
|
|
3
3
|
* every element is one of these files, interpreted by the runtime, and the canvas edits the same
|
|
4
4
|
* bytes the shell runs.
|
|
5
5
|
*/
|
|
6
|
-
import
|
|
6
|
+
import { overlayScopeFor } from "@jxsuite/schema/overlays";
|
|
7
|
+
import type { JxDocument, JxElement } from "@jxsuite/schema/types";
|
|
8
|
+
import jxAccordion from "../components/jx-accordion.json";
|
|
9
|
+
import jxAccordionItem from "../components/jx-accordion-item.json";
|
|
7
10
|
import jxActionButton from "../components/jx-action-button.json";
|
|
11
|
+
import jxActionGroup from "../components/jx-action-group.json";
|
|
8
12
|
import jxButton from "../components/jx-button.json";
|
|
13
|
+
import jxCheckbox from "../components/jx-checkbox.json";
|
|
14
|
+
import jxColorArea from "../components/jx-color-area.json";
|
|
15
|
+
import jxColorField from "../components/jx-color-field.json";
|
|
16
|
+
import jxColorSlider from "../components/jx-color-slider.json";
|
|
17
|
+
import jxDialog from "../components/jx-dialog.json";
|
|
18
|
+
import jxDivider from "../components/jx-divider.json";
|
|
19
|
+
import jxDot from "../components/jx-dot.json";
|
|
20
|
+
import jxField from "../components/jx-field.json";
|
|
21
|
+
import jxKbd from "../components/jx-kbd.json";
|
|
9
22
|
import jxIcon from "../components/jx-icon.json";
|
|
10
23
|
import jxMenu from "../components/jx-menu.json";
|
|
11
24
|
import jxMenuItem from "../components/jx-menu-item.json";
|
|
25
|
+
import jxPopover from "../components/jx-popover.json";
|
|
26
|
+
import jxCombobox from "../components/jx-combobox.json";
|
|
27
|
+
import jxListbox from "../components/jx-listbox.json";
|
|
28
|
+
import jxOption from "../components/jx-option.json";
|
|
29
|
+
import jxSelect from "../components/jx-select.json";
|
|
30
|
+
import jxSpinner from "../components/jx-spinner.json";
|
|
31
|
+
import jxSplit from "../components/jx-split.json";
|
|
32
|
+
import jxTooltip from "../components/jx-tooltip.json";
|
|
33
|
+
import jxNumberField from "../components/jx-number-field.json";
|
|
34
|
+
import jxSwatch from "../components/jx-swatch.json";
|
|
35
|
+
import jxSwatchGroup from "../components/jx-swatch-group.json";
|
|
36
|
+
import jxSwitch from "../components/jx-switch.json";
|
|
37
|
+
import jxTab from "../components/jx-tab.json";
|
|
38
|
+
import jxTabPanel from "../components/jx-tab-panel.json";
|
|
39
|
+
import jxTabs from "../components/jx-tabs.json";
|
|
40
|
+
import jxTextfield from "../components/jx-textfield.json";
|
|
41
|
+
import jxToast from "../components/jx-toast.json";
|
|
42
|
+
import jxTree from "../components/jx-tree.json";
|
|
43
|
+
import jxTreeItem from "../components/jx-tree-item.json";
|
|
44
|
+
import jxToastHost from "../components/jx-toast-host.json";
|
|
45
|
+
import jxToolbar from "../components/jx-toolbar.json";
|
|
12
46
|
|
|
13
47
|
/** Tag name → document, in registration order (a document's `$elements` come before it). */
|
|
14
48
|
export const documents: Readonly<Record<string, JxDocument>> = {
|
|
15
49
|
"jx-icon": jxIcon as unknown as JxDocument,
|
|
50
|
+
"jx-spinner": jxSpinner as unknown as JxDocument,
|
|
51
|
+
// Before the buttons: each renders a `jx-tooltip` for its `hint`.
|
|
52
|
+
"jx-tooltip": jxTooltip as unknown as JxDocument,
|
|
16
53
|
"jx-button": jxButton as unknown as JxDocument,
|
|
17
54
|
"jx-action-button": jxActionButton as unknown as JxDocument,
|
|
18
55
|
"jx-menu-item": jxMenuItem as unknown as JxDocument,
|
|
19
56
|
"jx-menu": jxMenu as unknown as JxDocument,
|
|
57
|
+
"jx-popover": jxPopover as unknown as JxDocument,
|
|
58
|
+
"jx-textfield": jxTextfield as unknown as JxDocument,
|
|
59
|
+
"jx-checkbox": jxCheckbox as unknown as JxDocument,
|
|
60
|
+
"jx-switch": jxSwitch as unknown as JxDocument,
|
|
61
|
+
"jx-number-field": jxNumberField as unknown as JxDocument,
|
|
62
|
+
"jx-select": jxSelect as unknown as JxDocument,
|
|
63
|
+
"jx-option": jxOption as unknown as JxDocument,
|
|
64
|
+
"jx-listbox": jxListbox as unknown as JxDocument,
|
|
65
|
+
"jx-combobox": jxCombobox as unknown as JxDocument,
|
|
66
|
+
"jx-action-group": jxActionGroup as unknown as JxDocument,
|
|
67
|
+
"jx-toolbar": jxToolbar as unknown as JxDocument,
|
|
68
|
+
"jx-tab": jxTab as unknown as JxDocument,
|
|
69
|
+
"jx-tab-panel": jxTabPanel as unknown as JxDocument,
|
|
70
|
+
"jx-tabs": jxTabs as unknown as JxDocument,
|
|
71
|
+
"jx-tree-item": jxTreeItem as unknown as JxDocument,
|
|
72
|
+
"jx-tree": jxTree as unknown as JxDocument,
|
|
73
|
+
"jx-accordion-item": jxAccordionItem as unknown as JxDocument,
|
|
74
|
+
"jx-accordion": jxAccordion as unknown as JxDocument,
|
|
75
|
+
"jx-divider": jxDivider as unknown as JxDocument,
|
|
76
|
+
"jx-split": jxSplit as unknown as JxDocument,
|
|
77
|
+
"jx-kbd": jxKbd as unknown as JxDocument,
|
|
78
|
+
"jx-dot": jxDot as unknown as JxDocument,
|
|
79
|
+
"jx-field": jxField as unknown as JxDocument,
|
|
80
|
+
"jx-dialog": jxDialog as unknown as JxDocument,
|
|
81
|
+
"jx-toast": jxToast as unknown as JxDocument,
|
|
82
|
+
"jx-toast-host": jxToastHost as unknown as JxDocument,
|
|
83
|
+
"jx-color-area": jxColorArea as unknown as JxDocument,
|
|
84
|
+
"jx-color-slider": jxColorSlider as unknown as JxDocument,
|
|
85
|
+
"jx-swatch": jxSwatch as unknown as JxDocument,
|
|
86
|
+
"jx-swatch-group": jxSwatchGroup as unknown as JxDocument,
|
|
87
|
+
"jx-color-field": jxColorField as unknown as JxDocument,
|
|
20
88
|
};
|
|
21
89
|
|
|
22
90
|
/** Every tag the kit defines. */
|
|
23
91
|
export const KIT_TAGS: readonly string[] = Object.keys(documents);
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The tags whose DEFINITION declares `popover` on its root, and the tags that forward the invoker
|
|
95
|
+
* attributes to a native `<button>` inside themselves.
|
|
96
|
+
*
|
|
97
|
+
* A consumer writes `<jx-menu id="m1">` and the `popover` attribute is nowhere in their document,
|
|
98
|
+
* so every structural rule in `@jxsuite/schema/overlays` read it as an ordinary element: a command
|
|
99
|
+
* aimed at it was a target mismatch, and a host asking "does this document have a popover?" was
|
|
100
|
+
* told no. The mirror image is `popovertarget`, which HTML gives only to `<button>` and `<input>`,
|
|
101
|
+
* so the lints refused it on a kit button that observes it and passes it to its own control.
|
|
102
|
+
*
|
|
103
|
+
* DERIVED from the documents by the schema's own `overlayScopeFor`, not listed here: the compiler
|
|
104
|
+
* asks the same question of a project's `*.class.json` definitions, and two derivations of "what
|
|
105
|
+
* makes a tag a popover" is two chances to disagree about one project.
|
|
106
|
+
*/
|
|
107
|
+
const KIT_SCOPE = overlayScopeFor(Object.values(documents) as JxElement[]);
|
|
108
|
+
|
|
109
|
+
/** Tags whose definition declares `popover`. */
|
|
110
|
+
export const POPOVER_TAGS: ReadonlySet<string> = KIT_SCOPE.popoverTags ?? new Set();
|
|
111
|
+
|
|
112
|
+
/** Tags that forward `popovertarget` and `commandfor` to a native button inside themselves. */
|
|
113
|
+
export const INVOKER_TAGS: ReadonlySet<string> = KIT_SCOPE.invokerTags ?? new Set();
|
package/src/index.ts
CHANGED
|
@@ -10,12 +10,32 @@
|
|
|
10
10
|
* @docs extending/ui-kit
|
|
11
11
|
*/
|
|
12
12
|
import { defineElement, preloadDocument, preloadModule } from "@jxsuite/runtime";
|
|
13
|
+
import * as color from "./color.ts";
|
|
14
|
+
import * as colorArea from "./behaviors/color-area.ts";
|
|
15
|
+
import * as colorField from "./behaviors/color-field.ts";
|
|
16
|
+
import * as colorSlider from "./behaviors/color-slider.ts";
|
|
17
|
+
import * as combobox from "./behaviors/combobox.ts";
|
|
18
|
+
import * as listbox from "./behaviors/listbox.ts";
|
|
19
|
+
import * as dialog from "./behaviors/dialog.ts";
|
|
13
20
|
import * as menu from "./behaviors/menu.ts";
|
|
21
|
+
import * as numberField from "./behaviors/number-field.ts";
|
|
22
|
+
import * as actionGroup from "./behaviors/action-group.ts";
|
|
23
|
+
import * as popover from "./behaviors/popover.ts";
|
|
24
|
+
import * as select from "./behaviors/select.ts";
|
|
25
|
+
import * as split from "./behaviors/split.ts";
|
|
26
|
+
import * as swatchGroup from "./behaviors/swatch-group.ts";
|
|
27
|
+
import * as tabs from "./behaviors/tabs.ts";
|
|
28
|
+
import * as toolbar from "./behaviors/toolbar.ts";
|
|
29
|
+
import * as tree from "./behaviors/tree.ts";
|
|
30
|
+
import * as tooltip from "./behaviors/tooltip.ts";
|
|
31
|
+
import * as textfield from "./behaviors/textfield.ts";
|
|
32
|
+
import * as toast from "./behaviors/toast.ts";
|
|
33
|
+
import * as toastHost from "./behaviors/toast-host.ts";
|
|
14
34
|
import { documents } from "./documents.ts";
|
|
15
35
|
import * as icons from "./icons.ts";
|
|
16
36
|
import { installTheme } from "./theme.ts";
|
|
17
37
|
|
|
18
|
-
export { documents, KIT_TAGS } from "./documents.ts";
|
|
38
|
+
export { documents, INVOKER_TAGS, KIT_TAGS, POPOVER_TAGS } from "./documents.ts";
|
|
19
39
|
export { hasIcon, ICON_NAMES, ICON_WEIGHTS, iconPath, iconViewBox } from "./icons.ts";
|
|
20
40
|
export type { IconWeight } from "./icons.ts";
|
|
21
41
|
export {
|
|
@@ -32,7 +52,27 @@ export const KIT_BASE = "jx-ui:/components/";
|
|
|
32
52
|
|
|
33
53
|
/** Behaviour modules, under the `$src` specifiers the documents use. */
|
|
34
54
|
export const KIT_MODULES: Readonly<Record<string, Record<string, unknown>>> = {
|
|
55
|
+
"jx-ui:/behaviors/color-area.ts": colorArea,
|
|
56
|
+
"jx-ui:/behaviors/color-field.ts": colorField,
|
|
57
|
+
"jx-ui:/behaviors/color-slider.ts": colorSlider,
|
|
58
|
+
"jx-ui:/behaviors/combobox.ts": combobox,
|
|
59
|
+
"jx-ui:/behaviors/dialog.ts": dialog,
|
|
60
|
+
"jx-ui:/behaviors/listbox.ts": listbox,
|
|
35
61
|
"jx-ui:/behaviors/menu.ts": menu,
|
|
62
|
+
"jx-ui:/behaviors/number-field.ts": numberField,
|
|
63
|
+
"jx-ui:/behaviors/action-group.ts": actionGroup,
|
|
64
|
+
"jx-ui:/behaviors/popover.ts": popover,
|
|
65
|
+
"jx-ui:/behaviors/select.ts": select,
|
|
66
|
+
"jx-ui:/behaviors/split.ts": split,
|
|
67
|
+
"jx-ui:/behaviors/swatch-group.ts": swatchGroup,
|
|
68
|
+
"jx-ui:/behaviors/tabs.ts": tabs,
|
|
69
|
+
"jx-ui:/behaviors/toolbar.ts": toolbar,
|
|
70
|
+
"jx-ui:/behaviors/tree.ts": tree,
|
|
71
|
+
"jx-ui:/behaviors/tooltip.ts": tooltip,
|
|
72
|
+
"jx-ui:/behaviors/textfield.ts": textfield,
|
|
73
|
+
"jx-ui:/behaviors/toast.ts": toast,
|
|
74
|
+
"jx-ui:/behaviors/toast-host.ts": toastHost,
|
|
75
|
+
"jx-ui:/color.ts": color,
|
|
36
76
|
"jx-ui:/icons.ts": icons,
|
|
37
77
|
};
|
|
38
78
|
|
package/src/loaders.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The kit's `$src` modules as LOADERS: the same table as `KIT_MODULES`, under the same specifiers,
|
|
3
|
+
* with every entry a dynamic import rather than a namespace.
|
|
4
|
+
*
|
|
5
|
+
* A host that registers the kit's elements needs the behaviours at once, because the elements are
|
|
6
|
+
* about to render — that is `registerUi()` and `KIT_MODULES`. A host that only MIGHT render a kit
|
|
7
|
+
* element, from documents it does not know in advance, should not pay for twenty sidecars on the
|
|
8
|
+
* chance: Studio's canvas frame is that host. It renders whatever project is open, and a project
|
|
9
|
+
* that uses a kit element names the element's document by URL and the document names its sidecar by
|
|
10
|
+
* `jx-ui:` specifier; this table is what lets the frame answer that specifier when it comes, and
|
|
11
|
+
* not before. Each entry is its own chunk under a bundler that splits on `import()`, so the frame's
|
|
12
|
+
* entry grows by a table of twenty thunks and nothing else (embedding.md §6).
|
|
13
|
+
*
|
|
14
|
+
* `loaders.test.ts` holds the two tables to one key set, so a behaviour added to one cannot be
|
|
15
|
+
* forgotten in the other.
|
|
16
|
+
*
|
|
17
|
+
* @docs extending/ui-kit
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** A module the runtime imports on first use, keyed as the kit's documents spell their `$src`. */
|
|
21
|
+
export type KitModuleLoader = () => Promise<Record<string, unknown>>;
|
|
22
|
+
|
|
23
|
+
/** Behaviour modules as loaders, under the `$src` specifiers the documents use. */
|
|
24
|
+
export const KIT_LOADERS: Readonly<Record<string, KitModuleLoader>> = {
|
|
25
|
+
"jx-ui:/behaviors/color-area.ts": () => import("./behaviors/color-area.ts"),
|
|
26
|
+
"jx-ui:/behaviors/color-field.ts": () => import("./behaviors/color-field.ts"),
|
|
27
|
+
"jx-ui:/behaviors/color-slider.ts": () => import("./behaviors/color-slider.ts"),
|
|
28
|
+
"jx-ui:/behaviors/combobox.ts": () => import("./behaviors/combobox.ts"),
|
|
29
|
+
"jx-ui:/behaviors/dialog.ts": () => import("./behaviors/dialog.ts"),
|
|
30
|
+
"jx-ui:/behaviors/listbox.ts": () => import("./behaviors/listbox.ts"),
|
|
31
|
+
"jx-ui:/behaviors/menu.ts": () => import("./behaviors/menu.ts"),
|
|
32
|
+
"jx-ui:/behaviors/number-field.ts": () => import("./behaviors/number-field.ts"),
|
|
33
|
+
"jx-ui:/behaviors/action-group.ts": () => import("./behaviors/action-group.ts"),
|
|
34
|
+
"jx-ui:/behaviors/popover.ts": () => import("./behaviors/popover.ts"),
|
|
35
|
+
"jx-ui:/behaviors/select.ts": () => import("./behaviors/select.ts"),
|
|
36
|
+
"jx-ui:/behaviors/split.ts": () => import("./behaviors/split.ts"),
|
|
37
|
+
"jx-ui:/behaviors/swatch-group.ts": () => import("./behaviors/swatch-group.ts"),
|
|
38
|
+
"jx-ui:/behaviors/tabs.ts": () => import("./behaviors/tabs.ts"),
|
|
39
|
+
"jx-ui:/behaviors/toolbar.ts": () => import("./behaviors/toolbar.ts"),
|
|
40
|
+
"jx-ui:/behaviors/tree.ts": () => import("./behaviors/tree.ts"),
|
|
41
|
+
"jx-ui:/behaviors/tooltip.ts": () => import("./behaviors/tooltip.ts"),
|
|
42
|
+
"jx-ui:/behaviors/textfield.ts": () => import("./behaviors/textfield.ts"),
|
|
43
|
+
"jx-ui:/behaviors/toast.ts": () => import("./behaviors/toast.ts"),
|
|
44
|
+
"jx-ui:/behaviors/toast-host.ts": () => import("./behaviors/toast-host.ts"),
|
|
45
|
+
"jx-ui:/color.ts": () => import("./color.ts"),
|
|
46
|
+
"jx-ui:/icons.ts": () => import("./icons.ts"),
|
|
47
|
+
};
|