@godxjp/ui 28.9.0 → 28.12.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/agent/START-HERE.md +193 -0
- package/agent/anti-ai-tells.json +158 -0
- package/agent/components/Accordion.json +60 -0
- package/agent/components/AccountChip.json +59 -0
- package/agent/components/Actions.json +78 -0
- package/agent/components/Activity.json +80 -0
- package/agent/components/Affix.json +78 -0
- package/agent/components/Alert.json +65 -0
- package/agent/components/AlertDialog.json +109 -0
- package/agent/components/AlertDialogRoot.json +52 -0
- package/agent/components/Anchor.json +119 -0
- package/agent/components/AppLauncher.json +95 -0
- package/agent/components/AppProvider.json +105 -0
- package/agent/components/AppSettingPicker.json +88 -0
- package/agent/components/AppSettingToggle.json +70 -0
- package/agent/components/AppShell.json +159 -0
- package/agent/components/AreaChart.json +105 -0
- package/agent/components/AspectRatio.json +38 -0
- package/agent/components/Attachments.json +77 -0
- package/agent/components/AuthAccountSummary.json +64 -0
- package/agent/components/AuthDivider.json +35 -0
- package/agent/components/AuthFooter.json +48 -0
- package/agent/components/AuthIdentity.json +42 -0
- package/agent/components/AuthShell.json +108 -0
- package/agent/components/AuthStack.json +21 -0
- package/agent/components/Avatar.json +89 -0
- package/agent/components/Badge.json +96 -0
- package/agent/components/Banner.json +48 -0
- package/agent/components/BarChart.json +108 -0
- package/agent/components/BranchScopePicker.json +89 -0
- package/agent/components/Breadcrumb.json +54 -0
- package/agent/components/Button.json +133 -0
- package/agent/components/Calendar.json +259 -0
- package/agent/components/Callout.json +46 -0
- package/agent/components/Card.json +112 -0
- package/agent/components/CardBar.json +49 -0
- package/agent/components/CardContent.json +51 -0
- package/agent/components/Carousel.json +50 -0
- package/agent/components/Cascader.json +209 -0
- package/agent/components/CenteredShell.json +66 -0
- package/agent/components/ChatBubble.json +100 -0
- package/agent/components/ChatBubbleList.json +64 -0
- package/agent/components/ChatComposer.json +160 -0
- package/agent/components/ChatSuggestion.json +86 -0
- package/agent/components/Checkbox.json +68 -0
- package/agent/components/CheckboxGroup.json +96 -0
- package/agent/components/CodeBlock.json +64 -0
- package/agent/components/Collapsible.json +74 -0
- package/agent/components/ColorPicker.json +87 -0
- package/agent/components/Command.json +168 -0
- package/agent/components/CommandPalette.json +84 -0
- package/agent/components/CompactBarTrend.json +101 -0
- package/agent/components/Conversations.json +82 -0
- package/agent/components/CredentialReveal.json +93 -0
- package/agent/components/DataState.json +79 -0
- package/agent/components/DataTable.json +268 -0
- package/agent/components/DatePicker.json +275 -0
- package/agent/components/Descriptions.json +67 -0
- package/agent/components/Dialog.json +78 -0
- package/agent/components/DraggablePanel.json +106 -0
- package/agent/components/DropdownMenu.json +102 -0
- package/agent/components/EmptyState.json +83 -0
- package/agent/components/ErrorSurface.json +128 -0
- package/agent/components/FeatureList.json +43 -0
- package/agent/components/Field.json +64 -0
- package/agent/components/FilterBar.json +99 -0
- package/agent/components/Flex.json +153 -0
- package/agent/components/FloatButton.json +91 -0
- package/agent/components/Form.json +87 -0
- package/agent/components/FormErrors.json +51 -0
- package/agent/components/FormField.json +137 -0
- package/agent/components/FormFieldArray.json +39 -0
- package/agent/components/FormFieldControl.json +129 -0
- package/agent/components/FormRoot.json +122 -0
- package/agent/components/Heading.json +61 -0
- package/agent/components/HoverCard.json +55 -0
- package/agent/components/Icon.json +60 -0
- package/agent/components/InfiniteQueryState.json +58 -0
- package/agent/components/Input.json +122 -0
- package/agent/components/InputOTP.json +106 -0
- package/agent/components/Label.json +43 -0
- package/agent/components/LegalDocumentShell.json +102 -0
- package/agent/components/Legend.json +42 -0
- package/agent/components/LineChart.json +103 -0
- package/agent/components/Link.json +41 -0
- package/agent/components/ListRow.json +92 -0
- package/agent/components/Logo.json +85 -0
- package/agent/components/Marquee.json +91 -0
- package/agent/components/Masonry.json +82 -0
- package/agent/components/MasterDetail.json +95 -0
- package/agent/components/MegaMenu.json +120 -0
- package/agent/components/MobileShell.json +73 -0
- package/agent/components/NavList.json +63 -0
- package/agent/components/NumberInput.json +158 -0
- package/agent/components/OrgSwitcher.json +89 -0
- package/agent/components/OverlayPortalProvider.json +42 -0
- package/agent/components/PageContainer.json +181 -0
- package/agent/components/Pagination.json +132 -0
- package/agent/components/Paragraph.json +40 -0
- package/agent/components/PasswordInput.json +79 -0
- package/agent/components/PasswordStrength.json +51 -0
- package/agent/components/PermissionMatrix.json +81 -0
- package/agent/components/PieChart.json +99 -0
- package/agent/components/Popover.json +110 -0
- package/agent/components/PrefetchLink.json +65 -0
- package/agent/components/Progress.json +79 -0
- package/agent/components/Prose.json +57 -0
- package/agent/components/QrCode.json +62 -0
- package/agent/components/Radio.json +98 -0
- package/agent/components/RadioGroup.json +91 -0
- package/agent/components/RangeTimeline.json +80 -0
- package/agent/components/Rating.json +92 -0
- package/agent/components/ResizablePanel.json +69 -0
- package/agent/components/ResponsiveGrid.json +77 -0
- package/agent/components/Reveal.json +70 -0
- package/agent/components/ScrollArea.json +104 -0
- package/agent/components/SearchInput.json +98 -0
- package/agent/components/Segmented.json +96 -0
- package/agent/components/Select.json +397 -0
- package/agent/components/Separator.json +86 -0
- package/agent/components/ServiceCatalogCta.json +46 -0
- package/agent/components/ServiceLauncherCard.json +93 -0
- package/agent/components/ServiceRolePanel.json +83 -0
- package/agent/components/Sheet.json +85 -0
- package/agent/components/Sidebar.json +118 -0
- package/agent/components/Skeleton.json +57 -0
- package/agent/components/SkeletonArticle.json +71 -0
- package/agent/components/SkeletonAvatar.json +50 -0
- package/agent/components/SkeletonButton.json +57 -0
- package/agent/components/SkeletonForm.json +52 -0
- package/agent/components/SkeletonImage.json +37 -0
- package/agent/components/SkeletonInput.json +51 -0
- package/agent/components/SkeletonNode.json +42 -0
- package/agent/components/SkeletonRows.json +49 -0
- package/agent/components/SkeletonTable.json +45 -0
- package/agent/components/Slider.json +160 -0
- package/agent/components/SplitPane.json +66 -0
- package/agent/components/StatCard.json +83 -0
- package/agent/components/Steps.json +95 -0
- package/agent/components/Swatch.json +41 -0
- package/agent/components/Switch.json +81 -0
- package/agent/components/Table.json +112 -0
- package/agent/components/Tabs.json +158 -0
- package/agent/components/TagInput.json +105 -0
- package/agent/components/Text.json +201 -0
- package/agent/components/Textarea.json +126 -0
- package/agent/components/ThoughtChain.json +76 -0
- package/agent/components/Thumbnail.json +70 -0
- package/agent/components/TimePicker.json +200 -0
- package/agent/components/TimeRangePicker.json +90 -0
- package/agent/components/Timeline.json +47 -0
- package/agent/components/TimelineGrid.json +92 -0
- package/agent/components/Title.json +67 -0
- package/agent/components/Toaster.json +42 -0
- package/agent/components/Toggle.json +90 -0
- package/agent/components/ToggleGroup.json +102 -0
- package/agent/components/Toolbar.json +120 -0
- package/agent/components/Tooltip.json +110 -0
- package/agent/components/Topbar.json +83 -0
- package/agent/components/TopbarItem.json +79 -0
- package/agent/components/Transfer.json +141 -0
- package/agent/components/Tree.json +185 -0
- package/agent/components/TreeSelect.json +232 -0
- package/agent/components/TwoFactorSetup.json +79 -0
- package/agent/components/Typography.json +42 -0
- package/agent/components/Upload.json +221 -0
- package/agent/components/UploadCropDialog.json +60 -0
- package/agent/components/VisuallyHidden.json +20 -0
- package/agent/components/Welcome.json +65 -0
- package/agent/components/formatDate.json +46 -0
- package/agent/components/inertiaUpload.json +32 -0
- package/agent/components/useZodForm.json +39 -0
- package/agent/components-index.json +884 -0
- package/agent/components.json +15515 -0
- package/agent/index.json +56 -0
- package/agent/llms.txt +32 -0
- package/agent/patterns/account-recovery-settings.json +19 -0
- package/agent/patterns/async-data-state.json +20 -0
- package/agent/patterns/auth-recovery-panels.json +29 -0
- package/agent/patterns/badge-coloring.json +14 -0
- package/agent/patterns/common-fixes.json +16 -0
- package/agent/patterns/confirm-destructive.json +11 -0
- package/agent/patterns/data-table-page.json +18 -0
- package/agent/patterns/deferred-loading.json +12 -0
- package/agent/patterns/error-pages.json +28 -0
- package/agent/patterns/inertia-detail-page.json +13 -0
- package/agent/patterns/inertia-list-page.json +15 -0
- package/agent/patterns/inertia-persistent-layout.json +14 -0
- package/agent/patterns/organization-memberships.json +19 -0
- package/agent/patterns/page-sections.json +18 -0
- package/agent/patterns/settings-page-responsive.json +18 -0
- package/agent/patterns/settings-section-rows.json +23 -0
- package/agent/patterns/signup-form.json +13 -0
- package/agent/patterns/topbar-account-chip.json +18 -0
- package/agent/patterns/transactional-email.json +22 -0
- package/agent/patterns-index.json +323 -0
- package/agent/patterns.json +342 -0
- package/agent/rules.json +237 -0
- package/agent/tokens.json +8427 -0
- package/agent/vocabulary.json +198 -0
- package/dist/components/data-display/service-launcher-card.d.ts +19 -0
- package/dist/components/data-display/service-launcher-card.js +14 -1
- package/dist/components/data-entry/attachments.js +77 -33
- package/dist/components/data-entry/input.js +8 -1
- package/dist/components/layout/flex.d.ts +2 -2
- package/dist/components/layout/flex.js +2 -0
- package/dist/components/ui/avatar.d.ts +1 -18
- package/dist/components/ui/avatar.js +1 -36
- package/dist/components/ui/tag-input.d.ts +10 -0
- package/dist/components/ui/tag-input.js +35 -2
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +190 -1
- package/dist/i18n/messages/ja.json +188 -1
- package/dist/i18n/messages/vi.json +188 -1
- package/dist/lib/image-loading-status.d.ts +25 -0
- package/dist/lib/image-loading-status.js +41 -0
- package/dist/props/components/data-entry.prop.d.ts +21 -2
- package/dist/props/components/layout.prop.d.ts +42 -0
- package/dist/props/registry.d.ts +14 -1
- package/dist/props/registry.js +18 -1
- package/dist/styles/card-layout.css +10 -4
- package/dist/styles/control.css +33 -4
- package/dist/styles/data-display-layout.css +1 -1
- package/dist/styles/data-entry-layout.css +245 -2
- package/dist/styles/layout.css +17 -0
- package/dist/styles/navigation-layout.css +3 -1
- package/dist/styles/shell-layout.css +2 -0
- package/dist/styles/table-layout.css +50 -9
- package/dist/tokens/components/attachments.css +18 -9
- package/dist/tokens/components/segmented.css +7 -3
- package/dist/tokens/components/table.css +2 -1
- package/docs/COMPOSITION-VS-COMPONENT.md +41 -0
- package/docs/DESIGN-AUTHORITY.md +52 -0
- package/docs/DEVELOPMENT.md +81 -6
- package/docs/assets/service-mark-rose.svg +6 -0
- package/docs/assets/service-mark-teal.svg +5 -0
- package/docs/data-display/service-launcher-card.tsx +232 -92
- package/docs/data-entry/tag-input.tsx +37 -0
- package/docs/layout/flex.tsx +40 -0
- package/docs/roadmap/website-components.md +34 -0
- package/docs/showcase/marketing-page.tsx +54 -45
- package/docs/showcase/table-pagination.tsx +99 -18
- package/docs/showcase/theme-customization.tsx +25 -2
- package/package.json +8 -5
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"concept": "Abstract controlled value.",
|
|
4
|
+
"name": "ValueProp<T = string>",
|
|
5
|
+
"usedBy": [
|
|
6
|
+
"CheckboxGroup",
|
|
7
|
+
"Upload",
|
|
8
|
+
"Cascader",
|
|
9
|
+
"TreeSelect",
|
|
10
|
+
"Tabs",
|
|
11
|
+
"SearchSelect"
|
|
12
|
+
],
|
|
13
|
+
"values": [
|
|
14
|
+
"generic"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"concept": "Abstract uncontrolled initial value.",
|
|
19
|
+
"name": "DefaultValueProp<T = string>",
|
|
20
|
+
"usedBy": [
|
|
21
|
+
"CheckboxGroup",
|
|
22
|
+
"Upload",
|
|
23
|
+
"Cascader",
|
|
24
|
+
"TreeSelect",
|
|
25
|
+
"Tabs"
|
|
26
|
+
],
|
|
27
|
+
"values": [
|
|
28
|
+
"generic"
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"concept": "Callback for abstract value changes. DOM events continue to use onChange.",
|
|
33
|
+
"name": "OnValueChangeProp<T = string>",
|
|
34
|
+
"usedBy": [
|
|
35
|
+
"CheckboxGroup",
|
|
36
|
+
"Upload",
|
|
37
|
+
"Cascader",
|
|
38
|
+
"TreeSelect",
|
|
39
|
+
"Transfer",
|
|
40
|
+
"settings pickers"
|
|
41
|
+
],
|
|
42
|
+
"values": [
|
|
43
|
+
"(value: T) => void"
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"concept": "Disclosure state.",
|
|
48
|
+
"name": "OpenProp / DefaultOpenProp / OnOpenChangeProp",
|
|
49
|
+
"usedBy": [
|
|
50
|
+
"Dialog",
|
|
51
|
+
"Sheet",
|
|
52
|
+
"Popover"
|
|
53
|
+
],
|
|
54
|
+
"values": [
|
|
55
|
+
"boolean",
|
|
56
|
+
"(open: boolean) => void"
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"concept": "Shared public size names.",
|
|
61
|
+
"name": "SizeProp",
|
|
62
|
+
"notes": "Component-specific subsets must be documented. Old alias small is sm.",
|
|
63
|
+
"usedBy": [
|
|
64
|
+
"Button",
|
|
65
|
+
"Steps",
|
|
66
|
+
"Switch"
|
|
67
|
+
],
|
|
68
|
+
"values": [
|
|
69
|
+
"xs",
|
|
70
|
+
"sm",
|
|
71
|
+
"md",
|
|
72
|
+
"lg"
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"concept": "Semantic status/color intent.",
|
|
77
|
+
"name": "ToneProp",
|
|
78
|
+
"notes": "Status values belong in tone, not variant.",
|
|
79
|
+
"usedBy": [
|
|
80
|
+
"Badge",
|
|
81
|
+
"Alert"
|
|
82
|
+
],
|
|
83
|
+
"values": [
|
|
84
|
+
"default",
|
|
85
|
+
"success",
|
|
86
|
+
"warning",
|
|
87
|
+
"destructive",
|
|
88
|
+
"info",
|
|
89
|
+
"muted",
|
|
90
|
+
"neutral"
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"concept": "Shared layout gap scale.",
|
|
95
|
+
"name": "GapProp",
|
|
96
|
+
"notes": "The single shared gap scale on Flex — the one layout primitive (default direction=\"row\"; use direction=\"col\" for vertical rhythm; these replaced the removed Stack/Inline).",
|
|
97
|
+
"usedBy": [
|
|
98
|
+
"Flex"
|
|
99
|
+
],
|
|
100
|
+
"values": [
|
|
101
|
+
"xs",
|
|
102
|
+
"sm",
|
|
103
|
+
"md",
|
|
104
|
+
"lg",
|
|
105
|
+
"xl"
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"concept": "Primary heading text.",
|
|
110
|
+
"name": "TitleProp",
|
|
111
|
+
"usedBy": [
|
|
112
|
+
"PageContainer",
|
|
113
|
+
"PageHeader",
|
|
114
|
+
"EmptyState",
|
|
115
|
+
"Dialog"
|
|
116
|
+
],
|
|
117
|
+
"values": [
|
|
118
|
+
"React.ReactNode"
|
|
119
|
+
]
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"concept": "Page/subtree density.",
|
|
123
|
+
"name": "DensityProp",
|
|
124
|
+
"usedBy": [
|
|
125
|
+
"PageContainer"
|
|
126
|
+
],
|
|
127
|
+
"values": [
|
|
128
|
+
"compact",
|
|
129
|
+
"default",
|
|
130
|
+
"comfortable"
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"concept": "Named whole-page SHELL contract for CenteredShell — the page measure, section rhythm, chrome flatness and hero heading tier, all owned by component tokens.",
|
|
135
|
+
"name": "CenteredShellPresetProp",
|
|
136
|
+
"notes": "public-landing is the PUBLIC marketing/product landing geometry: one 67.5rem measure shared by the header bar, the centred column and the footer, the section rhythm, flat elevation-free cards and the hero h1 tier. \"default\" emits NO data-preset at all, so the existing shell box is untouched. Pair with `Flex hideBelow`/`hideFrom` for header region visibility — a landing must never own a page-local media query or a max-width wrapper.",
|
|
137
|
+
"usedBy": [
|
|
138
|
+
"CenteredShell"
|
|
139
|
+
],
|
|
140
|
+
"values": [
|
|
141
|
+
"default",
|
|
142
|
+
"public-landing"
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"concept": "Named collection contract for a table — how a dense collection behaves when its container gets narrow.",
|
|
147
|
+
"name": "TablePresetProp",
|
|
148
|
+
"notes": "action-collection swaps the desktop intrinsic column widths for table-layout: fixed + the token-owned column PRIORITY measures below `collapseBelow`, so a five-column approval queue keeps every column — including row actions — inside a 390px frame with no page-local CSS, no hidden column and no horizontal scroll. It changes the SIZING model only: no display change, no role rewriting, no card transformation, so table semantics and screen-reader navigation are identical at every width. ONE contract, two entry points: the `Table` primitive (mark `priority` on each TableHead/TableCell yourself) and the TanStack `DataTable` (mark `priority` on the ColumnDef; it stamps both cells for you, and additionally releases the surface's `--table-surface-min-inline-size` floor). Both read the same `--table-action-collection-*` tokens — there is no parallel DataTable family.",
|
|
149
|
+
"usedBy": [
|
|
150
|
+
"Table",
|
|
151
|
+
"DataTable"
|
|
152
|
+
],
|
|
153
|
+
"values": [
|
|
154
|
+
"default",
|
|
155
|
+
"action-collection"
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"concept": "Relative importance of a table column, used by the action-collection preset to allocate the narrow-frame measure.",
|
|
160
|
+
"name": "TableColumnPriorityProp",
|
|
161
|
+
"notes": "On the raw `Table`, set it on BOTH the TableHead and the TableCell of a column; on `DataTable` set it once on the `ColumnDef` and it is stamped onto the <th> and every <td>. `actions` reserves its measure first so the row-action affordance can never be pushed outside the viewport; a column with no priority takes the remaining space (the free-text column).",
|
|
162
|
+
"usedBy": [
|
|
163
|
+
"Table",
|
|
164
|
+
"DataTable"
|
|
165
|
+
],
|
|
166
|
+
"values": [
|
|
167
|
+
"primary",
|
|
168
|
+
"secondary",
|
|
169
|
+
"meta",
|
|
170
|
+
"actions"
|
|
171
|
+
]
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"concept": "Named hosted-identity FLOW MEASURE for AuthShell — the card max-width plus the desktop and mobile page gutters, all owned by component tokens.",
|
|
175
|
+
"name": "AuthShellPresetProp",
|
|
176
|
+
"notes": "Orthogonal to AuthShell's `variant` (which owns control density + heading size) and applied after it, so `variant=\"canonical\" preset=\"device-authorization\"` composes. device-authorization = 380px card / 5px inline gutter at 390; context-selection = 25rem card, edge-to-edge on mobile. Selecting a preset REPLACES any consumer-side --auth-shell-card-max-width override — a page-local width is the anti-pattern these presets exist to remove.",
|
|
177
|
+
"usedBy": [
|
|
178
|
+
"AuthShell"
|
|
179
|
+
],
|
|
180
|
+
"values": [
|
|
181
|
+
"default",
|
|
182
|
+
"device-authorization",
|
|
183
|
+
"context-selection"
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"concept": "Avatar geometry — WHAT the identity mark represents: a person or an entity. Deliberately NOT the control ShapeProp (default|pill|sharp), whose `sharp` is --radius-sharp: 0 and cannot express the rounded rect an entity mark needs.",
|
|
188
|
+
"name": "AvatarShapeProp",
|
|
189
|
+
"notes": "circle (default, inert) = the round --radius-pill person avatar on the muted surface; square = the entity-header organization/service mark — a compact rounded square on the brand surface driven by --avatar-square-{radius,size,background,foreground}. Choose by meaning, never by look: a className=\"rounded-md bg-primary\" override on the call site is the anti-pattern this prop removes.",
|
|
190
|
+
"usedBy": [
|
|
191
|
+
"Avatar"
|
|
192
|
+
],
|
|
193
|
+
"values": [
|
|
194
|
+
"circle",
|
|
195
|
+
"square"
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
]
|
|
@@ -28,7 +28,26 @@ import type { LucideIcon } from "lucide-react";
|
|
|
28
28
|
import type { HeadingLevelProp, ToneProp } from "../../props/vocabulary/index.js";
|
|
29
29
|
export type ServiceLauncherStatusTone = Extract<ToneProp, "success" | "warning" | "destructive" | "info" | "neutral" | "muted">;
|
|
30
30
|
export interface ServiceLauncherCardProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
|
31
|
+
/**
|
|
32
|
+
* The generic glyph for this KIND of service. Stays REQUIRED and stays a `LucideIcon`: every
|
|
33
|
+
* service has one, including a service created ten seconds ago that has no logo yet, and it is
|
|
34
|
+
* the thing `logo` falls back to. See the `logo` note for why this was not widened to a union.
|
|
35
|
+
*/
|
|
31
36
|
icon: LucideIcon;
|
|
37
|
+
/**
|
|
38
|
+
* URL of the service's OWN uploaded mark — the PNG/WebP an administrator uploaded (gh#850).
|
|
39
|
+
* When it loads, it replaces `icon` in the medallion, in the glyph's exact box.
|
|
40
|
+
*
|
|
41
|
+
* It is a fallback chain, not a switch: the tile shows `icon` while the URL is in flight, and
|
|
42
|
+
* KEEPS showing `icon` if the URL 404s, is empty, or decodes to nothing. A launcher never
|
|
43
|
+
* degrades to an empty medallion or a torn-page glyph because a logo went missing — which is the
|
|
44
|
+
* only state that reaches production, since a logo path outlives the file it points at.
|
|
45
|
+
*
|
|
46
|
+
* Decorative, and deliberately has no `logoAlt` companion: the medallion is `aria-hidden` and the
|
|
47
|
+
* service NAME sits beside it. A screen reader must not hear "Attendance" twice (WCAG 2.2 · 1.1.1
|
|
48
|
+
* — an image adjacent to text conveying the same thing is decorative).
|
|
49
|
+
*/
|
|
50
|
+
logo?: string;
|
|
32
51
|
title: React.ReactNode;
|
|
33
52
|
titleLevel?: HeadingLevelProp;
|
|
34
53
|
statusLabel?: React.ReactNode;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Plus } from "lucide-react";
|
|
5
|
+
import { useImageLoadingStatus } from "../../lib/image-loading-status.js";
|
|
5
6
|
import { cn } from "../../lib/utils.js";
|
|
6
7
|
import { Badge } from "./badge.js";
|
|
7
8
|
import { Card, CardContent } from "./card.js";
|
|
@@ -9,6 +10,7 @@ const ServiceLauncherCard = React.forwardRef(
|
|
|
9
10
|
({
|
|
10
11
|
className,
|
|
11
12
|
icon: Icon,
|
|
13
|
+
logo,
|
|
12
14
|
title,
|
|
13
15
|
titleLevel = 2,
|
|
14
16
|
statusLabel,
|
|
@@ -20,6 +22,11 @@ const ServiceLauncherCard = React.forwardRef(
|
|
|
20
22
|
...props
|
|
21
23
|
}, ref) => {
|
|
22
24
|
const Heading = `h${titleLevel}`;
|
|
25
|
+
const [logoStatus, setLogoStatus] = React.useState("idle");
|
|
26
|
+
const logoLoadingStatus = useImageLoadingStatus(logo || void 0, {
|
|
27
|
+
loadingStatus: logoStatus,
|
|
28
|
+
setLoadingStatus: setLogoStatus
|
|
29
|
+
});
|
|
23
30
|
return /* @__PURE__ */ jsx(
|
|
24
31
|
Card,
|
|
25
32
|
{
|
|
@@ -32,7 +39,13 @@ const ServiceLauncherCard = React.forwardRef(
|
|
|
32
39
|
children: /* @__PURE__ */ jsxs(CardContent, { solo: true, children: [
|
|
33
40
|
/* @__PURE__ */ jsxs("div", { "data-slot": "service-launcher-heading", children: [
|
|
34
41
|
/* @__PURE__ */ jsxs("div", { "data-slot": "service-launcher-identity", children: [
|
|
35
|
-
/* @__PURE__ */ jsx("span", { "data-slot": "service-launcher-icon", "aria-hidden": "true", children:
|
|
42
|
+
/* @__PURE__ */ jsx("span", { "data-slot": "service-launcher-icon", "aria-hidden": "true", children: logoLoadingStatus === "loaded" ? (
|
|
43
|
+
/* No `loading="lazy"`: the probe above has ALREADY fetched this URL by the time
|
|
44
|
+
* this element exists, so a lazy hint would describe a request that is over.
|
|
45
|
+
* `decoding="async"` is the hint that still means something here — it keeps the
|
|
46
|
+
* decode of a grid of logos off the main thread. */
|
|
47
|
+
/* @__PURE__ */ jsx("img", { "data-slot": "service-launcher-logo", src: logo, alt: "", decoding: "async" })
|
|
48
|
+
) : /* @__PURE__ */ jsx(Icon, {}) }),
|
|
36
49
|
/* @__PURE__ */ jsx(Heading, { "data-slot": "service-launcher-title", children: title })
|
|
37
50
|
] }),
|
|
38
51
|
statusLabel != null ? /* @__PURE__ */ jsx(Badge, { tone: statusTone, "data-slot": "service-launcher-status", children: statusLabel }) : null
|
|
@@ -6,7 +6,6 @@ import { useTranslation } from "../../i18n/use-translation.js";
|
|
|
6
6
|
import { formatBytes } from "../../lib/format.js";
|
|
7
7
|
import { cn } from "../../lib/utils.js";
|
|
8
8
|
import { Button } from "../general/button.js";
|
|
9
|
-
import { Text } from "../general/typography.js";
|
|
10
9
|
import { Progress } from "../data-display/progress.js";
|
|
11
10
|
import { createUploadItem } from "./upload-types.js";
|
|
12
11
|
import { readDroppedFiles } from "./upload-files.js";
|
|
@@ -30,6 +29,10 @@ function resolvePlaceholder(placeholder, type) {
|
|
|
30
29
|
if (!placeholder) return {};
|
|
31
30
|
return typeof placeholder === "function" ? placeholder(type) : placeholder;
|
|
32
31
|
}
|
|
32
|
+
function splitFileName(name) {
|
|
33
|
+
const match = /^(.*)\.[^.]+$/.exec(name ?? "");
|
|
34
|
+
return match ? [match[1], (name ?? "").slice(match[1].length)] : [name ?? "", ""];
|
|
35
|
+
}
|
|
33
36
|
function fileMatchesAccept(file, accept) {
|
|
34
37
|
if (!accept) return true;
|
|
35
38
|
return accept.split(",").some((rule) => {
|
|
@@ -60,6 +63,7 @@ function PlaceholderUploader({
|
|
|
60
63
|
className: cn("ui-attachments-placeholder", className),
|
|
61
64
|
style,
|
|
62
65
|
"data-drag-active": dragActive ? "true" : void 0,
|
|
66
|
+
"data-disabled": disabled ? "true" : void 0,
|
|
63
67
|
"aria-hidden": disabled ? true : void 0,
|
|
64
68
|
onDragEnter: (event) => {
|
|
65
69
|
event.preventDefault();
|
|
@@ -71,6 +75,7 @@ function PlaceholderUploader({
|
|
|
71
75
|
},
|
|
72
76
|
onDrop: async (event) => {
|
|
73
77
|
event.preventDefault();
|
|
78
|
+
event.stopPropagation();
|
|
74
79
|
setDragActive(false);
|
|
75
80
|
if (disabled) return;
|
|
76
81
|
const files = await readDroppedFiles(event.dataTransfer, false);
|
|
@@ -107,27 +112,55 @@ function AttachmentCard({
|
|
|
107
112
|
const { t } = useTranslation();
|
|
108
113
|
const { disabled } = React.useContext(AttachmentContext);
|
|
109
114
|
const preview = item.thumbUrl ?? item.url;
|
|
115
|
+
const [namePrefix, nameSuffix] = splitFileName(item.name);
|
|
110
116
|
const desc = description ?? item.description ?? (item.status === "uploading" ? `${item.percent ?? 0}%` : item.status === "error" ? typeof item.response === "string" ? item.response : t("dataEntry.attachments.error") : item.size ? formatBytes(item.size) : "");
|
|
111
|
-
return /* @__PURE__ */ jsxs(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
117
|
+
return /* @__PURE__ */ jsxs(
|
|
118
|
+
"li",
|
|
119
|
+
{
|
|
120
|
+
className: cn("ui-attachments-card", classNames?.card, classNames?.file),
|
|
121
|
+
"data-status": item.status,
|
|
122
|
+
children: [
|
|
123
|
+
preview ? /* @__PURE__ */ jsx("img", { src: preview, alt: "", className: "ui-attachments-card-preview", "aria-hidden": "true" }) : /* @__PURE__ */ jsx("span", { className: "ui-attachments-card-placeholder", "aria-hidden": "true", children: /* @__PURE__ */ jsx(FileIcon, {}) }),
|
|
124
|
+
/* @__PURE__ */ jsxs("span", { className: "ui-attachments-card-content", children: [
|
|
125
|
+
/* @__PURE__ */ jsxs("span", { className: "ui-attachments-card-name", title: item.name, children: [
|
|
126
|
+
/* @__PURE__ */ jsx("span", { className: "ui-attachments-card-name-prefix", children: namePrefix }),
|
|
127
|
+
/* @__PURE__ */ jsx("span", { className: "ui-attachments-card-name-suffix", children: nameSuffix })
|
|
128
|
+
] }),
|
|
129
|
+
desc ? /* @__PURE__ */ jsx(
|
|
130
|
+
"span",
|
|
131
|
+
{
|
|
132
|
+
className: "ui-attachments-card-meta",
|
|
133
|
+
title: typeof desc === "string" ? desc : void 0,
|
|
134
|
+
children: desc
|
|
135
|
+
}
|
|
136
|
+
) : null
|
|
137
|
+
] }),
|
|
138
|
+
item.status === "uploading" && item.percent != null ? /* @__PURE__ */ jsx("span", { className: "ui-attachments-card-progress", children: /* @__PURE__ */ jsx(
|
|
139
|
+
Progress,
|
|
140
|
+
{
|
|
141
|
+
size: "sm",
|
|
142
|
+
value: item.percent,
|
|
143
|
+
"aria-label": t("dataEntry.attachments.uploading")
|
|
144
|
+
}
|
|
145
|
+
) }) : null,
|
|
146
|
+
!disabled && onRemove ? /* @__PURE__ */ jsx(
|
|
147
|
+
Button,
|
|
148
|
+
{
|
|
149
|
+
type: "button",
|
|
150
|
+
variant: "ghost",
|
|
151
|
+
size: "icon-xs",
|
|
152
|
+
className: "ui-attachments-card-remove",
|
|
153
|
+
"aria-label": t("dataEntry.attachments.remove", { name: item.name }),
|
|
154
|
+
onClick: (event) => {
|
|
155
|
+
event.stopPropagation();
|
|
156
|
+
onRemove();
|
|
157
|
+
},
|
|
158
|
+
children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
|
|
159
|
+
}
|
|
160
|
+
) : null
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
);
|
|
131
164
|
}
|
|
132
165
|
const Attachments = React.forwardRef(
|
|
133
166
|
({
|
|
@@ -151,6 +184,7 @@ const Attachments = React.forwardRef(
|
|
|
151
184
|
const containerRef = React.useRef(null);
|
|
152
185
|
const inputRef = React.useRef(null);
|
|
153
186
|
const [internalItems, setInternalItems] = React.useState([]);
|
|
187
|
+
const [dropActive, setDropActive] = React.useState(false);
|
|
154
188
|
const items = controlledItems ?? internalItems;
|
|
155
189
|
const setItems = React.useCallback(
|
|
156
190
|
(next, file) => {
|
|
@@ -207,7 +241,7 @@ const Attachments = React.forwardRef(
|
|
|
207
241
|
{
|
|
208
242
|
ref: inputRef,
|
|
209
243
|
type: "file",
|
|
210
|
-
className: "ui-attachments-input",
|
|
244
|
+
className: cn("ui-attachments-input", "sr-only"),
|
|
211
245
|
accept,
|
|
212
246
|
multiple: multiple ?? (maxCount == null || maxCount > 1),
|
|
213
247
|
disabled,
|
|
@@ -244,7 +278,7 @@ const Attachments = React.forwardRef(
|
|
|
244
278
|
)
|
|
245
279
|
] });
|
|
246
280
|
}
|
|
247
|
-
const
|
|
281
|
+
const dropScope = getDropContainer?.() ? "viewport" : "control";
|
|
248
282
|
return /* @__PURE__ */ jsx(AttachmentContext.Provider, { value: { disabled }, children: /* @__PURE__ */ jsxs(
|
|
249
283
|
"div",
|
|
250
284
|
{
|
|
@@ -252,6 +286,21 @@ const Attachments = React.forwardRef(
|
|
|
252
286
|
className: cn("ui-attachments", className, rootClassName, classNames.root),
|
|
253
287
|
style: styles.root,
|
|
254
288
|
dir: "auto",
|
|
289
|
+
onDragEnter: (event) => {
|
|
290
|
+
event.preventDefault();
|
|
291
|
+
if (!disabled) setDropActive(true);
|
|
292
|
+
},
|
|
293
|
+
onDragOver: (event) => event.preventDefault(),
|
|
294
|
+
onDragLeave: (event) => {
|
|
295
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setDropActive(false);
|
|
296
|
+
},
|
|
297
|
+
onDrop: async (event) => {
|
|
298
|
+
event.preventDefault();
|
|
299
|
+
setDropActive(false);
|
|
300
|
+
if (disabled) return;
|
|
301
|
+
const files = await readDroppedFiles(event.dataTransfer, false);
|
|
302
|
+
if (files.length) dispatchFiles(files);
|
|
303
|
+
},
|
|
255
304
|
children: [
|
|
256
305
|
fileInput,
|
|
257
306
|
hasFiles ? /* @__PURE__ */ jsxs(
|
|
@@ -297,20 +346,15 @@ const Attachments = React.forwardRef(
|
|
|
297
346
|
hidden: hasFiles
|
|
298
347
|
}
|
|
299
348
|
),
|
|
300
|
-
|
|
349
|
+
/* @__PURE__ */ jsx(
|
|
301
350
|
"div",
|
|
302
351
|
{
|
|
303
352
|
className: "ui-attachments-drop-layer",
|
|
304
|
-
"data-
|
|
305
|
-
"
|
|
306
|
-
|
|
307
|
-
onDrop: async (event) => {
|
|
308
|
-
event.preventDefault();
|
|
309
|
-
const files = await readDroppedFiles(event.dataTransfer, false);
|
|
310
|
-
dispatchFiles(files);
|
|
311
|
-
}
|
|
353
|
+
"data-scope": dropScope,
|
|
354
|
+
"data-active": dropActive ? "true" : void 0,
|
|
355
|
+
"aria-hidden": "true"
|
|
312
356
|
}
|
|
313
|
-
)
|
|
357
|
+
)
|
|
314
358
|
]
|
|
315
359
|
}
|
|
316
360
|
) });
|
|
@@ -13,7 +13,14 @@ import {
|
|
|
13
13
|
} from "./control-appearance.js";
|
|
14
14
|
import { resolveAllowClear } from "./control-surface.js";
|
|
15
15
|
const inputBaseClass = [
|
|
16
|
-
|
|
16
|
+
// PER-EDGE RADIUS THROUGH A KNOB, because a joined control has to square one side (gh#841).
|
|
17
|
+
// `rounded-[var(--control-radius)]` is a UTILITY, and Tailwind v4 orders utilities after
|
|
18
|
+
// `@layer components` — so `.ui-input-group:has(…) .ui-input { border-start-start-radius: 0 }`
|
|
19
|
+
// resolved, was catalogued, and lost: the field kept 6px on its leading corners and drew a
|
|
20
|
+
// rounded edge exactly where the addon meets it. Fifth instance of the gh#366/#375/#819 trap.
|
|
21
|
+
// The repair is gh#366's: emit the value FROM a token as a utility, and let the components
|
|
22
|
+
// layer decide WHICH token — a custom property has no utility competing with it.
|
|
23
|
+
"ui-control ui-input w-full rounded-s-[var(--input-radius-start,var(--control-radius))] rounded-e-[var(--input-radius-end,var(--control-radius))] transition-[color,box-shadow]",
|
|
17
24
|
"selection:bg-primary selection:text-primary-foreground",
|
|
18
25
|
"placeholder:text-muted-foreground",
|
|
19
26
|
"aria-invalid:border-destructive",
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { FlexProp } from "../../props/components/layout.prop.js";
|
|
2
|
-
export type { FlexAlignProp, FlexDirectionProp, FlexJustifyProp, FlexMarkerProp, FlexProp, FlexProp as FlexProps, } from "../../props/components/layout.prop.js";
|
|
3
|
-
export declare function Flex({ as: Element, direction, marker, grow, shrink, surface, bleed, reveal, gap, gapRaw, pad, padRaw, align, justify, wrap, hideBelow, hideFrom, hideBelowRaw, hideFromRaw, fill, width, className, style, children, ...props }: FlexProp): import("react").JSX.Element;
|
|
2
|
+
export type { FlexAlignProp, FlexDirectionProp, FlexJustifyProp, FlexMarkerProp, FlexMeasureProp, FlexProp, FlexProp as FlexProps, } from "../../props/components/layout.prop.js";
|
|
3
|
+
export declare function Flex({ as: Element, direction, marker, grow, shrink, surface, bleed, reveal, gap, gapRaw, pad, padRaw, align, justify, wrap, hideBelow, hideFrom, hideBelowRaw, hideFromRaw, fill, width, measure, className, style, children, ...props }: FlexProp): import("react").JSX.Element;
|
|
@@ -33,6 +33,7 @@ function Flex({
|
|
|
33
33
|
hideFromRaw,
|
|
34
34
|
fill = false,
|
|
35
35
|
width,
|
|
36
|
+
measure,
|
|
36
37
|
className,
|
|
37
38
|
style,
|
|
38
39
|
children,
|
|
@@ -81,6 +82,7 @@ function Flex({
|
|
|
81
82
|
"data-hide-from-raw": fromRaw,
|
|
82
83
|
"data-fill": fill ? "" : void 0,
|
|
83
84
|
"data-width-raw": width === void 0 ? void 0 : "",
|
|
85
|
+
"data-measure": measure,
|
|
84
86
|
"data-gap-raw": gapRaw,
|
|
85
87
|
className: cn("ui-flex", gapRaw === void 0 ? flexGapClass[gap] : void 0, className),
|
|
86
88
|
"data-pad-raw": padRaw === void 0 ? void 0 : "",
|
|
@@ -1,22 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import { type ImageLoadingStatus } from "../../lib/image-loading-status.js";
|
|
2
3
|
import type { AvatarProp } from "../../props/components/data-display.prop.js";
|
|
3
|
-
/**
|
|
4
|
-
* Trạng thái tải của ảnh trong avatar — chia sẻ giữa `AvatarImage` và
|
|
5
|
-
* `AvatarFallback` qua context, đúng như Radix làm.
|
|
6
|
-
*
|
|
7
|
-
* React Aria KHÔNG có avatar, nên đây là việc TỰ DỰNG. Nhưng phần cần dựng lại
|
|
8
|
-
* không phải cái vỏ `<span>` — mà là logic thật mà `@radix-ui/react-avatar` cầm:
|
|
9
|
-
* ảnh chỉ được vẽ khi đã tải XONG, fallback chỉ được vẽ khi ảnh CHƯA xong hoặc
|
|
10
|
-
* HỎNG, và `delayMs` giữ fallback im lặng đủ lâu để mạng nhanh không kịp nháy
|
|
11
|
-
* một khung initials trước khi ảnh về.
|
|
12
|
-
*
|
|
13
|
-
* Phép dò là một `new Image()` RỜI, không phải `onLoad`/`onError` trên chính
|
|
14
|
-
* thẻ được vẽ: thẻ chỉ tồn tại khi đã `loaded`, nên handler trên nó không bao
|
|
15
|
-
* giờ có cơ hội báo "đang tải" hay "hỏng". Đây cũng là lý do một `<img>` hỏng
|
|
16
|
-
* không bao giờ nằm trong DOM để trình duyệt vẽ biểu tượng ảnh vỡ lên trên
|
|
17
|
-
* fallback.
|
|
18
|
-
*/
|
|
19
|
-
type ImageLoadingStatus = "idle" | "loading" | "loaded" | "error";
|
|
20
4
|
export declare const Avatar: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & {
|
|
21
5
|
asChild?: boolean;
|
|
22
6
|
} & Pick<AvatarProp, "appearance" | "presence" | "presenceLabel" | "shape" | "size"> & React.RefAttributes<HTMLSpanElement>>;
|
|
@@ -28,4 +12,3 @@ export declare const AvatarFallback: React.ForwardRefExoticComponent<Omit<React.
|
|
|
28
12
|
asChild?: boolean;
|
|
29
13
|
delayMs?: number;
|
|
30
14
|
} & React.RefAttributes<HTMLSpanElement>>;
|
|
31
|
-
export {};
|
|
@@ -3,6 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useEffectEvent, useLayoutEffect } from "@react-aria/utils";
|
|
4
4
|
import * as React from "react";
|
|
5
5
|
import { useTranslation } from "../../i18n/use-translation.js";
|
|
6
|
+
import { useImageLoadingStatus } from "../../lib/image-loading-status.js";
|
|
6
7
|
import { Slot } from "../../lib/slot.js";
|
|
7
8
|
import { cn } from "../../lib/utils.js";
|
|
8
9
|
const AvatarContext = React.createContext(null);
|
|
@@ -13,42 +14,6 @@ function useAvatarContext(part) {
|
|
|
13
14
|
}
|
|
14
15
|
return context;
|
|
15
16
|
}
|
|
16
|
-
function getImageLoadingStatus(image) {
|
|
17
|
-
if (!image.complete) {
|
|
18
|
-
return "loading";
|
|
19
|
-
}
|
|
20
|
-
return image.naturalWidth > 0 ? "loaded" : "error";
|
|
21
|
-
}
|
|
22
|
-
function useImageLoadingStatus(src, {
|
|
23
|
-
loadingStatus,
|
|
24
|
-
setLoadingStatus,
|
|
25
|
-
referrerPolicy,
|
|
26
|
-
crossOrigin
|
|
27
|
-
}) {
|
|
28
|
-
useLayoutEffect(() => {
|
|
29
|
-
if (!src) {
|
|
30
|
-
setLoadingStatus("error");
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
const image = new window.Image();
|
|
34
|
-
const handleLoad = (event) => setLoadingStatus(getImageLoadingStatus(event.currentTarget));
|
|
35
|
-
const handleError = () => setLoadingStatus("error");
|
|
36
|
-
image.addEventListener("load", handleLoad);
|
|
37
|
-
image.addEventListener("error", handleError);
|
|
38
|
-
if (referrerPolicy) {
|
|
39
|
-
image.referrerPolicy = referrerPolicy;
|
|
40
|
-
}
|
|
41
|
-
image.crossOrigin = crossOrigin ?? null;
|
|
42
|
-
image.src = src;
|
|
43
|
-
setLoadingStatus(getImageLoadingStatus(image));
|
|
44
|
-
return () => {
|
|
45
|
-
image.removeEventListener("load", handleLoad);
|
|
46
|
-
image.removeEventListener("error", handleError);
|
|
47
|
-
setLoadingStatus("idle");
|
|
48
|
-
};
|
|
49
|
-
}, [src, crossOrigin, referrerPolicy, setLoadingStatus]);
|
|
50
|
-
return loadingStatus;
|
|
51
|
-
}
|
|
52
17
|
const Avatar = React.forwardRef(
|
|
53
18
|
({
|
|
54
19
|
className,
|
|
@@ -40,6 +40,16 @@ export type TagInputProps = {
|
|
|
40
40
|
maxTagCount?: MaxTagCountProp;
|
|
41
41
|
/** The node standing in for what `maxTagCount` hid (antd `maxTagPlaceholder`). */
|
|
42
42
|
maxTagPlaceholder?: MaxTagPlaceholderProp;
|
|
43
|
+
/**
|
|
44
|
+
* antd `maxTagTextLength` — cut each chip's TEXT to this many characters; the value is
|
|
45
|
+
* untouched and stays in `title` and in the remover's accessible name.
|
|
46
|
+
*
|
|
47
|
+
* It was declared on the public prop type and never destructured here, so a 71-character
|
|
48
|
+
* identifier set the chip's width and the chip set the row's, painting outside the control
|
|
49
|
+
* (gh#840). Declared and inert is the same defect as absent, with a worse failure mode:
|
|
50
|
+
* a consumer reads the prop, passes it, and watches nothing happen.
|
|
51
|
+
*/
|
|
52
|
+
maxTagTextLength?: number;
|
|
43
53
|
/** Render one chip yourself (antd `tagRender`) — receives the value and an `onClose` remover. */
|
|
44
54
|
tagRender?: (props: {
|
|
45
55
|
value: string;
|
|
@@ -30,6 +30,7 @@ const TagInput = React.forwardRef(
|
|
|
30
30
|
onClear,
|
|
31
31
|
maxTagCount,
|
|
32
32
|
maxTagPlaceholder,
|
|
33
|
+
maxTagTextLength,
|
|
33
34
|
tagRender,
|
|
34
35
|
tokenSeparators = [","]
|
|
35
36
|
}, ref) => {
|
|
@@ -95,6 +96,12 @@ const TagInput = React.forwardRef(
|
|
|
95
96
|
disabled && "ui-tag-input-disabled",
|
|
96
97
|
className
|
|
97
98
|
),
|
|
99
|
+
onMouseDown: (event) => {
|
|
100
|
+
if (event.target !== event.currentTarget) return;
|
|
101
|
+
event.preventDefault();
|
|
102
|
+
const field = event.currentTarget.querySelector(".ui-tag-input-field");
|
|
103
|
+
field?.focus();
|
|
104
|
+
},
|
|
98
105
|
children: [
|
|
99
106
|
tags.length > 0 ? /* @__PURE__ */ jsxs("ul", { role: "list", className: "ui-tag-input-list", "data-slot": "tag-input-list", children: [
|
|
100
107
|
visibleChips.map((chip) => /* @__PURE__ */ jsx(
|
|
@@ -110,7 +117,14 @@ const TagInput = React.forwardRef(
|
|
|
110
117
|
disabled: Boolean(disabled) || Boolean(readOnly),
|
|
111
118
|
onClose: () => removeAt(chip.index)
|
|
112
119
|
}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
113
|
-
|
|
120
|
+
/* @__PURE__ */ jsx(
|
|
121
|
+
"span",
|
|
122
|
+
{
|
|
123
|
+
className: "ui-tag-input-chip-label",
|
|
124
|
+
title: maxTagTextLength && chip.value.length > maxTagTextLength ? chip.value : void 0,
|
|
125
|
+
children: maxTagTextLength && chip.value.length > maxTagTextLength ? `${chip.value.slice(0, maxTagTextLength)}\u2026` : chip.value
|
|
126
|
+
}
|
|
127
|
+
),
|
|
114
128
|
!disabled && !readOnly ? /* @__PURE__ */ jsx(
|
|
115
129
|
"button",
|
|
116
130
|
{
|
|
@@ -125,7 +139,26 @@ const TagInput = React.forwardRef(
|
|
|
125
139
|
},
|
|
126
140
|
chip.value
|
|
127
141
|
)),
|
|
128
|
-
omittedChips.length > 0 ?
|
|
142
|
+
omittedChips.length > 0 ? (
|
|
143
|
+
/* `+N` NAMES WHAT IT HIDES (gh#840). It used to be a bare `<li role="listitem">` with
|
|
144
|
+
no title, no tabindex and no role — so a reader who saw `+1` had no way, by mouse
|
|
145
|
+
or by keyboard, to learn which tag it stood for. antd's `maxTagPlaceholder` exists
|
|
146
|
+
for exactly this; the default now carries the omitted values itself. */
|
|
147
|
+
/* @__PURE__ */ jsx(
|
|
148
|
+
"li",
|
|
149
|
+
{
|
|
150
|
+
role: "listitem",
|
|
151
|
+
className: "ui-tag-input-chip",
|
|
152
|
+
"data-slot": "tag-input-overflow",
|
|
153
|
+
tabIndex: 0,
|
|
154
|
+
title: omittedChips.map((c) => c.value).join(", "),
|
|
155
|
+
"aria-label": t("ui.tagInput.overflowLabel", {
|
|
156
|
+
tags: omittedChips.map((c) => c.value).join(", ")
|
|
157
|
+
}),
|
|
158
|
+
children: overflow ?? t("dataEntry.selection.overflow", { count: omittedChips.length })
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
) : null
|
|
129
162
|
] }) : null,
|
|
130
163
|
/* @__PURE__ */ jsx(
|
|
131
164
|
"input",
|