@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,11 +1,17 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { Check, ChevronRight, ChevronsUpDown, Minus, X } from "lucide-react";
|
|
4
|
+
import { Check, ChevronRight, ChevronsUpDown, Loader2, Minus, X } from "lucide-react";
|
|
5
5
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
6
|
import { cn } from "../../lib/utils.js";
|
|
7
7
|
import { pickFieldA11y, useFieldIdentity } from "../../lib/field-a11y.js";
|
|
8
|
-
import {
|
|
8
|
+
import { controlSurfaceTriggerClass } from "../../lib/control-styles.js";
|
|
9
|
+
import {
|
|
10
|
+
applyMaxTagCount,
|
|
11
|
+
controlSurfaceAttrs,
|
|
12
|
+
resolveAllowClear,
|
|
13
|
+
resolveAriaInvalid
|
|
14
|
+
} from "./control-surface.js";
|
|
9
15
|
import { Popover, PopoverContent, PopoverTrigger } from "../data-display/popover.js";
|
|
10
16
|
import { ScrollArea, ScrollBar } from "../data-display/scroll-area.js";
|
|
11
17
|
import { Command, CommandInput } from "./command.js";
|
|
@@ -15,7 +21,8 @@ import {
|
|
|
15
21
|
getNodeByPath,
|
|
16
22
|
normalizeTreeOptions,
|
|
17
23
|
pathKey,
|
|
18
|
-
pathsEqual
|
|
24
|
+
pathsEqual,
|
|
25
|
+
reactNodeText
|
|
19
26
|
} from "./tree-utils.js";
|
|
20
27
|
function pathInValues(path, values) {
|
|
21
28
|
return values.some((v) => pathsEqual(v, path));
|
|
@@ -66,6 +73,41 @@ function aggregateCheckState(node, path, values) {
|
|
|
66
73
|
if (total === 0 || selected === 0) return "none";
|
|
67
74
|
return selected === total ? "checked" : "indeterminate";
|
|
68
75
|
}
|
|
76
|
+
function leafPathsUnder(node, path) {
|
|
77
|
+
const out = [];
|
|
78
|
+
const walk = (current, prefix) => {
|
|
79
|
+
const isLeaf = (current.children?.length ?? 0) === 0 || current.isLeaf === true;
|
|
80
|
+
if (isLeaf) {
|
|
81
|
+
out.push(prefix);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
for (const child of current.children) walk(child, [...prefix, child.value]);
|
|
85
|
+
};
|
|
86
|
+
walk(node, path);
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
function collapseToParents(values, options) {
|
|
90
|
+
const representative = /* @__PURE__ */ new Map();
|
|
91
|
+
const walk = (nodes, prefix) => {
|
|
92
|
+
for (const node of nodes) {
|
|
93
|
+
const path = [...prefix, node.value];
|
|
94
|
+
const hasChildren = (node.children?.length ?? 0) > 0 && node.isLeaf !== true;
|
|
95
|
+
if (!hasChildren) continue;
|
|
96
|
+
if (aggregateCheckState(node, path, values) === "checked") {
|
|
97
|
+
for (const leaf of leafPathsUnder(node, path)) representative.set(pathKey(leaf), path);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
walk(node.children, path);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
walk(options, []);
|
|
104
|
+
const out = [];
|
|
105
|
+
for (const value of values) {
|
|
106
|
+
const shown = representative.get(pathKey(value)) ?? value;
|
|
107
|
+
if (!out.some((existing) => pathsEqual(existing, shown))) out.push(shown);
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
69
111
|
function Cascader({
|
|
70
112
|
options: optionsProp,
|
|
71
113
|
value,
|
|
@@ -76,25 +118,76 @@ function Cascader({
|
|
|
76
118
|
showSearch,
|
|
77
119
|
placeholder,
|
|
78
120
|
disabled,
|
|
121
|
+
readOnly,
|
|
122
|
+
name,
|
|
79
123
|
className,
|
|
80
124
|
id,
|
|
81
125
|
expandTrigger = "click",
|
|
82
126
|
fieldNames,
|
|
83
|
-
allowClear
|
|
127
|
+
allowClear,
|
|
128
|
+
size,
|
|
129
|
+
status,
|
|
130
|
+
variant,
|
|
131
|
+
loading = false,
|
|
132
|
+
open: openProp,
|
|
133
|
+
defaultOpen = false,
|
|
134
|
+
onOpenChange,
|
|
135
|
+
showCheckedStrategy = "SHOW_CHILD",
|
|
136
|
+
loadData,
|
|
137
|
+
displayRender,
|
|
138
|
+
optionRender,
|
|
139
|
+
maxTagCount,
|
|
140
|
+
maxTagPlaceholder,
|
|
141
|
+
notFoundContent,
|
|
142
|
+
autoClearSearchValue = true,
|
|
143
|
+
search: searchProp,
|
|
144
|
+
onSearchChange,
|
|
84
145
|
...ariaProps
|
|
85
146
|
}) {
|
|
86
147
|
const { t } = useTranslation();
|
|
87
148
|
const fieldA11y = pickFieldA11y(ariaProps);
|
|
88
|
-
const identity = useFieldIdentity({ id, "data-field": fieldA11y["data-field"] });
|
|
149
|
+
const identity = useFieldIdentity({ id, name, "data-field": fieldA11y["data-field"] });
|
|
150
|
+
const resolvedName = name ?? identity.name;
|
|
89
151
|
const reactId = React.useId();
|
|
90
152
|
const panelId = `${id ?? reactId}-panel`;
|
|
91
153
|
const options = React.useMemo(
|
|
92
154
|
() => normalizeTreeOptions(optionsProp, fieldNames),
|
|
93
155
|
[optionsProp, fieldNames]
|
|
94
156
|
);
|
|
95
|
-
const [
|
|
157
|
+
const [internalOpen, setInternalOpen] = React.useState(defaultOpen);
|
|
158
|
+
const isOpenControlled = openProp !== void 0;
|
|
159
|
+
const open = isOpenControlled ? openProp : internalOpen;
|
|
160
|
+
const setOpen = React.useCallback(
|
|
161
|
+
(next) => {
|
|
162
|
+
if (!isOpenControlled) setInternalOpen(next);
|
|
163
|
+
onOpenChange?.(next);
|
|
164
|
+
},
|
|
165
|
+
[isOpenControlled, onOpenChange]
|
|
166
|
+
);
|
|
96
167
|
const [activePath, setActivePath] = React.useState([]);
|
|
97
|
-
const [
|
|
168
|
+
const [internalSearch, setInternalSearch] = React.useState("");
|
|
169
|
+
const isSearchControlled = searchProp !== void 0;
|
|
170
|
+
const search = isSearchControlled ? searchProp : internalSearch;
|
|
171
|
+
const setSearch = React.useCallback(
|
|
172
|
+
(next) => {
|
|
173
|
+
if (!isSearchControlled) setInternalSearch(next);
|
|
174
|
+
onSearchChange?.(next);
|
|
175
|
+
},
|
|
176
|
+
[isSearchControlled, onSearchChange]
|
|
177
|
+
);
|
|
178
|
+
const requestedLoads = React.useRef(/* @__PURE__ */ new Set());
|
|
179
|
+
const requestLoad = React.useCallback(
|
|
180
|
+
(path, chain) => {
|
|
181
|
+
if (!loadData) return;
|
|
182
|
+
const node = chain.at(-1);
|
|
183
|
+
if (!node || node.isLeaf === true || (node.children?.length ?? 0) > 0) return;
|
|
184
|
+
const key = pathKey(path);
|
|
185
|
+
if (requestedLoads.current.has(key)) return;
|
|
186
|
+
requestedLoads.current.add(key);
|
|
187
|
+
void loadData(chain);
|
|
188
|
+
},
|
|
189
|
+
[loadData]
|
|
190
|
+
);
|
|
98
191
|
const isControlledSingle = !multiple && value !== void 0;
|
|
99
192
|
const isControlledMulti = multiple && value !== void 0;
|
|
100
193
|
const [internalSingle, setInternalSingle] = React.useState(
|
|
@@ -106,14 +199,52 @@ function Cascader({
|
|
|
106
199
|
const singleValue = isControlledSingle ? value : internalSingle;
|
|
107
200
|
const multiValue = isControlledMulti ? value : internalMulti;
|
|
108
201
|
const resolvedPlaceholder = placeholder ?? t("dataEntry.cascader.placeholder");
|
|
202
|
+
const hasValue = multiple ? multiValue.length > 0 : singleValue.length > 0;
|
|
109
203
|
const displayLabel = React.useMemo(() => {
|
|
110
204
|
if (multiple) {
|
|
111
205
|
if (!multiValue.length) return null;
|
|
112
|
-
|
|
206
|
+
const shown = showCheckedStrategy === "SHOW_PARENT" ? collapseToParents(multiValue, options) : multiValue;
|
|
207
|
+
const items = shown.map((path) => {
|
|
208
|
+
const chain2 = getNodeByPath(options, path);
|
|
209
|
+
const labels = chain2.map((node) => reactNodeText(node.label));
|
|
210
|
+
return {
|
|
211
|
+
value: pathKey(path),
|
|
212
|
+
label: displayRender ? displayRender(labels, chain2) : formatPathLabels(chain2)
|
|
213
|
+
};
|
|
214
|
+
});
|
|
215
|
+
const { visible, omitted, overflow } = applyMaxTagCount(
|
|
216
|
+
items,
|
|
217
|
+
maxTagCount,
|
|
218
|
+
maxTagPlaceholder
|
|
219
|
+
);
|
|
220
|
+
const body = visible.map((item, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
221
|
+
index > 0 ? ", " : null,
|
|
222
|
+
item.label
|
|
223
|
+
] }, item.value));
|
|
224
|
+
if (omitted.length === 0) return body;
|
|
225
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
226
|
+
body,
|
|
227
|
+
", ",
|
|
228
|
+
/* @__PURE__ */ jsx("span", { "data-slot": "cascader-overflow", children: overflow ?? t("dataEntry.selection.overflow", { count: omitted.length }) })
|
|
229
|
+
] });
|
|
113
230
|
}
|
|
114
231
|
if (!singleValue.length) return null;
|
|
115
|
-
|
|
116
|
-
|
|
232
|
+
const chain = getNodeByPath(options, singleValue);
|
|
233
|
+
return displayRender ? displayRender(
|
|
234
|
+
chain.map((node) => reactNodeText(node.label)),
|
|
235
|
+
chain
|
|
236
|
+
) : formatPathLabels(chain);
|
|
237
|
+
}, [
|
|
238
|
+
multiple,
|
|
239
|
+
multiValue,
|
|
240
|
+
singleValue,
|
|
241
|
+
options,
|
|
242
|
+
showCheckedStrategy,
|
|
243
|
+
displayRender,
|
|
244
|
+
maxTagCount,
|
|
245
|
+
maxTagPlaceholder,
|
|
246
|
+
t
|
|
247
|
+
]);
|
|
117
248
|
const setSingleValue = (path) => {
|
|
118
249
|
if (!isControlledSingle) setInternalSingle(path);
|
|
119
250
|
onValueChange?.(path, getNodeByPath(options, path));
|
|
@@ -122,7 +253,7 @@ function Cascader({
|
|
|
122
253
|
setSingleValue(path);
|
|
123
254
|
setOpen(false);
|
|
124
255
|
setActivePath([]);
|
|
125
|
-
setSearch("");
|
|
256
|
+
if (autoClearSearchValue) setSearch("");
|
|
126
257
|
};
|
|
127
258
|
const commitMulti = (paths) => {
|
|
128
259
|
if (!isControlledMulti) setInternalMulti(paths);
|
|
@@ -143,6 +274,11 @@ function Cascader({
|
|
|
143
274
|
}, [options, activePath]);
|
|
144
275
|
const handleSelectNode = (node, path) => {
|
|
145
276
|
const hasChildren = (node.children?.length ?? 0) > 0 && node.isLeaf !== true;
|
|
277
|
+
requestLoad(path, getNodeByPath(options, path));
|
|
278
|
+
if (loadData && node.isLeaf === false && !(node.children?.length ?? 0)) {
|
|
279
|
+
setActivePath(path);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
146
282
|
if (multiple) {
|
|
147
283
|
if (hasChildren && !changeOnSelect) setActivePath(path);
|
|
148
284
|
else if (!node.disableCheckbox) commitMulti(togglePath(multiValue, path));
|
|
@@ -161,11 +297,12 @@ function Cascader({
|
|
|
161
297
|
[options, search, isSearching]
|
|
162
298
|
);
|
|
163
299
|
const handleOpenChange = (next) => {
|
|
300
|
+
if (readOnly && next) return;
|
|
164
301
|
setOpen(next);
|
|
165
302
|
if (next) {
|
|
166
303
|
setActivePath(multiple ? [] : singleValue);
|
|
167
304
|
} else {
|
|
168
|
-
setSearch("");
|
|
305
|
+
if (autoClearSearchValue) setSearch("");
|
|
169
306
|
setActivePath([]);
|
|
170
307
|
}
|
|
171
308
|
};
|
|
@@ -208,7 +345,11 @@ function Cascader({
|
|
|
208
345
|
// deeper column but keeps its own. Never collapse on the column's mouseleave
|
|
209
346
|
// — moving the pointer toward the next column would strand the deeper levels
|
|
210
347
|
// and make a depth-3 leaf unreachable.
|
|
211
|
-
expandTrigger === "hover" && !node.disabled ? () =>
|
|
348
|
+
expandTrigger === "hover" && !node.disabled ? () => {
|
|
349
|
+
const loadable = node.isLeaf === false && !(node.children?.length ?? 0);
|
|
350
|
+
requestLoad(path, getNodeByPath(options, path));
|
|
351
|
+
setActivePath(hasChildren || loadable ? path : path.slice(0, -1));
|
|
352
|
+
} : void 0
|
|
212
353
|
),
|
|
213
354
|
onClick: () => !node.disabled && handleSelectNode(node, path),
|
|
214
355
|
children: [
|
|
@@ -222,8 +363,8 @@ function Cascader({
|
|
|
222
363
|
}
|
|
223
364
|
),
|
|
224
365
|
!multiple && selected && /* @__PURE__ */ jsx(Check, { className: "ui-cascader-option-icon", "aria-hidden": "true" }),
|
|
225
|
-
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-start", children: node.label }),
|
|
226
|
-
hasChildren && /* @__PURE__ */ jsx(ChevronRight, { className: "ui-cascader-option-chevron", "aria-hidden": "true" })
|
|
366
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-start", children: optionRender ? optionRender(node) : node.label }),
|
|
367
|
+
(hasChildren || loadData && node.isLeaf === false) && /* @__PURE__ */ jsx(ChevronRight, { className: "ui-cascader-option-chevron", "aria-hidden": "true" })
|
|
227
368
|
]
|
|
228
369
|
}
|
|
229
370
|
) }, node.value);
|
|
@@ -233,7 +374,9 @@ function Cascader({
|
|
|
233
374
|
)) }),
|
|
234
375
|
/* @__PURE__ */ jsx(ScrollBar, { orientation: "horizontal" })
|
|
235
376
|
] });
|
|
236
|
-
const
|
|
377
|
+
const clearControl = resolveAllowClear(allowClear, true, t("dataEntry.cascader.clear"));
|
|
378
|
+
const showClear = clearControl.enabled && hasValue && !disabled && !readOnly && !loading;
|
|
379
|
+
const surface = controlSurfaceAttrs({ variant, status, size });
|
|
237
380
|
return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), children: [
|
|
238
381
|
/* @__PURE__ */ jsxs(Popover, { open, onOpenChange: handleOpenChange, children: [
|
|
239
382
|
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
@@ -247,17 +390,31 @@ function Cascader({
|
|
|
247
390
|
"aria-haspopup": "listbox",
|
|
248
391
|
"aria-controls": open ? panelId : void 0,
|
|
249
392
|
...fieldA11y,
|
|
393
|
+
...surface,
|
|
394
|
+
"aria-invalid": resolveAriaInvalid(fieldA11y["aria-invalid"], status),
|
|
395
|
+
"aria-busy": loading || void 0,
|
|
396
|
+
"aria-readonly": readOnly || void 0,
|
|
250
397
|
disabled,
|
|
251
398
|
className: cn(
|
|
252
|
-
|
|
399
|
+
controlSurfaceTriggerClass,
|
|
253
400
|
"w-full justify-start",
|
|
254
401
|
// Reserve trailing room for the single clear-or-chevron overlay rendered below.
|
|
255
402
|
"ui-control-trigger-affixed",
|
|
256
|
-
!
|
|
403
|
+
!hasValue && "text-muted-foreground"
|
|
257
404
|
),
|
|
258
|
-
children: /* @__PURE__ */ jsx("span", { className: "truncate", children: displayLabel
|
|
405
|
+
children: /* @__PURE__ */ jsx("span", { className: "truncate", children: hasValue ? displayLabel : resolvedPlaceholder })
|
|
259
406
|
}
|
|
260
407
|
) }),
|
|
408
|
+
resolvedName ? (multiple ? multiValue : singleValue.length ? [singleValue] : []).map((path, index) => /* @__PURE__ */ jsx(
|
|
409
|
+
"input",
|
|
410
|
+
{
|
|
411
|
+
type: "hidden",
|
|
412
|
+
name: resolvedName,
|
|
413
|
+
value: path.join("/"),
|
|
414
|
+
readOnly: true
|
|
415
|
+
},
|
|
416
|
+
`${pathKey(path)}-${index}`
|
|
417
|
+
)) : null,
|
|
261
418
|
/* @__PURE__ */ jsxs(PopoverContent, { id: panelId, className: "ui-cascader-popover", align: "start", children: [
|
|
262
419
|
showSearch && /* @__PURE__ */ jsx(Command, { shouldFilter: false, children: /* @__PURE__ */ jsx(
|
|
263
420
|
CommandInput,
|
|
@@ -273,7 +430,7 @@ function Cascader({
|
|
|
273
430
|
className: "ui-cascader-panel",
|
|
274
431
|
role: "listbox",
|
|
275
432
|
"aria-multiselectable": multiple ? true : void 0,
|
|
276
|
-
children: searchResults.length === 0 ? /* @__PURE__ */ jsx("p", { className: "ui-cascader-empty", children: t("dataEntry.cascader.empty") }) : searchResults.map(({ path, labels }) => {
|
|
433
|
+
children: searchResults.length === 0 ? /* @__PURE__ */ jsx("p", { className: "ui-cascader-empty", children: notFoundContent ?? t("dataEntry.cascader.empty") }) : searchResults.map(({ path, labels }) => {
|
|
277
434
|
const label = labels.join(" / ");
|
|
278
435
|
const selected = multiple ? pathInValues(path, multiValue) : pathsEqual(path, singleValue);
|
|
279
436
|
return /* @__PURE__ */ jsxs(
|
|
@@ -307,14 +464,21 @@ function Cascader({
|
|
|
307
464
|
) }) : renderCascadeColumns()
|
|
308
465
|
] })
|
|
309
466
|
] }),
|
|
310
|
-
/* @__PURE__ */ jsx("div", { className: "ui-control-affix ui-cascader-affix", children:
|
|
467
|
+
/* @__PURE__ */ jsx("div", { className: "ui-control-affix ui-cascader-affix", children: loading ? /* @__PURE__ */ jsx(
|
|
468
|
+
Loader2,
|
|
469
|
+
{
|
|
470
|
+
"data-slot": "cascader-loading",
|
|
471
|
+
className: "ui-control-affix-icon ui-control-affix-indicator animate-spin",
|
|
472
|
+
"aria-hidden": "true"
|
|
473
|
+
}
|
|
474
|
+
) : showClear ? /* @__PURE__ */ jsx(
|
|
311
475
|
"button",
|
|
312
476
|
{
|
|
313
477
|
type: "button",
|
|
314
|
-
"aria-label":
|
|
478
|
+
"aria-label": clearControl.label,
|
|
315
479
|
className: "ui-control-affix-action",
|
|
316
480
|
onClick: clearValue,
|
|
317
|
-
children: /* @__PURE__ */ jsx(X, { className: "ui-control-affix-icon", "aria-hidden": "true" })
|
|
481
|
+
children: clearControl.clearIcon ?? /* @__PURE__ */ jsx(X, { className: "ui-control-affix-icon", "aria-hidden": "true" })
|
|
318
482
|
}
|
|
319
483
|
) : /* @__PURE__ */ jsx(
|
|
320
484
|
ChevronsUpDown,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { ColorPickerProp } from "../../props/components/data-entry.prop.js";
|
|
3
3
|
export type { ColorPickerProp, ColorPickerProp as ColorPickerProps, } from "../../props/components/data-entry.prop.js";
|
|
4
|
-
export declare function ColorPicker({ value, onValueChange, disabled, className, id, showHexInput, ...ariaProps }: ColorPickerProp): React.JSX.Element;
|
|
4
|
+
export declare function ColorPicker({ value: valueProp, defaultValue, onValueChange, disabled, name, className, id, showHexInput, ...ariaProps }: ColorPickerProp): React.JSX.Element;
|
|
@@ -3,26 +3,35 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
5
5
|
import { cn } from "../../lib/utils.js";
|
|
6
|
-
import { resolveFieldA11y } from "../../lib/field-a11y.js";
|
|
6
|
+
import { resolveFieldA11y, useFieldIdentity } from "../../lib/field-a11y.js";
|
|
7
7
|
import { controlIconClass } from "../../lib/control-styles.js";
|
|
8
8
|
import { Input } from "./input.js";
|
|
9
9
|
const HEX_PATTERN = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
|
|
10
|
+
const NATIVE_COLOR_FALLBACK = "#000000";
|
|
10
11
|
function normalizeHex(value) {
|
|
11
12
|
if (!value.startsWith("#")) return `#${value}`;
|
|
12
13
|
return value;
|
|
13
14
|
}
|
|
14
15
|
function ColorPicker({
|
|
15
|
-
value
|
|
16
|
+
value: valueProp,
|
|
17
|
+
defaultValue,
|
|
16
18
|
onValueChange,
|
|
17
19
|
disabled,
|
|
20
|
+
name,
|
|
18
21
|
className,
|
|
19
22
|
id,
|
|
20
23
|
showHexInput = true,
|
|
21
24
|
...ariaProps
|
|
22
25
|
}) {
|
|
23
26
|
const { t } = useTranslation();
|
|
27
|
+
const isControlled = valueProp !== void 0;
|
|
28
|
+
const [internalValue, setInternalValue] = React.useState(defaultValue ?? "");
|
|
29
|
+
const value = isControlled ? valueProp : internalValue;
|
|
24
30
|
const [draft, setDraft] = React.useState(null);
|
|
25
31
|
const display = draft ?? value;
|
|
32
|
+
const swatchValue = HEX_PATTERN.test(display) ? display : HEX_PATTERN.test(value) ? value : NATIVE_COLOR_FALLBACK;
|
|
33
|
+
const identity = useFieldIdentity({ id, name, "data-field": ariaProps["data-field"] });
|
|
34
|
+
const resolvedName = name ?? identity.name;
|
|
26
35
|
const swatchA11y = resolveFieldA11y(ariaProps, t("dataEntry.colorPicker.ariaLabel"));
|
|
27
36
|
const commit = (next) => {
|
|
28
37
|
const normalized = normalizeHex(next);
|
|
@@ -31,6 +40,7 @@ function ColorPicker({
|
|
|
31
40
|
return;
|
|
32
41
|
}
|
|
33
42
|
setDraft(null);
|
|
43
|
+
if (!isControlled) setInternalValue(normalized);
|
|
34
44
|
onValueChange?.(normalized);
|
|
35
45
|
};
|
|
36
46
|
return /* @__PURE__ */ jsxs("div", { className: cn("ui-color-picker", className), children: [
|
|
@@ -39,7 +49,7 @@ function ColorPicker({
|
|
|
39
49
|
"div",
|
|
40
50
|
{
|
|
41
51
|
className: "ui-color-picker-preview",
|
|
42
|
-
style: { backgroundColor:
|
|
52
|
+
style: { backgroundColor: swatchValue },
|
|
43
53
|
"aria-hidden": "true"
|
|
44
54
|
}
|
|
45
55
|
),
|
|
@@ -48,10 +58,11 @@ function ColorPicker({
|
|
|
48
58
|
{
|
|
49
59
|
id,
|
|
50
60
|
type: "color",
|
|
51
|
-
value:
|
|
61
|
+
value: swatchValue,
|
|
52
62
|
disabled,
|
|
53
63
|
onChange: (event) => commit(event.target.value),
|
|
54
64
|
className: "ui-color-picker-input",
|
|
65
|
+
"data-field": ariaProps["data-field"] ?? identity["data-field"],
|
|
55
66
|
...swatchA11y
|
|
56
67
|
}
|
|
57
68
|
)
|
|
@@ -71,7 +82,8 @@ function ColorPicker({
|
|
|
71
82
|
className: "ui-color-picker-hex",
|
|
72
83
|
spellCheck: false
|
|
73
84
|
}
|
|
74
|
-
)
|
|
85
|
+
),
|
|
86
|
+
resolvedName ? /* @__PURE__ */ jsx("input", { type: "hidden", name: resolvedName, value, readOnly: true }) : null
|
|
75
87
|
] });
|
|
76
88
|
}
|
|
77
89
|
export {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
import type { ControlStatusProp, ControlVariantProp } from "../../props/vocabulary/index.js";
|
|
3
|
+
import type { ControlCountProp } from "../../props/components/data-entry.prop.js";
|
|
4
|
+
/**
|
|
5
|
+
* The two appearance axes Ant Design puts on every text field — `status` and `variant` — resolved
|
|
6
|
+
* once, here, so Input / Textarea / NumberInput / PasswordInput / SearchInput cannot drift into
|
|
7
|
+
* three different spellings of "this field is wrong".
|
|
8
|
+
*
|
|
9
|
+
* WHY A HELPER AND NOT A CLASS PER COMPONENT. `status` has a SECOND half that is not paint:
|
|
10
|
+
* `error` must also report `aria-invalid`, or the red boundary is a lie to a screen reader. That
|
|
11
|
+
* pairing is the thing worth keeping in one place; the CSS is the easy half.
|
|
12
|
+
*/
|
|
13
|
+
export type ControlAppearanceInput = {
|
|
14
|
+
status?: ControlStatusProp;
|
|
15
|
+
variant?: ControlVariantProp;
|
|
16
|
+
"aria-invalid"?: React.AriaAttributes["aria-invalid"];
|
|
17
|
+
};
|
|
18
|
+
export type ControlAppearanceAttributes = {
|
|
19
|
+
"data-status"?: ControlStatusProp;
|
|
20
|
+
"data-variant"?: ControlVariantProp;
|
|
21
|
+
"aria-invalid"?: React.AriaAttributes["aria-invalid"];
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* `status="error"` REPORTS `aria-invalid` — unless the caller (or FormField, which injects it)
|
|
25
|
+
* already said something, in which case the explicit value wins and nothing is overwritten.
|
|
26
|
+
* `status="warning"` deliberately reports nothing: a warning is not a validity failure, and
|
|
27
|
+
* antd does not announce one either (es/_util/statusUtils.js only reaches for the class name).
|
|
28
|
+
*/
|
|
29
|
+
export declare function controlAppearanceAttributes({ status, variant, "aria-invalid": ariaInvalid, }: ControlAppearanceInput): ControlAppearanceAttributes;
|
|
30
|
+
/**
|
|
31
|
+
* The chrome utilities a variant carries, kept in the COMPONENT rather than in `control.css`.
|
|
32
|
+
*
|
|
33
|
+
* A Tailwind utility lives in `@layer utilities`, which outranks `@layer components` no matter
|
|
34
|
+
* how specific the component rule is — so `bg-background` baked unconditionally into the class
|
|
35
|
+
* list would silently defeat `filled` and `borderless` and no gate would notice. The outlined
|
|
36
|
+
* field therefore keeps exactly the utilities it always had, and the other two simply do not
|
|
37
|
+
* receive them; their surface comes from `.ui-control--filled` / `.ui-control--borderless`.
|
|
38
|
+
*/
|
|
39
|
+
export declare const CONTROL_VARIANT_CHROME_CLASS: Record<ControlVariantProp, string>;
|
|
40
|
+
/**
|
|
41
|
+
* The status BOUNDARY, for the same cascade-layer reason — and this one was measured the hard way.
|
|
42
|
+
*
|
|
43
|
+
* `.ui-control[data-status="warning"] { border-color: … }` in `@layer components` renders exactly
|
|
44
|
+
* nothing: `border-input` is a utility, utilities are a later layer, and a later layer wins over
|
|
45
|
+
* any specificity. In Chromium the warned field measured `rgb(144 135 127)` — the resting border —
|
|
46
|
+
* with the component rule in place and no error anywhere. Painting it from a utility instead puts
|
|
47
|
+
* the two declarations in the SAME layer, where `data-[status=…]` (0,2,0) genuinely outranks
|
|
48
|
+
* `border-input` (0,1,0). It is also the mechanism the library already used for the invalid state
|
|
49
|
+
* (`aria-invalid:border-destructive`), so the two states now paint the same way.
|
|
50
|
+
*
|
|
51
|
+
* The `--focus-*` reassignments stay in `control.css`: those are custom properties, and no utility
|
|
52
|
+
* competes for them.
|
|
53
|
+
*/
|
|
54
|
+
export declare const CONTROL_STATUS_CHROME_CLASS = "data-[status=error]:border-destructive data-[status=warning]:border-warning";
|
|
55
|
+
/** Default `count.strategy` — code points, so a surrogate pair counts once. */
|
|
56
|
+
export declare function countCodePoints(value: string): number;
|
|
57
|
+
export type ResolvedControlCount = {
|
|
58
|
+
count: number;
|
|
59
|
+
max?: number;
|
|
60
|
+
exceeded: boolean;
|
|
61
|
+
content: React.ReactNode;
|
|
62
|
+
};
|
|
63
|
+
/** `null` when the counter is switched off (no `count`, or `count.show === false`). */
|
|
64
|
+
export declare function resolveControlCount(config: ControlCountProp | undefined, value: string): ResolvedControlCount | null;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
function controlAppearanceAttributes({
|
|
2
|
+
status,
|
|
3
|
+
variant,
|
|
4
|
+
"aria-invalid": ariaInvalid
|
|
5
|
+
}) {
|
|
6
|
+
return {
|
|
7
|
+
"data-status": status,
|
|
8
|
+
"data-variant": variant,
|
|
9
|
+
"aria-invalid": ariaInvalid ?? (status === "error" ? true : void 0)
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const CONTROL_VARIANT_CHROME_CLASS = {
|
|
13
|
+
outlined: "border-input bg-background",
|
|
14
|
+
filled: "ui-control--filled",
|
|
15
|
+
borderless: "ui-control--borderless"
|
|
16
|
+
};
|
|
17
|
+
const CONTROL_STATUS_CHROME_CLASS = "data-[status=error]:border-destructive data-[status=warning]:border-warning";
|
|
18
|
+
function countCodePoints(value) {
|
|
19
|
+
return [...value].length;
|
|
20
|
+
}
|
|
21
|
+
function resolveControlCount(config, value) {
|
|
22
|
+
if (!config || config.show === false) return null;
|
|
23
|
+
const strategy = config.strategy ?? countCodePoints;
|
|
24
|
+
const count = strategy(value);
|
|
25
|
+
const max = config.max;
|
|
26
|
+
return {
|
|
27
|
+
count,
|
|
28
|
+
max,
|
|
29
|
+
exceeded: max != null && count > max,
|
|
30
|
+
content: config.formatter ? config.formatter({ value, count, max }) : max == null ? String(count) : `${count} / ${max}`
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
CONTROL_STATUS_CHROME_CLASS,
|
|
35
|
+
CONTROL_VARIANT_CHROME_CLASS,
|
|
36
|
+
controlAppearanceAttributes,
|
|
37
|
+
countCodePoints,
|
|
38
|
+
resolveControlCount
|
|
39
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
import type { AllowClearProp, ControlStatusProp, ControlVariantProp, MaxTagPlaceholderProp, SizeProp } from "../../props/vocabulary/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* The DOM attributes that put a control on the `variant` × `status` × `size` matrix owned by
|
|
5
|
+
* `.ui-control-surface` in `src/styles/control.css`.
|
|
6
|
+
*
|
|
7
|
+
* Written once, here, rather than five times: every member of the select family (Select,
|
|
8
|
+
* SearchSelect, Cascader, TreeSelect, TagInput) states the antd surface contract the same way, and
|
|
9
|
+
* a per-component copy is how the five drift apart. Each default (`outlined`, `md`) emits NO
|
|
10
|
+
* attribute at all, so a control that says nothing keeps the exact DOM it had.
|
|
11
|
+
*/
|
|
12
|
+
export declare function controlSurfaceAttrs(props: {
|
|
13
|
+
variant?: ControlVariantProp;
|
|
14
|
+
status?: ControlStatusProp;
|
|
15
|
+
size?: SizeProp;
|
|
16
|
+
}): {
|
|
17
|
+
"data-variant"?: ControlVariantProp;
|
|
18
|
+
"data-status"?: ControlStatusProp;
|
|
19
|
+
"data-size"?: SizeProp;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* `status="error"` must also reach assistive tech, so it folds into `aria-invalid`.
|
|
23
|
+
*
|
|
24
|
+
* antd's own `status="error"` is a pure recolour — a state announced by hue alone, which WCAG 2.2
|
|
25
|
+
* SC 1.4.1 does not accept. An `aria-invalid` arriving from `FormField` always wins: the field's
|
|
26
|
+
* validation state is the authority, and a `status` prop must never be able to talk it down.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveAriaInvalid<T extends React.AriaAttributes["aria-invalid"]>(ariaInvalid: T, status: ControlStatusProp | undefined): T | true | undefined;
|
|
29
|
+
/** The normalised clear affordance: whether to offer it, with what icon, under what label. */
|
|
30
|
+
export type ResolvedAllowClear = {
|
|
31
|
+
enabled: boolean;
|
|
32
|
+
clearIcon?: React.ReactNode;
|
|
33
|
+
label?: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Reconcile antd's `allowClear` with this library's own `clearable`.
|
|
37
|
+
*
|
|
38
|
+
* `allowClear` wins when present because it is the more specific statement — the object form
|
|
39
|
+
* carries an icon and a label that `clearable` has no way to express. `clearable` remains the
|
|
40
|
+
* name every existing call site uses, and keeps its `true` default.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveAllowClear(allowClear: AllowClearProp | undefined, clearable: boolean | undefined, fallbackLabel?: string): ResolvedAllowClear;
|
|
43
|
+
/**
|
|
44
|
+
* Apply antd's `maxTagCount` / `maxTagPlaceholder` to a list of selected values.
|
|
45
|
+
*
|
|
46
|
+
* Returns the values that stay visible plus the overflow node, so the three call sites (Cascader,
|
|
47
|
+
* TreeSelect, TagInput) agree on what "+2" means. `maxTagCount` is only honoured when it is a
|
|
48
|
+
* non-negative number — `undefined` shows everything, which is the antd default.
|
|
49
|
+
*
|
|
50
|
+
* `overflow` is `undefined` when the consumer supplied no `maxTagPlaceholder`: the fallback wording
|
|
51
|
+
* is a COUNT, and a count has to go through `t()` + `Intl.PluralRules` at the call site rather than
|
|
52
|
+
* be concatenated here.
|
|
53
|
+
*/
|
|
54
|
+
export declare function applyMaxTagCount<T extends {
|
|
55
|
+
value: string;
|
|
56
|
+
label: React.ReactNode;
|
|
57
|
+
}>(items: T[], maxTagCount: number | undefined, maxTagPlaceholder: MaxTagPlaceholderProp | undefined): {
|
|
58
|
+
visible: T[];
|
|
59
|
+
omitted: T[];
|
|
60
|
+
overflow: React.ReactNode | undefined;
|
|
61
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
function controlSurfaceAttrs(props) {
|
|
2
|
+
return {
|
|
3
|
+
"data-variant": props.variant && props.variant !== "outlined" ? props.variant : void 0,
|
|
4
|
+
"data-status": props.status,
|
|
5
|
+
"data-size": props.size && props.size !== "md" ? props.size : void 0
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
function resolveAriaInvalid(ariaInvalid, status) {
|
|
9
|
+
if (ariaInvalid !== void 0) return ariaInvalid;
|
|
10
|
+
return status === "error" ? true : void 0;
|
|
11
|
+
}
|
|
12
|
+
function resolveAllowClear(allowClear, clearable, fallbackLabel) {
|
|
13
|
+
if (allowClear !== void 0) {
|
|
14
|
+
if (typeof allowClear === "boolean") {
|
|
15
|
+
return { enabled: allowClear, label: fallbackLabel };
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
enabled: true,
|
|
19
|
+
clearIcon: allowClear.clearIcon,
|
|
20
|
+
label: allowClear.label ?? fallbackLabel
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return { enabled: clearable !== false, label: fallbackLabel };
|
|
24
|
+
}
|
|
25
|
+
function applyMaxTagCount(items, maxTagCount, maxTagPlaceholder) {
|
|
26
|
+
if (maxTagCount === void 0 || maxTagCount < 0 || items.length <= maxTagCount) {
|
|
27
|
+
return { visible: items, omitted: [], overflow: void 0 };
|
|
28
|
+
}
|
|
29
|
+
const visible = items.slice(0, maxTagCount);
|
|
30
|
+
const omitted = items.slice(maxTagCount);
|
|
31
|
+
const overflow = typeof maxTagPlaceholder === "function" ? maxTagPlaceholder(omitted.map(({ value, label }) => ({ value, label }))) : maxTagPlaceholder;
|
|
32
|
+
return { visible, omitted, overflow };
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
applyMaxTagCount,
|
|
36
|
+
controlSurfaceAttrs,
|
|
37
|
+
resolveAllowClear,
|
|
38
|
+
resolveAriaInvalid
|
|
39
|
+
};
|
|
@@ -6,4 +6,4 @@ export type { DatePickerProp, DatePickerProp as DatePickerProps, } from "../../p
|
|
|
6
6
|
* `yyyy-MM-dd` string (the international standard): it is form-submittable (give it a `name`),
|
|
7
7
|
* screen-reader friendly, and e2e-testable by simply filling the input.
|
|
8
8
|
*/
|
|
9
|
-
export declare function DatePicker(
|
|
9
|
+
export declare function DatePicker(props: DatePickerProp): React.JSX.Element;
|