@godxjp/ui 22.0.0 → 23.0.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.
Files changed (83) hide show
  1. package/README.md +4 -4
  2. package/dist/components/data-display/scroll-area.d.ts +24 -4
  3. package/dist/components/data-display/scroll-area.js +34 -72
  4. package/dist/components/data-entry/cascader.js +8 -6
  5. package/dist/components/data-entry/checkbox.js +2 -0
  6. package/dist/components/data-entry/choice-hit-target.d.ts +24 -0
  7. package/dist/components/data-entry/choice-hit-target.js +12 -0
  8. package/dist/components/data-entry/control-surface.d.ts +8 -0
  9. package/dist/components/data-entry/control-surface.js +10 -1
  10. package/dist/components/data-entry/form-field.js +3 -2
  11. package/dist/components/data-entry/radio.js +3 -2
  12. package/dist/components/data-entry/search-select.js +225 -95
  13. package/dist/components/data-entry/select.d.ts +76 -14
  14. package/dist/components/data-entry/select.js +495 -163
  15. package/dist/components/data-entry/slider.d.ts +3 -11
  16. package/dist/components/data-entry/slider.js +582 -93
  17. package/dist/components/data-entry/switch.js +2 -0
  18. package/dist/components/navigation/dropdown-menu.d.ts +11 -3
  19. package/dist/components/navigation/dropdown-menu.js +190 -15
  20. package/dist/components/navigation/index.d.ts +0 -3
  21. package/dist/components/navigation/index.js +0 -78
  22. package/dist/components/ui/index.d.ts +0 -3
  23. package/dist/components/ui/index.js +0 -3
  24. package/dist/components/ui/input-otp.d.ts +1 -1
  25. package/dist/components/ui/segmented.js +3 -1
  26. package/dist/components/ui/tag-input.js +3 -5
  27. package/dist/lib/select-options.d.ts +21 -0
  28. package/dist/lib/select-options.js +46 -0
  29. package/dist/props/components/data-display.prop.d.ts +9 -6
  30. package/dist/props/components/data-entry.prop.d.ts +323 -30
  31. package/dist/props/components/index.d.ts +1 -1
  32. package/dist/props/components/navigation.prop.d.ts +17 -0
  33. package/dist/props/registry.d.ts +63 -0
  34. package/dist/props/registry.js +69 -0
  35. package/dist/styles/control.css +205 -23
  36. package/dist/styles/data-display-layout.css +18 -10
  37. package/dist/styles/focus-ring.css +2 -2
  38. package/dist/styles/fonts.css +88 -4
  39. package/dist/styles/navigation-layout.css +11 -236
  40. package/dist/styles/shell-layout.css +144 -139
  41. package/dist/styles/table-layout.css +7 -3
  42. package/dist/tokens/components/control.css +4 -0
  43. package/dist/tokens/components/data-display.css +0 -5
  44. package/dist/tokens/components/navigation.css +3 -5
  45. package/dist/tokens/components/scroll-area.css +7 -0
  46. package/docs/CUSTOMER-THEMING.md +7 -1
  47. package/docs/FRAME-COVERAGE-REPORT.md +4 -7
  48. package/docs/data-display/data-table/index.tsx +27 -1
  49. package/docs/data-display/popover.tsx +4 -6
  50. package/docs/data-display/scroll-area.tsx +51 -45
  51. package/docs/data-entry/date-picker.tsx +2 -3
  52. package/docs/data-entry/input-otp.tsx +5 -2
  53. package/docs/data-entry/segmented.tsx +40 -1
  54. package/docs/data-entry/select-matrix.tsx +1 -2
  55. package/docs/data-entry/select.tsx +108 -3
  56. package/docs/data-entry/slider.tsx +125 -47
  57. package/docs/feedback/dialog.tsx +1 -2
  58. package/docs/feedback/sheet.tsx +1 -2
  59. package/docs/layout/app-shell-states.tsx +143 -0
  60. package/docs/navigation/dropdown-menu.tsx +75 -4
  61. package/docs/roadmap/parity-audit-data-entry.md +44 -33
  62. package/docs/roadmap/parity-audit-layout-navigation-general.md +304 -310
  63. package/docs/roadmap/parity-backlog.md +2 -3
  64. package/package.json +20 -44
  65. package/scripts/ui-audit.mjs +1 -1
  66. package/scripts/visual-audit-rules.mjs +0 -7
  67. package/scripts/visual-audit.mjs +6 -27
  68. package/dist/components/navigation/context-menu.d.ts +0 -21
  69. package/dist/components/navigation/context-menu.js +0 -149
  70. package/dist/components/navigation/menubar.d.ts +0 -28
  71. package/dist/components/navigation/menubar.js +0 -136
  72. package/dist/components/navigation/navigation-menu.d.ts +0 -10
  73. package/dist/components/navigation/navigation-menu.js +0 -91
  74. package/dist/components/ui/context-menu.d.ts +0 -1
  75. package/dist/components/ui/context-menu.js +0 -2
  76. package/dist/components/ui/menubar.d.ts +0 -1
  77. package/dist/components/ui/menubar.js +0 -2
  78. package/dist/components/ui/navigation-menu.d.ts +0 -1
  79. package/dist/components/ui/navigation-menu.js +0 -2
  80. package/docs/FRAME-A11Y-CI.md +0 -349
  81. package/docs/navigation/context-menu.tsx +0 -128
  82. package/docs/navigation/menubar.tsx +0 -141
  83. package/docs/navigation/navigation-menu.tsx +0 -158
