@godxjp/ui 20.0.0 → 20.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/dist/app/app-provider.js +37 -21
- package/dist/app/index.d.ts +2 -0
- package/dist/app/index.js +2 -0
- package/dist/app/storage.d.ts +17 -0
- package/dist/app/storage.js +28 -0
- package/dist/app/theme-axes.d.ts +2 -0
- package/dist/app/theme-axes.js +45 -0
- package/dist/components/charts/chart-cartesian.d.ts +2 -1
- package/dist/components/charts/chart-cartesian.js +52 -3
- package/dist/components/charts/chart-category-axis.d.ts +55 -0
- package/dist/components/charts/chart-category-axis.js +93 -0
- package/dist/components/charts/chart-frame.d.ts +10 -1
- package/dist/components/charts/chart-frame.js +19 -3
- package/dist/components/charts/compact-bar-trend.d.ts +1 -1
- package/dist/components/charts/compact-bar-trend.js +2 -0
- package/dist/components/charts/pie-chart.d.ts +1 -1
- package/dist/components/charts/pie-chart.js +12 -1
- package/dist/components/charts/recharts-peer.d.ts +49 -0
- package/dist/components/charts/recharts-peer.js +45 -0
- package/dist/components/data-display/card.d.ts +12 -1
- package/dist/components/data-display/card.js +22 -4
- package/dist/components/data-display/code-block.js +6 -2
- package/dist/components/data-display/data-table.d.ts +39 -4
- package/dist/components/data-display/data-table.js +802 -270
- package/dist/components/data-display/descriptions.d.ts +27 -6
- package/dist/components/data-display/descriptions.js +58 -17
- package/dist/components/data-display/index.d.ts +2 -0
- package/dist/components/data-display/index.js +2 -0
- package/dist/components/data-display/list-row.d.ts +10 -1
- package/dist/components/data-display/list-row.js +7 -1
- package/dist/components/data-display/popover.d.ts +9 -3
- package/dist/components/data-display/popover.js +65 -11
- package/dist/components/data-display/range-timeline.d.ts +38 -0
- package/dist/components/data-display/range-timeline.js +161 -0
- package/dist/components/data-display/scroll-area.js +13 -2
- package/dist/components/data-display/service-launcher-card.js +14 -12
- package/dist/components/data-display/table.d.ts +71 -3
- package/dist/components/data-display/table.js +56 -18
- package/dist/components/data-display/tree-list.js +4 -9
- package/dist/components/data-entry/calendar.d.ts +2 -2
- package/dist/components/data-entry/calendar.js +23 -3
- package/dist/components/data-entry/cascader.d.ts +1 -1
- package/dist/components/data-entry/cascader.js +188 -24
- package/dist/components/data-entry/color-picker.d.ts +1 -1
- package/dist/components/data-entry/color-picker.js +17 -5
- package/dist/components/data-entry/control-appearance.d.ts +64 -0
- package/dist/components/data-entry/control-appearance.js +39 -0
- package/dist/components/data-entry/control-surface.d.ts +61 -0
- package/dist/components/data-entry/control-surface.js +39 -0
- package/dist/components/data-entry/date-picker.d.ts +1 -1
- package/dist/components/data-entry/date-picker.js +344 -113
- package/dist/components/data-entry/date-range-picker.d.ts +1 -1
- package/dist/components/data-entry/date-range-picker.js +278 -140
- package/dist/components/data-entry/form-field.d.ts +1 -1
- package/dist/components/data-entry/form-field.js +39 -4
- package/dist/components/data-entry/form.d.ts +4 -0
- package/dist/components/data-entry/form.js +4 -2
- package/dist/components/data-entry/index.d.ts +7 -3
- package/dist/components/data-entry/index.js +10 -1
- package/dist/components/data-entry/input-otp.d.ts +1 -1
- package/dist/components/data-entry/input.d.ts +12 -34
- package/dist/components/data-entry/input.js +92 -24
- package/dist/components/data-entry/month-picker.d.ts +1 -1
- package/dist/components/data-entry/month-picker.js +47 -10
- package/dist/components/data-entry/month-range-picker.d.ts +1 -1
- package/dist/components/data-entry/month-range-picker.js +51 -11
- package/dist/components/data-entry/number-input.d.ts +7 -0
- package/dist/components/data-entry/number-input.js +147 -98
- package/dist/components/data-entry/password-input.d.ts +1 -1
- package/dist/components/data-entry/radio.d.ts +1 -1
- package/dist/components/data-entry/radio.js +61 -13
- package/dist/components/data-entry/search-input.d.ts +1 -1
- package/dist/components/data-entry/search-input.js +16 -2
- package/dist/components/data-entry/search-select.d.ts +2 -2
- package/dist/components/data-entry/search-select.js +209 -78
- package/dist/components/data-entry/select.d.ts +22 -4
- package/dist/components/data-entry/select.js +215 -163
- package/dist/components/data-entry/slider.d.ts +9 -1
- package/dist/components/data-entry/slider.js +87 -9
- package/dist/components/data-entry/switch.d.ts +3 -0
- package/dist/components/data-entry/switch.js +29 -3
- package/dist/components/data-entry/textarea.d.ts +14 -56
- package/dist/components/data-entry/textarea.js +80 -33
- package/dist/components/data-entry/time-picker.d.ts +2 -2
- package/dist/components/data-entry/time-picker.js +333 -109
- package/dist/components/data-entry/time-range-picker.d.ts +5 -0
- package/dist/components/data-entry/time-range-picker.js +89 -0
- package/dist/components/data-entry/transfer.d.ts +1 -1
- package/dist/components/data-entry/transfer.js +86 -28
- package/dist/components/data-entry/tree-select.d.ts +1 -1
- package/dist/components/data-entry/tree-select.js +201 -113
- package/dist/components/data-entry/tree-utils.js +7 -14
- package/dist/components/data-entry/upload-files.d.ts +2 -0
- package/dist/components/data-entry/upload-files.js +31 -0
- package/dist/components/data-entry/upload-request.d.ts +4 -0
- package/dist/components/data-entry/upload-request.js +53 -0
- package/dist/components/data-entry/upload-types.d.ts +28 -0
- package/dist/components/data-entry/upload-types.js +2 -0
- package/dist/components/data-entry/upload.d.ts +2 -2
- package/dist/components/data-entry/upload.js +475 -115
- package/dist/components/feedback/dialog.js +19 -19
- package/dist/components/feedback/sheet.js +3 -0
- package/dist/components/feedback/tooltip.js +3 -0
- package/dist/components/general/button.d.ts +2 -1
- package/dist/components/general/button.js +7 -1
- package/dist/components/general/index.d.ts +1 -0
- package/dist/components/general/index.js +2 -0
- package/dist/components/general/typography.d.ts +3 -0
- package/dist/components/general/typography.js +15 -1
- package/dist/components/general/visually-hidden.d.ts +3 -0
- package/dist/components/general/visually-hidden.js +9 -0
- package/dist/components/layout/app-launcher.d.ts +34 -0
- package/dist/components/layout/app-launcher.js +261 -0
- package/dist/components/layout/app-shell.d.ts +3 -1
- package/dist/components/layout/app-shell.js +34 -14
- package/dist/components/layout/auth-shell.d.ts +1 -1
- package/dist/components/layout/auth-shell.js +2 -0
- package/dist/components/layout/breadcrumb.d.ts +14 -2
- package/dist/components/layout/breadcrumb.js +46 -4
- package/dist/components/layout/flex.d.ts +1 -1
- package/dist/components/layout/flex.js +23 -2
- package/dist/components/layout/index.d.ts +3 -0
- package/dist/components/layout/index.js +5 -1
- package/dist/components/layout/org-switcher.js +20 -1
- package/dist/components/layout/page-container.d.ts +1 -1
- package/dist/components/layout/page-container.js +5 -3
- package/dist/components/layout/responsive-grid.d.ts +16 -2
- package/dist/components/layout/responsive-grid.js +29 -2
- package/dist/components/layout/sidebar.js +2 -1
- package/dist/components/layout/topbar-item.d.ts +3 -0
- package/dist/components/layout/topbar-item.js +18 -3
- package/dist/components/layout/topbar.d.ts +1 -1
- package/dist/components/layout/topbar.js +27 -6
- package/dist/components/navigation/dropdown-menu.d.ts +30 -2
- package/dist/components/navigation/dropdown-menu.js +73 -11
- package/dist/components/navigation/pagination.d.ts +2 -2
- package/dist/components/navigation/pagination.js +155 -83
- package/dist/components/navigation/steps.d.ts +2 -2
- package/dist/components/navigation/steps.js +29 -4
- package/dist/components/navigation/tabs.d.ts +5 -33
- package/dist/components/navigation/tabs.js +232 -64
- package/dist/components/ui/hover-card.js +3 -0
- package/dist/components/ui/input-otp.d.ts +26 -28
- package/dist/components/ui/input-otp.js +50 -7
- package/dist/components/ui/password-input.d.ts +36 -2
- package/dist/components/ui/password-input.js +27 -7
- package/dist/components/ui/rating.d.ts +25 -0
- package/dist/components/ui/rating.js +67 -27
- package/dist/components/ui/segmented.d.ts +9 -0
- package/dist/components/ui/segmented.js +17 -2
- package/dist/components/ui/separator.d.ts +4 -0
- package/dist/components/ui/separator.js +13 -0
- package/dist/components/ui/tag-input.d.ts +45 -0
- package/dist/components/ui/tag-input.js +109 -27
- package/dist/form/form-context.d.ts +29 -0
- package/dist/form/form-context.js +44 -4
- package/dist/form/form-field-array.d.ts +22 -0
- package/dist/form/form-field-array.js +50 -0
- package/dist/form/form-field-control.d.ts +2 -1
- package/dist/form/form-field-control.js +116 -40
- package/dist/form/form-root.d.ts +2 -1
- package/dist/form/form-root.js +142 -23
- package/dist/form/index.d.ts +3 -1
- package/dist/form/index.js +12 -1
- package/dist/i18n/index.d.ts +1 -1
- package/dist/i18n/index.js +3 -1
- package/dist/i18n/messages/en.json +59 -6
- package/dist/i18n/messages/ja.json +59 -6
- package/dist/i18n/messages/vi.json +59 -6
- package/dist/i18n/translate.d.ts +19 -0
- package/dist/i18n/translate.js +24 -0
- package/dist/inertia/index.d.ts +20 -0
- package/dist/inertia/index.js +60 -1
- package/dist/lib/control-styles.d.ts +25 -3
- package/dist/lib/control-styles.js +9 -3
- package/dist/lib/datetime/picker-format.d.ts +8 -0
- package/dist/lib/datetime/picker-format.js +54 -0
- package/dist/lib/overlay-portal.d.ts +18 -0
- package/dist/lib/overlay-portal.js +17 -0
- package/dist/props/components/app.prop.d.ts +13 -2
- package/dist/props/components/charts.prop.d.ts +21 -0
- package/dist/props/components/data-display.prop.d.ts +55 -4
- package/dist/props/components/data-entry.prop.d.ts +687 -43
- package/dist/props/components/form.prop.d.ts +142 -4
- package/dist/props/components/general.prop.d.ts +30 -1
- package/dist/props/components/index.d.ts +2 -2
- package/dist/props/components/layout.prop.d.ts +255 -8
- package/dist/props/components/navigation.prop.d.ts +113 -3
- package/dist/props/registry.d.ts +376 -10
- package/dist/props/registry.js +493 -9
- package/dist/props/vocabulary/data.prop.d.ts +179 -1
- package/dist/props/vocabulary/index.d.ts +4 -4
- package/dist/props/vocabulary/interaction.prop.d.ts +51 -2
- package/dist/props/vocabulary/navigation.prop.d.ts +40 -0
- package/dist/props/vocabulary/shared.prop.d.ts +33 -0
- package/dist/styles/badge-layout.css +2 -1
- package/dist/styles/card-layout.css +36 -5
- package/dist/styles/chart-layout.css +17 -1
- package/dist/styles/control.css +520 -14
- package/dist/styles/data-display-layout.css +248 -7
- package/dist/styles/data-entry-layout.css +13 -0
- package/dist/styles/layout.css +221 -4
- package/dist/styles/navigation-layout.css +230 -1
- package/dist/styles/shell-layout.css +690 -21
- package/dist/styles/table-layout.css +95 -6
- package/dist/styles/text-layout.css +5 -0
- package/dist/tokens/base.css +1 -0
- package/dist/tokens/components/badge.css +1 -0
- package/dist/tokens/components/chart.css +6 -0
- package/dist/tokens/components/control.css +81 -3
- package/dist/tokens/components/data-display.css +9 -0
- package/dist/tokens/components/descriptions.css +11 -0
- package/dist/tokens/components/flex.css +8 -0
- package/dist/tokens/components/navigation.css +55 -0
- package/dist/tokens/components/shell.css +100 -5
- package/dist/tokens/components/table.css +14 -0
- package/dist/tokens/foundation.css +2 -0
- package/dist/tokens/semantic/layout.css +9 -0
- package/docs/CONSUMER-RULES.md +13 -0
- package/docs/DESIGN-AUTHORITY.md +43 -6
- package/docs/DEVELOPMENT.md +4 -3
- package/docs/FORMS.md +151 -6
- package/docs/FRAME-COVERAGE-REPORT.md +29 -16
- package/docs/README.md +1 -1
- package/docs/STANDARDS-vocabulary-tokens.md +1 -1
- package/docs/TESTING.md +15 -6
- package/docs/charts/cjk-category-axis.tsx +81 -0
- package/docs/data-display/card/index.tsx +22 -0
- package/docs/data-display/data-table/examples/antd-parity.tsx +257 -0
- package/docs/data-display/data-table/index.tsx +23 -0
- package/docs/data-display/descriptions.tsx +41 -0
- package/docs/data-display/list-row.tsx +6 -6
- package/docs/data-display/scroll-area.tsx +31 -25
- package/docs/data-display/table.tsx +104 -45
- package/docs/data-display/timeline.tsx +41 -0
- package/docs/data-entry/date-picker.tsx +85 -0
- package/docs/data-entry/date-range-picker.tsx +26 -0
- package/docs/data-entry/form-dynamic-fields.tsx +199 -0
- package/docs/data-entry/form.tsx +74 -4
- package/docs/data-entry/input-otp.tsx +24 -0
- package/docs/data-entry/input.tsx +46 -1
- package/docs/data-entry/month-range-picker.tsx +1 -1
- package/docs/data-entry/select.tsx +27 -0
- package/docs/data-entry/switch.tsx +41 -0
- package/docs/data-entry/textarea.tsx +38 -0
- package/docs/data-entry/time-picker.tsx +85 -1
- package/docs/data-entry/time-range-picker.tsx +55 -0
- package/docs/data-entry/transfer.tsx +17 -0
- package/docs/data-entry/upload.tsx +56 -12
- package/docs/feedback/tooltip.tsx +1 -1
- package/docs/general/activity.tsx +2 -2
- package/docs/general/button/index.tsx +14 -1
- package/docs/general/typography.tsx +14 -1
- package/docs/layout/app-launcher.tsx +195 -0
- package/docs/layout/app-shell-arrangements.tsx +5 -5
- package/docs/layout/app-shell.tsx +11 -0
- package/docs/layout/flex.tsx +50 -0
- package/docs/layout/responsive-grid.tsx +21 -1
- package/docs/layout/topbar.tsx +5 -9
- package/docs/navigation/app-setting-picker.tsx +11 -0
- package/docs/navigation/breadcrumb.tsx +48 -0
- package/docs/navigation/dropdown-menu.tsx +21 -0
- package/docs/navigation/pagination.tsx +52 -0
- package/docs/navigation/steps.tsx +37 -0
- package/docs/navigation/tabs.tsx +97 -1
- package/docs/query/button-refetch.tsx +1 -0
- package/docs/showcase/settings-security-mfa.tsx +3 -3
- package/package.json +24 -4
- package/scripts/_agent-setup.mjs +76 -9
- package/scripts/init-guinea-pig.mjs +26 -4
- package/scripts/ui-audit.mjs +243 -30
- package/scripts/visual-audit.mjs +45 -7
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/** Form module prop types — react-hook-form + Zod 4 only. */
|
|
2
2
|
import type * as React from "react";
|
|
3
|
-
import type { FieldPath, FieldValues, UseFormProps, UseFormReturn } from "react-hook-form";
|
|
3
|
+
import type { FieldArray, FieldArrayPath, FieldErrors, FieldPath, FieldValues, UseFormProps, UseFormReturn } from "react-hook-form";
|
|
4
4
|
import type { z } from "zod";
|
|
5
|
-
import type { ErrorProp, HelperProp, IdProp, LabelProp, RequiredProp } from "../vocabulary/index.js";
|
|
5
|
+
import type { BreakpointProp, DensityProp, DisabledProp, ErrorBagProp, ErrorProp, FormLayoutProp, HelperProp, IdProp, LabelProp, RequiredProp, WidthProp } from "../vocabulary/index.js";
|
|
6
6
|
/** Schema passed to useZodForm — must be Zod 4 object schema. */
|
|
7
7
|
export type ZodSchemaProp<T extends z.ZodType = z.ZodType> = T;
|
|
8
8
|
/** Options for useZodForm (resolver injected automatically). */
|
|
@@ -27,6 +27,11 @@ export interface FormStateAdapter {
|
|
|
27
27
|
/** Optional blur handler (e.g. touch-tracking); called with the field name. */
|
|
28
28
|
onBlur?(name: string): void;
|
|
29
29
|
getValues?(): unknown;
|
|
30
|
+
/**
|
|
31
|
+
* Restore the store's initial values (e.g. Inertia's `form.reset()`). `FormRoot` calls it for a
|
|
32
|
+
* native `<Button type="reset">`, so the reset button behaves identically on both paths.
|
|
33
|
+
*/
|
|
34
|
+
reset?(): void;
|
|
30
35
|
}
|
|
31
36
|
/** @see FormRoot */
|
|
32
37
|
export type FormRootProp<TFieldValues extends FieldValues> = {
|
|
@@ -39,26 +44,159 @@ export type FormRootProp<TFieldValues extends FieldValues> = {
|
|
|
39
44
|
* Framework-agnostic form-state adapter (e.g. `inertiaAdapter(form)` from `@godxjp/ui/inertia`).
|
|
40
45
|
*/
|
|
41
46
|
adapter?: FormStateAdapter;
|
|
42
|
-
/** Submit handler. */
|
|
47
|
+
/** Submit handler — runs only after validation passes (antd `onFinish`). */
|
|
43
48
|
onSubmit: (values: TFieldValues) => void | Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Validation FAILED (antd `onFinishFailed`) — receives react-hook-form's error tree. Use it to
|
|
51
|
+
* announce a summary; the first invalid field is focused (and scrolled to) regardless.
|
|
52
|
+
* react-hook-form path only: on the adapter path the server owns validation.
|
|
53
|
+
*/
|
|
54
|
+
onSubmitFailed?: (errors: FieldErrors<TFieldValues>) => void;
|
|
55
|
+
/**
|
|
56
|
+
* `onSubmit` REJECTED (the network call failed). Without this the rejection escapes as an
|
|
57
|
+
* unhandled promise rejection, which is what an async `onSubmit` does today — pass a handler to
|
|
58
|
+
* render a form-level error instead.
|
|
59
|
+
*/
|
|
60
|
+
onSubmitError?: (error: unknown) => void;
|
|
61
|
+
/**
|
|
62
|
+
* A native `<Button type="reset">` inside the form restores `defaultValues` (adapter path:
|
|
63
|
+
* `adapter.reset()`); this runs afterwards for any extra app state (a status banner, a step).
|
|
64
|
+
*/
|
|
65
|
+
onReset?: () => void;
|
|
66
|
+
/**
|
|
67
|
+
* Scroll the first invalid field into view after a failed submit (antd `scrollToFirstError`).
|
|
68
|
+
* Honours `prefers-reduced-motion`. Default `true`.
|
|
69
|
+
*/
|
|
70
|
+
scrollToFirstError?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Disable EVERY field under this form (antd Form `disabled`) — read by `FormFieldControl`
|
|
73
|
+
* (which hands `disabled` to its render prop) and by `useFormDisabled()` for the action buttons.
|
|
74
|
+
* Values are NOT dropped from the submitted payload.
|
|
75
|
+
*/
|
|
76
|
+
disabled?: DisabledProp;
|
|
77
|
+
/**
|
|
78
|
+
* Field layout for every `FormFieldControl` below (antd `layout`). Setting ANY layout prop makes
|
|
79
|
+
* `FormRoot` render the `Form` layout shell, whose `--form-block-gap` spacing replaces the plain
|
|
80
|
+
* `ui-stack-md` stack used by a bare `FormRoot`.
|
|
81
|
+
*/
|
|
82
|
+
layout?: FormLayoutProp;
|
|
83
|
+
/** Label column width in the `horizontal` layout (antd `labelCol`). */
|
|
84
|
+
labelWidth?: WidthProp;
|
|
85
|
+
/** Control column width (antd `wrapperCol`). */
|
|
86
|
+
controlWidth?: WidthProp;
|
|
87
|
+
/** Label text alignment in the `horizontal` layout (antd `labelAlign`). */
|
|
88
|
+
labelAlign?: "start" | "end";
|
|
89
|
+
/** Viewport below which a `horizontal`/`inline` layout stacks; `false` never stacks. */
|
|
90
|
+
collapseBelow?: BreakpointProp | false;
|
|
91
|
+
/** Control density for the fields below (antd Form `size`). */
|
|
92
|
+
density?: DensityProp;
|
|
93
|
+
columns?: import("./data-entry.prop.js").FormProp["columns"];
|
|
94
|
+
/** How required/optional fields are marked in their labels (antd `requiredMark`). */
|
|
95
|
+
requiredMark?: boolean | "optional";
|
|
96
|
+
/**
|
|
97
|
+
* Server validation error bag. Fields claim their own key and `<FormErrors />` renders the
|
|
98
|
+
* unclaimed remainder — the same mechanism as `Form errors`, without a second `Form` wrapper.
|
|
99
|
+
*/
|
|
100
|
+
errors?: ErrorBagProp;
|
|
44
101
|
children: React.ReactNode;
|
|
45
102
|
className?: string;
|
|
46
103
|
id?: IdProp;
|
|
47
104
|
};
|
|
48
105
|
/** @see FormFieldControl */
|
|
49
|
-
export type FormFieldControlProp<TFieldValues extends FieldValues> = {
|
|
106
|
+
export type FormFieldControlProp<TFieldValues extends FieldValues> = Pick<import("./data-entry.prop.js").FormFieldProp, "id" | "field" | "labelAddon" | "layout" | "labelWidth" | "controlWidth" | "colSpan"> & {
|
|
50
107
|
name: FieldPath<TFieldValues>;
|
|
51
108
|
label: LabelProp;
|
|
52
109
|
required?: RequiredProp;
|
|
53
110
|
helper?: HelperProp;
|
|
111
|
+
/**
|
|
112
|
+
* Disable this field only. Defaults to the surrounding `FormRoot disabled`. The resolved state
|
|
113
|
+
* reaches the control through the render prop's `disabled`; the field's value is still submitted
|
|
114
|
+
* (antd semantics), unlike react-hook-form's own `disabled`.
|
|
115
|
+
*/
|
|
116
|
+
disabled?: DisabledProp;
|
|
117
|
+
/**
|
|
118
|
+
* Re-validate this field whenever one of these fields changes (antd `dependencies`) — the
|
|
119
|
+
* confirm-password / date-range case, where the schema's cross-field `.superRefine()` reports on
|
|
120
|
+
* THIS field but only re-runs when the OTHER one is edited. Runs only once the form has been
|
|
121
|
+
* submitted, so a dependency edit never flags a field the user has not reached yet.
|
|
122
|
+
*/
|
|
123
|
+
dependencies?: FieldPath<TFieldValues>[];
|
|
124
|
+
/**
|
|
125
|
+
* Read the value out of the control's change payload (antd `getValueFromEvent`). Replaces the
|
|
126
|
+
* built-in DOM-event/raw-value detection — needed by a control that reports `(value, option)`.
|
|
127
|
+
*/
|
|
128
|
+
getValueFromEvent?: (...args: unknown[]) => unknown;
|
|
129
|
+
/**
|
|
130
|
+
* Transform the extracted value before it is stored (antd `normalize`) — trim, upper-case,
|
|
131
|
+
* digits-only. Receives the previous value so a normaliser can reject a change.
|
|
132
|
+
*/
|
|
133
|
+
normalize?: (value: unknown, previousValue: unknown) => unknown;
|
|
134
|
+
/**
|
|
135
|
+
* Message shown in the error slot INSTEAD of the resolved validation error (antd `help`) — for a
|
|
136
|
+
* server error the schema cannot know about.
|
|
137
|
+
*/
|
|
138
|
+
help?: ErrorProp;
|
|
139
|
+
/**
|
|
140
|
+
* Force the validation state shown by the field (antd `validateStatus`) — `"validating"` for a
|
|
141
|
+
* remote check in flight, `"success"` for a confirmed-unique value. A real validation error
|
|
142
|
+
* always wins; mirrors `FormField validateStatus`.
|
|
143
|
+
*/
|
|
144
|
+
validateStatus?: "success" | "warning" | "error" | "validating";
|
|
145
|
+
/** Render the status icon + localized status text for `validateStatus` (antd `hasFeedback`). */
|
|
146
|
+
hasFeedback?: boolean;
|
|
147
|
+
/** Replace the default feedback text next to the `hasFeedback` icon. */
|
|
148
|
+
feedback?: React.ReactNode;
|
|
149
|
+
/**
|
|
150
|
+
* Keep the value in the form state after the field unmounts (antd `preserve`, default `true`).
|
|
151
|
+
* `false` unregisters it — a conditional branch that must not submit a stale value.
|
|
152
|
+
*/
|
|
153
|
+
preserve?: boolean;
|
|
54
154
|
className?: string;
|
|
55
155
|
children: (field: {
|
|
56
156
|
id: string;
|
|
57
157
|
name: string;
|
|
58
158
|
value: unknown;
|
|
59
159
|
onChange: (...args: unknown[]) => void;
|
|
160
|
+
onValueChange: (...args: unknown[]) => void;
|
|
60
161
|
onBlur: () => void;
|
|
61
162
|
ref: React.Ref<HTMLInputElement>;
|
|
163
|
+
/** Present (and `true`) only when the field is disabled, so `{...field}` never re-enables a control. */
|
|
164
|
+
disabled?: DisabledProp;
|
|
165
|
+
}) => React.ReactNode;
|
|
166
|
+
};
|
|
167
|
+
/**
|
|
168
|
+
* @see FormFieldArray — dynamic repeating fields (antd `Form.List`) on react-hook-form's
|
|
169
|
+
* `useFieldArray`. `name` is the array field; each row hands back the dotted path prefix to build
|
|
170
|
+
* the child `FormFieldControl name`.
|
|
171
|
+
*/
|
|
172
|
+
export type FormFieldArrayProp<TFieldValues extends FieldValues, TName extends FieldArrayPath<TFieldValues> = FieldArrayPath<TFieldValues>> = {
|
|
173
|
+
/** Path of the array in the schema (`"contacts"`, `"invoice.lines"`). */
|
|
174
|
+
name: TName;
|
|
175
|
+
children: (list: {
|
|
176
|
+
/** One entry per row, in order. `key` is react-hook-form's stable row id — use it as the React key. */
|
|
177
|
+
fields: Array<{
|
|
178
|
+
key: string;
|
|
179
|
+
name: `${TName}.${number}`;
|
|
180
|
+
index: number;
|
|
181
|
+
}>;
|
|
182
|
+
/** Append one row (or several) at the end. */
|
|
183
|
+
append: (value: FieldArray<TFieldValues, TName> | FieldArray<TFieldValues, TName>[]) => void;
|
|
184
|
+
/** Insert one row (or several) at the front. */
|
|
185
|
+
prepend: (value: FieldArray<TFieldValues, TName> | FieldArray<TFieldValues, TName>[]) => void;
|
|
186
|
+
/** Insert at `index`. */
|
|
187
|
+
insert: (index: number, value: FieldArray<TFieldValues, TName> | FieldArray<TFieldValues, TName>[]) => void;
|
|
188
|
+
/** Remove `index` (or several); omit to clear every row. */
|
|
189
|
+
remove: (index?: number | number[]) => void;
|
|
190
|
+
/** Move a row, keeping its state (drag-reorder). */
|
|
191
|
+
move: (from: number, to: number) => void;
|
|
192
|
+
/** Exchange two rows. */
|
|
193
|
+
swap: (indexA: number, indexB: number) => void;
|
|
194
|
+
/** Replace every row at once. */
|
|
195
|
+
replace: (values: FieldArray<TFieldValues, TName>[]) => void;
|
|
196
|
+
/** Array-LEVEL validation message (a `.min(1)` on the array itself), not a row's. */
|
|
197
|
+
error?: string;
|
|
198
|
+
/** Resolved `FormRoot disabled` — gate the add/remove buttons on it. */
|
|
199
|
+
disabled: DisabledProp;
|
|
62
200
|
}) => React.ReactNode;
|
|
63
201
|
};
|
|
64
202
|
/** Mapped field error from RHF — displayed via FormField error slot. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** Foundation component prop types — @see docs/COMPONENTS.md#foundation */
|
|
2
2
|
import type * as React from "react";
|
|
3
|
-
import type { ActivityAnnounceProp, ActivityVariantProp, AsChildProp, ButtonSizeProp, ButtonVariantProp, ChildrenProp, ClassNameProp, DisabledProp, FontWeightProp, HeadingLevelProp, LabelProp, OnClickProp, PendingProp, RevealDelayProp, ShapeProp, SizeProp, TextAlignProp, TextSizeProp, TextToneProp } from "../vocabulary/index.js";
|
|
3
|
+
import type { ActivityAnnounceProp, ActivityVariantProp, AsChildProp, ButtonSizeProp, ButtonVariantProp, ChildrenProp, ClassNameProp, DisabledProp, FontWeightProp, HeadingLevelProp, LabelProp, OnClickProp, PendingProp, RevealDelayProp, ShapeProp, SizeProp, TextAlignProp, TextSizeProp, TextToneProp, TextWhitespaceProp } from "../vocabulary/index.js";
|
|
4
4
|
/** @see Text — typographic primitive; replaces hand-rolled `<span className="text-[13px] …">`. */
|
|
5
5
|
export type TextProp = Omit<React.HTMLAttributes<HTMLElement>, "color"> & {
|
|
6
6
|
/** Render element. Default `span`. */
|
|
@@ -37,8 +37,24 @@ export type TextProp = Omit<React.HTMLAttributes<HTMLElement>, "color"> & {
|
|
|
37
37
|
* line-clamp styling (never write the `line-clamp-N` utility page-side).
|
|
38
38
|
*/
|
|
39
39
|
clamp?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Whitespace handling. Default `normal` (CSS's own: newlines and space runs collapse).
|
|
42
|
+
*
|
|
43
|
+
* `pre-wrap` is for text a PERSON typed — a plain-text note, an issue description, a pasted log
|
|
44
|
+
* — where the line breaks and the indentation are CONTENT, not formatting. It preserves both and
|
|
45
|
+
* still wraps long lines at the container edge, and it breaks an over-long unbroken token (a URL,
|
|
46
|
+
* an id) rather than letting it overflow.
|
|
47
|
+
*
|
|
48
|
+
* Precedence is explicit and resolved in the component, not by CSS ordering: `truncate` is a
|
|
49
|
+
* single-line contract and WINS (dev builds warn, and `data-whitespace` is not emitted), while
|
|
50
|
+
* `clamp` composes with it — a clamped pre-wrap block shows its first N real lines.
|
|
51
|
+
*/
|
|
52
|
+
whitespace?: TextWhitespaceProp;
|
|
40
53
|
/** Tabular figures for aligned numbers. */
|
|
41
54
|
tabular?: boolean;
|
|
55
|
+
decoration?: "none" | "underline" | "line-through";
|
|
56
|
+
/** Inline code chip; use with as="code". */
|
|
57
|
+
chip?: boolean;
|
|
42
58
|
/** Monospace family (codes, ids). */
|
|
43
59
|
mono?: boolean;
|
|
44
60
|
htmlFor?: string;
|
|
@@ -73,6 +89,19 @@ export type ButtonProp = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
|
|
73
89
|
/** Corner shape — `default` (control radius) · `pill` (fully rounded) · `sharp` (square). */
|
|
74
90
|
shape?: ShapeProp;
|
|
75
91
|
fullWidth?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Take the space the siblings leave, and let a long label ELLIPSE instead of widening the row —
|
|
94
|
+
* the same axis `Flex` calls `fill`, for the same reason.
|
|
95
|
+
*
|
|
96
|
+
* Button ships `flex-shrink: 0`, right almost everywhere and wrong in a constrained bar: an
|
|
97
|
+
* account menu holding an avatar plus a person's name keeps its full width while the cluster
|
|
98
|
+
* clips it, so a keyboard user tabs to a control they cannot see (SC 2.4.7). Until this axis
|
|
99
|
+
* existed the only move was `className="min-w-0 flex-1"`, which ui-audit blocks — and which this
|
|
100
|
+
* package's own Topbar guidance recommended, so the docs prescribed the utility the audit forbids.
|
|
101
|
+
*
|
|
102
|
+
* Sets `flex: 1 1 auto` and `min-inline-size: 0`; pair it with a `<Text truncate>` label.
|
|
103
|
+
*/
|
|
104
|
+
fill?: boolean;
|
|
76
105
|
/** Allow a text button to grow vertically for multi-line labels. */
|
|
77
106
|
wrap?: boolean;
|
|
78
107
|
/** Logical content alignment, especially for full-width collection actions. */
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export type { AppShellProp, AuthFooterProp, AuthIdentityProp, OrgSwitcherLabels, OrgSwitcherOrganization, OrgSwitcherProp, PageContainerProp, PageInsetProp, SidebarItemProp, SidebarProductProp, SidebarProp, SidebarSectionProp, TopbarProp, } from "./layout.prop.js";
|
|
1
|
+
export type { AppLauncherApp, AppLauncherGroup, AppLauncherLabels, AppLauncherProp, AppShellProp, AuthFooterProp, AuthIdentityProp, OrgSwitcherLabels, OrgSwitcherOrganization, OrgSwitcherProp, PageContainerProp, PageInsetProp, SidebarItemProp, SidebarProductProp, SidebarProp, SidebarSectionProp, TopbarProp, } from "./layout.prop.js";
|
|
2
2
|
export type { ButtonProp } from "./general.prop.js";
|
|
3
3
|
export type { InputProp, TextareaProp, FormFieldProp, SearchInputProp, CheckboxProp, CheckboxGroupProp, ChoiceOptionProp, RadioProp, RadioGroupProp, SwitchProp, SliderProp, CalendarProp, DatePickerProp, DateRangePickerProp, TimePickerProp, ColorPickerProp, UploadProp, UploadFileItemProp, UploadVariantProp, TreeOptionProp, TreeFieldNamesProp, CascaderProp, TreeSelectProp, ShowCheckedStrategyProp, TransferProp, TransferItemProp, } from "./data-entry.prop.js";
|
|
4
4
|
export type { AvatarProp, EmptyStateProp, DescriptionsProp, DescriptionsItemProp, BadgeProp, DataTableProp, QrCodeProp, } from "./data-display.prop.js";
|
|
5
5
|
export type { ChartDatum, ChartSeriesProp, LineChartProp, BarChartProp, AreaChartProp, PieChartProp, } from "./charts.prop.js";
|
|
6
6
|
export type { AlertQueryErrorProp, AlertProp, AlertTitleProp, AlertContentProp, AlertDescriptionProp, AlertActionsProp, SheetResponsiveProp, SkeletonRowsProp, } from "./feedback.prop.js";
|
|
7
7
|
export type { DataStateProp, InfiniteQueryStateProp, InfiniteQueryHelpers, PrefetchLinkProp, } from "./query.prop.js";
|
|
8
|
-
export type { PaginationProp, StepsProp, StepItemProp, StepStatusProp, TabsProp, TabItemProp, } from "./navigation.prop.js";
|
|
8
|
+
export type { DropdownMenuPlacementProp, PaginationProp, PaginationSizeProp, PaginationAlignProp, StepsProp, StepItemProp, StepStatusProp, StepsTypeProp, TabsProp, TabItemProp, TabsVariantProp, TabsPlacementProp, TabsExtraProp, TabsOnEditProp, } from "./navigation.prop.js";
|
|
9
9
|
export type { AppProviderProp, AppContextValue, AppSettingKind, AppSettingPickerProp, AppSettingToggleKind, AppSettingToggleProp, } from "./app.prop.js";
|
|
10
10
|
export type { ZodSchemaProp, UseZodFormOptionsProp, UseZodFormReturnProp, FormRootProp, FormFieldControlProp, FieldErrorMessageProp, } from "./form.prop.js";
|
|
@@ -24,6 +24,8 @@ export type PageContainerMeasureProp = "default" | "narrow" | "medium";
|
|
|
24
24
|
export type PageContainerHeaderScaleProp = "document" | "chrome";
|
|
25
25
|
/** @see PageContainer */
|
|
26
26
|
export type PageContainerProp = {
|
|
27
|
+
toolbarPad?: PadProp;
|
|
28
|
+
footerPad?: PadProp;
|
|
27
29
|
title: TitleProp;
|
|
28
30
|
subtitle?: SubtitleProp;
|
|
29
31
|
/**
|
|
@@ -101,7 +103,7 @@ export type FlexDirectionProp = "row" | "col";
|
|
|
101
103
|
export type FlexAlignProp = "start" | "center" | "end" | "stretch" | "baseline";
|
|
102
104
|
export type FlexJustifyProp = "start" | "center" | "end" | "between" | "around" | "evenly";
|
|
103
105
|
/** @see Flex */
|
|
104
|
-
export type FlexProp = React.HTMLAttributes<
|
|
106
|
+
export type FlexProp = React.HTMLAttributes<HTMLElement> & {
|
|
105
107
|
/**
|
|
106
108
|
* Render element — `div` (default) or `span` when the Flex sits in a PHRASING context and a
|
|
107
109
|
* `<div>` would be invalid HTML: inside a `TabsTrigger`/`PopoverTrigger`/`Button` (all of which
|
|
@@ -109,8 +111,16 @@ export type FlexProp = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
109
111
|
* inside a `<p>`. Same closed shape as `ListRow`'s `as` — it swaps the TAG, nothing else: the
|
|
110
112
|
* `.ui-flex` rules carry `display: flex`, so the box is identical either way.
|
|
111
113
|
*/
|
|
112
|
-
as?: "div" | "span";
|
|
113
|
-
|
|
114
|
+
as?: "div" | "span" | "ul" | "ol" | "li";
|
|
115
|
+
/** Lightweight surfaces for rows and notices; no Card elevation by default. */
|
|
116
|
+
surface?: "muted" | "popover" | "warning";
|
|
117
|
+
/** Negative inline inset, using the same spacing scale as pad. */
|
|
118
|
+
bleed?: GapProp;
|
|
119
|
+
/** Floating row actions: show on parent hover, focus-within, and touch. */
|
|
120
|
+
reveal?: "hover";
|
|
121
|
+
direction?: FlexDirectionProp | Partial<Record<"base" | BreakpointProp, FlexDirectionProp>>;
|
|
122
|
+
grow?: boolean;
|
|
123
|
+
shrink?: boolean;
|
|
114
124
|
gap?: GapProp;
|
|
115
125
|
/**
|
|
116
126
|
* CỬA THOÁT: một khoảng cách tính bằng pixel, ngoài mọi bậc của thang.
|
|
@@ -213,8 +223,9 @@ export type ResponsiveGridColumnsProp = number | {
|
|
|
213
223
|
* `pricing-plans` — the canonical billing/pricing-plan collection: 1 column until the `lg` step
|
|
214
224
|
* (container ≥ 64rem), then 3 columns from `lg` upward.
|
|
215
225
|
*/
|
|
226
|
+
export type ResponsiveGridFlowProp = "rows" | "columns";
|
|
216
227
|
export type ResponsiveGridPresetProp = "pricing-plans";
|
|
217
|
-
export type MasterDetailRailWidthProp = "compact" | "standard";
|
|
228
|
+
export type MasterDetailRailWidthProp = "narrow" | "compact" | "standard" | "wide";
|
|
218
229
|
export type MasterDetailRailProp = "master" | "detail";
|
|
219
230
|
/**
|
|
220
231
|
* Bounded viewport preset for the master collection. `auto` (default) never bounds it — the region
|
|
@@ -262,7 +273,8 @@ export type PageInsetProp = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
262
273
|
};
|
|
263
274
|
/** @see AppShell */
|
|
264
275
|
export type AppShellProp = {
|
|
265
|
-
sidebar
|
|
276
|
+
/** Omit or pass null/false for a shell without the sidebar landmark or grid track. */
|
|
277
|
+
sidebar?: ReactNode;
|
|
266
278
|
children: ReactNode;
|
|
267
279
|
topbar?: ReactNode;
|
|
268
280
|
topbarLeft?: ReactNode;
|
|
@@ -324,6 +336,30 @@ export type AppShellProp = {
|
|
|
324
336
|
* to be tellable apart by name; the shell always supplies both defaults rather than requiring
|
|
325
337
|
* this prop, so the two columns of equal rank behave the same way.
|
|
326
338
|
*/
|
|
339
|
+
/**
|
|
340
|
+
* WHICH EDGE the rail sits on. `start` (default) and `end` are the INLINE edges — logical, so an
|
|
341
|
+
* RTL document mirrors them without a `[dir]` rule; `top` and `bottom` are the block edges, where
|
|
342
|
+
* the rail becomes a full-measure horizontal strip and the shell grows a ROW instead of a column.
|
|
343
|
+
*
|
|
344
|
+
* The scope contract does not move with it: wherever it sits, the rail is PLATFORM scope. The
|
|
345
|
+
* edge is a presentation choice — a docked column reads as permanent chrome (Slack), a bottom
|
|
346
|
+
* strip reads as the phone/tab-bar shape, a top strip as a platform band above the app's own bar.
|
|
347
|
+
* Thickness follows the orientation: `--app-shell-nav-rail-width` as a column,
|
|
348
|
+
* `--app-shell-nav-rail-height` as a strip.
|
|
349
|
+
*
|
|
350
|
+
* Collapsing the sidebar folds the sidebar track only, at every position.
|
|
351
|
+
*/
|
|
352
|
+
navRailPosition?: "start" | "end" | "top" | "bottom";
|
|
353
|
+
/**
|
|
354
|
+
* Rail content pinned to its FAR end — the counterpart of `Sidebar`'s `footer`, and the tray end
|
|
355
|
+
* of a taskbar: settings, appearance, the account glyph. It follows the orientation, so it is the
|
|
356
|
+
* bottom of a column and the inline-end of a strip, and it stays put while `navRail` scrolls.
|
|
357
|
+
*
|
|
358
|
+
* A slot rather than "whatever you put last", because pinning it needs an auto margin on the
|
|
359
|
+
* right axis — geometry that would otherwise land in consumer CSS, which this library does not
|
|
360
|
+
* accept. Ignored when `navRail` is not passed: there is no rail to pin anything to.
|
|
361
|
+
*/
|
|
362
|
+
navRailEnd?: ReactNode;
|
|
327
363
|
navRailLabel?: string;
|
|
328
364
|
/**
|
|
329
365
|
* Navigation shown in the mobile drawer at the DXS 900px breakpoint, where the docked sidebar is
|
|
@@ -386,6 +422,22 @@ export type AuthShellProp = {
|
|
|
386
422
|
* flow geometry.
|
|
387
423
|
*/
|
|
388
424
|
measure?: "default" | "wide";
|
|
425
|
+
/**
|
|
426
|
+
* Block-axis placement of the auth column, ORTHOGONAL to `preset` the way `variant` is: a preset
|
|
427
|
+
* owns the page MEASURE (card width, inline gutters, section rhythm), `align` owns where that
|
|
428
|
+
* column sits vertically.
|
|
429
|
+
*
|
|
430
|
+
* Omit it to keep the preset's own choice — `"login"` and `"registration"` anchor so a
|
|
431
|
+
* requester/identity line that wraps to two lines cannot move the card, every other preset
|
|
432
|
+
* centres. Pass `"center"` for a vertically centred column (the block-start inset collapses to
|
|
433
|
+
* the preset's block-end one, desktop and mobile, so the padding is symmetric) or `"anchored"`
|
|
434
|
+
* for a top-anchored one. This replaces re-declaring a preset's offset tokens from consumer CSS.
|
|
435
|
+
*
|
|
436
|
+
* HAZARD on a tall flow: a vertically centred tall card overflows ABOVE the scroll origin on a
|
|
437
|
+
* short viewport, putting its first field out of reach. That is why `"registration"` anchors by
|
|
438
|
+
* default; `"center"` is legal there but is the caller's judgement.
|
|
439
|
+
*/
|
|
440
|
+
align?: "anchored" | "center";
|
|
389
441
|
/**
|
|
390
442
|
* Vertical density scoped to auth-card descendants. The canonical variant defaults to
|
|
391
443
|
* `"compact"`; the default variant defaults to `"comfortable"`.
|
|
@@ -467,6 +519,10 @@ export type MobileShellProp = {
|
|
|
467
519
|
* so a service retunes a day divider or an unread watermark from its theme and never forks CSS.
|
|
468
520
|
*/
|
|
469
521
|
export type SeparatorProp = Omit<React.HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
522
|
+
labelSize?: "2xs" | "xs" | "sm" | "md";
|
|
523
|
+
space?: GapProp;
|
|
524
|
+
hideBelow?: BreakpointProp;
|
|
525
|
+
hideFrom?: BreakpointProp;
|
|
470
526
|
/** Rule axis. Default `"horizontal"`. */
|
|
471
527
|
orientation?: OrientationProp;
|
|
472
528
|
/**
|
|
@@ -869,14 +925,170 @@ export type OrgSwitcherProp = {
|
|
|
869
925
|
error?: ReactNode;
|
|
870
926
|
onRetry?: () => void;
|
|
871
927
|
labels: OrgSwitcherLabels;
|
|
928
|
+
/**
|
|
929
|
+
* WHICH SURFACE the panel opens on.
|
|
930
|
+
*
|
|
931
|
+
* - `"auto"` (default) — popover above `--sheet-responsive-breakpoint-width`, focus-trapped
|
|
932
|
+
* bottom Sheet at or below it.
|
|
933
|
+
* - `"dialog"` — a centred modal above that breakpoint, the same bottom Sheet below it. Reach for
|
|
934
|
+
* this once the panel carries more than a name per row — a role, a plan, a member count, a
|
|
935
|
+
* "create organization" action. A popover is anchored to its trigger, clipped by the viewport
|
|
936
|
+
* and sized by `--org-switcher-menu-width`; a dialog has a real title, a scrolling body and a
|
|
937
|
+
* footer, and takes the reader's full attention, which is the right trade when switching
|
|
938
|
+
* organization re-scopes everything on screen.
|
|
939
|
+
* - `"popover"` / `"sheet"` — pinned to one surface at every width. Useful for a deterministic
|
|
940
|
+
* embedded surface or a component test, rarely in a product.
|
|
941
|
+
*
|
|
942
|
+
* `auto` and `dialog` are the two RESPONSIVE pairs and differ only in their desktop half; the
|
|
943
|
+
* mobile half is the same Sheet, because a centred modal on a phone is a Sheet with worse
|
|
944
|
+
* ergonomics. All four resolve the breakpoint through the shared `useSheetResponsiveMode()`
|
|
945
|
+
* hook, so a service moves the line once for every overlay.
|
|
946
|
+
*/
|
|
947
|
+
responsive?: "auto" | "popover" | "sheet" | "dialog";
|
|
948
|
+
open?: boolean;
|
|
949
|
+
onOpenChange?: (open: boolean) => void;
|
|
950
|
+
className?: ClassNameProp;
|
|
951
|
+
};
|
|
952
|
+
/**
|
|
953
|
+
* One app tile in the public {@link AppLauncher} contract.
|
|
954
|
+
*
|
|
955
|
+
* A tile is a REAL LINK, always. There is no `onClick`-only entry and no `disabled` entry: an app
|
|
956
|
+
* the viewer may not open is an app the consumer does not pass. A launcher that renders inert tiles
|
|
957
|
+
* is a launcher that teaches its users to stop trusting the grid.
|
|
958
|
+
*/
|
|
959
|
+
export type AppLauncherApp = {
|
|
960
|
+
/** Stable app identifier — the React key, and the `data-app` hook an end-to-end test holds. */
|
|
961
|
+
id: string;
|
|
962
|
+
/** Visible app name. It is also the tile's accessible name — the mark is presentational. */
|
|
963
|
+
name: string;
|
|
964
|
+
/** The destination. Required, because the tile IS an `<a href>` (WCAG 2.2 / APG: link, not button). */
|
|
965
|
+
href: string;
|
|
966
|
+
/**
|
|
967
|
+
* Owned mark — a Lucide icon, a `<Logo>`, an `<img>`, an `<Avatar>`. Rendered `aria-hidden`;
|
|
968
|
+
* when omitted the launcher falls back to the first character of `name`, exactly as
|
|
969
|
+
* `OrgSwitcher` does for an organization with no avatar.
|
|
970
|
+
*/
|
|
971
|
+
icon?: ReactNode;
|
|
972
|
+
/** The app the viewer is currently inside. That tile carries `aria-current="page"`. */
|
|
973
|
+
current?: boolean;
|
|
974
|
+
/**
|
|
975
|
+
* The destination leaves this SPA. Such a tile renders a plain `<a target="_blank" rel="noreferrer
|
|
976
|
+
* noopener">` and DELIBERATELY bypasses `linkComponent`: a client-side router link to another
|
|
977
|
+
* origin is a router asked to route somewhere it does not own, which is how a launcher entry
|
|
978
|
+
* turns into a blank screen. Pair it with `labels.externalHint` (WCAG 3.2.5).
|
|
979
|
+
*/
|
|
980
|
+
external?: boolean;
|
|
981
|
+
};
|
|
982
|
+
/** A labelled section of the launcher grid — the "more from …" band of the Google-style panel. */
|
|
983
|
+
export type AppLauncherGroup = {
|
|
984
|
+
/** Section heading. Rendered as a real heading and used to name the section's own grid. */
|
|
985
|
+
label: string;
|
|
986
|
+
apps: readonly AppLauncherApp[];
|
|
987
|
+
};
|
|
988
|
+
/** Localized copy owned by the consuming product, never hard-coded by the component. */
|
|
989
|
+
export type AppLauncherLabels = {
|
|
990
|
+
/**
|
|
991
|
+
* Accessible name of the nine-dot trigger ("Apps" / "アプリ"). A plain string, NOT a function of
|
|
992
|
+
* the current app the way `OrgSwitcherLabels.trigger` is a function of the organization: the
|
|
993
|
+
* launcher's trigger shows no current value, so naming one in the trigger would announce a
|
|
994
|
+
* destination the button does not go to.
|
|
995
|
+
*/
|
|
996
|
+
trigger: string;
|
|
997
|
+
/** Panel name — the popover's accessible name and the bottom Sheet's header title. */
|
|
998
|
+
title: string;
|
|
999
|
+
empty: string;
|
|
1000
|
+
loading: string;
|
|
1001
|
+
retry?: string;
|
|
1002
|
+
/** Screen-reader suffix for an `external` tile, e.g. "(opens in a new tab)" (WCAG 3.2.5). */
|
|
1003
|
+
externalHint?: string;
|
|
1004
|
+
};
|
|
1005
|
+
/**
|
|
1006
|
+
* @see AppLauncher — the PLATFORM-scope app switcher that lives in the topbar.
|
|
1007
|
+
*
|
|
1008
|
+
* Related, and repeatedly confused with it:
|
|
1009
|
+
*
|
|
1010
|
+
* - `ServiceLauncherCard` (data-display) is also a launcher tile, but a PAGE-SIZED one: status,
|
|
1011
|
+
* hostname, plan, an action button, a reason it is locked. It belongs on a service-catalogue
|
|
1012
|
+
* page, where choosing is a considered act. `AppLauncher`'s tile is bar-sized — mark plus name,
|
|
1013
|
+
* the whole tile a link — because switching app is a reflex, not a decision. Neither is built
|
|
1014
|
+
* out of the other; a grid of `ServiceLauncherCard`s inside a popover is the wrong component.
|
|
1015
|
+
* - `AppShellProp.navRail` expresses the SAME platform scope as a docked column. These are the two
|
|
1016
|
+
* ways to say it, and a product picks ONE: the launcher suits a platform with MANY apps where
|
|
1017
|
+
* switching is occasional (the Google Workspace shape), the rail suits a single product where
|
|
1018
|
+
* switching workspace is a constant action worth permanent screen width (the Slack shape).
|
|
1019
|
+
* Shipping both puts one scope in two places and makes neither authoritative.
|
|
1020
|
+
*/
|
|
1021
|
+
export type AppLauncherProp = {
|
|
1022
|
+
/** Ungrouped apps, rendered first, with no heading above them. */
|
|
1023
|
+
apps: readonly AppLauncherApp[];
|
|
1024
|
+
/** Labelled sections rendered after `apps`, in order. */
|
|
1025
|
+
groups?: readonly AppLauncherGroup[];
|
|
1026
|
+
labels: AppLauncherLabels;
|
|
1027
|
+
/**
|
|
1028
|
+
* Grid column count. Omit it and the panel keeps the stylesheet's own `--app-launcher-columns`
|
|
1029
|
+
* (3, the Google-launcher shape, declared on `.ui-app-launcher-panel`): the default is where a
|
|
1030
|
+
* theme can reach it, and this prop is the per-instance override written inline on top.
|
|
1031
|
+
*
|
|
1032
|
+
* `responsive="fullscreen"` steps that DEFAULT up with the surface — 3 · 4 · 5 · 6 on the house
|
|
1033
|
+
* container ladder — because a fixed three columns on a full viewport is three columns of tiles
|
|
1034
|
+
* and a screen of nothing. Passing `columns` still wins everywhere: an inline custom property
|
|
1035
|
+
* beats every stylesheet rule, so a stated count is a stated count on both surfaces.
|
|
1036
|
+
*/
|
|
1037
|
+
columns?: number;
|
|
1038
|
+
/**
|
|
1039
|
+
* THE framework-router contract, reusing `SidebarLinkComponentProp` VERBATIM — the same type
|
|
1040
|
+
* `Sidebar` and `NavList` take, so a service that already wrote `inertiaSidebarLink(Link)` or
|
|
1041
|
+
* `createSidebarLink(Link, "to")` for its rail hands the same value here. The launcher still
|
|
1042
|
+
* composes the tile (mark, name, `aria-current`, the external hint); the consumer supplies only
|
|
1043
|
+
* the element type. `external` apps bypass it — see {@link AppLauncherApp.external}.
|
|
1044
|
+
*/
|
|
1045
|
+
linkComponent?: SidebarLinkComponentProp;
|
|
1046
|
+
loading?: boolean;
|
|
1047
|
+
/** Error content replaces the grid while preserving the trigger and the retry affordance. */
|
|
1048
|
+
error?: ReactNode;
|
|
1049
|
+
onRetry?: () => void;
|
|
872
1050
|
/**
|
|
873
1051
|
* `"auto"` (default) uses the desktop popover above `--sheet-responsive-breakpoint-width` and a
|
|
874
1052
|
* focus-trapped bottom Sheet at/below it — the SAME token that drives `SheetContent
|
|
875
1053
|
* responsive="auto"`, resolved through the shared `useSheetResponsiveMode()` hook, so a service
|
|
876
1054
|
* moves the drawer line once for every overlay instead of per component.
|
|
877
|
-
*
|
|
1055
|
+
*
|
|
1056
|
+
* `"fullscreen"` is the LAUNCHPAD: one full-viewport surface, the page behind it blurred, the
|
|
1057
|
+
* grid floating on that ground at tile size — the macOS Launchpad / Windows Start shape. It is
|
|
1058
|
+
* pinned at every width, because a start surface that becomes a popover on a wide screen is two
|
|
1059
|
+
* different products.
|
|
1060
|
+
*
|
|
1061
|
+
* WHEN IT IS RIGHT, AND WHEN IT IS NOT. This prop used to say a modal was always wrong here:
|
|
1062
|
+
* "a launcher grid is a jump table, and a modal that takes over the screen to offer nine links
|
|
1063
|
+
* is heavier than the errand." That reasoning is sound for a launcher in ONE application's
|
|
1064
|
+
* topbar, where the grid is a shortcut away from the work on screen and the work should stay
|
|
1065
|
+
* visible. It does not hold for a PLATFORM start bar — a strip that is present in every service,
|
|
1066
|
+
* whose launcher is the primary way to move between products rather than a shortcut. There the
|
|
1067
|
+
* grid IS the errand, the page behind it is the thing being left, and the interruption is the
|
|
1068
|
+
* point. Keep `"auto"` for a topbar launcher; reach for `"fullscreen"` for a dock.
|
|
1069
|
+
*/
|
|
1070
|
+
responsive?: "auto" | "popover" | "sheet" | "fullscreen";
|
|
1071
|
+
/**
|
|
1072
|
+
* The BOX the trigger takes — the same split `AppSettingToggle` draws, and for the same reason.
|
|
1073
|
+
* `bar` (default) is a `TopbarItem`: a cell as tall as the bar, whose hover is the bar's own
|
|
1074
|
+
* surface. `icon` is a square ghost `Button`, for chrome that is NOT a bar — a nav rail, a card
|
|
1075
|
+
* header, a toolbar. A `TopbarItem` outside a bar has nothing to bleed to: it stretches to a
|
|
1076
|
+
* container that never set a band height, and its squared corners and full-bleed hover read as a
|
|
1077
|
+
* broken cell. The panel, the grid and the responsive contract are identical either way.
|
|
1078
|
+
*/
|
|
1079
|
+
appearance?: "bar" | "icon";
|
|
1080
|
+
/**
|
|
1081
|
+
* Which side of the trigger the panel opens on, and how it aligns to it. Both default from
|
|
1082
|
+
* `appearance` — a bar drops the grid below and aligns to the bar's end; anything else opens to
|
|
1083
|
+
* the inline-end aligned to the trigger's start.
|
|
1084
|
+
*
|
|
1085
|
+
* State them when the chrome can be RE-DOCKED. `appearance` says the trigger is not in a bar; it
|
|
1086
|
+
* cannot say which way is out, and a rail pinned to the top edge still opens downward. Measured
|
|
1087
|
+
* without this: a launcher in a top strip opened sideways and left the panel 440px from the
|
|
1088
|
+
* trigger it belonged to.
|
|
878
1089
|
*/
|
|
879
|
-
|
|
1090
|
+
side?: "top" | "right" | "bottom" | "left";
|
|
1091
|
+
align?: "start" | "center" | "end";
|
|
880
1092
|
open?: boolean;
|
|
881
1093
|
onOpenChange?: (open: boolean) => void;
|
|
882
1094
|
className?: ClassNameProp;
|
|
@@ -890,7 +1102,21 @@ export type SidebarProp = {
|
|
|
890
1102
|
sections?: SidebarSectionProp[];
|
|
891
1103
|
product?: SidebarProductProp;
|
|
892
1104
|
onProductClick?: () => void;
|
|
893
|
-
|
|
1105
|
+
/**
|
|
1106
|
+
* Header slot ABOVE the navigation, replacing `product`.
|
|
1107
|
+
*
|
|
1108
|
+
* Pass a FUNCTION to follow the EFFECTIVE collapsed state. A plain node cannot: `AppShell` hands
|
|
1109
|
+
* the same Sidebar to the drawer and the drawer un-collapses the rows (see `NavSurface`), so a
|
|
1110
|
+
* node built from the consumer's own `collapsed` boolean renders a glyph-only lockup inside a
|
|
1111
|
+
* full-width drawer. The escape hatch consumers reach for is a SECOND hand-built `Sidebar` in
|
|
1112
|
+
* `AppShell.mobileNav` — and that override is precisely what switches off `railInDrawer`, so the
|
|
1113
|
+
* `navRail` silently stops reaching mobile. The function is called with the surface-effective
|
|
1114
|
+
* value, which removes the reason to build the second node at all.
|
|
1115
|
+
*
|
|
1116
|
+
* SCOPE: this is the APP's brand lockup. A PLATFORM switch (which organization, which app) does
|
|
1117
|
+
* not belong here — see AppShell's `navRail` for where it goes and why.
|
|
1118
|
+
*/
|
|
1119
|
+
brand?: ReactNode | ((collapsed: boolean) => ReactNode);
|
|
894
1120
|
collapsed?: boolean;
|
|
895
1121
|
children?: ChildrenProp;
|
|
896
1122
|
/**
|
|
@@ -939,6 +1165,8 @@ export type SidebarProp = {
|
|
|
939
1165
|
* configured via THEIR own props and dropped into a slot.
|
|
940
1166
|
*/
|
|
941
1167
|
export type TopbarProp = Omit<React.HTMLAttributes<HTMLDivElement>, "children"> & {
|
|
1168
|
+
height?: "bar" | "auto";
|
|
1169
|
+
pad?: PadProp;
|
|
942
1170
|
/** Inline-start cluster — typically the sidebar toggle + a brand mark (`Avatar`) + primary nav. */
|
|
943
1171
|
start?: ReactNode;
|
|
944
1172
|
/** Center cluster — optional (e.g. a search trigger or a page/entity switcher). */
|
|
@@ -964,6 +1192,25 @@ export type TopbarItemProp = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
|
964
1192
|
* Render the bar-cell shape ONTO the child instead of emitting a `<button>` — for a router link,
|
|
965
1193
|
* or for a menu/popover trigger that needs to own the element itself.
|
|
966
1194
|
*/
|
|
1195
|
+
/** Hide below a shared responsive breakpoint, without changing cell height. */
|
|
1196
|
+
hideBelow?: BreakpointProp;
|
|
1197
|
+
/**
|
|
1198
|
+
* Unread count OVERLAID on the cell's glyph — the notification-bell affordance the cell's own use
|
|
1199
|
+
* cases name (gh#398). Pass the CONTENT ONLY, exactly like `SidebarItemProp.badge`: a number, a
|
|
1200
|
+
* string, `"99+"`. Position, size and colour come from `--topbar-item-badge-*`, so the count does
|
|
1201
|
+
* NOT widen the cell (an inline chip pushes the end cluster's shrink budget around every time the
|
|
1202
|
+
* count crosses a digit boundary) and a theme can retune the overlay once.
|
|
1203
|
+
*
|
|
1204
|
+
* The count is not an accessible name: give the cell an `aria-label` that says what the number
|
|
1205
|
+
* means (`aria-label={t("topbar.notifications.unread", { count })}`).
|
|
1206
|
+
*/
|
|
1207
|
+
badge?: ReactNode;
|
|
1208
|
+
/**
|
|
1209
|
+
* Emphasis of `badge`. Vocabulary shared VERBATIM with `SidebarItemProp.badgeTone` so one count
|
|
1210
|
+
* pill reads the same in the rail and in the bar: `destructive` when the count is addressed to
|
|
1211
|
+
* the user rather than merely unread.
|
|
1212
|
+
*/
|
|
1213
|
+
badgeTone?: SidebarBadgeToneProp;
|
|
967
1214
|
asChild?: boolean;
|
|
968
1215
|
children?: ReactNode;
|
|
969
1216
|
className?: ClassNameProp;
|