@ttoss/fsl-ui 0.2.4
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/LICENSE +21 -0
- package/README.md +76 -0
- package/dist/_virtual/_rolldown/runtime.cjs +30 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.cjs +129 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.d.cts +61 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.d.mts +61 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.mjs +126 -0
- package/dist/components/Button/Button.cjs +80 -0
- package/dist/components/Button/Button.d.cts +68 -0
- package/dist/components/Button/Button.d.mts +68 -0
- package/dist/components/Button/Button.mjs +79 -0
- package/dist/components/Checkbox/Checkbox.cjs +183 -0
- package/dist/components/Checkbox/Checkbox.d.cts +45 -0
- package/dist/components/Checkbox/Checkbox.d.mts +45 -0
- package/dist/components/Checkbox/Checkbox.mjs +182 -0
- package/dist/components/CheckboxGroup/CheckboxGroup.cjs +97 -0
- package/dist/components/CheckboxGroup/CheckboxGroup.d.cts +56 -0
- package/dist/components/CheckboxGroup/CheckboxGroup.d.mts +56 -0
- package/dist/components/CheckboxGroup/CheckboxGroup.mjs +96 -0
- package/dist/components/FileTrigger/FileTrigger.cjs +51 -0
- package/dist/components/FileTrigger/FileTrigger.d.cts +52 -0
- package/dist/components/FileTrigger/FileTrigger.d.mts +52 -0
- package/dist/components/FileTrigger/FileTrigger.mjs +50 -0
- package/dist/components/GridList/GridList.cjs +220 -0
- package/dist/components/GridList/GridList.d.cts +72 -0
- package/dist/components/GridList/GridList.d.mts +72 -0
- package/dist/components/GridList/GridList.mjs +217 -0
- package/dist/components/Group/Group.cjs +77 -0
- package/dist/components/Group/Group.d.cts +61 -0
- package/dist/components/Group/Group.d.mts +61 -0
- package/dist/components/Group/Group.mjs +74 -0
- package/dist/components/Icon/Icon.cjs +52 -0
- package/dist/components/Icon/Icon.mjs +52 -0
- package/dist/components/Icon/glyphs.cjs +72 -0
- package/dist/components/Icon/glyphs.mjs +63 -0
- package/dist/components/Link/Link.cjs +62 -0
- package/dist/components/Link/Link.d.cts +38 -0
- package/dist/components/Link/Link.d.mts +38 -0
- package/dist/components/Link/Link.mjs +61 -0
- package/dist/components/ListBox/ListBox.cjs +126 -0
- package/dist/components/ListBox/ListBox.d.cts +63 -0
- package/dist/components/ListBox/ListBox.d.mts +63 -0
- package/dist/components/ListBox/ListBox.mjs +123 -0
- package/dist/components/Meter/Meter.cjs +126 -0
- package/dist/components/Meter/Meter.d.cts +63 -0
- package/dist/components/Meter/Meter.d.mts +63 -0
- package/dist/components/Meter/Meter.mjs +125 -0
- package/dist/components/NumberField/NumberField.cjs +221 -0
- package/dist/components/NumberField/NumberField.d.cts +68 -0
- package/dist/components/NumberField/NumberField.d.mts +68 -0
- package/dist/components/NumberField/NumberField.mjs +220 -0
- package/dist/components/ProgressBar/ProgressBar.cjs +132 -0
- package/dist/components/ProgressBar/ProgressBar.d.cts +56 -0
- package/dist/components/ProgressBar/ProgressBar.d.mts +56 -0
- package/dist/components/ProgressBar/ProgressBar.mjs +129 -0
- package/dist/components/RadioGroup/RadioGroup.cjs +218 -0
- package/dist/components/RadioGroup/RadioGroup.d.cts +82 -0
- package/dist/components/RadioGroup/RadioGroup.d.mts +82 -0
- package/dist/components/RadioGroup/RadioGroup.mjs +215 -0
- package/dist/components/Select/Select.cjs +253 -0
- package/dist/components/Select/Select.d.cts +86 -0
- package/dist/components/Select/Select.d.mts +86 -0
- package/dist/components/Select/Select.mjs +250 -0
- package/dist/components/Separator/Separator.cjs +64 -0
- package/dist/components/Separator/Separator.d.cts +44 -0
- package/dist/components/Separator/Separator.d.mts +44 -0
- package/dist/components/Separator/Separator.mjs +63 -0
- package/dist/components/Slider/Slider.cjs +160 -0
- package/dist/components/Slider/Slider.d.cts +45 -0
- package/dist/components/Slider/Slider.d.mts +45 -0
- package/dist/components/Slider/Slider.mjs +159 -0
- package/dist/components/Switch/Switch.cjs +176 -0
- package/dist/components/Switch/Switch.d.cts +43 -0
- package/dist/components/Switch/Switch.d.mts +43 -0
- package/dist/components/Switch/Switch.mjs +175 -0
- package/dist/components/Tabs/Tabs.cjs +180 -0
- package/dist/components/Tabs/Tabs.d.cts +85 -0
- package/dist/components/Tabs/Tabs.d.mts +85 -0
- package/dist/components/Tabs/Tabs.mjs +173 -0
- package/dist/components/Toast/Toast.cjs +218 -0
- package/dist/components/Toast/Toast.d.cts +96 -0
- package/dist/components/Toast/Toast.d.mts +96 -0
- package/dist/components/Toast/Toast.mjs +214 -0
- package/dist/components/ToggleButton/ToggleButton.cjs +125 -0
- package/dist/components/ToggleButton/ToggleButton.d.cts +43 -0
- package/dist/components/ToggleButton/ToggleButton.d.mts +43 -0
- package/dist/components/ToggleButton/ToggleButton.mjs +124 -0
- package/dist/components/ToggleButtonGroup/ToggleButtonGroup.cjs +48 -0
- package/dist/components/ToggleButtonGroup/ToggleButtonGroup.d.cts +41 -0
- package/dist/components/ToggleButtonGroup/ToggleButtonGroup.d.mts +41 -0
- package/dist/components/ToggleButtonGroup/ToggleButtonGroup.mjs +47 -0
- package/dist/components/Toolbar/Toolbar.cjs +67 -0
- package/dist/components/Toolbar/Toolbar.d.cts +60 -0
- package/dist/components/Toolbar/Toolbar.d.mts +60 -0
- package/dist/components/Toolbar/Toolbar.mjs +66 -0
- package/dist/composites/Accordion/Accordion.cjs +279 -0
- package/dist/composites/Accordion/Accordion.d.cts +176 -0
- package/dist/composites/Accordion/Accordion.d.mts +176 -0
- package/dist/composites/Accordion/Accordion.mjs +270 -0
- package/dist/composites/ConfirmationDialog/ConfirmationDialog.cjs +206 -0
- package/dist/composites/ConfirmationDialog/ConfirmationDialog.d.cts +166 -0
- package/dist/composites/ConfirmationDialog/ConfirmationDialog.d.mts +166 -0
- package/dist/composites/ConfirmationDialog/ConfirmationDialog.mjs +203 -0
- package/dist/composites/Dialog/Dialog.cjs +372 -0
- package/dist/composites/Dialog/Dialog.d.cts +234 -0
- package/dist/composites/Dialog/Dialog.d.mts +234 -0
- package/dist/composites/Dialog/Dialog.mjs +360 -0
- package/dist/composites/Disclosure/Disclosure.cjs +214 -0
- package/dist/composites/Disclosure/Disclosure.d.cts +118 -0
- package/dist/composites/Disclosure/Disclosure.d.mts +118 -0
- package/dist/composites/Disclosure/Disclosure.mjs +207 -0
- package/dist/composites/Form/Form.cjs +161 -0
- package/dist/composites/Form/Form.d.cts +176 -0
- package/dist/composites/Form/Form.d.mts +176 -0
- package/dist/composites/Form/Form.mjs +156 -0
- package/dist/composites/Menu/Menu.cjs +190 -0
- package/dist/composites/Menu/Menu.d.cts +158 -0
- package/dist/composites/Menu/Menu.d.mts +158 -0
- package/dist/composites/Menu/Menu.mjs +186 -0
- package/dist/composites/Popover/Popover.cjs +80 -0
- package/dist/composites/Popover/Popover.d.cts +56 -0
- package/dist/composites/Popover/Popover.d.mts +56 -0
- package/dist/composites/Popover/Popover.mjs +78 -0
- package/dist/composites/SearchField/SearchField.cjs +194 -0
- package/dist/composites/SearchField/SearchField.d.cts +81 -0
- package/dist/composites/SearchField/SearchField.d.mts +81 -0
- package/dist/composites/SearchField/SearchField.mjs +189 -0
- package/dist/composites/TagGroup/TagGroup.cjs +212 -0
- package/dist/composites/TagGroup/TagGroup.d.cts +84 -0
- package/dist/composites/TagGroup/TagGroup.d.mts +84 -0
- package/dist/composites/TagGroup/TagGroup.mjs +209 -0
- package/dist/composites/TextArea/TextArea.cjs +190 -0
- package/dist/composites/TextArea/TextArea.d.cts +95 -0
- package/dist/composites/TextArea/TextArea.d.mts +95 -0
- package/dist/composites/TextArea/TextArea.mjs +181 -0
- package/dist/composites/TextField/TextField.cjs +200 -0
- package/dist/composites/TextField/TextField.d.cts +111 -0
- package/dist/composites/TextField/TextField.d.mts +111 -0
- package/dist/composites/TextField/TextField.mjs +191 -0
- package/dist/composites/Tooltip/Tooltip.cjs +79 -0
- package/dist/composites/Tooltip/Tooltip.d.cts +53 -0
- package/dist/composites/Tooltip/Tooltip.d.mts +53 -0
- package/dist/composites/Tooltip/Tooltip.mjs +77 -0
- package/dist/composites/Wizard/Wizard.cjs +333 -0
- package/dist/composites/Wizard/Wizard.d.cts +206 -0
- package/dist/composites/Wizard/Wizard.d.mts +206 -0
- package/dist/composites/Wizard/Wizard.mjs +324 -0
- package/dist/composites/scope.cjs +124 -0
- package/dist/composites/scope.mjs +121 -0
- package/dist/index.cjs +186 -0
- package/dist/index.d.cts +37 -0
- package/dist/index.d.mts +37 -0
- package/dist/index.mjs +39 -0
- package/dist/semantics/componentMeta.d.cts +37 -0
- package/dist/semantics/componentMeta.d.mts +37 -0
- package/dist/semantics/index.cjs +16 -0
- package/dist/semantics/index.d.cts +4 -0
- package/dist/semantics/index.d.mts +4 -0
- package/dist/semantics/index.mjs +3 -0
- package/dist/semantics/taxonomy.cjs +150 -0
- package/dist/semantics/taxonomy.d.cts +153 -0
- package/dist/semantics/taxonomy.d.mts +153 -0
- package/dist/semantics/taxonomy.mjs +140 -0
- package/dist/tokens/escapeHatch.cjs +13 -0
- package/dist/tokens/escapeHatch.mjs +13 -0
- package/dist/tokens/focusRing.cjs +28 -0
- package/dist/tokens/focusRing.mjs +28 -0
- package/dist/tokens/keyframes.cjs +76 -0
- package/dist/tokens/keyframes.mjs +75 -0
- package/dist/tokens/resolveInteractiveStyle.cjs +41 -0
- package/dist/tokens/resolveInteractiveStyle.mjs +41 -0
- package/llms.txt +142 -0
- package/package.json +70 -0
- package/src/tokens/CONTRACT.md +505 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
import { vars } from "@ttoss/fsl-theme/vars";
|
|
3
|
+
|
|
4
|
+
//#region src/tokens/focusRing.ts
|
|
5
|
+
/**
|
|
6
|
+
* Focus ring resolver — the cross-cutting keyboard-focus outline every
|
|
7
|
+
* interactive component applies per `CONTRACT.md §3`:
|
|
8
|
+
*
|
|
9
|
+
* - always via `outline` (never `border`) so focus cannot layout-shift;
|
|
10
|
+
* - always from the cross-cutting `vars.focus.ring.*` tokens (CONTRACT §1
|
|
11
|
+
* cross-cutting table), never from per-entity color subtrees.
|
|
12
|
+
*
|
|
13
|
+
* Companion to `resolveInteractiveStyle`: that helper owns the state
|
|
14
|
+
* cascade for color dimensions; this one owns the single state-dependent
|
|
15
|
+
* outline. Both exist so components never re-implement state ternaries
|
|
16
|
+
* (structural tokens like radii/spacing stay literal reads by design).
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Returns the `outline` CSS value for the current focus-visible state.
|
|
20
|
+
* Pair with `outlineOffset` at the call site when the component floats the
|
|
21
|
+
* ring off its edge.
|
|
22
|
+
*/
|
|
23
|
+
var focusRingOutline = isFocusVisible => {
|
|
24
|
+
return isFocusVisible ? `${vars.focus.ring.width} ${vars.focus.ring.style} ${vars.focus.ring.color}` : "none";
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
export { focusRingOutline };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
//#region src/tokens/keyframes.ts
|
|
3
|
+
/**
|
|
4
|
+
* Runtime `@keyframes` registry for `@ttoss/fsl-ui`.
|
|
5
|
+
*
|
|
6
|
+
* Components style themselves exclusively through inline styles (no CSS
|
|
7
|
+
* files ship with the package), but CSS animations can only reference
|
|
8
|
+
* `@keyframes` declared in a real stylesheet. This module owns that
|
|
9
|
+
* stylesheet: a single `<style id="fsl-ui-keyframes">` element injected
|
|
10
|
+
* once per document, SSR-safe and idempotent.
|
|
11
|
+
*
|
|
12
|
+
* Rules (enforced by `tests/unit/tests/keyframes.test.ts` and the contract
|
|
13
|
+
* suite):
|
|
14
|
+
*
|
|
15
|
+
* 1. Every animation name used by a component MUST be registered in
|
|
16
|
+
* `ANIMATION_NAMES` — components reference `ANIMATION_NAMES.*`, never a
|
|
17
|
+
* bare string.
|
|
18
|
+
* 2. Every registered name MUST have a matching `@keyframes` block in
|
|
19
|
+
* `KEYFRAMES_CSS`.
|
|
20
|
+
* 3. The stylesheet disables all registered animations under
|
|
21
|
+
* `prefers-reduced-motion: reduce`.
|
|
22
|
+
*/
|
|
23
|
+
var STYLE_ELEMENT_ID = "fsl-ui-keyframes";
|
|
24
|
+
/**
|
|
25
|
+
* Canonical animation names shipped by the package. Components must
|
|
26
|
+
* reference these constants (never string literals) so the contract test
|
|
27
|
+
* can prove every `animation:` in `src/` resolves to a real `@keyframes`.
|
|
28
|
+
*/
|
|
29
|
+
var ANIMATION_NAMES = {
|
|
30
|
+
/** Indeterminate ProgressBar fill sweep. */
|
|
31
|
+
progressBarIndeterminate: "tt-progressbar-indeterminate"
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The full stylesheet injected by {@link ensureKeyframes}. Uses logical
|
|
35
|
+
* properties only (`margin-inline-start`) so the sweep direction follows
|
|
36
|
+
* the writing direction under `dir="rtl"`.
|
|
37
|
+
*/
|
|
38
|
+
var KEYFRAMES_CSS = `
|
|
39
|
+
@keyframes ${ANIMATION_NAMES.progressBarIndeterminate} {
|
|
40
|
+
0% { margin-inline-start: -40%; }
|
|
41
|
+
100% { margin-inline-start: 100%; }
|
|
42
|
+
}
|
|
43
|
+
@media (prefers-reduced-motion: reduce) {
|
|
44
|
+
[data-scope] [data-part] { animation: none !important; }
|
|
45
|
+
}
|
|
46
|
+
`.trim();
|
|
47
|
+
var injected = false;
|
|
48
|
+
/**
|
|
49
|
+
* Injects the package stylesheet into `document.head` exactly once.
|
|
50
|
+
* No-op on the server (SSR-safe) and when the element already exists
|
|
51
|
+
* (e.g. two copies of the package on one page).
|
|
52
|
+
*
|
|
53
|
+
* Call it from a `React.useInsertionEffect` in any component that uses a
|
|
54
|
+
* name from {@link ANIMATION_NAMES} — insertion effects never run during
|
|
55
|
+
* server rendering, so the SSR guard below is defense in depth for direct
|
|
56
|
+
* callers, not the primary mechanism.
|
|
57
|
+
*/
|
|
58
|
+
var ensureKeyframes = () => {
|
|
59
|
+
if (injected) return;
|
|
60
|
+
const doc = globalThis.document;
|
|
61
|
+
/* istanbul ignore next -- SSR guard: jsdom always provides a document */
|
|
62
|
+
if (doc === void 0) return;
|
|
63
|
+
if (doc.getElementById(STYLE_ELEMENT_ID) !== null) {
|
|
64
|
+
injected = true;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const style = doc.createElement("style");
|
|
68
|
+
style.id = STYLE_ELEMENT_ID;
|
|
69
|
+
style.textContent = KEYFRAMES_CSS;
|
|
70
|
+
doc.head.appendChild(style);
|
|
71
|
+
injected = true;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
//#endregion
|
|
75
|
+
exports.ANIMATION_NAMES = ANIMATION_NAMES;
|
|
76
|
+
exports.ensureKeyframes = ensureKeyframes;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
//#region src/tokens/keyframes.ts
|
|
3
|
+
/**
|
|
4
|
+
* Runtime `@keyframes` registry for `@ttoss/fsl-ui`.
|
|
5
|
+
*
|
|
6
|
+
* Components style themselves exclusively through inline styles (no CSS
|
|
7
|
+
* files ship with the package), but CSS animations can only reference
|
|
8
|
+
* `@keyframes` declared in a real stylesheet. This module owns that
|
|
9
|
+
* stylesheet: a single `<style id="fsl-ui-keyframes">` element injected
|
|
10
|
+
* once per document, SSR-safe and idempotent.
|
|
11
|
+
*
|
|
12
|
+
* Rules (enforced by `tests/unit/tests/keyframes.test.ts` and the contract
|
|
13
|
+
* suite):
|
|
14
|
+
*
|
|
15
|
+
* 1. Every animation name used by a component MUST be registered in
|
|
16
|
+
* `ANIMATION_NAMES` — components reference `ANIMATION_NAMES.*`, never a
|
|
17
|
+
* bare string.
|
|
18
|
+
* 2. Every registered name MUST have a matching `@keyframes` block in
|
|
19
|
+
* `KEYFRAMES_CSS`.
|
|
20
|
+
* 3. The stylesheet disables all registered animations under
|
|
21
|
+
* `prefers-reduced-motion: reduce`.
|
|
22
|
+
*/
|
|
23
|
+
var STYLE_ELEMENT_ID = "fsl-ui-keyframes";
|
|
24
|
+
/**
|
|
25
|
+
* Canonical animation names shipped by the package. Components must
|
|
26
|
+
* reference these constants (never string literals) so the contract test
|
|
27
|
+
* can prove every `animation:` in `src/` resolves to a real `@keyframes`.
|
|
28
|
+
*/
|
|
29
|
+
var ANIMATION_NAMES = {
|
|
30
|
+
/** Indeterminate ProgressBar fill sweep. */
|
|
31
|
+
progressBarIndeterminate: "tt-progressbar-indeterminate"
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* The full stylesheet injected by {@link ensureKeyframes}. Uses logical
|
|
35
|
+
* properties only (`margin-inline-start`) so the sweep direction follows
|
|
36
|
+
* the writing direction under `dir="rtl"`.
|
|
37
|
+
*/
|
|
38
|
+
var KEYFRAMES_CSS = `
|
|
39
|
+
@keyframes ${ANIMATION_NAMES.progressBarIndeterminate} {
|
|
40
|
+
0% { margin-inline-start: -40%; }
|
|
41
|
+
100% { margin-inline-start: 100%; }
|
|
42
|
+
}
|
|
43
|
+
@media (prefers-reduced-motion: reduce) {
|
|
44
|
+
[data-scope] [data-part] { animation: none !important; }
|
|
45
|
+
}
|
|
46
|
+
`.trim();
|
|
47
|
+
var injected = false;
|
|
48
|
+
/**
|
|
49
|
+
* Injects the package stylesheet into `document.head` exactly once.
|
|
50
|
+
* No-op on the server (SSR-safe) and when the element already exists
|
|
51
|
+
* (e.g. two copies of the package on one page).
|
|
52
|
+
*
|
|
53
|
+
* Call it from a `React.useInsertionEffect` in any component that uses a
|
|
54
|
+
* name from {@link ANIMATION_NAMES} — insertion effects never run during
|
|
55
|
+
* server rendering, so the SSR guard below is defense in depth for direct
|
|
56
|
+
* callers, not the primary mechanism.
|
|
57
|
+
*/
|
|
58
|
+
var ensureKeyframes = () => {
|
|
59
|
+
if (injected) return;
|
|
60
|
+
const doc = globalThis.document;
|
|
61
|
+
/* istanbul ignore next -- SSR guard: jsdom always provides a document */
|
|
62
|
+
if (doc === void 0) return;
|
|
63
|
+
if (doc.getElementById(STYLE_ELEMENT_ID) !== null) {
|
|
64
|
+
injected = true;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const style = doc.createElement("style");
|
|
68
|
+
style.id = STYLE_ELEMENT_ID;
|
|
69
|
+
style.textContent = KEYFRAMES_CSS;
|
|
70
|
+
doc.head.appendChild(style);
|
|
71
|
+
injected = true;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
//#endregion
|
|
75
|
+
export { ANIMATION_NAMES, ensureKeyframes };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
const require_taxonomy = require('../semantics/taxonomy.cjs');
|
|
3
|
+
|
|
4
|
+
//#region src/tokens/resolveInteractiveStyle.ts
|
|
5
|
+
/**
|
|
6
|
+
* Resolves a single style value from a semantic interactive state object
|
|
7
|
+
* using the canonical cascade defined by `STATE_PRIORITY` in
|
|
8
|
+
* `../semantics/taxonomy`.
|
|
9
|
+
*
|
|
10
|
+
* `STATE_PRIORITY` is the single source of truth for both the **order** of
|
|
11
|
+
* the cascade and the **flag → state-key** mapping. `CONTRACT.md §3`
|
|
12
|
+
* references it by name; this helper iterates it. Order and mapping cannot
|
|
13
|
+
* drift between the contract doc and the implementation because they are
|
|
14
|
+
* derived from the same tuple.
|
|
15
|
+
*
|
|
16
|
+
* The helper is **strict**: when a flag is set, the corresponding state's
|
|
17
|
+
* value is returned as-is (even when `undefined`). Two authoring knobs keep
|
|
18
|
+
* component call sites expressive without re-introducing the copy-pasted
|
|
19
|
+
* ternary chain:
|
|
20
|
+
*
|
|
21
|
+
* - **Omit irrelevant flags** from `flags` to skip that level entirely —
|
|
22
|
+
* e.g. drop `isFocusVisible` for `background`/`text` dimensions that
|
|
23
|
+
* never change on focus.
|
|
24
|
+
* - **Apply `?? states.default` at the call site** when fallback to the
|
|
25
|
+
* resting state is desired (e.g. Button text, where `action.*.text.active`
|
|
26
|
+
* and `action.*.text.hover` are not always defined on every evaluation).
|
|
27
|
+
*
|
|
28
|
+
* Consumed by `Button`, `Link`, `Checkbox`, `Switch`, `RadioGroup`, `Select`,
|
|
29
|
+
* `TextField`, and `Accordion`.
|
|
30
|
+
*/
|
|
31
|
+
var resolveInteractiveStyle = (states, flags) => {
|
|
32
|
+
if (!states) return void 0;
|
|
33
|
+
for (const {
|
|
34
|
+
flag,
|
|
35
|
+
state
|
|
36
|
+
} of require_taxonomy.STATE_PRIORITY) if (flags[flag]) return states[state];
|
|
37
|
+
return states.default;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
exports.resolveInteractiveStyle = resolveInteractiveStyle;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
import { STATE_PRIORITY } from "../semantics/taxonomy.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/tokens/resolveInteractiveStyle.ts
|
|
5
|
+
/**
|
|
6
|
+
* Resolves a single style value from a semantic interactive state object
|
|
7
|
+
* using the canonical cascade defined by `STATE_PRIORITY` in
|
|
8
|
+
* `../semantics/taxonomy`.
|
|
9
|
+
*
|
|
10
|
+
* `STATE_PRIORITY` is the single source of truth for both the **order** of
|
|
11
|
+
* the cascade and the **flag → state-key** mapping. `CONTRACT.md §3`
|
|
12
|
+
* references it by name; this helper iterates it. Order and mapping cannot
|
|
13
|
+
* drift between the contract doc and the implementation because they are
|
|
14
|
+
* derived from the same tuple.
|
|
15
|
+
*
|
|
16
|
+
* The helper is **strict**: when a flag is set, the corresponding state's
|
|
17
|
+
* value is returned as-is (even when `undefined`). Two authoring knobs keep
|
|
18
|
+
* component call sites expressive without re-introducing the copy-pasted
|
|
19
|
+
* ternary chain:
|
|
20
|
+
*
|
|
21
|
+
* - **Omit irrelevant flags** from `flags` to skip that level entirely —
|
|
22
|
+
* e.g. drop `isFocusVisible` for `background`/`text` dimensions that
|
|
23
|
+
* never change on focus.
|
|
24
|
+
* - **Apply `?? states.default` at the call site** when fallback to the
|
|
25
|
+
* resting state is desired (e.g. Button text, where `action.*.text.active`
|
|
26
|
+
* and `action.*.text.hover` are not always defined on every evaluation).
|
|
27
|
+
*
|
|
28
|
+
* Consumed by `Button`, `Link`, `Checkbox`, `Switch`, `RadioGroup`, `Select`,
|
|
29
|
+
* `TextField`, and `Accordion`.
|
|
30
|
+
*/
|
|
31
|
+
var resolveInteractiveStyle = (states, flags) => {
|
|
32
|
+
if (!states) return void 0;
|
|
33
|
+
for (const {
|
|
34
|
+
flag,
|
|
35
|
+
state
|
|
36
|
+
} of STATE_PRIORITY) if (flags[flag]) return states[state];
|
|
37
|
+
return states.default;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
export { resolveInteractiveStyle };
|
package/llms.txt
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# @ttoss/fsl-ui
|
|
2
|
+
|
|
3
|
+
Semantic, token-native React component library. Built on React Aria
|
|
4
|
+
Components; styled exclusively with `@ttoss/fsl-theme` CSS-variable tokens.
|
|
5
|
+
Every component declares a formal semantic identity (FSL — Foundational
|
|
6
|
+
Semantic Language) that determines which tokens it may consume.
|
|
7
|
+
|
|
8
|
+
Ground truth in the published package: `src/tokens/CONTRACT.md` (full Layer-2
|
|
9
|
+
contract). This file is the distilled, machine-oriented summary.
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { createTheme } from '@ttoss/fsl-theme';
|
|
15
|
+
import { ThemeProvider } from '@ttoss/fsl-theme/react';
|
|
16
|
+
import { Button } from '@ttoss/fsl-ui';
|
|
17
|
+
|
|
18
|
+
const theme = createTheme(); // base theme + dark alternate
|
|
19
|
+
|
|
20
|
+
<ThemeProvider theme={theme} defaultMode="system">
|
|
21
|
+
<Button evaluation="primary" onPress={save}>Save</Button>
|
|
22
|
+
</ThemeProvider>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Without a ThemeProvider (or ThemeHead/ThemeScript on SSR) the CSS variables
|
|
26
|
+
are undefined and components render unstyled.
|
|
27
|
+
|
|
28
|
+
## Semantic model (what the props mean)
|
|
29
|
+
|
|
30
|
+
- entity — what the component IS (fixed per component, not a prop):
|
|
31
|
+
Action, Input, Selection, Navigation, Disclosure, Overlay, Feedback,
|
|
32
|
+
Collection, Structure.
|
|
33
|
+
- evaluation (prop) — AUTHORIAL emphasis, chosen when writing the UI:
|
|
34
|
+
e.g. 'primary' | 'secondary' | 'accent' | 'muted' | 'negative' on Action.
|
|
35
|
+
Input/Selection components have NO evaluation prop (data entry carries no
|
|
36
|
+
authorial voice); validation is the runtime `invalid` STATE (`isInvalid`),
|
|
37
|
+
never `evaluation="negative"`.
|
|
38
|
+
- consequence (prop) — effect on state: 'neutral' | 'committing' |
|
|
39
|
+
'destructive'. Drives MECHANISM (e.g. ConfirmationDialog two-click arming),
|
|
40
|
+
not color. Destructive ≠ negative color; pair them explicitly if desired.
|
|
41
|
+
- composition (prop on leaves) — slot the element plays inside a parent:
|
|
42
|
+
'primaryAction' | 'secondaryAction' | 'dismissAction' … DialogActions
|
|
43
|
+
reorders children by it per platform convention.
|
|
44
|
+
- State — runtime only (hover, active, focused, disabled, checked,
|
|
45
|
+
indeterminate, expanded, invalid, …). Emitted by React Aria render props;
|
|
46
|
+
priority cascade: disabled > invalid > expanded > indeterminate >
|
|
47
|
+
selected > focusVisible > pressed > hovered > default.
|
|
48
|
+
|
|
49
|
+
## Component catalog (all exports also ship a `*Meta` identity object)
|
|
50
|
+
|
|
51
|
+
Action: Button, ToggleButton, FileTrigger (opens OS file picker),
|
|
52
|
+
MenuItem (inside Menu), FormSubmit (inside Form)
|
|
53
|
+
Navigation: Link, Breadcrumbs/Breadcrumb, Tabs/TabList/Tab
|
|
54
|
+
(TabPanel is Structure — the content revealed by the tabs)
|
|
55
|
+
Disclosure: Accordion, AccordionItem, AccordionTrigger, AccordionPanel,
|
|
56
|
+
Disclosure, DisclosureTrigger, DisclosurePanel (standalone single
|
|
57
|
+
section — Accordion is the DisclosureGroup)
|
|
58
|
+
Selection: Checkbox, CheckboxGroup, RadioGroup/Radio, Switch,
|
|
59
|
+
Select/SelectItem, ToggleButtonGroup, TagGroup/Tag
|
|
60
|
+
(CheckboxGroup group-level isInvalid propagates to child Checkboxes;
|
|
61
|
+
its label part is Selection/label, but description + validationMessage
|
|
62
|
+
are internal parts — Selection has no such structural roles.
|
|
63
|
+
TagGroup/Tag: a chosen tag uses the `selected` State, NOT `pressed`
|
|
64
|
+
— a tag is set membership, not a toggle; remove button is an
|
|
65
|
+
internal closeTrigger part (not a Selection role))
|
|
66
|
+
Collection: ListBox/ListBoxItem, GridList/GridListItem (+ Menu/MenuItem,
|
|
67
|
+
listed under Overlay for its popover surface). ADR-007: the
|
|
68
|
+
CONTAINER is Collection (informational surface) but selectable
|
|
69
|
+
ITEMS are Selection (input chrome, `selected` State) — one
|
|
70
|
+
composite, two entities. GridListItem also has a selectionControl
|
|
71
|
+
(per-row checkbox).
|
|
72
|
+
Input: TextField, TextArea (each + Label/Control/Description/Error),
|
|
73
|
+
SearchField (+ Label/Control; leading search + clear glyphs),
|
|
74
|
+
NumberField (label/description/errorMessage props + −/+ steppers),
|
|
75
|
+
Slider (label + value output; single value or [min,max] range)
|
|
76
|
+
Overlay: Dialog, DialogModal, DialogTrigger, DialogHeading, DialogBody,
|
|
77
|
+
DialogActions, Menu, MenuTrigger, ConfirmationDialog,
|
|
78
|
+
Popover, PopoverTrigger, Tooltip, TooltipTrigger
|
|
79
|
+
Feedback: ProgressBar, Meter, Toast, ToastRegion, createToastQueue
|
|
80
|
+
(Meter = static quantity in a range e.g. battery/disk;
|
|
81
|
+
ProgressBar = a task in flight. Pick by "state" vs "progressing")
|
|
82
|
+
Structure: Form, FormActions, Wizard, WizardStep, WizardSummary,
|
|
83
|
+
WizardNavigation, Separator, Group (labelled role=group frame),
|
|
84
|
+
Toolbar (role=toolbar, arrow-key roving nav over its controls)
|
|
85
|
+
|
|
86
|
+
Icons: INTERNAL only (not exported). Components render glyphs through an
|
|
87
|
+
internal semantic Icon layer (`src/components/Icon/`) that maps `icon.{family}.{intent}`
|
|
88
|
+
intents to Lucide glyphs via the Iconify provider (`@iconify-icon/react`),
|
|
89
|
+
registered offline (no runtime API fetch). Future standalone `@ttoss/fsl-icon`.
|
|
90
|
+
See CONTRIBUTING ADR-005.
|
|
91
|
+
|
|
92
|
+
## Entity → token map (a component reads ONLY its row)
|
|
93
|
+
|
|
94
|
+
Action → colors.action, radii.control, motion.feedback
|
|
95
|
+
Input → colors.input, radii.control, motion.feedback
|
|
96
|
+
Selection → colors.input, radii.control, motion.feedback
|
|
97
|
+
Navigation → colors.navigation, radii.control, motion.feedback
|
|
98
|
+
Disclosure → colors.navigation, radii.control, motion.transition
|
|
99
|
+
Overlay → colors.informational, radii.surface, motion.transition, elevation.overlay
|
|
100
|
+
Feedback → colors.feedback, radii.surface, elevation.raised
|
|
101
|
+
Collection → colors.informational, radii.surface
|
|
102
|
+
Structure → colors.informational, radii.surface
|
|
103
|
+
|
|
104
|
+
Color path formula: `vars.colors.{ux}[evaluation][background|border|text][state]`
|
|
105
|
+
→ CSS var `--tt-colors-{ux}-{evaluation}-{dimension}-{state}`.
|
|
106
|
+
|
|
107
|
+
## Rules an integrating agent must follow
|
|
108
|
+
|
|
109
|
+
1. Composites accept NO `style`/`className`. Customize geometry via CSS
|
|
110
|
+
custom-property knobs on the composite scope (fallbacks built in):
|
|
111
|
+
`--fsl-dialog-max-width` (default min(500px, 90vw)),
|
|
112
|
+
`--fsl-dialog-max-height` (90vh), `--fsl-menu-min-width` (12rem),
|
|
113
|
+
`--fsl-menu-max-width` (min(320px, 90vw)),
|
|
114
|
+
`--fsl-popover-max-width` (min(320px, 90vw)),
|
|
115
|
+
`--fsl-tooltip-max-width` (min(280px, 90vw)).
|
|
116
|
+
Example: `[data-scope='dialog'] { --fsl-dialog-max-width: 720px; }`
|
|
117
|
+
2. There is NO `size` prop anywhere. Different density = different
|
|
118
|
+
semantic identity = different component.
|
|
119
|
+
3. i18n: flow-critical labels are REQUIRED props with no defaults —
|
|
120
|
+
ConfirmationDialog (confirmLabel, cancelLabel, armedLabel when
|
|
121
|
+
consequence="destructive") and WizardNavigation (prevLabel, nextLabel,
|
|
122
|
+
finishLabel). Supply localized copy from the host app.
|
|
123
|
+
4. Validation: pass `isInvalid` (or React Aria `validate`) — never a color
|
|
124
|
+
prop. Invalid styling comes from `input.*.{dimension}.invalid` tokens.
|
|
125
|
+
5. Destructive confirms: `<ConfirmationDialog consequence="destructive">`
|
|
126
|
+
requires TWO clicks (arming). `consequence="committing"` +
|
|
127
|
+
`evaluation="negative"` gives a red one-click confirm.
|
|
128
|
+
6. Every element exposes `data-scope`/`data-part` (plus `data-evaluation`,
|
|
129
|
+
`data-consequence`, `data-composition`, `data-arming` where relevant) —
|
|
130
|
+
use these for CSS targeting and tests; never rely on internal class names.
|
|
131
|
+
7. All layout is logical-property based (RTL-safe); keep host CSS logical too.
|
|
132
|
+
|
|
133
|
+
## Package facts
|
|
134
|
+
|
|
135
|
+
- ESM only, `sideEffects: false`, tree-shakes (Button-only import ≈ 1.8 KB
|
|
136
|
+
min before deps). Peer deps: react >= 18, react-dom >= 18.
|
|
137
|
+
- `react-aria-components` pinned `~1.19.0` while Toast uses UNSTABLE_ APIs.
|
|
138
|
+
- SSR-safe: the only DOM injection (`@keyframes` for ProgressBar) happens in
|
|
139
|
+
a client-only insertion effect. Icon glyphs register into an in-memory
|
|
140
|
+
Iconify registry (no DOM, no network), so they are SSR-safe too.
|
|
141
|
+
- Icons: Iconify (`@iconify-icon/react`) is the official glyph provider;
|
|
142
|
+
default set is Lucide (`@iconify/icons-lucide`, per-icon / tree-shakeable).
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ttoss/fsl-ui",
|
|
3
|
+
"version": "0.2.4",
|
|
4
|
+
"description": "Semantic, token-native component library for the ttoss ecosystem. Built on React Aria Components with @ttoss/fsl-theme tokens.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/ttoss/ttoss.git",
|
|
8
|
+
"directory": "packages/fsl-ui"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"author": "Ennio Lopes <enniolopes@gmail.com>",
|
|
12
|
+
"sideEffects": false,
|
|
13
|
+
"type": "module",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.mts",
|
|
17
|
+
"module": "./dist/index.mjs",
|
|
18
|
+
"browser": "./dist/index.mjs",
|
|
19
|
+
"import": "./dist/index.mjs",
|
|
20
|
+
"default": "./dist/index.mjs"
|
|
21
|
+
},
|
|
22
|
+
"./semantics": {
|
|
23
|
+
"types": "./dist/semantics/index.d.mts",
|
|
24
|
+
"default": "./dist/semantics/index.mjs"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist",
|
|
29
|
+
"llms.txt",
|
|
30
|
+
"src/tokens/CONTRACT.md"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@iconify-icon/react": "^2.2.0",
|
|
34
|
+
"@iconify/icons-lucide": "^1.2.135",
|
|
35
|
+
"react-aria-components": "~1.19.0",
|
|
36
|
+
"@ttoss/fsl-theme": "^2.1.1"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@testing-library/react": "^16.3.2",
|
|
40
|
+
"@testing-library/user-event": "^14.6.1",
|
|
41
|
+
"@types/jest-axe": "^3.5.9",
|
|
42
|
+
"@types/node": "^24.12.0",
|
|
43
|
+
"@types/react": "^19.2.14",
|
|
44
|
+
"@types/react-dom": "^19.0.2",
|
|
45
|
+
"esbuild": "^0.28.0",
|
|
46
|
+
"jest": "^30.4.2",
|
|
47
|
+
"jest-axe": "^10.0.0",
|
|
48
|
+
"react": "^19.2.6",
|
|
49
|
+
"react-dom": "^19.2.6",
|
|
50
|
+
"react-hook-form": "^7.71.1",
|
|
51
|
+
"tsdown": "^0.22.2",
|
|
52
|
+
"zod": "^4.4.3",
|
|
53
|
+
"@ttoss/test-utils": "^4.2.18",
|
|
54
|
+
"@ttoss/config": "^1.37.17",
|
|
55
|
+
"@ttoss/forms": "^0.44.4"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"react": ">=18",
|
|
59
|
+
"react-dom": ">=18"
|
|
60
|
+
},
|
|
61
|
+
"publishConfig": {
|
|
62
|
+
"access": "public",
|
|
63
|
+
"provenance": true
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"build": "tsdown",
|
|
67
|
+
"test": "tsc --noEmit -p tsconfig.json && jest --projects tests/unit",
|
|
68
|
+
"verify:treeshake": "node scripts/verify-treeshake.mjs"
|
|
69
|
+
}
|
|
70
|
+
}
|