@@ -1,6 +1,12 @@
1
1
  # Ant Design parity — `data-entry` group
2
2
 
3
3
  > Audit pass, read-only, 2026-09-10. Method and ledger format: `antd-parity.md` §0 / §2.
4
+ > **Cập nhật 11/09/2026 — `Select` (§2.1).** Chủ dự án yêu cầu Select bám sát Ant Design 6, nên
5
+ > nhiều dòng `WONT-PORT` / `COVERED-ELSEWHERE` bên dưới ĐÃ BỊ ĐẢO và nay là `PRESENT`. Lý do cũ
6
+ > không sai về kỹ thuật; nó chỉ không còn là quyết định hiện hành. Hai dòng KHÔNG đảo, và vì sao:
7
+ > `variant: "underlined"` (docs/DESIGN-AUTHORITY.md nêu đích danh: SmartHR vẽ ô kín — đó là quyết
8
+ > định của design authority, không phải của bản kiểm này) và `virtual` (vẫn thiếu — xem dòng của nó).
9
+ >
4
10
  > Skipped by instruction: `TreeSelect`, `Segmented`, `TagInput` chip rendering, everything chat-shaped
5
11
  > (`ChatComposer`, `ChatSuggestion`). 45 catalog entries carry `group: "data-entry"`.
6
12
 
@@ -31,8 +37,9 @@ The five that matter, in order:
31
37
  3. **No option-list virtualization anywhere in `src/`** (`grep -rl 'virtual\|useVirtualizer\|react-window\|@tanstack/react-virtual' src/` → nothing).
32
38
  antd `Select virtual` is on by default; a 5 000-row `Select`/`Cascader` here renders 5 000 DOM
33
39
  nodes. Same root cause as the missing `List` in `list-masonry.md`. **P1.**
34
- 4. **`Slider` never exposes a formatted value to assistive tech** — `tooltip.formatter` paints an
35
- `aria-hidden` bubble and no `aria-valuetext`, so a ¥/%/件 slider announces a bare number. **P1 a11y.**
40
+ 4. ~~**`Slider` never exposes a formatted value to assistive tech.**~~ CLOSED — `tooltip.formatter`
41
+ is now also the thumb's `aria-valuetext` (`slider.tsx`, `slider-antd.test.tsx`), landed with the
42
+ move to react-aria-components.
36
43
  5. **`ColorPicker` is a native `<input type="color">` + hex box** and is the one control in the group
