@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,206 @@
|
|
|
1
|
+
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
|
|
4
|
+
//#region src/composites/Wizard/Wizard.d.ts
|
|
5
|
+
type WizardSlotComposition = 'step' | 'summary' | 'navigation';
|
|
6
|
+
type WizardSlotComponent<P, K extends WizardSlotComposition> = React.ComponentType<P> & {
|
|
7
|
+
composition: K;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Public state surface shared by the {@link Wizard} host with its
|
|
11
|
+
* sub-parts. Consumed by {@link WizardNavigationProps.children} as a
|
|
12
|
+
* render-prop signature so authors can compose custom navigation while
|
|
13
|
+
* still observing the same state machine the default row uses.
|
|
14
|
+
*/
|
|
15
|
+
interface WizardState {
|
|
16
|
+
currentStep: number;
|
|
17
|
+
totalSteps: number;
|
|
18
|
+
isFirst: boolean;
|
|
19
|
+
isLast: boolean;
|
|
20
|
+
isComplete: boolean;
|
|
21
|
+
goNext: () => void;
|
|
22
|
+
goPrev: () => void;
|
|
23
|
+
goTo: (step: number) => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Formal semantic identity — Wizard root (Structure entity, root part).
|
|
27
|
+
*/
|
|
28
|
+
declare const wizardMeta: {
|
|
29
|
+
readonly displayName: "Wizard";
|
|
30
|
+
readonly entity: "Structure";
|
|
31
|
+
readonly structure: "root";
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Props for {@link Wizard}.
|
|
35
|
+
*
|
|
36
|
+
* The Wizard root carries no `evaluation` prop: under the evidence rule
|
|
37
|
+
* (CONTRIBUTING §2.3) a dimension only appears on a component if a runtime
|
|
38
|
+
* dispatches on it. The Wizard root renders a transparent `<section>` —
|
|
39
|
+
* no color tokens are read here — so adding `evaluation` would reserve API
|
|
40
|
+
* surface for a speculative future consumer. Re-introduce it the day a
|
|
41
|
+
* real consumer (e.g. wizard frame chrome) needs it.
|
|
42
|
+
*
|
|
43
|
+
* The composite owns its layout; the root does not accept `style` or
|
|
44
|
+
* `className`. Pass them on a wrapping element instead. See CONTRIBUTING
|
|
45
|
+
* §4.
|
|
46
|
+
*/
|
|
47
|
+
interface WizardProps {
|
|
48
|
+
/**
|
|
49
|
+
* Controlled current step index (0-based). When the user has advanced
|
|
50
|
+
* past the last step, `currentStep === numberOfSteps` — this is the
|
|
51
|
+
* terminal state that renders the {@link WizardSummary} sub-part (if
|
|
52
|
+
* provided). Pair with {@link WizardProps.onStepChange}.
|
|
53
|
+
*/
|
|
54
|
+
currentStep?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Initial step index when rendering uncontrolled.
|
|
57
|
+
* @default 0
|
|
58
|
+
*/
|
|
59
|
+
defaultStep?: number;
|
|
60
|
+
/**
|
|
61
|
+
* Fires whenever the active step changes — controlled or uncontrolled.
|
|
62
|
+
*/
|
|
63
|
+
onStepChange?: (step: number) => void;
|
|
64
|
+
/**
|
|
65
|
+
* Accessible name for the wizard region (rendered as `aria-label`).
|
|
66
|
+
* Always provide one (or `aria-labelledby`) — the wizard renders a
|
|
67
|
+
* `<section>` landmark and screen-reader users navigate by its name.
|
|
68
|
+
*/
|
|
69
|
+
'aria-label'?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Builds the text announced by the wizard's visually-hidden
|
|
72
|
+
* `aria-live=polite` region whenever the active step changes.
|
|
73
|
+
* The default is an English fallback (`"Step 2 of 3"` / `"Complete"`) —
|
|
74
|
+
* localized hosts supply their own copy here (CONTRIBUTING §6).
|
|
75
|
+
*/
|
|
76
|
+
announceStep?: (state: {
|
|
77
|
+
currentStep: number;
|
|
78
|
+
totalSteps: number;
|
|
79
|
+
isComplete: boolean;
|
|
80
|
+
}) => string;
|
|
81
|
+
/**
|
|
82
|
+
* Expected children: any number of {@link WizardStep} (in order), an
|
|
83
|
+
* optional {@link WizardSummary}, and an optional {@link WizardNavigation}.
|
|
84
|
+
* Children are dispatched on their fixed `composition` identity.
|
|
85
|
+
*/
|
|
86
|
+
children?: React.ReactNode;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A step-at-a-time Structure composite. The host reads each child's
|
|
90
|
+
* fixed `composition` identity at runtime to decide which single step
|
|
91
|
+
* to render, whether to swap in the summary view, and whether to render
|
|
92
|
+
* the navigation row. This is the canonical evidence that FSL
|
|
93
|
+
* `Composition` drives behavior from *fixed-identity sub-parts*
|
|
94
|
+
* (complementing `DialogActions`, which drives behavior from *runtime
|
|
95
|
+
* values on leaves*).
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```tsx
|
|
99
|
+
* const [step, setStep] = React.useState(0);
|
|
100
|
+
* return (
|
|
101
|
+
* <Wizard currentStep={step} onStepChange={setStep} aria-label="Onboarding">
|
|
102
|
+
* <WizardStep>First step content</WizardStep>
|
|
103
|
+
* <WizardStep>Second step content</WizardStep>
|
|
104
|
+
* <WizardStep>Third step content</WizardStep>
|
|
105
|
+
* <WizardSummary>All done!</WizardSummary>
|
|
106
|
+
* <WizardNavigation prevLabel="Back" nextLabel="Next" finishLabel="Finish" />
|
|
107
|
+
* </Wizard>
|
|
108
|
+
* );
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
declare const Wizard: {
|
|
112
|
+
({
|
|
113
|
+
currentStep,
|
|
114
|
+
defaultStep,
|
|
115
|
+
onStepChange,
|
|
116
|
+
announceStep,
|
|
117
|
+
children,
|
|
118
|
+
...props
|
|
119
|
+
}: WizardProps): import("react/jsx-runtime").JSX.Element;
|
|
120
|
+
displayName: "Wizard";
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Formal semantic identity — a step body inside a {@link Wizard}.
|
|
124
|
+
*/
|
|
125
|
+
declare const wizardStepMeta: {
|
|
126
|
+
readonly displayName: "WizardStep";
|
|
127
|
+
readonly entity: "Structure";
|
|
128
|
+
readonly structure: "content";
|
|
129
|
+
readonly composition: "step";
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* Props for {@link WizardStep}.
|
|
133
|
+
*/
|
|
134
|
+
interface WizardStepProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style' | 'className'> {
|
|
135
|
+
/**
|
|
136
|
+
* Optional accessible label for this step (rendered as `aria-label`).
|
|
137
|
+
*/
|
|
138
|
+
'aria-label'?: string;
|
|
139
|
+
/** Step body — freeform content (fields, descriptions, media). */
|
|
140
|
+
children?: React.ReactNode;
|
|
141
|
+
}
|
|
142
|
+
declare const WizardStep: WizardSlotComponent<WizardStepProps, "step">;
|
|
143
|
+
/**
|
|
144
|
+
* Formal semantic identity — summary view shown after the last step.
|
|
145
|
+
*/
|
|
146
|
+
declare const wizardSummaryMeta: {
|
|
147
|
+
readonly displayName: "WizardSummary";
|
|
148
|
+
readonly entity: "Structure";
|
|
149
|
+
readonly structure: "content";
|
|
150
|
+
readonly composition: "summary";
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Props for {@link WizardSummary}.
|
|
154
|
+
*/
|
|
155
|
+
type WizardSummaryProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'style' | 'className'>;
|
|
156
|
+
declare const WizardSummary: WizardSlotComponent<WizardSummaryProps, "summary">;
|
|
157
|
+
/**
|
|
158
|
+
* Formal semantic identity — the navigation slot inside a {@link Wizard}.
|
|
159
|
+
*/
|
|
160
|
+
declare const wizardNavigationMeta: {
|
|
161
|
+
readonly displayName: "WizardNavigation";
|
|
162
|
+
readonly entity: "Structure";
|
|
163
|
+
readonly structure: "actions";
|
|
164
|
+
readonly composition: "navigation";
|
|
165
|
+
};
|
|
166
|
+
interface WizardNavigationCommonProps {
|
|
167
|
+
/**
|
|
168
|
+
* Fires when the user advances past the last step (first click of
|
|
169
|
+
* `Finish`). The Wizard host transitions to the `isComplete` state
|
|
170
|
+
* and renders the {@link WizardSummary} if one is provided.
|
|
171
|
+
*/
|
|
172
|
+
onFinish?: () => void;
|
|
173
|
+
}
|
|
174
|
+
interface WizardNavigationLabelProps extends WizardNavigationCommonProps {
|
|
175
|
+
/**
|
|
176
|
+
* Label for the "previous step" button. **Required** — flow-critical
|
|
177
|
+
* labels have no English default (CONTRIBUTING §6 / ADR-001); the
|
|
178
|
+
* caller supplies localized copy.
|
|
179
|
+
*/
|
|
180
|
+
prevLabel: React.ReactNode;
|
|
181
|
+
/** Label for the "next step" button. **Required** — see `prevLabel`. */
|
|
182
|
+
nextLabel: React.ReactNode;
|
|
183
|
+
/** Label for the final-step button. **Required** — see `prevLabel`. */
|
|
184
|
+
finishLabel: React.ReactNode;
|
|
185
|
+
children?: never;
|
|
186
|
+
}
|
|
187
|
+
interface WizardNavigationRenderProps extends WizardNavigationCommonProps {
|
|
188
|
+
/**
|
|
189
|
+
* Replace the default button row with a render-prop so the host can
|
|
190
|
+
* compose custom controls while still reading wizard state from the
|
|
191
|
+
* same context (validation gating, async next, etc.). When provided,
|
|
192
|
+
* the label props do not apply — the render-prop owns all copy.
|
|
193
|
+
*/
|
|
194
|
+
children: (state: WizardState) => React.ReactNode;
|
|
195
|
+
prevLabel?: never;
|
|
196
|
+
nextLabel?: never;
|
|
197
|
+
finishLabel?: never;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Props for {@link WizardNavigation} — either the default row with
|
|
201
|
+
* caller-supplied labels, or a render-prop that owns the whole row.
|
|
202
|
+
*/
|
|
203
|
+
type WizardNavigationProps = WizardNavigationLabelProps | WizardNavigationRenderProps;
|
|
204
|
+
declare const WizardNavigation: WizardSlotComponent<WizardNavigationProps, "navigation">;
|
|
205
|
+
//#endregion
|
|
206
|
+
export { Wizard, WizardNavigation, WizardNavigationProps, WizardProps, WizardStep, WizardStepProps, WizardSummary, WizardSummaryProps, wizardMeta, wizardNavigationMeta, wizardStepMeta, wizardSummaryMeta };
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
import { Button } from "../../components/Button/Button.mjs";
|
|
3
|
+
import { createCompositeScope } from "../scope.mjs";
|
|
4
|
+
import { vars } from "@ttoss/fsl-theme/vars";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
|
|
8
|
+
//#region src/composites/Wizard/Wizard.tsx
|
|
9
|
+
/**
|
|
10
|
+
* Tag a component as a Wizard slot. Returns the same component augmented
|
|
11
|
+
* with a typed `.composition` literal — no cast, no post-declaration
|
|
12
|
+
* mutation in the caller.
|
|
13
|
+
*/
|
|
14
|
+
var defineWizardSlot = (Component, composition) => {
|
|
15
|
+
return Object.assign(Component, {
|
|
16
|
+
composition
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
var REACT_MEMO_TYPE = Symbol.for("react.memo");
|
|
20
|
+
var REACT_FORWARD_REF_TYPE = Symbol.for("react.forward_ref");
|
|
21
|
+
var readComposition = type => {
|
|
22
|
+
if (type === null || typeof type !== "object" && typeof type !== "function") return;
|
|
23
|
+
const candidate = type.composition;
|
|
24
|
+
if (candidate === "step" || candidate === "summary" || candidate === "navigation") return candidate;
|
|
25
|
+
};
|
|
26
|
+
var getChildComposition = child => {
|
|
27
|
+
if (!React.isValidElement(child)) return void 0;
|
|
28
|
+
let type = child.type;
|
|
29
|
+
for (let depth = 0; depth < 8; depth++) {
|
|
30
|
+
const direct = readComposition(type);
|
|
31
|
+
if (direct !== void 0) return direct;
|
|
32
|
+
if (typeof type !== "object" || type === null) return void 0;
|
|
33
|
+
const wrapped = type;
|
|
34
|
+
if (wrapped.$$typeof === REACT_MEMO_TYPE) {
|
|
35
|
+
type = wrapped.type;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (wrapped.$$typeof === REACT_FORWARD_REF_TYPE) {
|
|
39
|
+
type = wrapped.render;
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
var wizardScope = createCompositeScope("Wizard");
|
|
46
|
+
var VISUALLY_HIDDEN_STYLE = {
|
|
47
|
+
position: "absolute",
|
|
48
|
+
width: "1px",
|
|
49
|
+
height: "1px",
|
|
50
|
+
padding: 0,
|
|
51
|
+
margin: "-1px",
|
|
52
|
+
overflow: "hidden",
|
|
53
|
+
clipPath: "inset(50%)",
|
|
54
|
+
whiteSpace: "nowrap",
|
|
55
|
+
border: 0
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Default step announcement — an English fallback, documented per the
|
|
59
|
+
* package i18n rule (CONTRIBUTING §6 / ADR-001): supplementary AT feedback
|
|
60
|
+
* may ship a default; localized hosts override via
|
|
61
|
+
* {@link WizardProps.announceStep}.
|
|
62
|
+
*/
|
|
63
|
+
var defaultAnnounceStep = ({
|
|
64
|
+
currentStep,
|
|
65
|
+
totalSteps,
|
|
66
|
+
isComplete
|
|
67
|
+
}) => {
|
|
68
|
+
if (isComplete) return "Complete";
|
|
69
|
+
return `Step ${currentStep + 1} of ${totalSteps}`;
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Formal semantic identity — Wizard root (Structure entity, root part).
|
|
73
|
+
*/
|
|
74
|
+
var wizardMeta = {
|
|
75
|
+
displayName: "Wizard",
|
|
76
|
+
entity: "Structure",
|
|
77
|
+
structure: "root"
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* A step-at-a-time Structure composite. The host reads each child's
|
|
81
|
+
* fixed `composition` identity at runtime to decide which single step
|
|
82
|
+
* to render, whether to swap in the summary view, and whether to render
|
|
83
|
+
* the navigation row. This is the canonical evidence that FSL
|
|
84
|
+
* `Composition` drives behavior from *fixed-identity sub-parts*
|
|
85
|
+
* (complementing `DialogActions`, which drives behavior from *runtime
|
|
86
|
+
* values on leaves*).
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```tsx
|
|
90
|
+
* const [step, setStep] = React.useState(0);
|
|
91
|
+
* return (
|
|
92
|
+
* <Wizard currentStep={step} onStepChange={setStep} aria-label="Onboarding">
|
|
93
|
+
* <WizardStep>First step content</WizardStep>
|
|
94
|
+
* <WizardStep>Second step content</WizardStep>
|
|
95
|
+
* <WizardStep>Third step content</WizardStep>
|
|
96
|
+
* <WizardSummary>All done!</WizardSummary>
|
|
97
|
+
* <WizardNavigation prevLabel="Back" nextLabel="Next" finishLabel="Finish" />
|
|
98
|
+
* </Wizard>
|
|
99
|
+
* );
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
var Wizard = ({
|
|
103
|
+
currentStep,
|
|
104
|
+
defaultStep = 0,
|
|
105
|
+
onStepChange,
|
|
106
|
+
announceStep = defaultAnnounceStep,
|
|
107
|
+
children,
|
|
108
|
+
...props
|
|
109
|
+
}) => {
|
|
110
|
+
const [internalStep, setInternalStep] = React.useState(defaultStep);
|
|
111
|
+
const step = currentStep ?? internalStep;
|
|
112
|
+
const rootRef = React.useRef(null);
|
|
113
|
+
const setStep = React.useCallback(next => {
|
|
114
|
+
if (currentStep === void 0) setInternalStep(next);
|
|
115
|
+
onStepChange?.(next);
|
|
116
|
+
}, [currentStep, onStepChange]);
|
|
117
|
+
const classified = React.useMemo(() => {
|
|
118
|
+
const steps = [];
|
|
119
|
+
let summary = null;
|
|
120
|
+
let navigation = null;
|
|
121
|
+
const unknown = [];
|
|
122
|
+
React.Children.forEach(children, child => {
|
|
123
|
+
const composition = getChildComposition(child);
|
|
124
|
+
if (composition === "step" && React.isValidElement(child)) steps.push(child);else if (composition === "summary" && React.isValidElement(child)) summary = child;else if (composition === "navigation" && React.isValidElement(child)) navigation = child;else unknown.push(child);
|
|
125
|
+
});
|
|
126
|
+
return {
|
|
127
|
+
steps,
|
|
128
|
+
summary,
|
|
129
|
+
navigation,
|
|
130
|
+
unknown
|
|
131
|
+
};
|
|
132
|
+
}, [children]);
|
|
133
|
+
const totalSteps = classified.steps.length;
|
|
134
|
+
const safeStep = Math.max(0, Math.min(step, totalSteps));
|
|
135
|
+
const isComplete = safeStep >= totalSteps && totalSteps > 0;
|
|
136
|
+
const goTo = React.useCallback(next => {
|
|
137
|
+
setStep(Math.max(0, Math.min(next, totalSteps)));
|
|
138
|
+
}, [setStep, totalSteps]);
|
|
139
|
+
const goNext = React.useCallback(() => {
|
|
140
|
+
return goTo(safeStep + 1);
|
|
141
|
+
}, [goTo, safeStep]);
|
|
142
|
+
const goPrev = React.useCallback(() => {
|
|
143
|
+
return goTo(safeStep - 1);
|
|
144
|
+
}, [goTo, safeStep]);
|
|
145
|
+
const contextValue = React.useMemo(() => {
|
|
146
|
+
return {
|
|
147
|
+
currentStep: safeStep,
|
|
148
|
+
totalSteps,
|
|
149
|
+
isFirst: safeStep === 0,
|
|
150
|
+
isLast: totalSteps > 0 && safeStep === totalSteps - 1,
|
|
151
|
+
isComplete,
|
|
152
|
+
goNext,
|
|
153
|
+
goPrev,
|
|
154
|
+
goTo
|
|
155
|
+
};
|
|
156
|
+
}, [safeStep, totalSteps, isComplete, goNext, goPrev, goTo]);
|
|
157
|
+
const activeStep = isComplete ? classified.summary : classified.steps[safeStep] ?? null;
|
|
158
|
+
const prevStepRef = React.useRef(safeStep);
|
|
159
|
+
React.useEffect(() => {
|
|
160
|
+
if (prevStepRef.current === safeStep) return;
|
|
161
|
+
prevStepRef.current = safeStep;
|
|
162
|
+
rootRef.current?.querySelector("[data-part=\"content\"]")?.focus();
|
|
163
|
+
}, [safeStep]);
|
|
164
|
+
return /* @__PURE__ */jsx(wizardScope.Provider, {
|
|
165
|
+
value: contextValue,
|
|
166
|
+
children: /* @__PURE__ */jsxs("section", {
|
|
167
|
+
...props,
|
|
168
|
+
ref: rootRef,
|
|
169
|
+
"data-scope": "wizard",
|
|
170
|
+
"data-part": "root",
|
|
171
|
+
"data-current-step": safeStep,
|
|
172
|
+
"data-total-steps": totalSteps,
|
|
173
|
+
"data-complete": isComplete ? "true" : void 0,
|
|
174
|
+
style: {
|
|
175
|
+
boxSizing: "border-box",
|
|
176
|
+
display: "flex",
|
|
177
|
+
flexDirection: "column",
|
|
178
|
+
gap: vars.spacing.gap.stack.md,
|
|
179
|
+
...vars.text.body.md
|
|
180
|
+
},
|
|
181
|
+
children: [/* @__PURE__ */jsx("div", {
|
|
182
|
+
"data-scope": "wizard",
|
|
183
|
+
"data-part": "status",
|
|
184
|
+
"aria-live": "polite",
|
|
185
|
+
style: VISUALLY_HIDDEN_STYLE,
|
|
186
|
+
children: totalSteps > 0 ? announceStep({
|
|
187
|
+
currentStep: safeStep,
|
|
188
|
+
totalSteps,
|
|
189
|
+
isComplete
|
|
190
|
+
}) : null
|
|
191
|
+
}), activeStep, classified.navigation, classified.unknown]
|
|
192
|
+
})
|
|
193
|
+
});
|
|
194
|
+
};
|
|
195
|
+
Wizard.displayName = wizardMeta.displayName;
|
|
196
|
+
/**
|
|
197
|
+
* Formal semantic identity — a step body inside a {@link Wizard}.
|
|
198
|
+
*/
|
|
199
|
+
var wizardStepMeta = {
|
|
200
|
+
displayName: "WizardStep",
|
|
201
|
+
entity: "Structure",
|
|
202
|
+
structure: "content",
|
|
203
|
+
composition: "step"
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* A single step's body inside a Wizard. Only the step at the current
|
|
207
|
+
* index is rendered — selection is the Wizard host's runtime dispatch
|
|
208
|
+
* on this component's fixed `composition` identity.
|
|
209
|
+
*/
|
|
210
|
+
var WizardStepBase = props => {
|
|
211
|
+
wizardScope.use(wizardStepMeta.displayName);
|
|
212
|
+
return /* @__PURE__ */jsx("div", {
|
|
213
|
+
tabIndex: -1,
|
|
214
|
+
...props,
|
|
215
|
+
"data-scope": "wizard",
|
|
216
|
+
"data-part": "content",
|
|
217
|
+
"data-composition": "step",
|
|
218
|
+
style: {
|
|
219
|
+
boxSizing: "border-box",
|
|
220
|
+
display: "flex",
|
|
221
|
+
flexDirection: "column",
|
|
222
|
+
gap: vars.spacing.gap.stack.sm
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
};
|
|
226
|
+
WizardStepBase.displayName = wizardStepMeta.displayName;
|
|
227
|
+
var WizardStep = defineWizardSlot(WizardStepBase, "step");
|
|
228
|
+
/**
|
|
229
|
+
* Formal semantic identity — summary view shown after the last step.
|
|
230
|
+
*/
|
|
231
|
+
var wizardSummaryMeta = {
|
|
232
|
+
displayName: "WizardSummary",
|
|
233
|
+
entity: "Structure",
|
|
234
|
+
structure: "content",
|
|
235
|
+
composition: "summary"
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* A summary view shown once the user has advanced past the last
|
|
239
|
+
* {@link WizardStep}. Selection is the Wizard host's runtime dispatch on
|
|
240
|
+
* this component's fixed `composition` identity — rendered if and only if
|
|
241
|
+
* `currentStep === totalSteps`.
|
|
242
|
+
*/
|
|
243
|
+
var WizardSummaryBase = props => {
|
|
244
|
+
wizardScope.use(wizardSummaryMeta.displayName);
|
|
245
|
+
return /* @__PURE__ */jsx("div", {
|
|
246
|
+
tabIndex: -1,
|
|
247
|
+
...props,
|
|
248
|
+
"data-scope": "wizard",
|
|
249
|
+
"data-part": "content",
|
|
250
|
+
"data-composition": "summary",
|
|
251
|
+
style: {
|
|
252
|
+
boxSizing: "border-box",
|
|
253
|
+
display: "flex",
|
|
254
|
+
flexDirection: "column",
|
|
255
|
+
gap: vars.spacing.gap.stack.sm
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
};
|
|
259
|
+
WizardSummaryBase.displayName = wizardSummaryMeta.displayName;
|
|
260
|
+
var WizardSummary = defineWizardSlot(WizardSummaryBase, "summary");
|
|
261
|
+
/**
|
|
262
|
+
* Formal semantic identity — the navigation slot inside a {@link Wizard}.
|
|
263
|
+
*/
|
|
264
|
+
var wizardNavigationMeta = {
|
|
265
|
+
displayName: "WizardNavigation",
|
|
266
|
+
entity: "Structure",
|
|
267
|
+
structure: "actions",
|
|
268
|
+
composition: "navigation"
|
|
269
|
+
};
|
|
270
|
+
/**
|
|
271
|
+
* Default navigation row for a {@link Wizard}. Reads wizard state from
|
|
272
|
+
* React context — rendering is otherwise deterministic: `Back` is
|
|
273
|
+
* disabled on the first step; the primary button switches label from
|
|
274
|
+
* the next label to the finish label on the last step and fires
|
|
275
|
+
* `onFinish` before advancing. All labels are caller-supplied — the
|
|
276
|
+
* package ships no user-facing copy (CONTRIBUTING §6 / ADR-001).
|
|
277
|
+
*/
|
|
278
|
+
var WizardNavigationBase = ({
|
|
279
|
+
prevLabel,
|
|
280
|
+
nextLabel,
|
|
281
|
+
finishLabel,
|
|
282
|
+
onFinish,
|
|
283
|
+
children
|
|
284
|
+
}) => {
|
|
285
|
+
const ctx = wizardScope.use(wizardNavigationMeta.displayName);
|
|
286
|
+
const content = children ? children(ctx) : /* @__PURE__ */jsxs(Fragment, {
|
|
287
|
+
children: [/* @__PURE__ */jsx(Button, {
|
|
288
|
+
evaluation: "muted",
|
|
289
|
+
onPress: ctx.goPrev,
|
|
290
|
+
isDisabled: ctx.isFirst || ctx.isComplete,
|
|
291
|
+
children: prevLabel
|
|
292
|
+
}), /* @__PURE__ */jsx(Button, {
|
|
293
|
+
evaluation: "primary",
|
|
294
|
+
composition: "primaryAction",
|
|
295
|
+
consequence: ctx.isLast ? "committing" : "neutral",
|
|
296
|
+
isDisabled: ctx.isComplete,
|
|
297
|
+
onPress: () => {
|
|
298
|
+
if (ctx.isLast) onFinish?.();
|
|
299
|
+
ctx.goNext();
|
|
300
|
+
},
|
|
301
|
+
children: ctx.isLast ? finishLabel : nextLabel
|
|
302
|
+
})]
|
|
303
|
+
});
|
|
304
|
+
return /* @__PURE__ */jsx("div", {
|
|
305
|
+
"data-scope": "wizard",
|
|
306
|
+
"data-part": "actions",
|
|
307
|
+
"data-composition": "navigation",
|
|
308
|
+
style: {
|
|
309
|
+
boxSizing: "border-box",
|
|
310
|
+
display: "flex",
|
|
311
|
+
flexDirection: "row",
|
|
312
|
+
alignItems: "center",
|
|
313
|
+
justifyContent: "flex-end",
|
|
314
|
+
gap: vars.spacing.gap.inline.md,
|
|
315
|
+
marginBlockStart: vars.spacing.gap.stack.sm
|
|
316
|
+
},
|
|
317
|
+
children: content
|
|
318
|
+
});
|
|
319
|
+
};
|
|
320
|
+
WizardNavigationBase.displayName = wizardNavigationMeta.displayName;
|
|
321
|
+
var WizardNavigation = defineWizardSlot(WizardNavigationBase, "navigation");
|
|
322
|
+
|
|
323
|
+
//#endregion
|
|
324
|
+
export { Wizard, WizardNavigation, WizardStep, WizardSummary, wizardMeta, wizardNavigationMeta, wizardStepMeta, wizardSummaryMeta };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
let react = require("react");
|
|
4
|
+
react = require_runtime.__toESM(react, 1);
|
|
5
|
+
|
|
6
|
+
//#region src/composites/scope.ts
|
|
7
|
+
/**
|
|
8
|
+
* Composite Scope — runtime host-presence guard.
|
|
9
|
+
*
|
|
10
|
+
* The Component Semantics Projection treats Composition as a flat vocabulary
|
|
11
|
+
* (see `docs/.../component-model.md` §"Parent disambiguation"): a slot role
|
|
12
|
+
* like `'heading'` is legal on its declared Entity regardless of which host
|
|
13
|
+
* renders it. The data model intentionally has no `host` field, and the FSL
|
|
14
|
+
* Structural Language §10 lists no Host × Role legality matrix.
|
|
15
|
+
*
|
|
16
|
+
* What is *not* covered by that flatness is the runtime invariant that some
|
|
17
|
+
* sub-parts only make sense inside their host (e.g. `<DialogHeading>` solto
|
|
18
|
+
* is not a meaningful element). This module closes that gap with two tiny
|
|
19
|
+
* builders — chosen by *name*, not by a `<true>` type sentinel — so the
|
|
20
|
+
* author's intent is self-evident at the call site:
|
|
21
|
+
*
|
|
22
|
+
* 1. `createPresenceScope(host)` — pure host-presence guard. No state is
|
|
23
|
+
* shared; sub-parts only need to assert "I am inside <Host>". Provider
|
|
24
|
+
* takes no `value` prop. Used by Dialog, Form, TextField, Menu.
|
|
25
|
+
*
|
|
26
|
+
* 2. `createCompositeScope<T>(host)` — host shares typed state with its
|
|
27
|
+
* sub-parts (evaluation, controlled step, validation, …). Provider
|
|
28
|
+
* requires a `value` of type `T`. Used by Accordion, Wizard.
|
|
29
|
+
*
|
|
30
|
+
* Both builders share the same error contract: `use('<SubPart>')` outside
|
|
31
|
+
* the host throws an `Error` whose message names both the sub-part and the
|
|
32
|
+
* expected host. Both set `Context.displayName = '<Host>Scope'` for nice
|
|
33
|
+
* DevTools output.
|
|
34
|
+
*
|
|
35
|
+
* Authoring rule (mirrored in CONTRIBUTING §2.3): if the host has no value
|
|
36
|
+
* to share with sub-parts, use `createPresenceScope`. If TypeScript tempts
|
|
37
|
+
* you to write `createCompositeScope<true>('X')`, you wanted
|
|
38
|
+
* `createPresenceScope('X')`.
|
|
39
|
+
*
|
|
40
|
+
* @example Presence guard
|
|
41
|
+
* ```tsx
|
|
42
|
+
* const formScope = createPresenceScope('Form');
|
|
43
|
+
*
|
|
44
|
+
* export const Form = ({ children }) => (
|
|
45
|
+
* <formScope.Provider>{children}</formScope.Provider>
|
|
46
|
+
* );
|
|
47
|
+
*
|
|
48
|
+
* export const FormSubmit = (props) => {
|
|
49
|
+
* formScope.use('FormSubmit');
|
|
50
|
+
* return <button type="submit" {...props} />;
|
|
51
|
+
* };
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @example Stateful scope
|
|
55
|
+
* ```tsx
|
|
56
|
+
* const wizardScope = createCompositeScope<WizardContextValue>('Wizard');
|
|
57
|
+
*
|
|
58
|
+
* export const Wizard = ({ children }) => {
|
|
59
|
+
* const ctx = useWizardState(...);
|
|
60
|
+
* return <wizardScope.Provider value={ctx}>{children}</wizardScope.Provider>;
|
|
61
|
+
* };
|
|
62
|
+
*
|
|
63
|
+
* export const WizardNavigation = () => {
|
|
64
|
+
* const ctx = wizardScope.use('WizardNavigation');
|
|
65
|
+
* return <button onClick={ctx.goNext}>Next</button>;
|
|
66
|
+
* };
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
var PRESENT = Symbol("composite-scope.present");
|
|
70
|
+
/** Throws the canonical out-of-host error for a sub-part. */
|
|
71
|
+
var throwOutOfHost = (consumerDisplayName, hostDisplayName) => {
|
|
72
|
+
throw new Error(`<${consumerDisplayName}> must be rendered inside <${hostDisplayName}>.`);
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Creates a scope where the host shares typed state with its sub-parts.
|
|
76
|
+
*
|
|
77
|
+
* @param hostDisplayName - Display name of the host component, used in the
|
|
78
|
+
* error message thrown by `use()` when a sub-part is rendered outside.
|
|
79
|
+
* Must match the host's `*Meta.displayName`.
|
|
80
|
+
*/
|
|
81
|
+
var createCompositeScope = hostDisplayName => {
|
|
82
|
+
const Context = react.createContext(null);
|
|
83
|
+
Context.displayName = `${hostDisplayName}Scope`;
|
|
84
|
+
return {
|
|
85
|
+
Provider: Context.Provider,
|
|
86
|
+
use: consumerDisplayName => {
|
|
87
|
+
const value = react.useContext(Context);
|
|
88
|
+
if (value === null) return throwOutOfHost(consumerDisplayName, hostDisplayName);
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Creates a presence-only scope. Sub-parts use `scope.use('<Name>')` to
|
|
95
|
+
* assert they are rendered inside `<Host>` — no value is shared.
|
|
96
|
+
*
|
|
97
|
+
* Use this when the host has nothing to publish to sub-parts. If you find
|
|
98
|
+
* yourself reaching for `createCompositeScope<true>('X')`, use this
|
|
99
|
+
* instead — it is the same contract with a clearer name and zero
|
|
100
|
+
* boilerplate at the call site (`<Provider>` instead of
|
|
101
|
+
* `<Provider value={true}>`).
|
|
102
|
+
*/
|
|
103
|
+
var createPresenceScope = hostDisplayName => {
|
|
104
|
+
const Context = react.createContext(null);
|
|
105
|
+
Context.displayName = `${hostDisplayName}Scope`;
|
|
106
|
+
const Provider = ({
|
|
107
|
+
children
|
|
108
|
+
}) => {
|
|
109
|
+
return react.createElement(Context.Provider, {
|
|
110
|
+
value: PRESENT
|
|
111
|
+
}, children);
|
|
112
|
+
};
|
|
113
|
+
Provider.displayName = `${hostDisplayName}ScopeProvider`;
|
|
114
|
+
return {
|
|
115
|
+
Provider,
|
|
116
|
+
use: consumerDisplayName => {
|
|
117
|
+
if (react.useContext(Context) !== PRESENT) throwOutOfHost(consumerDisplayName, hostDisplayName);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
//#endregion
|
|
123
|
+
exports.createCompositeScope = createCompositeScope;
|
|
124
|
+
exports.createPresenceScope = createPresenceScope;
|