@usefragments/ui 1.1.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/ui.css +327 -274
- package/dist/components/Checkbox/Checkbox.module.scss.cjs +11 -11
- package/dist/components/Checkbox/Checkbox.module.scss.js +11 -11
- package/dist/components/Chip/Chip.module.scss.cjs +15 -15
- package/dist/components/Chip/Chip.module.scss.js +15 -15
- package/dist/components/Combobox/index.cjs +106 -9
- package/dist/components/Combobox/index.d.ts.map +1 -1
- package/dist/components/Combobox/index.js +106 -9
- package/dist/components/DataTable/DataTable.module.scss.cjs +37 -34
- package/dist/components/DataTable/DataTable.module.scss.js +37 -34
- package/dist/components/DataTable/index.d.ts.map +1 -1
- package/dist/components/Main/Main.module.scss.cjs +26 -0
- package/dist/components/Main/Main.module.scss.js +26 -0
- package/dist/components/Main/index.cjs +128 -0
- package/dist/components/Main/index.d.ts +22 -0
- package/dist/components/Main/index.d.ts.map +1 -0
- package/dist/components/Main/index.js +111 -0
- package/dist/components/Table/Table.module.scss.cjs +19 -19
- package/dist/components/Table/Table.module.scss.js +19 -19
- package/dist/components/TableOfContents/TableOfContents.module.scss.cjs +15 -15
- package/dist/components/TableOfContents/TableOfContents.module.scss.js +15 -15
- package/dist/components/TableOfContents/index.cjs +22 -42
- package/dist/components/TableOfContents/index.d.ts +3 -3
- package/dist/components/TableOfContents/index.d.ts.map +1 -1
- package/dist/components/TableOfContents/index.js +22 -42
- package/dist/{components/DataTable/DataTable.virtual.cjs → data-table-virtual.cjs} +1 -1
- package/dist/{components/DataTable/DataTable.virtual.js → data-table-virtual.js} +1 -1
- package/dist/data-table.cjs +27 -9
- package/dist/data-table.js +19 -1
- package/dist/index.cjs +87 -88
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/fragments.json +1 -1
- package/package.json +57 -2
- package/src/components/Checkbox/Checkbox.contract.json +5 -24
- package/src/components/Checkbox/Checkbox.module.scss +4 -2
- package/src/components/Chip/Chip.contract.json +11 -28
- package/src/components/Chip/Chip.module.scss +21 -45
- package/src/components/Combobox/Combobox.contract.json +6 -24
- package/src/components/Combobox/index.tsx +132 -6
- package/src/components/DataTable/DataTable.module.scss +31 -8
- package/src/components/DataTable/index.tsx +44 -16
- package/src/components/Main/Main.contract.json +79 -0
- package/src/components/Main/Main.module.scss +31 -0
- package/src/components/Main/index.tsx +124 -0
- package/src/components/Table/Table.module.scss +22 -5
- package/src/components/TableOfContents/TableOfContents.contract.json +6 -16
- package/src/components/TableOfContents/TableOfContents.module.scss +10 -13
- package/src/components/TableOfContents/index.tsx +27 -38
- package/src/index.ts +11 -9
- package/src/styles/styles.d.ts +8 -0
- package/src/tokens/_component-properties.scss +1 -0
- package/dist/components/Cloud/DashboardLayoutPrototypes.shared.d.ts +0 -33
- package/dist/components/Cloud/DashboardLayoutPrototypes.shared.d.ts.map +0 -1
- package/dist/components/Cloud/DashboardLayoutPrototypes.shell.d.ts +0 -11
- package/dist/components/Cloud/DashboardLayoutPrototypes.shell.d.ts.map +0 -1
- package/dist/components/Cloud/PullRequestsPrototypes.content.d.ts +0 -5
- package/dist/components/Cloud/PullRequestsPrototypes.content.d.ts.map +0 -1
- package/dist/components/Cloud/PullRequestsPrototypes.detail-panels.d.ts +0 -4
- package/dist/components/Cloud/PullRequestsPrototypes.detail-panels.d.ts.map +0 -1
- package/dist/components/Cloud/PullRequestsPrototypes.detail.d.ts +0 -3
- package/dist/components/Cloud/PullRequestsPrototypes.detail.d.ts.map +0 -1
- package/dist/components/Cloud/PullRequestsPrototypes.id-data.d.ts +0 -86
- package/dist/components/Cloud/PullRequestsPrototypes.id-data.d.ts.map +0 -1
- package/dist/components/Cloud/PullRequestsPrototypes.id-page.d.ts +0 -2
- package/dist/components/Cloud/PullRequestsPrototypes.id-page.d.ts.map +0 -1
- package/src/components/Accordion/Accordion.stories.tsx +0 -106
- package/src/components/Alert/Alert.stories.tsx +0 -119
- package/src/components/AppShell/AppShell.stories.tsx +0 -156
- package/src/components/Avatar/Avatar.stories.tsx +0 -74
- package/src/components/Badge/Badge.stories.tsx +0 -92
- package/src/components/BentoGrid/BentoGrid.stories.tsx +0 -98
- package/src/components/Box/Box.stories.tsx +0 -150
- package/src/components/Breadcrumbs/Breadcrumbs.stories.tsx +0 -87
- package/src/components/Button/Button.stories.tsx +0 -103
- package/src/components/ButtonGroup/ButtonGroup.stories.tsx +0 -97
- package/src/components/Card/Card.stories.tsx +0 -144
- package/src/components/Chart/Chart.stories.tsx +0 -119
- package/src/components/Checkbox/Checkbox.stories.tsx +0 -68
- package/src/components/Chip/Chip.stories.tsx +0 -70
- package/src/components/Cloud/CloudGovernanceShell.module.scss +0 -88
- package/src/components/Cloud/CloudGovernanceShell.stories.tsx +0 -446
- package/src/components/Cloud/ControlSizing.module.scss +0 -49
- package/src/components/Cloud/ControlSizing.stories.tsx +0 -238
- package/src/components/Cloud/DashboardLayoutPrototypes.module.scss +0 -462
- package/src/components/Cloud/DashboardLayoutPrototypes.shared.tsx +0 -375
- package/src/components/Cloud/DashboardLayoutPrototypes.shell.tsx +0 -147
- package/src/components/Cloud/DashboardLayoutPrototypes.stories.tsx +0 -328
- package/src/components/Cloud/PullRequestsPrototypes.content.tsx +0 -283
- package/src/components/Cloud/PullRequestsPrototypes.detail-panels.tsx +0 -381
- package/src/components/Cloud/PullRequestsPrototypes.detail.tsx +0 -204
- package/src/components/Cloud/PullRequestsPrototypes.id-data.ts +0 -395
- package/src/components/Cloud/PullRequestsPrototypes.id-page.tsx +0 -349
- package/src/components/Cloud/PullRequestsPrototypes.stories.tsx +0 -81
- package/src/components/CodeBlock/CodeBlock.stories.tsx +0 -147
- package/src/components/Collapsible/Collapsible.stories.tsx +0 -94
- package/src/components/ColorPicker/ColorPicker.stories.tsx +0 -74
- package/src/components/Combobox/Combobox.stories.tsx +0 -133
- package/src/components/Command/Command.stories.tsx +0 -102
- package/src/components/ConversationList/ConversationList.stories.tsx +0 -93
- package/src/components/DataTable/DataTable.stories.tsx +0 -129
- package/src/components/DatePicker/DatePicker.stories.tsx +0 -129
- package/src/components/Dialog/Dialog.stories.tsx +0 -154
- package/src/components/Drawer/Drawer.stories.tsx +0 -144
- package/src/components/Editor/Editor.stories.tsx +0 -99
- package/src/components/EmptyState/EmptyState.stories.tsx +0 -97
- package/src/components/Field/Field.stories.tsx +0 -109
- package/src/components/Fieldset/Fieldset.stories.tsx +0 -116
- package/src/components/Form/Form.stories.tsx +0 -142
- package/src/components/Grid/Grid.stories.tsx +0 -125
- package/src/components/Header/Header.stories.tsx +0 -133
- package/src/components/Icon/Icon.stories.tsx +0 -80
- package/src/components/IconButton/IconButton.stories.tsx +0 -78
- package/src/components/Image/Image.stories.tsx +0 -109
- package/src/components/Input/Input.stories.tsx +0 -104
- package/src/components/Link/Link.stories.tsx +0 -67
- package/src/components/List/List.stories.tsx +0 -95
- package/src/components/Listbox/Listbox.stories.tsx +0 -81
- package/src/components/Loading/Loading.stories.tsx +0 -69
- package/src/components/Markdown/Markdown.stories.tsx +0 -75
- package/src/components/Menu/Menu.stories.tsx +0 -113
- package/src/components/Message/Message.stories.tsx +0 -85
- package/src/components/NavigationMenu/NavigationMenu.stories.tsx +0 -161
- package/src/components/Pagination/Pagination.stories.tsx +0 -81
- package/src/components/Popover/Popover.stories.tsx +0 -152
- package/src/components/Progress/Progress.stories.tsx +0 -64
- package/src/components/Prompt/Prompt.stories.tsx +0 -93
- package/src/components/RadioGroup/RadioGroup.stories.tsx +0 -107
- package/src/components/ScrollArea/ScrollArea.stories.tsx +0 -97
- package/src/components/Select/Select.stories.tsx +0 -127
- package/src/components/Separator/Separator.stories.tsx +0 -84
- package/src/components/Sidebar/Sidebar.stories.tsx +0 -122
- package/src/components/Skeleton/Skeleton.stories.tsx +0 -80
- package/src/components/Slider/Slider.stories.tsx +0 -73
- package/src/components/Stack/Stack.stories.tsx +0 -73
- package/src/components/Switch/Switch.stories.tsx +0 -59
- package/src/components/Table/Table.stories.tsx +0 -219
- package/src/components/TableOfContents/TableOfContents.stories.tsx +0 -120
- package/src/components/Tabs/Tabs.stories.tsx +0 -136
- package/src/components/Text/Text.stories.tsx +0 -133
- package/src/components/Textarea/Textarea.stories.tsx +0 -107
- package/src/components/Theme/Theme.stories.tsx +0 -95
- package/src/components/ThinkingIndicator/ThinkingIndicator.stories.tsx +0 -85
- package/src/components/Toast/Toast.stories.tsx +0 -91
- package/src/components/ToggleGroup/ToggleGroup.stories.tsx +0 -114
- package/src/components/Tooltip/Tooltip.stories.tsx +0 -95
- package/src/components/VisuallyHidden/VisuallyHidden.stories.tsx +0 -78
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
.wrapper {
|
|
5
5
|
--fui-data-table-bg: var(--fui-table-bg, var(--fui-bg-elevated, #{$fui-bg-elevated}));
|
|
6
|
-
--fui-data-table-header-bg: var(
|
|
6
|
+
--fui-data-table-header-bg: var(
|
|
7
|
+
--fui-table-header-bg,
|
|
8
|
+
color-mix(in srgb, var(--fui-text-primary, #{$fui-text-primary}) 5%, transparent)
|
|
9
|
+
);
|
|
7
10
|
|
|
8
11
|
overflow-x: auto;
|
|
9
12
|
-webkit-overflow-scrolling: touch;
|
|
@@ -69,7 +72,10 @@
|
|
|
69
72
|
user-select: none;
|
|
70
73
|
// Opaque so the sticky header doesn't let rows bleed through on scroll,
|
|
71
74
|
// while inheriting the table surface instead of forcing the page background.
|
|
72
|
-
background: var(
|
|
75
|
+
background: var(
|
|
76
|
+
--fui-data-table-header-bg,
|
|
77
|
+
var(--fui-data-table-bg, var(--fui-bg-primary, #{$fui-bg-primary}))
|
|
78
|
+
);
|
|
73
79
|
|
|
74
80
|
&:first-child {
|
|
75
81
|
border-top-left-radius: var(--fui-radius-md, $fui-radius-md);
|
|
@@ -142,6 +148,10 @@
|
|
|
142
148
|
|
|
143
149
|
// Body
|
|
144
150
|
.tbody {
|
|
151
|
+
.td {
|
|
152
|
+
padding-top: var(--fui-space-2, $fui-space-2);
|
|
153
|
+
padding-bottom: var(--fui-space-2, $fui-space-2);
|
|
154
|
+
}
|
|
145
155
|
}
|
|
146
156
|
|
|
147
157
|
.row {
|
|
@@ -174,11 +184,18 @@
|
|
|
174
184
|
}
|
|
175
185
|
|
|
176
186
|
.selected {
|
|
177
|
-
background-color: var(
|
|
178
|
-
|
|
187
|
+
background-color: var(
|
|
188
|
+
--fui-table-row-selected-bg,
|
|
189
|
+
color-mix(in srgb, var(--fui-text-primary, #{$fui-text-primary}) 8%, transparent)
|
|
190
|
+
);
|
|
191
|
+
box-shadow: inset 1px 0 0
|
|
192
|
+
var(--fui-table-row-selected-border, var(--fui-border-strong, #{$fui-border-strong}));
|
|
179
193
|
|
|
180
194
|
&:hover {
|
|
181
|
-
background-color: var(
|
|
195
|
+
background-color: var(
|
|
196
|
+
--fui-table-row-selected-bg,
|
|
197
|
+
color-mix(in srgb, var(--fui-text-primary, #{$fui-text-primary}) 10%, transparent)
|
|
198
|
+
);
|
|
182
199
|
}
|
|
183
200
|
}
|
|
184
201
|
|
|
@@ -204,10 +221,16 @@
|
|
|
204
221
|
}
|
|
205
222
|
|
|
206
223
|
.selected {
|
|
207
|
-
background-color: var(
|
|
224
|
+
background-color: var(
|
|
225
|
+
--fui-table-row-selected-bg,
|
|
226
|
+
color-mix(in srgb, var(--fui-text-primary, #{$fui-text-primary}) 8%, transparent)
|
|
227
|
+
);
|
|
208
228
|
|
|
209
229
|
&:hover {
|
|
210
|
-
background-color: var(
|
|
230
|
+
background-color: var(
|
|
231
|
+
--fui-table-row-selected-bg,
|
|
232
|
+
color-mix(in srgb, var(--fui-text-primary, #{$fui-text-primary}) 10%, transparent)
|
|
233
|
+
);
|
|
211
234
|
}
|
|
212
235
|
}
|
|
213
236
|
}
|
|
@@ -216,7 +239,7 @@
|
|
|
216
239
|
.bordered {
|
|
217
240
|
border: 1px solid var(--fui-table-border, var(--fui-border, #{$fui-border}));
|
|
218
241
|
border-radius: var(--fui-radius-md, $fui-radius-md);
|
|
219
|
-
background: var(--fui-data-table-bg);
|
|
242
|
+
background: var(--fui-data-table-bg, var(--fui-bg-primary, #{$fui-bg-primary}));
|
|
220
243
|
overflow: hidden;
|
|
221
244
|
}
|
|
222
245
|
|
|
@@ -5,13 +5,40 @@ import styles from "./DataTable.module.scss";
|
|
|
5
5
|
import { Checkbox } from "../Checkbox";
|
|
6
6
|
import { ExpandIcon, SortAscIcon, SortDescIcon, SortIcon } from "./DataTable.icons";
|
|
7
7
|
import { DataTableSkeletonRows, useArrowKeyRowNav } from "./DataTable.support";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
|
|
9
|
+
// ============================================
|
|
10
|
+
// Dependency (@tanstack/react-table) — lazy-required
|
|
11
|
+
// ============================================
|
|
12
|
+
// `@tanstack/react-table` is an optional peer dependency. It is pulled in via a
|
|
13
|
+
// synchronous `require()` (mirroring Chart/Editor) rather than a static ESM
|
|
14
|
+
// import so a bundler's dep-optimizer (e.g. Vite dev) does not create a static
|
|
15
|
+
// import edge for consumers who only use, say, `{ Button }`. `require` resolves
|
|
16
|
+
// synchronously, so `useReactTable` (a hook) is available before the first hook
|
|
17
|
+
// call and hook order stays stable across renders. When the peer is absent the
|
|
18
|
+
// component throws a friendly install message on first render.
|
|
19
|
+
|
|
20
|
+
type ReactTableModule = {
|
|
21
|
+
flexRender: (...args: any[]) => React.ReactNode;
|
|
22
|
+
getCoreRowModel: (...args: any[]) => any;
|
|
23
|
+
getExpandedRowModel: (...args: any[]) => any;
|
|
24
|
+
getSortedRowModel: (...args: any[]) => any;
|
|
25
|
+
useReactTable: (options: any) => any;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
let _reactTable: ReactTableModule | null = null;
|
|
29
|
+
let _reactTableLoaded = false;
|
|
30
|
+
let _reactTableFailed = false;
|
|
31
|
+
|
|
32
|
+
function loadReactTable() {
|
|
33
|
+
if (_reactTableLoaded) return;
|
|
34
|
+
_reactTableLoaded = true;
|
|
35
|
+
try {
|
|
36
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
37
|
+
_reactTable = require("@tanstack/react-table") as ReactTableModule;
|
|
38
|
+
} catch {
|
|
39
|
+
_reactTableFailed = true;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
15
42
|
|
|
16
43
|
/** Horizontal alignment for a column's header + cells. */
|
|
17
44
|
export type ColumnAlign = "left" | "right" | "center";
|
|
@@ -50,15 +77,6 @@ export type DataTableRowClickEvent =
|
|
|
50
77
|
|
|
51
78
|
export type DataTableColumn<T> = ColumnDef<T, unknown>;
|
|
52
79
|
|
|
53
|
-
// ============================================
|
|
54
|
-
// Dependency (@tanstack/react-table)
|
|
55
|
-
// ============================================
|
|
56
|
-
// Statically imported (ESM) so DataTable works in every bundler, including
|
|
57
|
-
// browser SPAs (Vite) where `require` is unavailable. `useReactTable` is a
|
|
58
|
-
// hook and must resolve synchronously on first render. The package is an
|
|
59
|
-
// optional peer dep + `sideEffects: false`, so this import is tree-shaken
|
|
60
|
-
// out of bundles that never import DataTable.
|
|
61
|
-
|
|
62
80
|
export interface DataTableProps<T> extends Omit<React.HTMLAttributes<HTMLTableElement>, "onClick"> {
|
|
63
81
|
/** Column definitions */
|
|
64
82
|
columns: DataTableColumn<T>[];
|
|
@@ -186,6 +204,16 @@ function DataTableRoot<T>({
|
|
|
186
204
|
"aria-describedby": ariaDescribedBy,
|
|
187
205
|
...htmlProps
|
|
188
206
|
}: DataTableProps<T>) {
|
|
207
|
+
loadReactTable();
|
|
208
|
+
if (_reactTableFailed || !_reactTable) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
"[@usefragments/ui] DataTable: @tanstack/react-table is not installed. " +
|
|
211
|
+
"Install it with: npm install @tanstack/react-table"
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
const { flexRender, getCoreRowModel, getExpandedRowModel, getSortedRowModel, useReactTable } =
|
|
215
|
+
_reactTable;
|
|
216
|
+
|
|
189
217
|
const tableRef = React.useRef<HTMLTableElement>(null);
|
|
190
218
|
useArrowKeyRowNav(tableRef, !!onRowClick);
|
|
191
219
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://usefragments.com/schemas/contract.v1.json",
|
|
3
|
+
"name": "Main",
|
|
4
|
+
"description": "Canonical page composition owning gutter, measure, and vertical rhythm.",
|
|
5
|
+
"category": "layout",
|
|
6
|
+
"tags": ["main", "page", "layout", "compound"],
|
|
7
|
+
"status": "stable",
|
|
8
|
+
"sourcePath": "src/components/Main/index.tsx",
|
|
9
|
+
"exportName": "Main",
|
|
10
|
+
"propsSummary": [
|
|
11
|
+
"children: node (required)",
|
|
12
|
+
"measure: full|narrow (default: full)",
|
|
13
|
+
"as: semantic element (default: main)"
|
|
14
|
+
],
|
|
15
|
+
"props": {
|
|
16
|
+
"children": {
|
|
17
|
+
"type": "node",
|
|
18
|
+
"description": "Page regions composed from Main.Header, Main.Description, Main.Content, and Main.Footer",
|
|
19
|
+
"required": true
|
|
20
|
+
},
|
|
21
|
+
"measure": {
|
|
22
|
+
"type": "enum",
|
|
23
|
+
"description": "Maximum width applied to the page composition",
|
|
24
|
+
"default": "full",
|
|
25
|
+
"required": false,
|
|
26
|
+
"values": ["full", "narrow"]
|
|
27
|
+
},
|
|
28
|
+
"as": {
|
|
29
|
+
"type": "enum",
|
|
30
|
+
"description": "Semantic HTML element rendered by the root",
|
|
31
|
+
"default": "main",
|
|
32
|
+
"required": false,
|
|
33
|
+
"values": [
|
|
34
|
+
"div",
|
|
35
|
+
"section",
|
|
36
|
+
"nav",
|
|
37
|
+
"article",
|
|
38
|
+
"aside",
|
|
39
|
+
"header",
|
|
40
|
+
"footer",
|
|
41
|
+
"main",
|
|
42
|
+
"ul",
|
|
43
|
+
"ol"
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"usage": {
|
|
48
|
+
"when": ["Composing a page header, description, content, and footer"],
|
|
49
|
+
"whenNot": ["Application shell navigation (use AppShell)"],
|
|
50
|
+
"guidelines": [
|
|
51
|
+
"Use one Main gutter owner per page",
|
|
52
|
+
"Compose page regions with Main.Header, Main.Description, Main.Content, and Main.Footer"
|
|
53
|
+
],
|
|
54
|
+
"accessibility": [
|
|
55
|
+
"Use the default main element unless an existing shell already owns the main landmark"
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
"examples": [
|
|
59
|
+
{
|
|
60
|
+
"name": "Full Measure",
|
|
61
|
+
"description": "Page composition that uses the full available canvas",
|
|
62
|
+
"code": "<Main measure=\"full\">\n <Main.Header>Page statement</Main.Header>\n <Main.Description>Supporting page context.</Main.Description>\n <Main.Content>Page content</Main.Content>\n <Main.Footer>Page actions</Main.Footer>\n</Main>"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"name": "Narrow Measure",
|
|
66
|
+
"description": "Readable page composition constrained to the canonical 800px measure",
|
|
67
|
+
"code": "<Main measure=\"narrow\">\n <Main.Header>Settings</Main.Header>\n <Main.Description>Manage the workspace contract.</Main.Description>\n <Main.Content>Settings content</Main.Content>\n <Main.Footer>Save changes</Main.Footer>\n</Main>"
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
"ai": {
|
|
71
|
+
"compositionPattern": "compound",
|
|
72
|
+
"subComponents": ["Header", "Description", "Content", "Footer"]
|
|
73
|
+
},
|
|
74
|
+
"provenance": {
|
|
75
|
+
"source": "manual",
|
|
76
|
+
"verified": true,
|
|
77
|
+
"frameworkSupport": "native"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
@use "../../tokens/variables" as *;
|
|
2
|
+
|
|
3
|
+
$fui-main-measure-narrow: 57.143rem !default;
|
|
4
|
+
|
|
5
|
+
.root {
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
width: 100%;
|
|
8
|
+
min-width: 0;
|
|
9
|
+
min-height: 100%;
|
|
10
|
+
margin-inline: auto;
|
|
11
|
+
padding: var(--fui-space-3, $fui-space-3);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.full {
|
|
15
|
+
max-width: none;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.narrow {
|
|
19
|
+
max-width: $fui-main-measure-narrow;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.header,
|
|
23
|
+
.description,
|
|
24
|
+
.content,
|
|
25
|
+
.footer {
|
|
26
|
+
min-width: 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.description {
|
|
30
|
+
max-width: 68ch;
|
|
31
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Stack, type StackProps } from "../Stack";
|
|
3
|
+
import styles from "./Main.module.scss";
|
|
4
|
+
|
|
5
|
+
export type MainMeasure = "full" | "narrow";
|
|
6
|
+
|
|
7
|
+
export interface MainProps extends Omit<StackProps, "as" | "children" | "direction" | "gap"> {
|
|
8
|
+
as?: StackProps["as"];
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
/** Maximum readable width for the page content. */
|
|
11
|
+
measure?: MainMeasure;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type MainRegionProps = StackProps;
|
|
15
|
+
|
|
16
|
+
function joinClasses(...classes: Array<string | false | undefined>) {
|
|
17
|
+
return classes.filter(Boolean).join(" ");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const MainRoot = React.forwardRef<HTMLElement, MainProps>(function MainRoot(
|
|
21
|
+
{ as = "main", children, className, measure = "full", ...htmlProps },
|
|
22
|
+
ref
|
|
23
|
+
) {
|
|
24
|
+
return (
|
|
25
|
+
<Stack
|
|
26
|
+
{...htmlProps}
|
|
27
|
+
ref={ref}
|
|
28
|
+
as={as}
|
|
29
|
+
direction="column"
|
|
30
|
+
gap="lg"
|
|
31
|
+
className={joinClasses(
|
|
32
|
+
styles.root,
|
|
33
|
+
measure === "narrow" ? styles.narrow : styles.full,
|
|
34
|
+
className
|
|
35
|
+
)}
|
|
36
|
+
data-main-measure={measure}
|
|
37
|
+
>
|
|
38
|
+
{children}
|
|
39
|
+
</Stack>
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
function MainHeader({
|
|
44
|
+
as = "header",
|
|
45
|
+
direction = "column",
|
|
46
|
+
gap = "sm",
|
|
47
|
+
className,
|
|
48
|
+
...props
|
|
49
|
+
}: MainRegionProps) {
|
|
50
|
+
return (
|
|
51
|
+
<Stack
|
|
52
|
+
{...props}
|
|
53
|
+
as={as}
|
|
54
|
+
direction={direction}
|
|
55
|
+
gap={gap}
|
|
56
|
+
className={joinClasses(styles.header, className)}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function MainDescription({
|
|
62
|
+
as = "div",
|
|
63
|
+
direction = "column",
|
|
64
|
+
gap = "xs",
|
|
65
|
+
className,
|
|
66
|
+
...props
|
|
67
|
+
}: MainRegionProps) {
|
|
68
|
+
return (
|
|
69
|
+
<Stack
|
|
70
|
+
{...props}
|
|
71
|
+
as={as}
|
|
72
|
+
direction={direction}
|
|
73
|
+
gap={gap}
|
|
74
|
+
className={joinClasses(styles.description, className)}
|
|
75
|
+
/>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function MainContent({
|
|
80
|
+
as = "div",
|
|
81
|
+
direction = "column",
|
|
82
|
+
gap = "lg",
|
|
83
|
+
className,
|
|
84
|
+
...props
|
|
85
|
+
}: MainRegionProps) {
|
|
86
|
+
return (
|
|
87
|
+
<Stack
|
|
88
|
+
{...props}
|
|
89
|
+
as={as}
|
|
90
|
+
direction={direction}
|
|
91
|
+
gap={gap}
|
|
92
|
+
className={joinClasses(styles.content, className)}
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function MainFooter({
|
|
98
|
+
as = "footer",
|
|
99
|
+
direction = "row",
|
|
100
|
+
gap = "sm",
|
|
101
|
+
justify = "end",
|
|
102
|
+
wrap = true,
|
|
103
|
+
className,
|
|
104
|
+
...props
|
|
105
|
+
}: MainRegionProps) {
|
|
106
|
+
return (
|
|
107
|
+
<Stack
|
|
108
|
+
{...props}
|
|
109
|
+
as={as}
|
|
110
|
+
direction={direction}
|
|
111
|
+
gap={gap}
|
|
112
|
+
justify={justify}
|
|
113
|
+
wrap={wrap}
|
|
114
|
+
className={joinClasses(styles.footer, className)}
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const Main = Object.assign(MainRoot, {
|
|
120
|
+
Header: MainHeader,
|
|
121
|
+
Description: MainDescription,
|
|
122
|
+
Content: MainContent,
|
|
123
|
+
Footer: MainFooter,
|
|
124
|
+
});
|
|
@@ -108,14 +108,24 @@
|
|
|
108
108
|
|
|
109
109
|
// Body
|
|
110
110
|
.tbody {
|
|
111
|
+
.td {
|
|
112
|
+
padding-top: var(--fui-space-2, $fui-space-2);
|
|
113
|
+
padding-bottom: var(--fui-space-2, $fui-space-2);
|
|
114
|
+
}
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
.row {
|
|
114
|
-
--fui-table-row-base-bg: var(
|
|
118
|
+
--fui-table-row-base-bg: var(
|
|
119
|
+
--fui-table-bg,
|
|
120
|
+
var(--fui-main-bg, var(--fui-bg-primary, #{$fui-bg-primary}))
|
|
121
|
+
);
|
|
115
122
|
--fui-table-row-bg: var(--fui-table-row-base-bg);
|
|
116
123
|
|
|
117
124
|
border-bottom: 1px solid var(--fui-table-border, var(--fui-border, #{$fui-border}));
|
|
118
|
-
background-color: var(
|
|
125
|
+
background-color: var(
|
|
126
|
+
--fui-table-row-bg,
|
|
127
|
+
var(--fui-table-row-base-bg, var(--fui-bg-primary, #{$fui-bg-primary}))
|
|
128
|
+
);
|
|
119
129
|
transition: background-color var(--fui-transition-fast, $fui-transition-fast);
|
|
120
130
|
|
|
121
131
|
&:last-child {
|
|
@@ -128,11 +138,18 @@
|
|
|
128
138
|
}
|
|
129
139
|
|
|
130
140
|
.selected {
|
|
131
|
-
--fui-table-row-bg: var(
|
|
132
|
-
|
|
141
|
+
--fui-table-row-bg: var(
|
|
142
|
+
--fui-table-row-selected-bg,
|
|
143
|
+
color-mix(in srgb, var(--fui-text-primary, #{$fui-text-primary}) 8%, transparent)
|
|
144
|
+
);
|
|
145
|
+
box-shadow: inset 1px 0 0
|
|
146
|
+
var(--fui-table-row-selected-border, var(--fui-border-strong, #{$fui-border-strong}));
|
|
133
147
|
|
|
134
148
|
&:hover {
|
|
135
|
-
--fui-table-row-bg: var(
|
|
149
|
+
--fui-table-row-bg: var(
|
|
150
|
+
--fui-table-row-selected-bg,
|
|
151
|
+
color-mix(in srgb, var(--fui-text-primary, #{$fui-text-primary}) 10%, transparent)
|
|
152
|
+
);
|
|
136
153
|
}
|
|
137
154
|
}
|
|
138
155
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://usefragments.com/schemas/contract.v1.json",
|
|
3
3
|
"name": "TableOfContents",
|
|
4
|
-
"description": "Sticky sidebar navigation for long-form content or filterable lists. Renders
|
|
4
|
+
"description": "Sticky sidebar navigation for long-form content or filterable lists. Renders a quiet, flush index with nested indentation and an ink-weight active state.",
|
|
5
5
|
"category": "navigation",
|
|
6
6
|
"tags": [
|
|
7
7
|
"toc",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
86
|
"name": "Nested Groups",
|
|
87
|
-
"description": "Filterable list grouped by category
|
|
87
|
+
"description": "Filterable list grouped by category with clear nested indentation",
|
|
88
88
|
"code": "<TableOfContents title=\"Components\">\n <TableOfContents.Item id=\"all\" active>All</TableOfContents.Item>\n <TableOfContents.Group label=\"Primitives\" trailing={<span>3</span>}>\n <TableOfContents.Item id=\"button\">Button</TableOfContents.Item>\n <TableOfContents.Item id=\"card\">Card</TableOfContents.Item>\n <TableOfContents.Item id=\"input\">Input</TableOfContents.Item>\n </TableOfContents.Group>\n <TableOfContents.Group label=\"Custom\" defaultOpen={false}>\n <TableOfContents.Item id=\"features\">Features</TableOfContents.Item>\n <TableOfContents.Item id=\"plans\">Plans</TableOfContents.Item>\n </TableOfContents.Group>\n</TableOfContents>"
|
|
89
89
|
},
|
|
90
90
|
{
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
"hideTitle: boolean - hide the title",
|
|
127
127
|
"label: string - aria-label for nav landmark",
|
|
128
128
|
"TableOfContents.Item id: string - heading ID to link to",
|
|
129
|
-
"TableOfContents.Item active: boolean - highlight
|
|
129
|
+
"TableOfContents.Item active: boolean - highlight the current item with ink and weight",
|
|
130
130
|
"TableOfContents.Item indent: boolean - legacy depth=1 indent for flat lists; prefer Group for new code",
|
|
131
131
|
"TableOfContents.Item leading/trailing: node - optional adornments before/after the label",
|
|
132
132
|
"TableOfContents.Group label: node - header text for the group",
|
|
@@ -135,22 +135,12 @@
|
|
|
135
135
|
"TableOfContents.Group trailing/leading: node - optional adornments on the group header row",
|
|
136
136
|
"Forwards standard nav/anchor DOM props; TableOfContents.Item onClick can prevent default smooth scroll"
|
|
137
137
|
],
|
|
138
|
-
"a11yRules": [
|
|
139
|
-
"A11Y_NAV_LANDMARK",
|
|
140
|
-
"A11Y_ARIA_CURRENT",
|
|
141
|
-
"A11Y_ARIA_EXPANDED"
|
|
142
|
-
]
|
|
138
|
+
"a11yRules": ["A11Y_NAV_LANDMARK", "A11Y_ARIA_CURRENT", "A11Y_ARIA_EXPANDED"]
|
|
143
139
|
},
|
|
144
140
|
"ai": {
|
|
145
141
|
"compositionPattern": "compound",
|
|
146
|
-
"subComponents": [
|
|
147
|
-
|
|
148
|
-
"Group"
|
|
149
|
-
],
|
|
150
|
-
"requiredChildren": [
|
|
151
|
-
"Item",
|
|
152
|
-
"Group"
|
|
153
|
-
],
|
|
142
|
+
"subComponents": ["Item", "Group"],
|
|
143
|
+
"requiredChildren": ["Item", "Group"],
|
|
154
144
|
"commonPatterns": [
|
|
155
145
|
"<TableOfContents>{headings.map(h => <TableOfContents.Item key={h.id} id={h.id} active={activeId === h.id} indent={h.level === 3}>{h.text}</TableOfContents.Item>)}</TableOfContents>",
|
|
156
146
|
"<TableOfContents>{groups.map(g => <TableOfContents.Group key={g.label} label={g.label} trailing={g.count}>{g.items.map(i => <TableOfContents.Item key={i.id} id={i.id} active={activeId === i.id}>{i.label}</TableOfContents.Item>)}</TableOfContents.Group>)}</TableOfContents>"
|
|
@@ -2,21 +2,17 @@
|
|
|
2
2
|
@use "../../tokens/mixins" as *;
|
|
3
3
|
|
|
4
4
|
// ============================================
|
|
5
|
-
//
|
|
5
|
+
// Dormant single-rail geometry
|
|
6
6
|
// ============================================
|
|
7
7
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// at depth-1 → depth-0 transitions a closing elbow is drawn via `::after`
|
|
12
|
-
// on the depth-0 item. Active items paint their rail in accent; the curves
|
|
13
|
-
// into and out of an active depth-1 also flip to accent so the section
|
|
14
|
-
// reads as one continuous coloured snake.
|
|
8
|
+
// The public default is the quiet, flush index disabled below. These values
|
|
9
|
+
// retain the existing optional rail geometry without making its inset part of
|
|
10
|
+
// the default list contract.
|
|
15
11
|
|
|
16
12
|
$rail-x-0: 7px;
|
|
17
13
|
$rail-indent: 14px;
|
|
18
14
|
$rail-x-1: 21px; // = $rail-x-0 + $rail-indent
|
|
19
|
-
$rail-pad: 18px; // <ul> padding
|
|
15
|
+
$rail-pad: 18px; // optional rail inset; never default <ul> padding
|
|
20
16
|
$rail-curve: 14px;
|
|
21
17
|
$rail-thickness: 1px;
|
|
22
18
|
$rail-active-thickness: 1px;
|
|
@@ -44,11 +40,13 @@ $rail-from-item-1: $rail-x-1 - $rail-pad; // 3px
|
|
|
44
40
|
padding: 0 var(--fui-space-2, $fui-space-2);
|
|
45
41
|
}
|
|
46
42
|
|
|
43
|
+
// Plain index (the design-system default, see below) draws no rail, so the
|
|
44
|
+
// list is flush — its links/group headers share the same inline inset as
|
|
45
|
+
// .title. A future rail variant must reinstate its own $rail-pad inset.
|
|
47
46
|
.list {
|
|
48
47
|
list-style: none;
|
|
49
48
|
margin: 0;
|
|
50
49
|
padding: 0;
|
|
51
|
-
padding-left: $rail-pad;
|
|
52
50
|
position: relative;
|
|
53
51
|
display: flex;
|
|
54
52
|
flex-direction: column;
|
|
@@ -210,9 +208,8 @@ $rail-from-item-1: $rail-x-1 - $rail-pad; // 3px
|
|
|
210
208
|
}
|
|
211
209
|
}
|
|
212
210
|
|
|
213
|
-
// Depth-1
|
|
214
|
-
//
|
|
215
|
-
// paint over the line. Reduced padding-left keeps the text in the same x-position.
|
|
211
|
+
// Depth-1 links carry the hierarchy indent while the top-level index remains
|
|
212
|
+
// flush with the title and group headers.
|
|
216
213
|
[data-depth="1"] .link {
|
|
217
214
|
margin-left: var(--fui-space-2, $fui-space-2);
|
|
218
215
|
padding-left: $rail-indent;
|