37
44
  with no `size`/`status`/`variant`/`allowClear`/`readOnly`, and no `presets`. It cannot express a
38
45
  brand palette, which is the ordinary enterprise use. **P1.**
@@ -65,24 +72,24 @@ divergence was from the component beside them. They were merged into `DatePicker
65
72
 
66
73
  `src/props/components/data-entry.prop.ts:941-1142`, `src/components/data-entry/search-select.tsx`.
67
74
 
68
- | Ant Design prop / behaviour | Status | Evidence | Verdict |
69
- | ------------------------------------------------------------------------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
70
- | `virtual` — virtualized option list (default ON in antd) | `MISSING` | `grep -rl "virtual\|useVirtualizer\|react-window\|@tanstack/react-virtual" src/` → no files | Add `virtual?: boolean` + a windowed list. Shared work with `List` (`list-masonry.md`) — do it once. |
71
- | `fieldNames` — map `{label,value,options}` onto foreign row shapes | `MISSING` | `grep -n fieldNames src/props/components/data-entry.prop.ts` only `:1281` (Cascader), `:1362` (TreeSelect) | Add `fieldNames?: TreeFieldNamesProp`-shaped sibling. The spelling already exists on two neighbours; a Select that cannot eat `{id,name}` is the odd one out. |
72
- | `labelInValue` — report `{value,label}` instead of a bare value | `COVERED-ELSEWHERE` | `onValueChange?: (value, option?)` `:1091`, `:1110` | The option already rides the callback. No second dialect. |
73
- | `optionFilterProp` / `optionLabelProp` | `COVERED-ELSEWHERE` | `filterOption` `:1012`, `labelRender` `:954`, `optionRender` `:1031` | Function form is strictly more expressive than a prop-name string. |
74
- | `maxTagTextLength` — truncate each chip's text | `WONT-PORT` | trigger is a `<button>` with collapsed labels, `:1096-1104` | A character-count truncation is a text contract; use `maxTagCount` + `maxTagPlaceholder`, or CSS. |
75
- | `listHeight` — popup max height in px | `WONT-PORT` | `search-select.tsx:534` uses `collisionPadding` + available height | Pixel height is a design knob token (`--select-popup-*`), cardinal rule #44. |
76
- | `placement` (`bottomLeft`…) | `COVERED-ELSEWHERE` | Radix `PopoverContent` collision flip, `search-select.tsx:529-534` | Auto-placement beats a manual physical spelling (ground rule #4). |
77
- | `suffixIcon` / `removeIcon` / `loadingIcon` | `WONT-PORT` | `allowClear` object form already carries `clearIcon` `src/props/vocabulary/shared.prop.ts:105` | Icon-per-slot props are a token/`allowClear` job, not four more props. |
78
- | `getPopupContainer` / `styles` / `classNames` / `popupClassName` | `WONT-PORT` | ground rule #4 | Inline-style twins and portal escape hatches. |
79
- | `tagRender` in `mode="multiple"` | `WONT-PORT` | `:1096-1104` states the reason verbatim (button-in-button) | Documented refusal; `TagInput` owns `tagRender` where chips are legal. |
80
- | `onPopupScroll` | `COVERED-ELSEWHERE` | built-in infinite scroll + `renderLoadMore` `:1062` | |
81
- | `defaultActiveFirstOption` | `MISSING` | `grep -n defaultActiveFirstOption src/` → nothing | P2. Add as `defaultActiveFirstOption?: boolean`; a search box that pre-highlights row 1 makes Enter a one-key pick. |
82
- | `onInputKeyDown` | `MISSING` | grep → nothing | P2. Real for "Enter creates a new record" flows. |
83
- | `maxTagCount: "responsive"` | `WONT-PORT` | `src/props/vocabulary/shared.prop.ts:109-113` documents the refusal | Container queries instead. Already recorded. |
84
- | `variant: "underlined"` | `WONT-PORT` | `src/props/vocabulary/interaction.prop.ts:126-130` documents the refusal | SmartHR draws full boxes. Already recorded. |
85
- | `onSelect` / `onDeselect` / `maxTagPlaceholder` **exist but are undocumented** | `MISSING` (catalog) | props at `:1093`, `:1112-1114`, `:1124`; `sed -n '5560,5990p' mcp/src/data/components.ts \| grep -n "onSelect\|onDeselect\|maxTagPlaceholder"` → **no match** | Catalog drift: three shipped props the MCP never tells a consumer about. Add three catalog rows (`godxjp-ui-mcp-catalog-sync`). |
75
+ | Ant Design prop / behaviour | Status | Evidence | Verdict |
76
+ | ------------------------------------------------------------------------------ | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
77
+ | `virtual` — virtualized option list (default ON in antd) | `MISSING` | `grep -rl "virtual\|useVirtualizer\|react-window\|@tanstack/react-virtual" src/` → no files | Add `virtual?: boolean` + a windowed list. Shared work with `List` (`list-masonry.md`) — do it once. |
78
+ | `fieldNames` — map `{label,value,options}` onto foreign row shapes | `PRESENT` | `SelectFieldNamesProp`; `src/lib/select-options.ts` normalises rows, groups and foreign keys into one list for both branches | Done. Rows in a foreign shape need a cast at the call site, exactly as Cascader's and TreeSelect's do. |
79
+ | `labelInValue` — report `{value,label}` instead of a bare value | `PRESENT` | `SelectLabeledValueProp`; the adapter in `select.tsx` translates it in and out | Done. The earlier "no second dialect" ruling is REVERSED: the option riding the callback does not help the async EDIT form, which holds `{value,label}` and no option list yet. Labels that arrive on the value join the option list so the trigger can show them. |
80
+ | `optionFilterProp` | `PRESENT` | `SearchSelectBaseProp.optionFilterProp` + `showSearch.optionFilterProp` | Done. The function form is still more expressive, but naming one field is the two-word answer to the common case. `optionLabelProp` stays COVERED-ELSEWHERE (`labelRender`). |
81
+ | `maxTagTextLength` — truncate each chip's text | `PRESENT` | `chipLabel()` in `search-select.tsx`; test `select-antd-tags.test.tsx` | Done. Cuts what the TRIGGER shows; the value keeps its whole label. |
82
+ | `listHeight` — popup max height in px | `PRESENT` | writes `--select-content-max-height` / `--search-select-list-max-height` inline | Done, and it does NOT break rule #44: the height stays a token, and `listHeight` overrides that token for one instance — the same shape `popupMatchSelectWidth`'s number already had. |
83
+ | `placement` (`bottomLeft`…) | `PRESENT` | `SelectPlacementProp` `bottomStart` / `bottomEnd` / `topStart` / `topEnd` | Done, spelled LOGICALLY (the DESIGN-AUTHORITY override antd's physical names get everywhere else). Auto-placement with collision flipping is still the default. |
84
+ | `suffixIcon` / `prefix` | `PRESENT` | both branches; `suffixIcon={null}` removes the indicator (antd's own replacement for `showArrow`) | Done. `removeIcon` / `loadingIcon` stay WONT-PORT: `allowClear`'s object form already carries an icon, and the spinner is a token. |
85
+ | `getPopupContainer` / `styles` / `classNames` / `popupClassName` | `WONT-PORT` | ground rule #4 | Inline-style twins and portal escape hatches. |
86
+ | `tagRender` in `mode="multiple"` | `WONT-PORT` | `:1096-1104` states the reason verbatim (button-in-button) | Documented refusal; `TagInput` owns `tagRender` where chips are legal. |
87
+ | `onPopupScroll` | `PRESENT` | fires BESIDE the built-in infinite scroll, never instead of it | Done. The built-in pagination and `renderLoadMore` remain the reason most call sites need neither. |
88
+ | `defaultActiveFirstOption` | `MISSING` | `grep -n defaultActiveFirstOption src/` → nothing | P2. Add as `defaultActiveFirstOption?: boolean`; a search box that pre-highlights row 1 makes Enter a one-key pick. |
89
+ | `onInputKeyDown` | `MISSING` | grep → nothing | P2. Real for "Enter creates a new record" flows. |
90
+ | `maxTagCount: "responsive"` | `WONT-PORT` | `src/props/vocabulary/shared.prop.ts:109-113` documents the refusal | Container queries instead. Already recorded. |
91
+ | `variant: "underlined"` | `WONT-PORT` | `src/props/vocabulary/interaction.prop.ts:126-130` documents the refusal | SmartHR draws full boxes. Already recorded. |
92
+ | `onSelect` / `onDeselect` / `maxTagPlaceholder` **exist but are undocumented** | `MISSING` (catalog) | props at `:1093`, `:1112-1114`, `:1124`; `sed -n '5560,5990p' mcp/src/data/components.ts \| grep -n "onSelect\|onDeselect\|maxTagPlaceholder"` → **no match** | Catalog drift: three shipped props the MCP never tells a consumer about. Add three catalog rows (`godxjp-ui-mcp-catalog-sync`). |
86
93
 
87
94
  ### 2.2 `Cascader` — `CascaderProp` `src/props/components/data-entry.prop.ts:1253-1330`
88
95
 
@@ -108,18 +115,23 @@ divergence was from the component beside them. They were merged into `DatePicker
108
115
  | `onPressEnter` | `COVERED-ELSEWHERE` | commit-on-Enter is already the behaviour, `:237` | |
