@astryxdesign/core 0.1.7 → 0.1.8-canary.c5fa19b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/README.md +30 -19
- package/dist/Avatar/Avatar.d.ts +10 -5
- package/dist/Avatar/Avatar.d.ts.map +1 -1
- package/dist/Avatar/Avatar.js +12 -8
- package/dist/Avatar/AvatarSizeContext.d.ts +1 -1
- package/dist/Avatar/AvatarSizeContext.js +1 -1
- package/dist/Avatar/AvatarStatusDot.d.ts +2 -2
- package/dist/Avatar/AvatarStatusDot.js +2 -2
- package/dist/AvatarGroup/AvatarGroup.d.ts +2 -2
- package/dist/AvatarGroup/AvatarGroup.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroup.js +2 -2
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.d.ts.map +1 -1
- package/dist/AvatarGroup/AvatarGroupOverflow.js +16 -14
- package/dist/Carousel/Carousel.d.ts +3 -2
- package/dist/Carousel/Carousel.d.ts.map +1 -1
- package/dist/Carousel/Carousel.js +32 -2
- package/dist/Chat/ChatMessage.d.ts +1 -1
- package/dist/Chat/ChatMessage.d.ts.map +1 -1
- package/dist/Chat/ChatMessage.js +1 -1
- package/dist/Citation/Citation.d.ts.map +1 -1
- package/dist/Citation/Citation.js +10 -4
- package/dist/Collapsible/Collapsible.d.ts +11 -1
- package/dist/Collapsible/Collapsible.d.ts.map +1 -1
- package/dist/Collapsible/Collapsible.js +36 -4
- package/dist/ContextMenu/ContextMenu.d.ts.map +1 -1
- package/dist/ContextMenu/ContextMenu.js +4 -3
- package/dist/ContextMenu/index.d.ts +3 -0
- package/dist/ContextMenu/index.d.ts.map +1 -1
- package/dist/ContextMenu/index.js +7 -1
- package/dist/DropdownMenu/DropdownMenu.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenu.js +8 -4
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts +89 -0
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuCheckboxItem.js +170 -0
- package/dist/DropdownMenu/DropdownMenuContext.d.ts +17 -1
- package/dist/DropdownMenu/DropdownMenuContext.d.ts.map +1 -1
- package/dist/DropdownMenu/DropdownMenuContext.js +18 -0
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts +69 -0
- package/dist/DropdownMenu/DropdownMenuRadioGroup.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuRadioGroup.js +70 -0
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts +69 -0
- package/dist/DropdownMenu/DropdownMenuRadioItem.d.ts.map +1 -0
- package/dist/DropdownMenu/DropdownMenuRadioItem.js +178 -0
- package/dist/DropdownMenu/index.d.ts +5 -1
- package/dist/DropdownMenu/index.d.ts.map +1 -1
- package/dist/DropdownMenu/index.js +10 -1
- package/dist/DropdownMenu/menuItemRoles.d.ts +13 -0
- package/dist/DropdownMenu/menuItemRoles.d.ts.map +1 -0
- package/dist/DropdownMenu/menuItemRoles.js +15 -0
- package/dist/Icon/Icon.d.ts +43 -13
- package/dist/Icon/Icon.d.ts.map +1 -1
- package/dist/Icon/Icon.js +62 -27
- package/dist/Outline/Outline.d.ts +69 -3
- package/dist/Outline/Outline.d.ts.map +1 -1
- package/dist/Outline/Outline.js +129 -26
- package/dist/Outline/useScrollSpy.d.ts +27 -7
- package/dist/Outline/useScrollSpy.d.ts.map +1 -1
- package/dist/Outline/useScrollSpy.js +202 -70
- package/dist/TabList/Tab.d.ts.map +1 -1
- package/dist/TabList/Tab.js +2 -2
- package/dist/TabList/TabList.d.ts.map +1 -1
- package/dist/TabList/TabList.js +2 -0
- package/dist/TabList/TabMenu.d.ts.map +1 -1
- package/dist/TabList/TabMenu.js +1 -1
- package/dist/Table/Table.d.ts +1 -1
- package/dist/Table/Table.js +1 -1
- package/dist/Table/index.d.ts +2 -0
- package/dist/Table/index.d.ts.map +1 -1
- package/dist/Table/index.js +1 -0
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.d.ts.map +1 -1
- package/dist/Table/plugins/rowExpansion/useTableRowExpansion.js +24 -2
- package/dist/Table/plugins/sortable/useTableSortable.d.ts.map +1 -1
- package/dist/Table/plugins/sortable/useTableSortable.js +18 -7
- package/dist/Table/plugins/tree/index.d.ts +5 -0
- package/dist/Table/plugins/tree/index.d.ts.map +1 -0
- package/dist/Table/plugins/tree/index.js +4 -0
- package/dist/Table/plugins/tree/useTableTreeData.d.ts +38 -0
- package/dist/Table/plugins/tree/useTableTreeData.d.ts.map +1 -0
- package/dist/Table/plugins/tree/useTableTreeData.js +358 -0
- package/dist/Table/plugins/tree/useTableTreeState.d.ts +51 -0
- package/dist/Table/plugins/tree/useTableTreeState.d.ts.map +1 -0
- package/dist/Table/plugins/tree/useTableTreeState.js +175 -0
- package/dist/Table/useBaseTablePlugins.d.ts.map +1 -1
- package/dist/Table/useBaseTablePlugins.js +6 -3
- package/dist/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/Toolbar/Toolbar.js +8 -1
- package/dist/TreeList/TreeListItem.d.ts.map +1 -1
- package/dist/TreeList/TreeListItem.js +5 -14
- package/dist/astryx.css +24 -4
- package/dist/astryx.umd.js +54 -54
- package/dist/astryx.umd.js.map +4 -4
- package/dist/theme/Theme.js +1 -1
- package/dist/theme/defineTheme.d.ts +1 -1
- package/dist/theme/defineTheme.js +1 -1
- package/dist/theme/hct.d.ts +0 -1
- package/dist/theme/hct.d.ts.map +1 -1
- package/dist/theme/hct.js +0 -3
- package/docs.mjs +32 -461
- package/locales/en.json +200 -172
- package/locales/pseudo.json +21 -0
- package/package.json +3 -1
- package/scripts/postinstall.mjs +93 -0
- package/src/Avatar/Avatar.doc.mjs +6 -6
- package/src/Avatar/Avatar.tsx +16 -11
- package/src/Avatar/AvatarSizeContext.ts +1 -1
- package/src/Avatar/AvatarStatusDot.tsx +2 -2
- package/src/AvatarGroup/AvatarGroup.doc.mjs +1 -1
- package/src/AvatarGroup/AvatarGroup.test.tsx +17 -3
- package/src/AvatarGroup/AvatarGroup.tsx +3 -3
- package/src/AvatarGroup/AvatarGroupOverflow.doc.mjs +4 -4
- package/src/AvatarGroup/AvatarGroupOverflow.tsx +21 -5
- package/src/Carousel/Carousel.doc.mjs +1 -0
- package/src/Carousel/Carousel.test.tsx +96 -1
- package/src/Carousel/Carousel.tsx +29 -2
- package/src/Chat/Chat.doc.mjs +1 -1
- package/src/Chat/ChatMessage.tsx +4 -2
- package/src/CheckboxInput/CheckboxInput.doc.mjs +1 -1
- package/src/Citation/Citation.test.tsx +51 -0
- package/src/Citation/Citation.tsx +8 -2
- package/src/Collapsible/Collapsible.doc.mjs +7 -0
- package/src/Collapsible/Collapsible.test.tsx +80 -0
- package/src/Collapsible/Collapsible.tsx +58 -5
- package/src/ContextMenu/ContextMenu.test.tsx +75 -1
- package/src/ContextMenu/ContextMenu.tsx +10 -5
- package/src/ContextMenu/index.ts +15 -0
- package/src/DropdownMenu/DropdownMenu.doc.mjs +23 -0
- package/src/DropdownMenu/DropdownMenu.tsx +11 -6
- package/src/DropdownMenu/DropdownMenuCheckboxItem.doc.mjs +95 -0
- package/src/DropdownMenu/DropdownMenuCheckboxItem.tsx +237 -0
- package/src/DropdownMenu/DropdownMenuContext.tsx +29 -1
- package/src/DropdownMenu/DropdownMenuItem.doc.mjs +7 -1
- package/src/DropdownMenu/DropdownMenuRadioGroup.doc.mjs +80 -0
- package/src/DropdownMenu/DropdownMenuRadioGroup.tsx +119 -0
- package/src/DropdownMenu/DropdownMenuRadioItem.doc.mjs +80 -0
- package/src/DropdownMenu/DropdownMenuRadioItem.tsx +232 -0
- package/src/DropdownMenu/DropdownMenuSelectable.test.tsx +163 -0
- package/src/DropdownMenu/index.ts +23 -3
- package/src/DropdownMenu/menuItemRoles.ts +22 -0
- package/src/Field/Field.doc.mjs +1 -1
- package/src/Field/FieldLabel.doc.mjs +1 -1
- package/src/Icon/Icon.doc.mjs +12 -0
- package/src/Icon/Icon.test.tsx +142 -0
- package/src/Icon/Icon.tsx +98 -30
- package/src/Item/Item.doc.mjs +1 -1
- package/src/NumberInput/NumberInput.doc.mjs +2 -2
- package/src/Outline/Outline.doc.mjs +123 -3
- package/src/Outline/Outline.test.tsx +735 -0
- package/src/Outline/Outline.tsx +184 -33
- package/src/Outline/useScrollSpy.ts +248 -72
- package/src/Selector/SelectorOption.doc.mjs +1 -1
- package/src/SideNav/SideNavItem.doc.mjs +2 -2
- package/src/Switch/Switch.doc.mjs +1 -1
- package/src/TabList/Tab.tsx +5 -1
- package/src/TabList/TabList.doc.mjs +3 -3
- package/src/TabList/TabList.test.tsx +66 -0
- package/src/TabList/TabList.tsx +8 -0
- package/src/TabList/TabMenu.tsx +4 -1
- package/src/Table/Table.doc.mjs +2 -0
- package/src/Table/Table.tsx +1 -1
- package/src/Table/index.ts +7 -0
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.test.tsx +90 -1
- package/src/Table/plugins/rowExpansion/useTableRowExpansion.tsx +37 -5
- package/src/Table/plugins/sortable/useTableSortable.test.tsx +95 -9
- package/src/Table/plugins/sortable/useTableSortable.tsx +23 -7
- package/src/Table/plugins/tree/index.ts +12 -0
- package/src/Table/plugins/tree/useTableTreeData-perf.test.tsx +163 -0
- package/src/Table/plugins/tree/useTableTreeData.test.tsx +594 -0
- package/src/Table/plugins/tree/useTableTreeData.tsx +495 -0
- package/src/Table/plugins/tree/useTableTreeState.test.tsx +636 -0
- package/src/Table/plugins/tree/useTableTreeState.tsx +253 -0
- package/src/Table/useBaseTablePlugins.ts +6 -2
- package/src/Table/useTableTreeData.doc.mjs +75 -0
- package/src/Table/useTableTreeState.doc.mjs +98 -0
- package/src/TextArea/TextArea.doc.mjs +1 -1
- package/src/TextInput/TextInput.doc.mjs +1 -1
- package/src/Toolbar/Toolbar.tsx +10 -3
- package/src/TreeList/TreeList.test.tsx +32 -1
- package/src/TreeList/TreeListItem.tsx +19 -10
- package/src/docs-script.test.mjs +18 -13
- package/src/postinstall.test.mjs +71 -0
- package/src/theme/Theme.doc.mjs +4 -4
- package/src/theme/Theme.tsx +1 -1
- package/src/theme/defineTheme.ts +1 -1
- package/src/theme/hct.ts +0 -4
- package/dist/TreeList/treeListItem.markers.stylex.d.ts +0 -16
- package/dist/TreeList/treeListItem.markers.stylex.d.ts.map +0 -1
- package/dist/TreeList/treeListItem.markers.stylex.js +0 -22
- package/src/TreeList/treeListItem.markers.stylex.ts +0 -20
package/src/Icon/Icon.tsx
CHANGED
|
@@ -111,51 +111,64 @@ const colorStyles = stylex.create({
|
|
|
111
111
|
/**
|
|
112
112
|
* Size styles for direct SVG icon components.
|
|
113
113
|
* Uses width/height only — SVG components handle their own viewBox scaling.
|
|
114
|
+
*
|
|
115
|
+
* Sizes are expressed in `rem` (relative to the root font-size) so icons scale
|
|
116
|
+
* in step with text when the document font-size changes, matching the rest of
|
|
117
|
+
* the design system's rem-based type scale. Values are the px-equivalents at a
|
|
118
|
+
* 16px root: 12px → 0.75rem, 16px → 1rem, 20px → 1.25rem, 24px → 1.5rem.
|
|
114
119
|
*/
|
|
115
120
|
const sizeStyles = stylex.create({
|
|
116
121
|
xsm: {
|
|
117
|
-
width:
|
|
118
|
-
height:
|
|
122
|
+
width: '0.75rem',
|
|
123
|
+
height: '0.75rem',
|
|
119
124
|
},
|
|
120
125
|
sm: {
|
|
121
|
-
width:
|
|
122
|
-
height:
|
|
126
|
+
width: '1rem',
|
|
127
|
+
height: '1rem',
|
|
123
128
|
},
|
|
124
129
|
md: {
|
|
125
|
-
width:
|
|
126
|
-
height:
|
|
130
|
+
width: '1.25rem',
|
|
131
|
+
height: '1.25rem',
|
|
127
132
|
},
|
|
128
133
|
lg: {
|
|
129
|
-
width:
|
|
130
|
-
height:
|
|
134
|
+
width: '1.5rem',
|
|
135
|
+
height: '1.5rem',
|
|
131
136
|
},
|
|
132
137
|
});
|
|
133
138
|
|
|
134
139
|
/**
|
|
135
140
|
* Size styles for string-based (registry) icons.
|
|
136
141
|
* Includes fontSize so that 1em-based icons from the registry scale correctly.
|
|
142
|
+
*
|
|
143
|
+
* Expressed in `rem` for the same reason as {@link sizeStyles} — icons track the
|
|
144
|
+
* root font-size instead of being locked to absolute pixels.
|
|
137
145
|
*/
|
|
138
146
|
const spanSizeStyles = stylex.create({
|
|
147
|
+
/* eslint-disable @astryx/no-hardcoded-styles -- fontSize here sizes 1em-based
|
|
148
|
+
registry SVGs to the icon box; icons use their own 12/16/20/24 scale, not
|
|
149
|
+
the 14px-anchored textSizeVars type scale. Values are rem so icons track
|
|
150
|
+
the root font-size. */
|
|
139
151
|
xsm: {
|
|
140
|
-
width:
|
|
141
|
-
height:
|
|
142
|
-
fontSize:
|
|
152
|
+
width: '0.75rem',
|
|
153
|
+
height: '0.75rem',
|
|
154
|
+
fontSize: '0.75rem',
|
|
143
155
|
},
|
|
144
156
|
sm: {
|
|
145
|
-
width:
|
|
146
|
-
height:
|
|
147
|
-
fontSize:
|
|
157
|
+
width: '1rem',
|
|
158
|
+
height: '1rem',
|
|
159
|
+
fontSize: '1rem',
|
|
148
160
|
},
|
|
149
161
|
md: {
|
|
150
|
-
width:
|
|
151
|
-
height:
|
|
152
|
-
fontSize:
|
|
162
|
+
width: '1.25rem',
|
|
163
|
+
height: '1.25rem',
|
|
164
|
+
fontSize: '1.25rem',
|
|
153
165
|
},
|
|
154
166
|
lg: {
|
|
155
|
-
width:
|
|
156
|
-
height:
|
|
157
|
-
fontSize:
|
|
167
|
+
width: '1.5rem',
|
|
168
|
+
height: '1.5rem',
|
|
169
|
+
fontSize: '1.5rem',
|
|
158
170
|
},
|
|
171
|
+
/* eslint-enable @astryx/no-hardcoded-styles */
|
|
159
172
|
});
|
|
160
173
|
|
|
161
174
|
// =============================================================================
|
|
@@ -194,13 +207,57 @@ export interface IconProps extends Omit<
|
|
|
194
207
|
color?: IconColor;
|
|
195
208
|
/**
|
|
196
209
|
* The size of the icon.
|
|
197
|
-
* - 'xsm': 12px
|
|
198
|
-
* - 'sm': 16px
|
|
199
|
-
* - 'md': 20px
|
|
200
|
-
* - 'lg': 24px
|
|
210
|
+
* - 'xsm': 0.75rem (12px at a 16px root)
|
|
211
|
+
* - 'sm': 1rem (16px at a 16px root)
|
|
212
|
+
* - 'md': 1.25rem (20px at a 16px root)
|
|
213
|
+
* - 'lg': 1.5rem (24px at a 16px root)
|
|
201
214
|
* @default 'md'
|
|
202
215
|
*/
|
|
203
216
|
size?: IconSize;
|
|
217
|
+
/**
|
|
218
|
+
* Accessible name for the icon. Set this only when the icon is MEANINGFUL on
|
|
219
|
+
* its own — a standalone status glyph or an icon-only indicator with no
|
|
220
|
+
* adjacent text conveying the same information. Providing it exposes the icon
|
|
221
|
+
* to assistive tech as `role="img"` with this string as the accessible name
|
|
222
|
+
* (via `aria-label`) and drops the default `aria-hidden="true"`.
|
|
223
|
+
*
|
|
224
|
+
* Omit it (the default) for decorative icons — the common case, e.g. an icon
|
|
225
|
+
* beside a text label — and the icon stays hidden from assistive tech
|
|
226
|
+
* (`aria-hidden="true"`). An empty string (`''`) is treated the same as
|
|
227
|
+
* omitting it (decorative), since an empty accessible name is meaningless.
|
|
228
|
+
*
|
|
229
|
+
* Don't set `label` when an interactive parent (Button, IconButton, link)
|
|
230
|
+
* already names the control — that produces a duplicate announcement.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```
|
|
234
|
+
* // Meaningful, standalone icon
|
|
235
|
+
* <Icon icon="success" label="Completed" />
|
|
236
|
+
*
|
|
237
|
+
* // Decorative icon (default) — omit label
|
|
238
|
+
* <Icon icon="search" />
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
label?: string;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Derives the ARIA attributes for an icon from its `label` prop.
|
|
246
|
+
*
|
|
247
|
+
* - Non-empty `label` → meaningful image: `role="img"` + `aria-label`, and no
|
|
248
|
+
* `aria-hidden` (an `aria-hidden` element is removed from the accessibility
|
|
249
|
+
* tree, so its accessible name would be ignored).
|
|
250
|
+
* - Omitted or empty `label` → decorative default: `aria-hidden="true"`.
|
|
251
|
+
*
|
|
252
|
+
* The result is spread BEFORE `{...props}` in both render modes so an explicit
|
|
253
|
+
* `aria-hidden` / `role` / `aria-label` from the consumer always wins.
|
|
254
|
+
*/
|
|
255
|
+
function getIconA11yProps(
|
|
256
|
+
label: string | undefined,
|
|
257
|
+
): {role: 'img'; 'aria-label': string} | {'aria-hidden': 'true'} {
|
|
258
|
+
return label != null && label !== ''
|
|
259
|
+
? {role: 'img', 'aria-label': label}
|
|
260
|
+
: {'aria-hidden': 'true'};
|
|
204
261
|
}
|
|
205
262
|
|
|
206
263
|
// =============================================================================
|
|
@@ -219,9 +276,14 @@ export function Icon({
|
|
|
219
276
|
icon,
|
|
220
277
|
color = 'inherit',
|
|
221
278
|
size = 'md',
|
|
279
|
+
label,
|
|
222
280
|
ref,
|
|
223
281
|
...props
|
|
224
282
|
}: IconProps) {
|
|
283
|
+
// Derive ARIA from `label`: decorative (aria-hidden) by default, or a
|
|
284
|
+
// meaningful image (role="img" + aria-label) when `label` is non-empty.
|
|
285
|
+
const a11yProps = getIconA11yProps(label);
|
|
286
|
+
|
|
225
287
|
// String mode: resolve from icon registry, wrap in styled span
|
|
226
288
|
if (typeof icon === 'string') {
|
|
227
289
|
return (
|
|
@@ -229,6 +291,7 @@ export function Icon({
|
|
|
229
291
|
name={icon}
|
|
230
292
|
color={color}
|
|
231
293
|
size={size}
|
|
294
|
+
a11yProps={a11yProps}
|
|
232
295
|
spanProps={props}
|
|
233
296
|
/>
|
|
234
297
|
);
|
|
@@ -239,7 +302,10 @@ export function Icon({
|
|
|
239
302
|
return (
|
|
240
303
|
<IconComponent
|
|
241
304
|
ref={ref}
|
|
242
|
-
|
|
305
|
+
// Derived a11y (decorative default or meaningful `label`) is spread
|
|
306
|
+
// BEFORE {...props} so an explicit aria-hidden/role/aria-label from the
|
|
307
|
+
// consumer still wins as an escape hatch.
|
|
308
|
+
{...a11yProps}
|
|
243
309
|
{...mergeProps(
|
|
244
310
|
themeProps('icon', {size, color}),
|
|
245
311
|
stylex.props(styles.root, colorStyles[color], sizeStyles[size]),
|
|
@@ -266,11 +332,13 @@ function IconFromRegistry({
|
|
|
266
332
|
name,
|
|
267
333
|
color,
|
|
268
334
|
size,
|
|
335
|
+
a11yProps,
|
|
269
336
|
spanProps,
|
|
270
337
|
}: {
|
|
271
338
|
name: IconName;
|
|
272
339
|
color: IconColor;
|
|
273
340
|
size: IconSize;
|
|
341
|
+
a11yProps: {role: 'img'; 'aria-label': string} | {'aria-hidden': 'true'};
|
|
274
342
|
spanProps?: Omit<SVGProps<SVGSVGElement>, 'ref' | 'color'>;
|
|
275
343
|
}) {
|
|
276
344
|
const resolvedIcon = getIcon(name);
|
|
@@ -281,11 +349,11 @@ function IconFromRegistry({
|
|
|
281
349
|
|
|
282
350
|
return (
|
|
283
351
|
<span
|
|
284
|
-
//
|
|
285
|
-
//
|
|
286
|
-
//
|
|
287
|
-
//
|
|
288
|
-
|
|
352
|
+
// Derived a11y — decorative (aria-hidden) by default, or a meaningful
|
|
353
|
+
// image (role="img" + aria-label) when `label` is set. Placed BEFORE the
|
|
354
|
+
// prop spread so consumers can still override it with explicit
|
|
355
|
+
// aria-hidden/role/aria-label. This mirrors component-mode Icon.
|
|
356
|
+
{...a11yProps}
|
|
289
357
|
{...(spanProps as React.HTMLAttributes<HTMLSpanElement>)}
|
|
290
358
|
{...mergeProps(
|
|
291
359
|
themeProps('icon', {size, color}),
|
package/src/Item/Item.doc.mjs
CHANGED
|
@@ -28,7 +28,7 @@ export const docs = {
|
|
|
28
28
|
props: [
|
|
29
29
|
{name: 'label', type: 'ReactNode', description: 'Primary text identifying this item. Accepts string (auto-truncated) or ReactNode (for rich content).', required: true},
|
|
30
30
|
{name: 'marker', type: 'ReactNode', description: 'Marker rendered before startContent as a direct flex child. Use for list bullets/counters that need custom baseline alignment.'},
|
|
31
|
-
{name: 'startContent', type: 'ReactNode', description: 'Content rendered before the label/description area, such as an icon, avatar, or checkbox.', slotElements: [{__element: 'Avatar', props: {name: 'Ada Lovelace', size: '
|
|
31
|
+
{name: 'startContent', type: 'ReactNode', description: 'Content rendered before the label/description area, such as an icon, avatar, or checkbox.', slotElements: [{__element: 'Avatar', props: {name: 'Ada Lovelace', size: 'sm'}}, {__element: 'Icon', props: {icon: 'info', size: 'sm', color: 'secondary'}}]},
|
|
32
32
|
{name: 'description', type: 'ReactNode', description: 'Secondary text: subtitle, description, or supporting info.'},
|
|
33
33
|
{name: 'endContent', type: 'ReactNode', description: 'Content rendered after the label/description area, such as badges, metadata, timestamps, or action buttons.', slotElements: [{__element: 'Badge', props: {label: 3}}, {__element: 'Text', props: {color: 'secondary'}, children: '2h ago'}]},
|
|
34
34
|
{name: 'as', type: "'div' | 'li' | 'span'", description: 'HTML element to render as the root.', default: "'div'"},
|
|
@@ -82,12 +82,12 @@ export const docs = {
|
|
|
82
82
|
{
|
|
83
83
|
name: 'startIcon',
|
|
84
84
|
type: 'IconType',
|
|
85
|
-
description: 'Icon to display at the start of the input. See `
|
|
85
|
+
description: 'Icon to display at the start of the input. See `astryx docs icons` for valid semantic names.',
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
88
|
name: 'labelIcon',
|
|
89
89
|
type: 'IconType',
|
|
90
|
-
description: 'Icon to display before the label text. See `
|
|
90
|
+
description: 'Icon to display before the label text. See `astryx docs icons` for valid semantic names.',
|
|
91
91
|
},
|
|
92
92
|
{
|
|
93
93
|
name: 'status',
|
|
@@ -72,6 +72,38 @@ export const docs = {
|
|
|
72
72
|
description: "Density variant controlling item padding. 'compact' for dense UIs, 'default' for standard spacing.",
|
|
73
73
|
default: "'default'",
|
|
74
74
|
},
|
|
75
|
+
{
|
|
76
|
+
name: 'onNavigateStart',
|
|
77
|
+
type: '(id: string) => void',
|
|
78
|
+
description:
|
|
79
|
+
'Called with the item id when navigation begins, before the scroll starts. Pair with onNavigateEnd to drive an arrival effect (flash, ring, pulse) on the target heading.',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'onNavigateEnd',
|
|
83
|
+
type: '(id: string) => void',
|
|
84
|
+
description:
|
|
85
|
+
'Called with the item id once the navigation resolves - when the smooth scroll settles, or when reduced motion turns it into an instant jump. Fires exactly once per onNavigateStart, including when the user interrupts the scroll, so a "navigating" state can never leak.',
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: 'offset',
|
|
89
|
+
type: 'number',
|
|
90
|
+
description:
|
|
91
|
+
"Height in px of a fixed header overlaying the top of the scroll root. Shifts both the activation line and the scroll landing by the same amount, so a heading activates exactly where navigating to it puts it - below the header, not underneath it. Composes with each heading's own scroll-margin-top (the header, then the breathing room below it); it does not replace it. Leave at 0 when nothing overlays the content and let scroll-margin-top do the work.",
|
|
92
|
+
default: '0',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: 'scrollContainerRef',
|
|
96
|
+
type: 'React.RefObject<HTMLElement | null>',
|
|
97
|
+
description:
|
|
98
|
+
'Scroll container to track, instead of auto-detecting the nearest scrollable ancestor. Use it when content scrolls inside a split pane, modal, or dashboard panel rather than the viewport.',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'hasScrollOnClick',
|
|
102
|
+
type: 'boolean',
|
|
103
|
+
description:
|
|
104
|
+
'Whether activating an item smooth-scrolls to it. Set to false to own the scrolling yourself (virtualized content, a router): the Outline still updates the active item, the hash, and the navigate callbacks, but performs no scroll.',
|
|
105
|
+
default: 'true',
|
|
106
|
+
},
|
|
75
107
|
{
|
|
76
108
|
name: 'xstyle',
|
|
77
109
|
type: 'StyleXStyles',
|
|
@@ -137,6 +169,51 @@ function MarkdownOutline({markdown}) {
|
|
|
137
169
|
const items = useOutlineFromMarkdown(markdown);
|
|
138
170
|
return <Outline items={items} />;
|
|
139
171
|
}
|
|
172
|
+
`,
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
label: 'Scoped scroll container (split pane, modal, panel)',
|
|
176
|
+
code: `
|
|
177
|
+
import {useRef} from 'react';
|
|
178
|
+
import {Outline} from '@astryxdesign/core/Outline';
|
|
179
|
+
|
|
180
|
+
function PanelDocs() {
|
|
181
|
+
const scrollContainerRef = useRef(null);
|
|
182
|
+
|
|
183
|
+
// Without scrollContainerRef the outline auto-detects the nearest scrollable
|
|
184
|
+
// ancestor, which is the viewport here - so the highlight would never move.
|
|
185
|
+
return (
|
|
186
|
+
<div style={{display: 'flex'}}>
|
|
187
|
+
<div ref={scrollContainerRef} style={{overflowY: 'auto', height: 480}}>
|
|
188
|
+
<h2 id="overview">Overview</h2>
|
|
189
|
+
{/* ...content... */}
|
|
190
|
+
</div>
|
|
191
|
+
<Outline items={items} scrollContainerRef={scrollContainerRef} offset={64} />
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
`,
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
label: 'Navigate callbacks (flash the heading on arrival)',
|
|
199
|
+
code: `
|
|
200
|
+
import {useState} from 'react';
|
|
201
|
+
import {Outline} from '@astryxdesign/core/Outline';
|
|
202
|
+
|
|
203
|
+
function FlashOnArrival() {
|
|
204
|
+
const [flashId, setFlashId] = useState(null);
|
|
205
|
+
|
|
206
|
+
// onNavigateEnd fires once the smooth scroll settles - and once per
|
|
207
|
+
// onNavigateStart even if the user scrolls away mid-jump, so this never
|
|
208
|
+
// gets stuck.
|
|
209
|
+
return (
|
|
210
|
+
<Outline
|
|
211
|
+
items={items}
|
|
212
|
+
onNavigateStart={() => setFlashId(null)}
|
|
213
|
+
onNavigateEnd={id => setFlashId(id)}
|
|
214
|
+
/>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
140
217
|
`,
|
|
141
218
|
},
|
|
142
219
|
],
|
|
@@ -144,14 +221,17 @@ function MarkdownOutline({markdown}) {
|
|
|
144
221
|
],
|
|
145
222
|
usage: {
|
|
146
223
|
description:
|
|
147
|
-
'A table-of-contents sidebar for documentation pages, help centers, wikis, and long settings pages. Use it for navigation within a single page, not for app routes. Features a sliding indicator track that animates to the active heading.',
|
|
224
|
+
'A table-of-contents sidebar for documentation pages, help centers, wikis, and long settings pages. Use it for navigation within a single page, not for app routes. Features a sliding indicator track that animates to the active heading. The list is a single tab stop: arrow keys move between headings, Home/End jump to the ends, and Enter/Space activate.',
|
|
148
225
|
bestPractices: [
|
|
149
226
|
{guidance: true, description: 'Pass a flat ordered list of headings and let level control indentation.'},
|
|
150
227
|
{guidance: true, description: 'Use activeId when custom scroll logic owns the active section.'},
|
|
151
228
|
{guidance: true, description: 'Use density="compact" in dense sidebars where vertical space is tight.'},
|
|
152
229
|
{guidance: true, description: 'Use useOutlineFromMarkdown or useOutlineFromDOM when headings are generated from content.'},
|
|
230
|
+
{guidance: true, description: 'Pass scrollContainerRef when the content scrolls in a split pane, modal, or panel instead of the viewport.'},
|
|
231
|
+
{guidance: true, description: 'Set offset to the height of a fixed header that overlays the content, so headings land below it instead of underneath it.'},
|
|
153
232
|
{guidance: false, description: 'Use Outline for application navigation - use SideNav or TopNav for routes.'},
|
|
154
233
|
{guidance: false, description: 'Use Outline for expandable hierarchy - use TreeList when nodes need expand and collapse.'},
|
|
234
|
+
{guidance: false, description: 'Rely on onNavigateEnd to mean "arrived" - it also fires when the user interrupts the scroll.'},
|
|
155
235
|
],
|
|
156
236
|
},
|
|
157
237
|
};
|
|
@@ -205,6 +285,33 @@ export const docsZh = {
|
|
|
205
285
|
description: "控制项目内边距的密度变体。'compact' 用于紧凑界面,'default' 为标准间距。",
|
|
206
286
|
default: "'default'",
|
|
207
287
|
},
|
|
288
|
+
{
|
|
289
|
+
name: 'onNavigateStart',
|
|
290
|
+
type: '(id: string) => void',
|
|
291
|
+
description: '导航开始时(滚动前)调用,传入项目 id。与 onNavigateEnd 搭配可在目标标题上实现到达效果(闪烁、描边、脉冲)。',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
name: 'onNavigateEnd',
|
|
295
|
+
type: '(id: string) => void',
|
|
296
|
+
description: '导航结束时调用(平滑滚动停止,或减弱动效将其变为瞬时跳转)。每次 onNavigateStart 必定对应一次调用,包括用户中途手动滚动打断的情况,因此“导航中”状态不会泄漏。',
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
name: 'offset',
|
|
300
|
+
type: 'number',
|
|
301
|
+
description: '覆盖滚动根顶部的固定顶栏高度(px)。同时位移激活线与滚动落点,因此标题被激活的位置正是点击后停留的位置——落在顶栏下方,而非被顶栏遮住。它与每个标题自身的 scroll-margin-top 叠加(先顶栏,再留白),而非取代它。若没有元素覆盖内容,保持 0,交给 scroll-margin-top 处理。',
|
|
302
|
+
default: '0',
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: 'scrollContainerRef',
|
|
306
|
+
type: 'React.RefObject<HTMLElement | null>',
|
|
307
|
+
description: '要跟踪的滚动容器,替代自动探测最近的可滚动祖先。当内容在分栏、弹窗或面板中滚动而非视口时使用。',
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
name: 'hasScrollOnClick',
|
|
311
|
+
type: 'boolean',
|
|
312
|
+
description: '激活项目时是否平滑滚动。设为 false 可自行接管滚动(虚拟列表、路由):Outline 仍会更新激活项、hash 和导航回调,但不执行滚动。',
|
|
313
|
+
default: 'true',
|
|
314
|
+
},
|
|
208
315
|
{
|
|
209
316
|
name: 'xstyle',
|
|
210
317
|
type: 'StyleXStyles',
|
|
@@ -230,7 +337,7 @@ export const docsZh = {
|
|
|
230
337
|
/** @type {import('../docs-types').TranslationDoc} */
|
|
231
338
|
export const docsDense = {
|
|
232
339
|
description:
|
|
233
|
-
'Document outline/table-of-contents nav with sliding indicator track. Flat items array {id,label,level}; anchor links; density variant (default/compact); uncontrolled scroll-spy by scroll position (last heading past its scroll-margin-top
|
|
340
|
+
'Document outline/table-of-contents nav with sliding indicator track. Flat items array {id,label,level}; anchor links; density variant (default/compact); uncontrolled scroll-spy by scroll position (last heading past its resting line = offset + its own scroll-margin-top; first item at top, last at bottom); controlled with activeId; smooth-scroll on click that pins the active item until the next manual scroll. Single tab stop (roving tabindex): Arrow keys move, Home/End jump, Enter/Space activate. onNavigateStart/onNavigateEnd fire around the scroll. Scroll scoping via offset / scrollContainerRef / hasScrollOnClick.',
|
|
234
341
|
usage: {
|
|
235
342
|
description:
|
|
236
343
|
'A table-of-contents sidebar for documentation pages, help centers, wikis, and long settings pages. Use it for navigation within a single page, not for app routes.',
|
|
@@ -239,8 +346,11 @@ export const docsDense = {
|
|
|
239
346
|
{guidance: true, description: 'Use activeId when custom scroll logic owns the active section.'},
|
|
240
347
|
{guidance: true, description: 'Use density="compact" in dense sidebars where vertical space is tight.'},
|
|
241
348
|
{guidance: true, description: 'Use useOutlineFromMarkdown or useOutlineFromDOM when headings are generated from content.'},
|
|
349
|
+
{guidance: true, description: 'Pass scrollContainerRef when the content scrolls in a split pane, modal, or panel instead of the viewport.'},
|
|
350
|
+
{guidance: true, description: 'Set offset to the height of a fixed header that overlays the content, so headings land below it instead of underneath it.'},
|
|
242
351
|
{guidance: false, description: 'Use Outline for application navigation - use SideNav or TopNav for routes.'},
|
|
243
352
|
{guidance: false, description: 'Use Outline for expandable hierarchy - use TreeList when nodes need expand and collapse.'},
|
|
353
|
+
{guidance: false, description: 'Rely on onNavigateEnd to mean "arrived" - it also fires when the user interrupts the scroll.'},
|
|
244
354
|
],
|
|
245
355
|
},
|
|
246
356
|
propDescriptions: {
|
|
@@ -249,6 +359,11 @@ export const docsDense = {
|
|
|
249
359
|
onActiveIdChange: 'Called when active id changes from scroll-spy or click.',
|
|
250
360
|
label: "Accessible nav label. Default: 'Table of contents'.",
|
|
251
361
|
density: "Density variant: 'default' (standard) or 'compact'. Default: 'default'.",
|
|
362
|
+
onNavigateStart: 'Called with the id when navigation begins, before the scroll.',
|
|
363
|
+
onNavigateEnd: 'Called with the id once the scroll settles. Fires exactly once per onNavigateStart, including on user interruption.',
|
|
364
|
+
offset: 'Height in px of a fixed header overlaying the scroll root. Shifts BOTH the activation line and the scroll landing, so a heading activates where navigating to it lands it. Composes with (does not replace) each heading scroll-margin-top. Default: 0.',
|
|
365
|
+
scrollContainerRef: 'RefObject of the scroll container to track, instead of the nearest scrollable ancestor. For split panes, modals, panels.',
|
|
366
|
+
hasScrollOnClick: 'Whether activating an item smooth-scrolls to it. false = you own scrolling; active id, hash and callbacks still update. Default: true.',
|
|
252
367
|
xstyle: 'StyleX styles for layout. Must be stylex.create() value.',
|
|
253
368
|
},
|
|
254
369
|
components: [
|
|
@@ -256,13 +371,18 @@ export const docsDense = {
|
|
|
256
371
|
name: 'Outline',
|
|
257
372
|
displayName: 'Outline',
|
|
258
373
|
description:
|
|
259
|
-
'Document outline nav with sliding indicator. Renders heading anchors with a density variant and
|
|
374
|
+
'Document outline nav with sliding indicator. Renders heading anchors with a density variant, roving-tabindex keyboard nav, navigate callbacks, and scroll-spy active state when uncontrolled.',
|
|
260
375
|
propDescriptions: {
|
|
261
376
|
items: 'Ordered OutlineItem[]: {id,label,level}. id should match target heading DOM id.',
|
|
262
377
|
activeId: 'Controlled active heading id. Disables built-in scroll-spy.',
|
|
263
378
|
onActiveIdChange: 'Called when active id changes from scroll-spy or click.',
|
|
264
379
|
label: "Accessible nav label. Default: 'Table of contents'.",
|
|
265
380
|
density: "Density variant: 'default' | 'compact'. Default: 'default'.",
|
|
381
|
+
onNavigateStart: 'Called with the id when navigation begins, before the scroll.',
|
|
382
|
+
onNavigateEnd: 'Called with the id once the scroll settles. Fires exactly once per onNavigateStart, including on user interruption.',
|
|
383
|
+
offset: 'Height in px of a fixed header overlaying the scroll root. Shifts both the activation line and the scroll landing. Composes with scroll-margin-top. Default: 0.',
|
|
384
|
+
scrollContainerRef: 'RefObject of the scroll container to track, instead of the nearest scrollable ancestor.',
|
|
385
|
+
hasScrollOnClick: 'Whether activating an item smooth-scrolls to it. Default: true.',
|
|
266
386
|
xstyle: 'StyleX styles for layout. Must be stylex.create() value.',
|
|
267
387
|
},
|
|
268
388
|
},
|