@godxjp/ui 19.6.0 → 20.1.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/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 +14 -3
- package/dist/components/data-display/card.js +22 -4
- package/dist/components/data-display/code-block.js +6 -2
- package/dist/components/data-display/collapsible.d.ts +23 -4
- package/dist/components/data-display/collapsible.js +119 -4
- 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 +5 -1
- package/dist/components/data-display/index.js +4 -0
- package/dist/components/data-display/legend.d.ts +15 -0
- package/dist/components/data-display/legend.js +11 -0
- package/dist/components/data-display/list-row.d.ts +10 -1
- package/dist/components/data-display/list-row.js +8 -2
- package/dist/components/data-display/popover.d.ts +60 -7
- package/dist/components/data-display/popover.js +268 -39
- package/dist/components/data-display/progress.d.ts +50 -2
- package/dist/components/data-display/progress.js +57 -9
- 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 +10 -10
- package/dist/components/data-display/table.d.ts +11 -3
- package/dist/components/data-display/table.js +48 -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 +79 -33
- package/dist/components/data-entry/cascader.d.ts +1 -1
- package/dist/components/data-entry/cascader.js +188 -24
- package/dist/components/data-entry/checkbox.d.ts +19 -6
- package/dist/components/data-entry/checkbox.js +55 -16
- package/dist/components/data-entry/choice-option.d.ts +1 -1
- 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 +2 -2
- package/dist/components/data-entry/date-range-picker.js +278 -140
- package/dist/components/data-entry/field.js +0 -1
- 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 +5 -3
- 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/label.d.ts +3 -2
- package/dist/components/data-entry/label.js +23 -10
- package/dist/components/data-entry/month-picker.d.ts +2 -2
- package/dist/components/data-entry/month-picker.js +47 -10
- package/dist/components/data-entry/month-range-picker.d.ts +2 -2
- 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 +99 -31
- package/dist/components/data-entry/tree-select-strategy.d.ts +1 -1
- package/dist/components/data-entry/tree-select.d.ts +1 -1
- package/dist/components/data-entry/tree-select.js +197 -92
- package/dist/components/data-entry/tree-utils.d.ts +1 -1
- 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 +482 -117
- package/dist/components/feedback/dialog.d.ts +105 -38
- package/dist/components/feedback/dialog.js +268 -193
- package/dist/components/feedback/overlay-close-focus.d.ts +31 -0
- package/dist/components/feedback/overlay-close-focus.js +31 -0
- package/dist/components/feedback/overlay-header-tone.d.ts +1 -1
- package/dist/components/feedback/sheet.d.ts +53 -11
- package/dist/components/feedback/sheet.js +150 -81
- package/dist/components/feedback/tooltip.d.ts +51 -7
- package/dist/components/feedback/tooltip.js +107 -25
- package/dist/components/general/button.d.ts +1 -1
- package/dist/components/general/button.js +7 -3
- package/dist/components/general/index.d.ts +1 -0
- package/dist/components/general/index.js +2 -0
- package/dist/components/general/logo.d.ts +17 -0
- package/dist/components/general/logo.js +22 -16
- 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 +228 -0
- package/dist/components/layout/app-shell.d.ts +3 -1
- package/dist/components/layout/app-shell.js +69 -12
- package/dist/components/layout/aspect-ratio.js +0 -1
- package/dist/components/layout/auth-divider.js +0 -1
- 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 +41 -3
- package/dist/components/layout/index.d.ts +3 -0
- package/dist/components/layout/index.js +5 -1
- package/dist/components/layout/nav-surface.d.ts +26 -0
- package/dist/components/layout/nav-surface.js +17 -0
- package/dist/components/layout/org-switcher.d.ts +5 -1
- package/dist/components/layout/org-switcher.js +25 -4
- 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/separator.js +0 -1
- package/dist/components/layout/sidebar.js +4 -1
- package/dist/components/layout/split-pane.d.ts +14 -1
- package/dist/components/layout/split-pane.js +26 -13
- package/dist/components/layout/topbar-item.d.ts +3 -0
- package/dist/components/layout/topbar-item.js +19 -5
- package/dist/components/layout/topbar.d.ts +1 -1
- package/dist/components/layout/topbar.js +27 -6
- package/dist/components/navigation/app-setting-picker.js +21 -2
- package/dist/components/navigation/app-setting-toggle.d.ts +16 -0
- package/dist/components/navigation/app-setting-toggle.js +96 -0
- package/dist/components/navigation/dropdown-menu.d.ts +227 -18
- package/dist/components/navigation/dropdown-menu.js +402 -118
- package/dist/components/navigation/index.d.ts +2 -0
- package/dist/components/navigation/index.js +2 -0
- package/dist/components/navigation/pagination-utils.d.ts +2 -1
- 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 +29 -16
- package/dist/components/navigation/tabs.js +374 -144
- package/dist/components/ui/accordion.d.ts +50 -5
- package/dist/components/ui/accordion.js +239 -33
- package/dist/components/ui/aspect-ratio.d.ts +23 -2
- package/dist/components/ui/aspect-ratio.js +15 -13
- package/dist/components/ui/avatar.d.ts +29 -4
- package/dist/components/ui/avatar.js +111 -25
- package/dist/components/ui/hover-card.d.ts +42 -4
- package/dist/components/ui/hover-card.js +183 -27
- package/dist/components/ui/input-otp.d.ts +26 -28
- package/dist/components/ui/input-otp.js +50 -7
- package/dist/components/ui/label.js +0 -1
- 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 +12 -3
- package/dist/components/ui/segmented.js +17 -2
- package/dist/components/ui/separator.d.ts +12 -2
- package/dist/components/ui/separator.js +30 -9
- package/dist/components/ui/tag-input.d.ts +45 -0
- package/dist/components/ui/tag-input.js +109 -27
- package/dist/components/ui/toggle-group.d.ts +50 -5
- package/dist/components/ui/toggle-group.js +79 -20
- package/dist/components/ui/toggle.d.ts +31 -5
- package/dist/components/ui/toggle.js +42 -3
- 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/messages/en.json +64 -5
- package/dist/i18n/messages/ja.json +64 -5
- package/dist/i18n/messages/vi.json +64 -5
- 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/slot.d.ts +32 -0
- package/dist/lib/slot.js +22 -0
- package/dist/lib/variants.d.ts +22 -3
- package/dist/lib/variants.js +56 -1
- package/dist/props/components/app.prop.d.ts +25 -1
- package/dist/props/components/charts.prop.d.ts +21 -0
- package/dist/props/components/data-display.prop.d.ts +72 -4
- package/dist/props/components/data-entry.prop.d.ts +718 -46
- package/dist/props/components/form.prop.d.ts +142 -4
- package/dist/props/components/general.prop.d.ts +17 -1
- package/dist/props/components/index.d.ts +3 -3
- package/dist/props/components/layout.prop.d.ts +281 -11
- package/dist/props/components/navigation.prop.d.ts +114 -4
- package/dist/props/registry.d.ts +397 -11
- package/dist/props/registry.js +526 -11
- package/dist/props/vocabulary/content.prop.d.ts +1 -1
- package/dist/props/vocabulary/data.prop.d.ts +179 -1
- package/dist/props/vocabulary/index.d.ts +5 -5
- package/dist/props/vocabulary/interaction.prop.d.ts +68 -3
- package/dist/props/vocabulary/layout.prop.d.ts +57 -1
- 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 +26 -4
- package/dist/styles/chart-layout.css +15 -0
- package/dist/styles/control.css +657 -15
- package/dist/styles/core.css +5 -2
- package/dist/styles/data-display-layout.css +313 -6
- package/dist/styles/data-entry-layout.css +13 -0
- package/dist/styles/focus-ring.css +4 -0
- package/dist/styles/index.css +5 -2
- package/dist/styles/layout.css +327 -4
- package/dist/styles/navigation-layout.css +227 -0
- package/dist/styles/shell-layout.css +369 -13
- package/dist/styles/table-layout.css +95 -6
- package/dist/styles/text-layout.css +10 -4
- package/dist/tokens/base.css +2 -1
- package/dist/tokens/components/badge.css +1 -0
- package/dist/tokens/components/chart.css +6 -0
- package/dist/tokens/components/control.css +79 -2
- package/dist/tokens/components/data-display.css +20 -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 +53 -3
- 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/COMPONENTS.md +9 -3
- package/docs/CONSUMER-RULES.md +13 -0
- package/docs/DESIGN-AUTHORITY.md +145 -83
- package/docs/DEVELOPMENT.md +4 -3
- package/docs/FORMS.md +151 -6
- package/docs/FRAME-COVERAGE-REPORT.md +32 -17
- package/docs/README.md +15 -15
- package/docs/STANDARDS-vocabulary-tokens.md +1 -1
- package/docs/TESTING.md +15 -6
- package/docs/WHAT-BELONGS-HERE.md +179 -0
- 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/legend.tsx +145 -0
- package/docs/data-display/progress.tsx +32 -0
- 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/segmented.tsx +1 -1
- package/docs/data-entry/select.tsx +29 -2
- 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/sheet.tsx +1 -1
- 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 +151 -0
- package/docs/layout/app-shell-arrangements.tsx +225 -0
- package/docs/layout/app-shell.tsx +11 -0
- package/docs/layout/aspect-ratio.tsx +1 -1
- 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 +37 -1
- package/docs/navigation/app-setting-toggle.tsx +111 -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/package.json +28 -8
- package/scripts/_agent-setup.mjs +182 -3
- package/scripts/consumer-rule.md +98 -0
- package/scripts/guinea-pig-skill.md +322 -0
- package/scripts/init-guinea-pig.mjs +84 -0
- package/scripts/postinstall.mjs +13 -2
- package/scripts/ui-audit.mjs +351 -37
- /package/dist/tokens/{antd.generated.css → derived.css} +0 -0
package/docs/DESIGN-AUTHORITY.md
CHANGED
|
@@ -17,9 +17,9 @@ It changes no code by itself. It is the tie-breaker a reviewer points at.
|
|
|
17
17
|
| Interaction semantics, keyboard, ARIA | **WAI-ARIA APG** | already followed — 33 references in `src/` |
|
|
18
18
|
| Behaviour primitives | **Radix** | already the implementation — 193 references |
|
|
19
19
|
| Component composition shape | **shadcn** | already the structural convention — 23 references |
|
|
20
|
-
| Component taxonomy / grouping | **Ant Design** groups | already the catalog shape: `data-entry`, `data-display`, `layout`, `feedback`, `navigation`, `general`
|
|
20
|
+
| Component taxonomy / grouping | **Ant Design** groups | already the catalog shape: `data-entry`, `data-display`, `layout`, `feedback`, `navigation`, `general` — a naming precedent, nothing is installed |
|
|
21
21
|
| Colour foundation | **SmartHR** | already the palette source — `--primary` = SmartHR MAIN `#0071bd`, `--foreground` = TEXT_BLACK, `--border` = BORDER |
|
|
22
|
-
| **Derived colour —
|
|
22
|
+
| **Derived colour — the interaction states hanging off each seed** | **Measured contrast (WCAG 2.2 / JIS X 8341-3)** | Authored in `src/tokens/derived.css`; no algorithm derives them. Four contrast suites read that file and hold every value to a threshold — see below |
|
|
23
23
|
| **Japanese UI convention — density, JP typography, form patterns** | **SmartHR** | **NEW — this decision.** Extends SmartHR from "where the colours came from" to the authority for how a JP business screen behaves |
|
|
24
24
|
| **Japanese accessibility / public-sector convention** | **デジタル庁 Design System** (Digital Agency) | **NEW — this decision.** The reference when a JP customer asks which standard a screen meets (JIS X 8341-3) |
|
|
25
25
|
| **Spacing, density, type scale, information architecture** | **IBM Carbon** | **NEW — this decision** |
|
|
@@ -187,70 +187,131 @@ Recorded rather than silently fixed, because each is a real decision:
|
|
|
187
187
|
strokes blur far more visibly than Latin letterforms. Another reason to prefer Carbon's integer
|
|
188
188
|
steps if the scale is ever revisited.
|
|
189
189
|
|
|
190
|
-
##
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
`
|
|
202
|
-
own
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
190
|
+
## The PROP SURFACE of a component is antd's too, not just its group name
|
|
191
|
+
|
|
192
|
+
antd was already the authority for which components exist and which group they live in. That
|
|
193
|
+
answered "is there a Table" and never answered "what does a Table take". So every prop was decided
|
|
194
|
+
here, one at a time, and the answer drifted per component: `DataTable` grew `pin: "end"` where antd
|
|
195
|
+
has `fixed`, `sortable: true` where antd has `sorter`, and no answer at all for filters, expandable
|
|
196
|
+
rows or a totals row — which is how a consumer ends up hand-rolling a `<tfoot>` and a sticky column
|
|
197
|
+
in page CSS.
|
|
198
|
+
|
|
199
|
+
**The rule: where antd names a capability, this library takes antd's name and antd's semantics.**
|
|
200
|
+
The gap is read out of the INSTALLED types (`antd/es/table/interface.d.ts`,
|
|
201
|
+
`antd/es/table/InternalTable.d.ts` and the `@rc-component/table` interface they extend) — never
|
|
202
|
+
from memory, because antd's own names move between majors (`fixed: 'left'` is deprecated in favour
|
|
203
|
+
of `start` inside rc-table itself).
|
|
204
|
+
|
|
205
|
+
> Đọc sau 20.0.0: bản major ấy đã **gỡ `antd` khỏi devDependencies** cùng máy sinh màu của nó, và
|
|
206
|
+
> `check:no-antd-runtime` canh cho nó không quay lại. Câu trên mô tả cách bề mặt prop này ĐƯỢC ĐỌC
|
|
207
|
+
> lúc antd còn cài, không phải một lời mời cài lại. Lần sau muốn đối chiếu, đọc type ở một checkout
|
|
208
|
+
> riêng rồi ghi số hiệu bản vào đây — đừng thêm dependency.
|
|
209
|
+
|
|
210
|
+
**Three things override antd's spelling, each for a stated reason:**
|
|
211
|
+
|
|
212
|
+
- **Logical over physical.** antd's `fixed: 'left' | 'right'` cannot mirror for an RTL locale, so
|
|
213
|
+
only `start` / `end` are published. Same rule that makes `check:rtl` a gate.
|
|
214
|
+
- **This library's controlled vocabulary wins on values.** antd's `SortOrder` is
|
|
215
|
+
`'ascend' | 'descend'`; here it stays `SortDirectionProp` (`asc` / `desc`), because that type
|
|
216
|
+
already exists and a second spelling of the same axis is exactly what `check:prop-vocabulary`
|
|
217
|
+
exists to prevent.
|
|
218
|
+
- **A capability this library already has keeps its own name.** antd's `size`
|
|
219
|
+
(`small | middle | large`) IS `density` (`compact | default | comfortable`); antd's `locale` IS
|
|
220
|
+
the `t()` layer. Adding the antd spelling as an alias would be duplication, not parity.
|
|
221
|
+
|
|
222
|
+
**A knob that only a fork could reach is not parity either.** antd's `components`,
|
|
223
|
+
`filterDropdown`, `classNames`/`styles` semantic maps and `prefixCls` all exist to let a consumer
|
|
224
|
+
replace the rendered markup. This library answers that layer with tokens (cardinal rule #45), so
|
|
225
|
+
those are deliberately NOT adopted — adopting them would re-open the hole the token tiers close.
|
|
226
|
+
|
|
227
|
+
## Derived colour is AUTHORED, and MEASUREMENT is what makes it authoritative
|
|
228
|
+
|
|
229
|
+
**Twenty values, in `src/tokens/derived.css`:** `--primary-hover`, `--primary-active`,
|
|
230
|
+
`--primary-border`, `--destructive-hover`, `--destructive-active`, `--control-outline` and its
|
|
231
|
+
alpha, `--control-outline-error` and its alpha, and `--ring` — each in both themes. They are the
|
|
232
|
+
interaction states that hang off the five authored seeds per theme (SmartHR MAIN plus four 和色,
|
|
233
|
+
and the lifted dark ramp), which stay in `src/tokens/foundation.css`.
|
|
234
|
+
|
|
235
|
+
**These twenty were once generated.** A colour algorithm ran at build time, took the seeds and
|
|
236
|
+
emitted the derived map; the authority behind each value was "the algorithm said so". That
|
|
237
|
+
generator has been removed and its dependency with it. The values did not change — but the reason
|
|
238
|
+
to trust them had to.
|
|
239
|
+
|
|
240
|
+
**The authority is now the measurement, not the derivation.** Four suites read `derived.css`
|
|
241
|
+
directly and hold every value in it to a threshold this repo has already committed to:
|
|
242
|
+
|
|
243
|
+
| suite | what it holds |
|
|
244
|
+
| ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
245
|
+
| `src/tokens/__tests__/focus-ring-contrast.test.ts` | the focus mark in both switch positions — ≥3:1 (WCAG 2.2 SC 1.4.11) on every surface a control sits on, and the halo proven to be decoration rather than the indicator |
|
|
246
|
+
| `src/tokens/__tests__/interactive-fill-contrast.test.ts` | an interactive fill must clear **4.5:1** against the label sitting on it |
|
|
247
|
+
| `src/tokens/__tests__/destructive-contrast.test.ts` | `--destructive-hover` / `--destructive-active` against the same bar |
|
|
248
|
+
| `src/lib/__tests__/theme-tokens-css.test.ts` | the tier is actually loaded, and complete in both themes |
|
|
249
|
+
|
|
250
|
+
The first three also pin each value as a literal, so an edit to `derived.css` alone turns CI red
|
|
251
|
+
rather than quietly retinting the library. **That is a stronger claim than the generator made, not
|
|
252
|
+
a weaker one.** An algorithm guarantees a value is _consistent_; this repo had to override it four
|
|
253
|
+
times (below) precisely because consistent is not the same as _accessible_. A threshold guarantees
|
|
254
|
+
the property actually being sold.
|
|
255
|
+
|
|
256
|
+
**To change a derived value:** change it, run `pnpm test`, and if a threshold breaks the value is
|
|
257
|
+
wrong. Do not relax the threshold.
|
|
258
|
+
|
|
259
|
+
**The four overrides that existed even while a generator did** — the clearest evidence the
|
|
260
|
+
algorithm was never the real authority. Stepping an interactive fill towards the label sitting on
|
|
261
|
+
it lands under 4.5:1, so those four states take the same ramp at the same step size in the
|
|
262
|
+
opposite direction:
|
|
263
|
+
|
|
264
|
+
| token | conventional step | shipped |
|
|
265
|
+
| ------------------------ | ----------------- | ----------------- |
|
|
266
|
+
| light `--primary-hover` | #208bc9 · 3.69:1 | #005596 · 7.53:1 |
|
|
267
|
+
| light `--primary-active` | #005596 · 7.53:1 | #003c70 · 10.97:1 |
|
|
268
|
+
| dark `--primary-hover` | #61b6e8 · 7.92:1 | #61b6e8 · 7.92:1 |
|
|
269
|
+
| dark `--primary-active` | #2f76a6 · 3.60:1 | #8bd0f3 · 10.50:1 |
|
|
270
|
+
|
|
271
|
+
**What was deliberately never derived, and why — each a measurement, not a preference:**
|
|
212
272
|
|
|
213
273
|
- **The neutral spine.** Colour foundation stays SmartHR's (see the table above); the neutrals are
|
|
214
|
-
not derived from the brand seed.
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
- **The text ramp.**
|
|
218
|
-
opaque `H S% L%` triple without choosing a surface to composite against — lossy by
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
recorded
|
|
225
|
-
|
|
226
|
-
**
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
274
|
+
not derived from the brand seed. The one derived role on offer for a control boundary measures
|
|
275
|
+
**1.43:1** on the page, where `--input` is held to 3:1 by SC 1.4.11 and by
|
|
276
|
+
`input-boundary-contrast.test.ts`.
|
|
277
|
+
- **The text ramp.** The derived one is alpha-based (`rgba(0,0,0,0.88)`), which cannot enter this
|
|
278
|
+
library's opaque `H S% L%` triple without choosing a surface to composite against — lossy by
|
|
279
|
+
construction.
|
|
280
|
+
- **The dark `--primary` itself.** A mechanical dark derivation MOVES the seed. Deriving from this
|
|
281
|
+
library's light seed gives `#0363a4` at **2.81:1** on the dark spine, which
|
|
282
|
+
`primary-text-contrast.test.ts` rejects outright; deriving from the committed dark seed gives
|
|
283
|
+
`#3794d3` at 5.36:1, where the seed itself measures 7.07:1. The dark theme therefore keeps its
|
|
284
|
+
own seed — recorded, with the measurement, in `focus-ring-contrast.test.ts`.
|
|
285
|
+
|
|
286
|
+
**The focus geometry was already on the named scale.** Border 1, halo width 2, heavy outline 3,
|
|
287
|
+
radius 6, control height 32 and font size 14 all match scales this library already ships
|
|
288
|
+
(`--stroke-*`, `--radius`, `--band-height-md`, `--font-size-base`), so the focus tokens bind to
|
|
289
|
+
those steps instead of restating a second copy of the number.
|
|
290
|
+
|
|
291
|
+
**antd itself is gone from this repository**, and `pnpm check:no-antd-runtime` is what keeps it
|
|
292
|
+
gone: it fails if antd or any `@ant-design/*` package appears in any manifest field, is imported
|
|
293
|
+
anywhere in `src/`, or leaves a trace in `dist/`. Deleting that gate re-opens the door it closes.
|
|
294
|
+
|
|
295
|
+
## Focus appearance — a two-form convention, and the indicator SHIPS OFF
|
|
296
|
+
|
|
297
|
+
### The two forms
|
|
235
298
|
|
|
236
299
|
**SmartHR** (`smarthr-ui@99.6.0`) draws focus as an opaque ring held off the control by a white
|
|
237
|
-
spacer.
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
declarations, which is why
|
|
243
|
-
- **Non-field** —
|
|
244
|
-
`outline:
|
|
245
|
-
|
|
246
|
-
**
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
focus colour is `--ring`, which the generated tier declares as `var(--primary)` because antd has no
|
|
253
|
-
separate focus-colour token at all.
|
|
300
|
+
spacer. This library takes a different, two-form convention instead — widely used in enterprise
|
|
301
|
+
component libraries, and surveyed across several before it was adopted here:
|
|
302
|
+
|
|
303
|
+
- **Field** — the boundary RECOLOURS to the primary at its unchanged hairline width, plus
|
|
304
|
+
`box-shadow: 0 0 0 var(--control-outline-width) var(--control-outline)`. A Select emits the same
|
|
305
|
+
declarations as an Input, which is why the two focus identically.
|
|
306
|
+
- **Non-field** — an outline outside the box model:
|
|
307
|
+
`outline: var(--focus-outline-weight) solid var(--primary-border); outline-offset: 1px`.
|
|
308
|
+
|
|
309
|
+
**Why this shape wins.** The library previously shipped an opaque brand ring drawn immediately
|
|
310
|
+
outside an untouched grey border — two outlines of different colours claiming the same edge — and a
|
|
311
|
+
Select that could not be told to agree with an Input. Recolouring the existing boundary rather than
|
|
312
|
+
adding a second one resolves both. SmartHR still owns the hue: the focus colour is `--ring`, which
|
|
313
|
+
the derived tier declares as `var(--primary)`, because a focused field takes the primary rather than
|
|
314
|
+
a focus colour of its own.
|
|
254
315
|
|
|
255
316
|
### The indicator ships OFF. That forfeits WCAG 2.4.7 and a JIS X 8341-3 AA claim.
|
|
256
317
|
|
|
@@ -269,18 +330,18 @@ owner chose to ship it off and let whoever needs it turn it on. What that costs,
|
|
|
269
330
|
**Turning it back on is one attribute, on the root element, with no code change:**
|
|
270
331
|
|
|
271
332
|
```html
|
|
272
|
-
<html data-focus-outline="on">
|
|
333
|
+
<html data-focus-outline="on"></html>
|
|
273
334
|
```
|
|
274
335
|
|
|
275
|
-
**The switch is
|
|
276
|
-
|
|
336
|
+
**The switch is a single multiplier, not a scatter of overrides.** `--focus-outline` is one flag,
|
|
337
|
+
and every painted focus length multiplies by it:
|
|
277
338
|
|
|
278
|
-
```
|
|
279
|
-
|
|
339
|
+
```css
|
|
340
|
+
--focus-ring-width: calc(var(--focus-ring-weight) * var(--focus-outline));
|
|
280
341
|
```
|
|
281
342
|
|
|
282
|
-
|
|
283
|
-
|
|
343
|
+
Setting it to `0` zeroes every focus length at once, so no component rebind can bring the mark back
|
|
344
|
+
while it is off — which is the property a scatter of per-component overrides could never give. The two paints
|
|
284
345
|
that are not lengths — the halo and a field's recoloured boundary — are scoped to the same attribute
|
|
285
346
|
in `styles/focus-ring.css`. **Every `:focus-visible` selector stays exactly where it is**; only the
|
|
286
347
|
painted result disappears.
|
|
@@ -288,18 +349,18 @@ painted result disappears.
|
|
|
288
349
|
### The ON state is the LIGHT one
|
|
289
350
|
|
|
290
351
|
The complaint was weight, not existence, so the on-position is not the old mark restored. It paints
|
|
291
|
-
|
|
292
|
-
`
|
|
352
|
+
the **field** indicator on every control — one hairline (1px) in the focus hue, plus the
|
|
353
|
+
`--control-outline` halo — rather than the heavy 3px outline form. Measured, in Chromium, on
|
|
293
354
|
`ql.test` after the transition settles:
|
|
294
355
|
|
|
295
|
-
| control
|
|
296
|
-
|
|
|
297
|
-
| Input / Select trigger
|
|
298
|
-
| Button (primary)
|
|
299
|
-
| Sidebar nav row / list row | outline `0px`
|
|
356
|
+
| control | switch off | switch on |
|
|
357
|
+
| -------------------------- | ---------------------------------------------------- | --------------------------------------------------------------------- |
|
|
358
|
+
| Input / Select trigger | border `1px rgb(144,135,127)`, resting shadow intact | border `1px rgb(0,113,189)` + `rgba(0,182,228,0.11) 0 0 0 2px` |
|
|
359
|
+
| Button (primary) | outline `0px`, resting shadow intact | `outline: 1px solid rgb(0,113,189)` @ `0px` + same halo |
|
|
360
|
+
| Sidebar nav row / list row | outline `0px` | `outline: 1px solid rgb(0,113,189)` @ **`-1px`** (inset into the row) |
|
|
300
361
|
|
|
301
|
-
The field pair is
|
|
302
|
-
seed. The nav row insets its mark into its own shape rather than wrapping an already-shaded surface,
|
|
362
|
+
The field pair is the recoloured boundary plus the halo, exactly as the convention specifies for
|
|
363
|
+
this seed. The nav row insets its mark into its own shape rather than wrapping an already-shaded surface,
|
|
303
364
|
which is the specific stacking that read as heavy.
|
|
304
365
|
|
|
305
366
|
**No control's box moves when it is focused**, measured with `getBoundingClientRect()` before and
|
|
@@ -308,11 +369,12 @@ Input, Save and Delete all stay at 32.00px.
|
|
|
308
369
|
|
|
309
370
|
**Two criteria, and only one is met by thickness alone.** SC 1.4.11 (AA, non-text contrast) is about
|
|
310
371
|
COLOUR — the 1px mark measures 5.05:1 light and 7.07:1 dark on every surface a control sits on, so
|
|
311
|
-
the light weight costs nothing there.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
`--focus-outline-weight: var(--stroke-lg)` restores
|
|
372
|
+
the light weight costs nothing there. `--primary-border` (#6dc0e3) measures **2.00:1 light /
|
|
373
|
+
1.66:1 dark** and could not have satisfied it at any thickness, which is why the on-state takes the
|
|
374
|
+
focus hue instead — the single place it departs from the outline form. SC 2.4.13 Focus Appearance
|
|
375
|
+
(AAA) additionally wants a 2px perimeter; the on-state does not target it, and
|
|
376
|
+
`--focus-outline-weight: var(--stroke-lg)` restores the 3px weight if a customer needs the area
|
|
377
|
+
clause.
|
|
316
378
|
|
|
317
379
|
Both positions of the switch are gated in `src/tokens/__tests__/focus-ring-contrast.test.ts`: with
|
|
318
380
|
it off nothing paints and no rebind can route around it, with it on the geometry and the ≥3:1
|
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -117,13 +117,14 @@ A change isn't done until its documentation reflects it:
|
|
|
117
117
|
```bash
|
|
118
118
|
pnpm lint # eslint — self-contained flat config
|
|
119
119
|
pnpm typecheck # tsc --noEmit
|
|
120
|
-
pnpm
|
|
121
|
-
pnpm
|
|
120
|
+
pnpm vitest run src/components/<group>/__tests__ --maxWorkers=2 # ONLY what you touched
|
|
121
|
+
pnpm test # FULL suite — CI only, never from an agent loop
|
|
122
|
+
pnpm preview:build # integration test: examples + docs must build — at most once, pre-PR
|
|
122
123
|
pnpm audit # godxjp-ui-audit — 0 errors for touched files
|
|
123
124
|
pnpm check:mcp-sync # MCP registry ↔ library export drift guard
|
|
124
125
|
```
|
|
125
126
|
|
|
126
|
-
`pnpm verify` and `pnpm verify:release` run these together (verify:release also builds). It needs `pnpm exec playwright install chromium` once locally; see [FRAME-A11Y-CI.md](./FRAME-A11Y-CI.md) for how to run/scope it, read the evidence, and regenerate its baseline after an accessibility fix.
|
|
127
|
+
`pnpm verify` and `pnpm verify:release` run these together (verify:release also builds) — **both include the full suite, so both belong to CI.** Locally, run them at most once immediately before opening a PR, never inside an edit loop and never while other agents are working on the same machine. It needs `pnpm exec playwright install chromium` once locally; see [FRAME-A11Y-CI.md](./FRAME-A11Y-CI.md) for how to run/scope it, read the evidence, and regenerate its baseline after an accessibility fix.
|
|
127
128
|
|
|
128
129
|
All gates are **self-contained** — no internal/external tooling package required. The eslint, prettier, and vitest setup live in the package (`eslint.config.js`, `prettier.config.mjs`, `vitest.config.ts`, `src/test/`), so a fresh checkout can lint/type-check/test without anything beyond the declared devDependencies.
|
|
129
130
|
|
package/docs/FORMS.md
CHANGED
|
@@ -44,11 +44,127 @@ export function CreateCustomerForm({ onSubmit }: { onSubmit: (v: Values) => void
|
|
|
44
44
|
|
|
45
45
|
## API
|
|
46
46
|
|
|
47
|
-
| Export
|
|
48
|
-
|
|
|
49
|
-
| `useZodForm`
|
|
50
|
-
| `FormRoot`
|
|
51
|
-
| `FormFieldControl`
|
|
47
|
+
| Export | Prop type | Purpose |
|
|
48
|
+
| ------------------- | ----------------------------------------- | --------------------------------------------- |
|
|
49
|
+
| `useZodForm` | `ZodSchemaProp` + `UseZodFormOptionsProp` | Hook — injects zodResolver |
|
|
50
|
+
| `FormRoot` | `FormRootProp` | FormProvider + `<form onSubmit>` + layout |
|
|
51
|
+
| `FormFieldControl` | `FormFieldControlProp` | Controller + FormField + error display |
|
|
52
|
+
| `FormFieldArray` | `FormFieldArrayProp` | Repeating rows on RHF's `useFieldArray` |
|
|
53
|
+
| `useFormWatch` | — | Subscribe to one field (antd `Form.useWatch`) |
|
|
54
|
+
| `useFormInstance` | — | The RHF instance, without prop-drilling |
|
|
55
|
+
| `useFormSubmitting` | — | Submit in flight (both paths) |
|
|
56
|
+
| `useFormDisabled` | — | Form-level `disabled`, for the action row |
|
|
57
|
+
|
|
58
|
+
### `FormRoot` — form-level knobs
|
|
59
|
+
|
|
60
|
+
`onSubmit` runs only after validation passes (antd `onFinish`). Everything else is optional:
|
|
61
|
+
|
|
62
|
+
| Prop | antd | Notes |
|
|
63
|
+
| ----------------------------------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------- |
|
|
64
|
+
| `onSubmitFailed(errors)` | `onFinishFailed` | RHF error tree; the first invalid field is focused regardless |
|
|
65
|
+
| `onSubmitError(error)` | — | Optional error callback; rejected submissions show localized feedback and retain values |
|
|
66
|
+
| `onReset` | — | A native `<Button type="reset">` restores `defaultValues`; this runs after |
|
|
67
|
+
| `scrollToFirstError` | `scrollToFirstError` | Default `true`; centres the field row, honours `prefers-reduced-motion` |
|
|
68
|
+
| `disabled` | Form `disabled` | Disables fields and submission; values remain in state. Buttons read `useFormDisabled()` |
|
|
69
|
+
| `layout` `labelWidth` `controlWidth` `labelAlign` `collapseBelow` | `layout` `labelCol` … | Renders the `Form` layout shell (see the spacing note below) |
|
|
70
|
+
| `density` / `requiredMark` | `size` / `requiredMark` | Forwarded to the same shell |
|
|
71
|
+
| `errors` | — | Server error bag; fields claim their keys, `<FormErrors />` shows the rest |
|
|
72
|
+
|
|
73
|
+
> **Spacing.** A bare `FormRoot` stacks its children with `ui-stack-md`. Passing ANY layout prop
|
|
74
|
+
> switches it to the `Form` shell, whose `--form-block-gap` owns the spacing instead — the two are
|
|
75
|
+
> never combined, so a form does not silently gain double gaps when a `layout` is added.
|
|
76
|
+
|
|
77
|
+
`columns` renders a responsive grid inside the form. Duplicate submissions are ignored until the current attempt settles. Reset returns the form to its defaults.
|
|
78
|
+
|
|
79
|
+
### `FormFieldControl` — field-level knobs
|
|
80
|
+
|
|
81
|
+
| Prop | antd | Notes |
|
|
82
|
+
| ----------------------------------------- | ------------------- | -------------------------------------------------------------------- |
|
|
83
|
+
| `disabled` | Item `disabled` | Defaults to `FormRoot disabled`; `false` re-enables one field |
|
|
84
|
+
| `dependencies` | `dependencies` | Re-validate this field when another changes (after the first submit) |
|
|
85
|
+
| `getValueFromEvent` | `getValueFromEvent` | Replaces the built-in DOM-event / raw-value detection |
|
|
86
|
+
| `normalize` | `normalize` | Transform before storing; return `previousValue` to REJECT a change |
|
|
87
|
+
| `help` | `help` | Shown instead of the resolved validation message |
|
|
88
|
+
| `validateStatus` `hasFeedback` `feedback` | same | Forwarded to `FormField` — the in-flight remote-check state |
|
|
89
|
+
| `preserve` | `preserve` | `false` unregisters the value when the field unmounts |
|
|
90
|
+
|
|
91
|
+
The binding includes both native `onChange` and godx-ui `onValueChange`; spreading it onto Input, Textarea or Select updates the same store. Native fields do not emit the shared callback twice. IDs are unique across sibling forms. Per-field layout, label/control width, label addons and column spans are forwarded to FormField.
|
|
92
|
+
|
|
93
|
+
**There is no `rules` prop, by design.** react-hook-form ignores per-field register rules whenever a
|
|
94
|
+
`resolver` is set, and `useZodForm` always sets one — a `rules` prop would silently do nothing.
|
|
95
|
+
antd's `rules` is the Zod schema here, including the cross-field case:
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
const schema = z
|
|
99
|
+
.object({ password: z.string().min(8), confirm: z.string() })
|
|
100
|
+
.refine((v) => v.confirm === v.password, { path: ["confirm"], message: "Mật khẩu không khớp" });
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Pair that refinement with `dependencies={["password"]}` on the `confirm` field so editing the
|
|
104
|
+
password re-runs it.
|
|
105
|
+
|
|
106
|
+
## Dynamic rows — `FormFieldArray` (antd `Form.List`)
|
|
107
|
+
|
|
108
|
+
`FormFieldArray` is react-hook-form's `useFieldArray`, so rows keep their identity across
|
|
109
|
+
insert/remove/move instead of being re-keyed by index. Each row hands back its dotted path prefix;
|
|
110
|
+
build the child field name from it and nested validation lands on the right row.
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
import { FormFieldArray, FormFieldControl, FormRoot, useZodForm } from "@godxjp/ui/form";
|
|
114
|
+
|
|
115
|
+
const schema = z.object({
|
|
116
|
+
contacts: z.array(z.object({ email: z.string().email() })).min(1, "Cần ít nhất một liên hệ"),
|
|
117
|
+
});
|
|
118
|
+
type Values = z.infer<typeof schema>;
|
|
119
|
+
|
|
120
|
+
<FormFieldArray<Values, "contacts"> name="contacts">
|
|
121
|
+
{({ fields, append, remove, move, error, disabled }) => (
|
|
122
|
+
<>
|
|
123
|
+
{error && (
|
|
124
|
+
<Alert tone="destructive">
|
|
125
|
+
<AlertDescription>{error}</AlertDescription>
|
|
126
|
+
</Alert>
|
|
127
|
+
)}
|
|
128
|
+
{fields.map((row, position) => (
|
|
129
|
+
<Card key={row.key} variant="outline">
|
|
130
|
+
<CardContent>
|
|
131
|
+
<FormFieldControl<Values> name={`${row.name}.email`} label={`Liên hệ ${position + 1}`}>
|
|
132
|
+
{(field) => <Input {...field} value={String(field.value ?? "")} />}
|
|
133
|
+
</FormFieldControl>
|
|
134
|
+
<Button type="button" variant="destructive" onClick={() => remove(row.index)}>
|
|
135
|
+
Xoá
|
|
136
|
+
</Button>
|
|
137
|
+
</CardContent>
|
|
138
|
+
</Card>
|
|
139
|
+
))}
|
|
140
|
+
<Button
|
|
141
|
+
type="button"
|
|
142
|
+
variant="outline"
|
|
143
|
+
disabled={disabled}
|
|
144
|
+
onClick={() => append({ email: "" })}
|
|
145
|
+
>
|
|
146
|
+
Thêm liên hệ
|
|
147
|
+
</Button>
|
|
148
|
+
</>
|
|
149
|
+
)}
|
|
150
|
+
</FormFieldArray>;
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
- `row.key` is RHF's stable id — use it as the React key, **never** the index.
|
|
154
|
+
- `row.index` is the live position; pass it to `remove`/`move`, not the render position.
|
|
155
|
+
- `error` is the ARRAY-level message (a `.min(1)` on the array itself), not a row's.
|
|
156
|
+
- Also available: `prepend`, `insert`, `swap`, `replace`.
|
|
157
|
+
- react-hook-form path only — a server-driven `adapter` owns its own collection state.
|
|
158
|
+
|
|
159
|
+
## Watching a value — `useFormWatch`
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
const plan = useFormWatch<Values>("plan"); // one field
|
|
163
|
+
const all = useFormWatch<Values>(); // every value
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Returns `unknown` (the same contract as the render-prop `value`) — narrow at the call site. It
|
|
167
|
+
works on BOTH paths: react-hook-form's `useWatch`, or the adapter's `getValue`/`getValues`.
|
|
52
168
|
|
|
53
169
|
## Forbidden
|
|
54
170
|
|
|
@@ -108,7 +224,9 @@ const form = useForm({ customer_nm: "", action_mode: "regist" });
|
|
|
108
224
|
- Never hand-filter the bag per page; that except-list is exactly what this mechanism removes.
|
|
109
225
|
- `FormFieldControl` forwards its `name`, so `FormRoot` + adapter fields claim their keys too;
|
|
110
226
|
give `<FormErrors errors={form.errors} />` the bag explicitly when there is no surrounding
|
|
111
|
-
`Form errors
|
|
227
|
+
`Form errors` — or pass the bag straight to `FormRoot errors`, which provides the same registry.
|
|
228
|
+
- An adapter may implement the optional `reset()`, which `FormRoot` calls for a native
|
|
229
|
+
`<Button type="reset">`, so the reset button behaves identically on both paths.
|
|
112
230
|
|
|
113
231
|
### Sibling Forms — one bag over several Card+Form sections
|
|
114
232
|
|
|
@@ -141,3 +259,30 @@ import { Form, FormErrors, FormErrorsProvider, FormField, Input } from "@godxjp/
|
|
|
141
259
|
|
|
142
260
|
A nested Form **with** its own `errors` starts a new registry that shadows the provider — its
|
|
143
261
|
claims and messages stay inside it (use this for an embedded sub-form with a separate bag).
|
|
262
|
+
|
|
263
|
+
### Multipart uploads and Inertia visits
|
|
264
|
+
|
|
265
|
+
For a form that submits files together, use `<Upload name="attachments[]" pastable />`.
|
|
266
|
+
The uploader appends staged local files during the native `formdata` event, including
|
|
267
|
+
files selected by dropping or pasting. Completed media uploads remain the responsibility
|
|
268
|
+
of `collectUploadCommitActions`.
|
|
269
|
+
|
|
270
|
+
For an endpoint that redirects back after each uploaded file, connect the reusable
|
|
271
|
+
Inertia bridge. Errors retain the local file for retry; removing or cancelling a file
|
|
272
|
+
cancels its visit. Each request runs asynchronously so selecting another file does not
|
|
273
|
+
silently cancel an unrelated upload.
|
|
274
|
+
|
|
275
|
+
```tsx
|
|
276
|
+
import { inertiaUpload } from "@godxjp/ui/inertia";
|
|
277
|
+
import { Upload } from "@godxjp/ui/data-entry";
|
|
278
|
+
|
|
279
|
+
<Upload
|
|
280
|
+
value={items}
|
|
281
|
+
onValueChange={(next) => setItems(next.filter((item) => item.status !== "done"))}
|
|
282
|
+
onUpload={inertiaUpload(
|
|
283
|
+
(file, callbacks) => router.post(uploadUrl, { file }, { ...callbacks, preserveScroll: true }),
|
|
284
|
+
t("upload.failed"),
|
|
285
|
+
)}
|
|
286
|
+
pastable
|
|
287
|
+
/>;
|
|
288
|
+
```
|
|
@@ -3,16 +3,28 @@
|
|
|
3
3
|
> Generated by `scripts/frame-coverage.mjs` (issue #163). Do not edit by hand — run `pnpm check:frame-coverage`.
|
|
4
4
|
> Standard: [FRAME-COVERAGE-STANDARD.md](./FRAME-COVERAGE-STANDARD.md). A missing dimension is **UNTESTED**, never a pass.
|
|
5
5
|
|
|
6
|
-
- Public components: **
|
|
7
|
-
- With ≥1 frame: **
|
|
6
|
+
- Public components: **137**
|
|
7
|
+
- With ≥1 frame: **133** · zero-frame: **4**
|
|
8
8
|
- Fully covered (every contract axis declared): **0**
|
|
9
9
|
|
|
10
|
+
## UNTESTED — no `/frame/**` route
|
|
11
|
+
|
|
12
|
+
- [ ] `inertiaUpload`
|
|
13
|
+
- [ ] `CardBar`
|
|
14
|
+
- [ ] `VisuallyHidden`
|
|
15
|
+
- [ ] `RangeTimeline`
|
|
16
|
+
|
|
10
17
|
## Per-component contract axes
|
|
11
18
|
|
|
12
19
|
Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
13
20
|
|
|
14
21
|
| Component | Frame | visua | state | owner | conte | respo | inter | acces | prefe | async |
|
|
15
22
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
23
|
+
| inertiaUpload | **✗** | · | · | · | · | · | · | · | · | · |
|
|
24
|
+
| CardBar | **✗** | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
25
|
+
| TimeRangePicker | ✓ | · | · | · | · | · | · | · | · | · |
|
|
26
|
+
| VisuallyHidden | **✗** | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
27
|
+
| RangeTimeline | **✗** | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
16
28
|
| PageContainer | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
17
29
|
| Flex | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
18
30
|
| ResponsiveGrid | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
@@ -41,6 +53,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
|
41
53
|
| StatCard | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
42
54
|
| ServiceLauncherCard | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
43
55
|
| Badge | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
56
|
+
| Legend | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
44
57
|
| ListRow | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
45
58
|
| CredentialReveal | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
46
59
|
| QrCode | ✓ | ✓ | N/A | · | · | · | · | · | · | · |
|
|
@@ -54,21 +67,21 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
|
54
67
|
| Table | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
55
68
|
| DataState | ✓ | N/A | · | N/A | · | · | · | · | · | · |
|
|
56
69
|
| InfiniteQueryState | ✓ | N/A | · | N/A | · | · | · | · | · | · |
|
|
57
|
-
| Form | ✓ | ✓ |
|
|
70
|
+
| Form | ✓ | ✓ | · | N/A | · | · | · | · | · | · |
|
|
58
71
|
| FormField | ✓ | N/A | · | N/A | · | · | · | · | · | · |
|
|
59
72
|
| FormErrors | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
60
73
|
| Input | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
61
74
|
| NumberInput | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
62
|
-
| SearchInput | ✓ |
|
|
75
|
+
| SearchInput | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
63
76
|
| Select | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
64
77
|
| Switch | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
65
78
|
| Textarea | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
66
79
|
| Label | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
67
80
|
| Checkbox | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
68
81
|
| RadioGroup | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
69
|
-
| MonthPicker | ✓ |
|
|
70
|
-
| MonthRangePicker | ✓ |
|
|
71
|
-
| DatePicker | ✓ |
|
|
82
|
+
| MonthPicker | ✓ | · | · | · | · | · | · | · | · | · |
|
|
83
|
+
| MonthRangePicker | ✓ | · | · | · | · | · | · | · | · | · |
|
|
84
|
+
| DatePicker | ✓ | · | · | · | · | · | · | · | · | · |
|
|
72
85
|
| Dialog | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
73
86
|
| AlertDialog | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
74
87
|
| AlertDialogRoot | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
@@ -78,16 +91,16 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
|
78
91
|
| SkeletonTable | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
79
92
|
| Toaster | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
80
93
|
| Tabs | ✓ | ✓ | N/A | · | · | · | · | · | · | · |
|
|
81
|
-
| Pagination | ✓ |
|
|
94
|
+
| Pagination | ✓ | · | · | · | · | · | · | · | · | · |
|
|
82
95
|
| DropdownMenu | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
83
96
|
| Steps | ✓ | ✓ | N/A | · | · | · | · | · | · | · |
|
|
84
97
|
| Toolbar | ✓ | N/A | · | N/A | · | · | · | · | · | · |
|
|
85
98
|
| AppProvider | ✓ | · | · | · | · | · | · | · | · | · |
|
|
86
99
|
| formatDate | ✓ | · | · | · | · | · | · | · | · | · |
|
|
87
|
-
| TimePicker | ✓ |
|
|
88
|
-
| DateRangePicker | ✓ |
|
|
89
|
-
| Cascader | ✓ |
|
|
90
|
-
| TreeSelect | ✓ |
|
|
100
|
+
| TimePicker | ✓ | · | · | · | · | · | · | · | · | · |
|
|
101
|
+
| DateRangePicker | ✓ | · | · | · | · | · | · | · | · | · |
|
|
102
|
+
| Cascader | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
103
|
+
| TreeSelect | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
91
104
|
| Transfer | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
92
105
|
| Upload | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
93
106
|
| UploadCropDialog | ✓ | · | · | · | · | · | · | · | · | · |
|
|
@@ -106,23 +119,24 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
|
106
119
|
| Avatar | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
107
120
|
| Separator | ✓ | · | N/A | N/A | · | · | · | · | · | · |
|
|
108
121
|
| Skeleton | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
109
|
-
| Toggle | ✓ | ✓ |
|
|
122
|
+
| Toggle | ✓ | ✓ | · | N/A | · | · | · | · | · | · |
|
|
110
123
|
| ToggleGroup | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
111
124
|
| AspectRatio | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
112
125
|
| Accordion | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
113
126
|
| HoverCard | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
114
|
-
| PasswordInput | ✓ | ✓ |
|
|
127
|
+
| PasswordInput | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
115
128
|
| PasswordStrength | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
116
|
-
| InputOTP | ✓ |
|
|
129
|
+
| InputOTP | ✓ | · | · | · | · | · | · | · | · | · |
|
|
117
130
|
| Rating | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
118
|
-
| Segmented | ✓ |
|
|
119
|
-
| TagInput | ✓ |
|
|
131
|
+
| Segmented | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
132
|
+
| TagInput | ✓ | ✓ | · | · | · | · | · | · | · | · |
|
|
120
133
|
| ContextMenu | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
121
134
|
| Menubar | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
122
135
|
| NavigationMenu | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
123
136
|
| ResizablePanel | ✓ | N/A | · | N/A | · | · | · | · | · | · |
|
|
124
137
|
| Carousel | ✓ | N/A | N/A | · | · | · | · | · | · | · |
|
|
125
138
|
| AppSettingPicker | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
139
|
+
| AppSettingToggle | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
126
140
|
| Field | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
127
141
|
| LineChart | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
128
142
|
| BarChart | ✓ | ✓ | N/A | N/A | · | · | · | · | · | · |
|
|
@@ -138,6 +152,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
|
|
|
138
152
|
| AuthAccountSummary | ✓ | N/A | · | N/A | · | · | · | · | · | · |
|
|
139
153
|
| AuthStack | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
|
|
140
154
|
| OrgSwitcher | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
155
|
+
| AppLauncher | ✓ | N/A | · | · | · | · | · | · | · | · |
|
|
141
156
|
| FilterBar | ✓ | N/A | · | N/A | · | · | · | · | · | · |
|
|
142
157
|
| PermissionMatrix | ✓ | N/A | · | N/A | · | · | · | · | · | · |
|
|
143
158
|
| BranchScopePicker | ✓ | N/A | · | · | · | · | · | · | · | · |
|