109
116
  | `decimalSeparator` as an explicit prop | `WONT-PORT` | should follow the locale, not a prop | Fixing the round-trip above removes the need. |
110
117
 
111
- ### 2.4 `Slider` — `SliderProp` `:547-568` · `src/components/data-entry/slider.tsx`
118
+ ### 2.4 `Slider` — `SliderProp` · `src/components/data-entry/slider.tsx`
112
119
 
113
- | Ant Design prop / behaviour | Status | Evidence | Verdict |
114
- | ------------------------------------------------------------------------------------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
115
- | **`aria-valuetext` mirroring `tooltip.formatter`** (rc-slider `ariaValueTextFormatterForHandle`) | `MISSING` — **a11y** | `tooltipContent` returns the bare number `slider.tsx:33,35`; the bubble is `aria-hidden` `:152`; the `Thumb` `:138-157` sets no `aria-valuetext` | **P1.** A `¥50,000` / `3 件` slider announces "50000". Feed the resolved `tooltip.formatter` (or `Intl.NumberFormat`) into `aria-valuetext`. |
116
- | default tooltip / mark text is not `Intl`-formatted | `MISSING` — i18n | `tooltipContent` `:33` returns `value` verbatim; no `Intl.NumberFormat` in the file | P2 (same fix as the row above). |
117
- | `onChangeComplete` | `RENAMED` | Radix `onValueCommit`, inherited via `React.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>` `:547` | No work. Record both names in the catalog. |
118
- | `vertical` | `RENAMED` | Radix `orientation="vertical"`, `slider.tsx:55,88,112` | No work. |
119
- | `reverse` | `PRESENT` (aliased) | `inverted ?? reverse`, `slider.tsx:115` | |
120
- | `keyboard: false` | `WONT-PORT` | — | Removing keyboard operation from a `role="slider"` fails WCAG 2.1.1. |
121
- | `range: { draggableTrack }` drag the whole span | `MISSING` | grep `draggableTrack` → nothing; Radix has no equivalent | P2. |
122
- | `tooltip.placement` / `getPopupContainer` / `autoAdjustOverflow` | `WONT-PORT` | ground rule #4 | Token + collision-aware positioning. |
120
+ CLOSED. `Slider` moved to `react-aria-components` and took the antd 6 API with it, so every row
121
+ this table used to hold is now either `PRESENT` or a recorded non-port:
122
+
123
+ | Ant Design prop / behaviour | Status |
124
+ | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
125
+ | `aria-valuetext` mirroring `tooltip.formatter` | `PRESENT` — a string/number `formatter` result is written onto the thumb's input |
126
+ | `value`/`defaultValue`/`onChange`/`onChangeComplete` as antd spells them | `PRESENT` the Radix-era `number[]` + `onValueChange`/`onValueCommit` spelling still compiles |
127
+ | `step={null}`, `marks` object form, `dots`, `included`, `vertical` | `PRESENT` |
128
+ | `range: { editable, minCount, maxCount, draggableTrack }` | `PRESENT` |
129
+ | `disabled` as `boolean[]` | `PRESENT` |
130
+ | `tooltip.placement` / `autoAdjustOverflow` | `PRESENT` — logical sides, flip measured against the thumb's rect |
131
+ | `tooltip.getPopupContainer` | `WONT-PORT` — the bubble lives inside the thumb; nothing is portalled, so there is no container to choose |
132
+ | `keyboard: false` | `WONT-PORT` — removing keyboard operation from a `role="slider"` fails WCAG 2.1.1 |
133
+ | `classNames` / `styles` semantic slots | `WONT-PORT` — docs/WHAT-BELONGS-HERE.md, "lỗ kiểu dáng tự do" |
134
+ | default tooltip text is not `Intl`-formatted | `MISSING` — P2, unchanged: pass `tooltip.formatter` for ¥/%/件 |
123
135
 
124
136
  ### 2.5 `ColorPicker` — `ColorPickerProp` `:893-908` · `src/components/data-entry/color-picker.tsx` (114 lines)
125
137
 
@@ -281,7 +293,6 @@ Present: `count` (+ the legacy `max`), `allowHalf`, `allowClear`, `character` (n
281
293
 
282
294
  | # | Component | Gap | Cite |
283
295
  | --- | --------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
284
- | 3 | `Slider` | No `aria-valuetext`; `tooltip.formatter` paints an `aria-hidden` bubble only. Formatted sliders announce a bare number. | `slider.tsx:33,138-157` |
285
296
  | 4 | `Select` / `Cascader` | No list virtualization anywhere in `src/` (antd `virtual` is on by default). | `grep -rl "virtual\|useVirtualizer\|react-window\|@tanstack/react-virtual" src/` → ∅ |
286
297
  | 5 | `ColorPicker` | No `presets`, and the only control in the group with no `size`/`status`/`variant`. | `data-entry.prop.ts:893-908` |
287
298
  | 6 | `PasswordStrength` | `role="img"` on a value meter instead of `role="meter"` + `aria-valuenow`. | `password-strength.tsx:74-76` |
@@ -300,7 +311,7 @@ Present: `count` (+ the legacy `max`), `allowHalf`, `allowClear`, `character` (n
300
311
  `Select`: `defaultActiveFirstOption`, `onInputKeyDown`. ·
301
312
  `Cascader`: `Cascader.Panel`. ·
302
313
  `NumberInput`: `onStep`. ·
303
- `Slider`: `Intl`-formatted default tooltip, `draggableTrack`. ·
314
+ `Slider`: `Intl`-formatted default tooltip (`draggableTrack` has landed). ·
304
315
  `ColorPicker`: `allowClear`+`onClear`, `readOnly`, `format`/`defaultFormat`/`onFormatChange`. ·
305
316
  `DatePicker`: `mode`+`onPanelChange`, `onOk`, `maxTagCount` for `multiple`, `format` as an
306
317
  array of accepted inputs, and — with `range` — `separator` (token) and `onCalendarChange`. ·