@godxjp/ui-mcp 0.19.1 → 0.21.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/dist/index.js CHANGED
@@ -712,9 +712,9 @@ function MyShell({ children }: { content: React.ReactNode }) {
712
712
  props: [
713
713
  {
714
714
  name: "variant",
715
- type: '"default" | "destructive" | "outline" | "secondary" | "ghost" | "link"',
715
+ type: '"default" | "destructive" | "outline" | "dashed" | "secondary" | "ghost" | "link"',
716
716
  defaultValue: '"default"',
717
- description: "Visual style."
717
+ description: "Visual style. `dashed` = outline with a dashed border (Ant-style add-row / placeholder action)."
718
718
  },
719
719
  {
720
720
  name: "size",
@@ -722,6 +722,12 @@ function MyShell({ children }: { content: React.ReactNode }) {
722
722
  defaultValue: '"default"',
723
723
  description: "Size preset (height, padding, icon dims)."
724
724
  },
725
+ {
726
+ name: "shape",
727
+ type: '"default" | "pill" | "sharp"',
728
+ defaultValue: '"default"',
729
+ description: "Corner radius from the tokens \u2014 `default` (control radius), `pill` (fully rounded, --radius-pill), `sharp` (square, --radius-sharp). Use the prop instead of a `rounded-*` className."
730
+ },
725
731
  {
726
732
  name: "asChild",
727
733
  type: "boolean",
@@ -729,10 +735,21 @@ function MyShell({ children }: { content: React.ReactNode }) {
729
735
  description: "Render as Radix Slot \u2014 merge props onto the child (<a>/<Link>)."
730
736
  },
731
737
  { name: "disabled", type: "boolean", description: "Disable the button." },
738
+ {
739
+ name: "loading",
740
+ type: "boolean",
741
+ defaultValue: "false",
742
+ description: 'In-flight state \u2014 shows a leading `Loader2` spinner (replaces a leading icon if present), sets `aria-busy="true"`, and blocks activation (non-interactive, pointer-events disabled) while keeping the label visible so the width doesn\'t jump. Prefer this over a hand-rolled `<Loader2 className="animate-spin">` inside the button. Ignored when `asChild` (Slot requires a single child).'
743
+ },
744
+ {
745
+ name: "loadingText",
746
+ type: "string",
747
+ description: "Optional label to swap in while `loading` (pass the `t()`-translated string, e.g. `loadingText={t('saving')}`). When omitted the original children stay beside the spinner."
748
+ },
732
749
  {
733
750
  name: "onClick",
734
751
  type: "React.MouseEventHandler<HTMLButtonElement>",
735
- description: "Click handler."
752
+ description: "Click handler. Does not fire while `loading` or `disabled`."
736
753
  }
737
754
  ],
738
755
  usage: [
@@ -741,7 +758,8 @@ function MyShell({ children }: { content: React.ReactNode }) {
741
758
  "DO use `asChild` to render the button as a React Router/Inertia `<Link>` or native `<a>` while keeping all button styling and a11y: `<Button asChild variant=\"outline\"><Link href={route('invoices.show', id)}>\u8A73\u7D30</Link></Button>`. Never wrap a `<button>` around an `<a>` \u2014 that is invalid HTML.",
742
759
  "DON'T use raw `<button>` elements anywhere in the UI \u2014 always use this `Button`. The only exception is an `aria-hidden` native control used as an e2e/a11y hook paired with a visible godx-ui control.",
743
760
  'DO set `type="submit"` explicitly on form submit buttons (the default HTML button type inside `<form>` is already `submit`, but being explicit prevents accidental double-submissions when a `type="button"` sibling exists). For cancel/reset actions set `type="button"` to avoid accidental form submission.',
744
- "DON'T apply raw padding, height, or `rounded-*` overrides to `Button` via `className` \u2014 the size variants encode the full box model. If a custom size is truly needed, use `buttonVariants` from `@godxjp/ui/general` to compose a new cva class rather than fighting the existing ones."
761
+ "DON'T apply raw padding, height, or `rounded-*` overrides to `Button` via `className` \u2014 the size variants encode the full box model. If a custom size is truly needed, use `buttonVariants` from `@godxjp/ui/general` to compose a new cva class rather than fighting the existing ones.",
762
+ "DO use the `loading` prop for async/pending actions instead of hand-rolling `<Loader2 className=\"animate-spin\">` inside the button \u2014 `loading` renders the spinner, sets `aria-busy`, and blocks activation for you; pair with `loadingText={t('saving')}` to swap the label. For a TanStack Query refetch use `ButtonRefetch` (it owns its own loading lifecycle) rather than wiring `loading` manually."
745
763
  ],
746
764
  useCases: [
747
765
  'Primary form submission in a Dialog or Sheet (e.g. `<Button type="submit" disabled={form.processing}>\u4FDD\u5B58</Button>`) \u2014 the `disabled` prop greys it out and blocks pointer events, preventing double-submit during async operations.',
@@ -768,6 +786,98 @@ import { Trash2 } from "lucide-react";
768
786
  storyPath: "general/Button.stories.tsx",
769
787
  rules: [23]
770
788
  },
789
+ {
790
+ name: "Text",
791
+ group: "general",
792
+ tagline: 'Typographic primitive \u2014 use INSTEAD of a hand-rolled `<span className="text-[13px] font-medium text-muted-foreground">`. Size is a type-scale step (never px); tone/weight are tokens.',
793
+ props: [
794
+ {
795
+ name: "size",
796
+ type: '"2xs" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl"',
797
+ defaultValue: '"sm"',
798
+ description: "Golden-ratio type-scale step (2xs\u20262xl). NEVER an arbitrary px (`text-[13px]` is banned) \u2014 pick the nearest step."
799
+ },
800
+ {
801
+ name: "tone",
802
+ type: '"default" | "muted" | "primary" | "success" | "warning" | "destructive" | "info"',
803
+ defaultValue: '"default"',
804
+ description: "Semantic foreground colour. Replaces `text-muted-foreground` etc. on a raw span."
805
+ },
806
+ {
807
+ name: "weight",
808
+ type: '"regular" | "medium" | "bold"',
809
+ defaultValue: '"regular"',
810
+ description: "Font weight \u2014 the 3-weight canon: regular 400 (body), medium 500 (label), bold 700 (emphasis). 600/semibold is forbidden."
811
+ },
812
+ { name: "align", type: '"start" | "center" | "end"', description: "Logical text alignment." },
813
+ { name: "truncate", type: "boolean", description: "Single-line ellipsis." },
814
+ { name: "tabular", type: "boolean", description: "Tabular figures for aligned numbers." },
815
+ { name: "mono", type: "boolean", description: "Monospace family for codes / ids." },
816
+ {
817
+ name: "as",
818
+ type: '"span" | "p" | "div" | "label" | "strong" | "em" | "small" | "code" | "kbd" | "dt" | "dd" | "caption" | "abbr"',
819
+ defaultValue: '"span"',
820
+ description: "Rendered element. `code`/`kbd` are monospace by default."
821
+ }
822
+ ],
823
+ usage: [
824
+ "DO use `<Text>` for ALL body / inline / caption text instead of a styled `<span>`/`<p>`. Pick `size` from the scale; never write `text-[13px]`/`text-[11px]` or `font-semibold` by hand.",
825
+ 'DO use `tone` for colour (`muted`/`primary`/semantic), `tabular` for numbers, `mono` for codes \u2014 not `className="text-muted-foreground font-mono tabular-nums"`.',
826
+ "For a heading, use `<Heading level>` instead of a large-size `<Text>`."
827
+ ],
828
+ useCases: [
829
+ 'A muted caption under a value: `<Text size="xs" tone="muted">2026\u5E745\u6708\u5EA6</Text>`.',
830
+ 'A monospace id in a list row: `<Text size="xs" mono tone="muted">RC-204881</Text>`.',
831
+ 'An emphasized inline figure: `<Text weight="medium" tabular>\xA51,240,000</Text>`.'
832
+ ],
833
+ storyPath: "general/typography.tsx",
834
+ rules: [2, 23],
835
+ example: `import { Text } from "@godxjp/ui/general";
836
+
837
+ <Text size="xs" tone="muted">\u88DC\u52A9\u30C6\u30AD\u30B9\u30C8</Text>
838
+ <Text weight="medium" tabular>\xA51,240,000</Text>
839
+ <Text size="xs" mono tone="muted">RC-204881</Text>`
840
+ },
841
+ {
842
+ name: "Heading",
843
+ group: "general",
844
+ tagline: "Section heading sized from the --heading-h* tokens. `level` sets the size AND the semantic <h1..h4>.",
845
+ props: [
846
+ {
847
+ name: "level",
848
+ type: "1 | 2 | 3 | 4",
849
+ defaultValue: "2",
850
+ description: "Heading level \u2014 sizes from --heading-h{1..4} and renders the matching <h*>."
851
+ },
852
+ {
853
+ name: "as",
854
+ type: '"h1" | "h2" | "h3" | "h4" | "div"',
855
+ description: "Override the rendered element (e.g. a visual h2 that is a real <h1>)."
856
+ },
857
+ {
858
+ name: "tone",
859
+ type: '"default" | "muted" | "primary" | "success" | "warning" | "destructive" | "info"',
860
+ defaultValue: '"default"',
861
+ description: "Semantic foreground colour."
862
+ },
863
+ { name: "align", type: '"start" | "center" | "end"', description: "Logical text alignment." },
864
+ { name: "truncate", type: "boolean", description: "Single-line ellipsis." }
865
+ ],
866
+ usage: [
867
+ 'DO use `<Heading level>` for section titles instead of a raw `<h2 className="text-lg font-semibold">`. The level drives both the token size and the semantic element.',
868
+ "Inside a Card use `<CardTitle>`; use `<Heading>` for free-standing page/section headings not covered by a component slot."
869
+ ],
870
+ useCases: [
871
+ "A section heading on a dashboard: `<Heading level={3}>\u4ECA\u6708\u306EKPI</Heading>`.",
872
+ 'A visually-smaller heading that must stay an <h1> for a11y: `<Heading level={1} as="h1">\u2026</Heading>`.'
873
+ ],
874
+ storyPath: "general/typography.tsx",
875
+ rules: [6, 23],
876
+ example: `import { Heading } from "@godxjp/ui/general";
877
+
878
+ <Heading level={2}>\u8ACB\u6C42\u66F8\u4E00\u89A7</Heading>
879
+ <Heading level={3} tone="muted">\u88DC\u8DB3\u30BB\u30AF\u30B7\u30E7\u30F3</Heading>`
880
+ },
771
881
  // ─── data-display ───────────────────────────────────────────────────────
772
882
  {
773
883
  name: "DataTable",
@@ -1015,6 +1125,13 @@ export default function InvoiceList({
1015
1125
  "Compose the compound parts as children: <DataGrid.Toolbar> (holds <DataGrid.BulkActions>, <DataGrid.Search>, <DataGrid.ViewOptions>, <DataGrid.DensityToggle>), then <DataGrid.Content> (auto-included if omitted) and <DataGrid.Pagination pageSizeOptions=[...]>.",
1016
1126
  "Server mode (default): drive sorting/globalFilter/pagination from useQuery and pass rowCount. Client mode: set manualSorting/manualFiltering/manualPagination={false} and the grid handles it on the data array."
1017
1127
  ],
1128
+ useCases: [
1129
+ "Server-paginated \u4ED5\u8A33 (journal entry) or \u8ACB\u6C42 (invoice) admin list backed by an AJAX/useQuery endpoint: drive sorting + globalFilter + pagination from the query and pass rowCount \u2014 the grid never loads the whole table into the browser. (Prefer DataTable here if the screen must NOT pull @tanstack/react-table.)",
1130
+ "Member / employee directory with a user-toggled 'set view' column picker (DataGrid.ViewOptions) \u2014 let admins hide columns like \u5165\u793E\u65E5 or \u90E8\u7F72 they don't need, persisting the columnVisibility state per user.",
1131
+ "Bulk-operation worklist (e.g. approve/export selected \u7D4C\u8CBB\u7CBE\u7B97 rows): enableRowSelection + DataGrid.BulkActions to show a 'N\u4EF6\u9078\u629E\u4E2D' action bar with \u4E00\u62EC\u627F\u8A8D / CSV\u51FA\u529B buttons only when rows are checked.",
1132
+ "Dense reconciliation or ledger table where operators flip between compact and comfortable row height via DataGrid.DensityToggle to fit more rows on screen during data-entry-heavy sessions.",
1133
+ "Client-side grid for a fully-loaded small dataset (e.g. a fixed master list of \u52D8\u5B9A\u79D1\u76EE): set manualSorting/manualFiltering/manualPagination={false} so TanStack sorts, searches, and paginates in-browser without any server round-trip."
1134
+ ],
1018
1135
  related: ["DataTable", "Table", "DataState", "Select", "DropdownMenu"],
1019
1136
  example: `import { DataGrid, type ColumnDef } from "@godxjp/ui/data-grid";
1020
1137
  import { Flex } from "@godxjp/ui/layout";
@@ -1225,14 +1342,25 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
1225
1342
  props: [
1226
1343
  {
1227
1344
  name: "variant",
1228
- type: '"default" | "secondary" | "outline" | "success" | "warning" | "destructive" | "info" | "neutral"',
1345
+ type: '"default" | "secondary" | "outline" | "dashed"',
1229
1346
  defaultValue: '"default"',
1230
- description: "Visual variant. Overrides the auto-mapped status tone when status is provided."
1347
+ description: "STRUCTURAL emphasis only (fill/border style) \u2014 NOT colour. Use `tone` for semantic colour. `dashed` = dashed border."
1348
+ },
1349
+ {
1350
+ name: "tone",
1351
+ type: '"default" | "success" | "warning" | "destructive" | "info" | "muted" | "neutral"',
1352
+ description: "SEMANTIC colour intent (ToneProp). This is the colour knob \u2014 success/warning/destructive/info/etc. Keep variant for structure, tone for meaning."
1353
+ },
1354
+ {
1355
+ name: "shape",
1356
+ type: '"default" | "pill" | "sharp"',
1357
+ defaultValue: '"default"',
1358
+ description: "Corner radius from the tokens \u2014 `default` (badge radius), `pill` (fully rounded), `sharp` (square). Use the prop instead of a `rounded-*` className."
1231
1359
  },
1232
1360
  {
1233
1361
  name: "status",
1234
1362
  type: "string",
1235
- description: "Lifecycle key. Known keys auto-map to variant + icon + i18n label; unknown keys fall back to neutral."
1363
+ description: "Lifecycle key. Known keys auto-map to tone + icon + i18n label; unknown keys fall back to neutral."
1236
1364
  },
1237
1365
  {
1238
1366
  name: "icon",
@@ -1801,6 +1929,119 @@ import { ResponsiveGrid } from "@godxjp/ui/layout";
1801
1929
  storyPath: "data-entry/Input.stories.tsx",
1802
1930
  rules: []
1803
1931
  },
1932
+ {
1933
+ name: "NumberInput",
1934
+ group: "data-entry",
1935
+ tagline: "WAI-ARIA spinbutton for localized numeric entry \u2014 composes the real Input (role=spinbutton, inputMode=decimal) with stacked increment/decrement step Buttons. Type freely, Arrow/Shift-Arrow step, value commits clamped to min/max + rounded to precision.",
1936
+ props: [
1937
+ {
1938
+ name: "value",
1939
+ type: "number | null",
1940
+ description: "Controlled value. `null` = empty field. Pair with `onValueChange`."
1941
+ },
1942
+ {
1943
+ name: "defaultValue",
1944
+ type: "number | null",
1945
+ defaultValue: "null",
1946
+ description: "Uncontrolled initial value."
1947
+ },
1948
+ {
1949
+ name: "onValueChange",
1950
+ type: "(value: number | null) => void",
1951
+ description: "Value change callback (vocabulary triad \u2014 NOT onChange). Receives `null` when the field is empty."
1952
+ },
1953
+ {
1954
+ name: "min",
1955
+ type: "number",
1956
+ description: "Lower bound \u2014 clamps on commit and disables the decrement stepper at the floor."
1957
+ },
1958
+ {
1959
+ name: "max",
1960
+ type: "number",
1961
+ description: "Upper bound \u2014 clamps on commit and disables the increment stepper at the ceiling."
1962
+ },
1963
+ {
1964
+ name: "step",
1965
+ type: "number",
1966
+ defaultValue: "1",
1967
+ description: "Increment for the steppers + ArrowUp/ArrowDown (Shift = \xD710)."
1968
+ },
1969
+ {
1970
+ name: "precision",
1971
+ type: "number",
1972
+ description: "Committed decimal places. Inferred from `step` when omitted."
1973
+ },
1974
+ { name: "disabled", type: "boolean", description: "Disables typing and stepping." },
1975
+ {
1976
+ name: "readOnly",
1977
+ type: "boolean",
1978
+ description: "Value is shown and selectable but not typeable or steppable."
1979
+ },
1980
+ {
1981
+ name: "size",
1982
+ type: '"xs" | "sm" | "md" | "lg"',
1983
+ defaultValue: '"md"',
1984
+ description: "Control height tier (--control-height). Aligns with sibling controls on a row."
1985
+ },
1986
+ { name: "placeholder", type: "string", description: "Placeholder shown when empty." },
1987
+ {
1988
+ name: "prefix",
1989
+ type: "React.ReactNode",
1990
+ description: "Leading decorative affix inside the field (e.g. `\xA5`). aria-hidden."
1991
+ },
1992
+ {
1993
+ name: "suffix",
1994
+ type: "React.ReactNode",
1995
+ description: "Trailing decorative affix inside the field (e.g. `%`). aria-hidden."
1996
+ },
1997
+ {
1998
+ name: "name",
1999
+ type: "string",
2000
+ description: "Form field name \u2014 submits its value natively."
2001
+ },
2002
+ { name: "id", type: "string", description: "Associates with a <label htmlFor> / FormField." },
2003
+ {
2004
+ name: "aria-label",
2005
+ type: "string",
2006
+ description: "Accessible name for the spinbutton when no visible FormField label is present."
2007
+ }
2008
+ ],
2009
+ usage: [
2010
+ "DO use NumberInput (not `<Input type='number'>`) whenever numeric entry wants steppers, min/max clamping, precision rounding, or a \xA5/% affix \u2014 it is the canonical numeric primitive. Plain Input has no stepper and no clamp.",
2011
+ "DO drive it controlled with `value` + `onValueChange` carrying `number | null` (the vocabulary triad \u2014 NOT `onChange`). `null` means the field is empty; never substitute 0 for empty.",
2012
+ "DON'T pass `value` without `onValueChange` \u2014 like every controlled @godxjp/ui input it would freeze. Omit both for uncontrolled (use `defaultValue`).",
2013
+ "DO set `step` to your increment and let `precision` (or the decimals of `step`) round the committed value: `step={0.25} precision={2}` gives quarter-step entry rounded to 2 places on blur/Enter.",
2014
+ "DO set `min`/`max` for bounded quantities \u2014 the value clamps on commit and the matching stepper Button auto-disables at the bound. The steppers are tabIndex=-1 so they never pollute the keyboard tab order (Arrow keys cover keyboard stepping).",
2015
+ "DON'T wrap it in a hand-rolled label/error markup \u2014 compose it inside FormField (matching `id`) for the aria wiring, exactly like Input.",
2016
+ "DON'T format the value yourself for display \u2014 NumberInput formats at rest via Intl.NumberFormat in the active locale while keeping the raw value typeable on focus."
2017
+ ],
2018
+ useCases: [
2019
+ "Quantity / line-item steppers in order, invoice, or cart forms (min={1}, step={1}) where \xB1 buttons and a floor are expected.",
2020
+ "Price / amount fields with a currency affix (prefix='\xA5', step={10}) \u2014 the affix is decorative and the committed value stays a plain number.",
2021
+ "Percentage / rate inputs bounded 0\u2013100 (suffix='%', min={0} max={100}).",
2022
+ "Decimal measurements \u2014 weight, dimensions, exchange rates (step={0.25}, precision={2}) needing rounded commit.",
2023
+ "Any bounded numeric setting (timeouts, retry counts, page sizes) where a slider is too coarse and a free Input lacks clamping."
2024
+ ],
2025
+ related: [
2026
+ "Input \u2014 the plain single-line field NumberInput composes; use Input directly only for free numeric text with no stepper/clamp need.",
2027
+ "Slider \u2014 use instead when the user picks an approximate value within a range by dragging; NumberInput is for exact keyed entry.",
2028
+ "FormField \u2014 compose NumberInput inside FormField (matching id) for label/helper/error a11y wiring.",
2029
+ "TimeInput \u2014 the HH:mm sibling spinbutton; NumberInput is for plain numbers, TimeInput for clock times."
2030
+ ],
2031
+ storyPath: "data-entry/NumberInput.stories.tsx",
2032
+ rules: [3, 6],
2033
+ example: `import { NumberInput } from "@godxjp/ui/data-entry";
2034
+
2035
+ <NumberInput
2036
+ value={qty}
2037
+ onValueChange={setQty}
2038
+ min={1}
2039
+ max={99}
2040
+ step={1}
2041
+ prefix="\xA5"
2042
+ aria-label="\u6570\u91CF"
2043
+ />`
2044
+ },
1804
2045
  {
1805
2046
  name: "SearchInput",
1806
2047
  group: "data-entry",
@@ -3316,7 +3557,7 @@ export function InvoicePeriodFilter() {
3316
3557
  description: "Initial value for uncontrolled mode. Same shape as value."
3317
3558
  },
3318
3559
  {
3319
- name: "onChange",
3560
+ name: "onValueChange",
3320
3561
  type: "(value: string[] | string[][], selectedOptions?: TreeOptionProp[] | TreeOptionProp[][]) => void",
3321
3562
  description: "Fires when selection changes. First arg is the selected path(s); second is the matching node objects. On clear, called with []."
3322
3563
  },
@@ -3500,7 +3741,7 @@ function MultiRegionPicker() {
3500
3741
  description: "Initial value for uncontrolled usage. Ignored once `value` is provided."
3501
3742
  },
3502
3743
  {
3503
- name: "onChange",
3744
+ name: "onValueChange",
3504
3745
  type: "(value: string | string[] | undefined) => void",
3505
3746
  description: "Called on selection change. Returns `string` in single mode, `string[]` in multi/checkable mode, or `undefined` when cleared."
3506
3747
  },
@@ -6024,18 +6265,35 @@ export default function PasswordBlock() {
6024
6265
  group: "navigation",
6025
6266
  tagline: "Context menu primitives with keyboard support and compound parts for command-style action surfaces.",
6026
6267
  props: [
6027
- { name: "open", type: "boolean", description: "Controlled open state." },
6028
6268
  {
6029
6269
  name: "onOpenChange",
6030
6270
  type: "(open: boolean) => void",
6031
6271
  description: "Open-state callback."
6032
6272
  },
6033
- { name: "value", type: "string", description: "Selected value (for controlled patterns)." }
6273
+ {
6274
+ name: "modal",
6275
+ type: "boolean",
6276
+ defaultValue: "true",
6277
+ description: "Modal mode \u2014 locks scroll + outside interaction while open. Set false to keep the rest of the page interactive."
6278
+ },
6279
+ {
6280
+ name: "dir",
6281
+ type: '"ltr" | "rtl"',
6282
+ description: "Reading direction for arrow-key navigation (inherits from the document if omitted)."
6283
+ }
6284
+ ],
6285
+ usage: [
6286
+ "DO trigger this on `onContextMenu` (right-click / long-press), NOT on left-click \u2014 for a button that opens a list of actions on left-click use `DropdownMenu` instead. The two are not interchangeable.",
6287
+ "DO wrap exactly the right-clickable surface in `<ContextMenuTrigger>` (a table row, a card, a file tile) \u2014 the menu anchors to the pointer position, so the trigger should be the whole interactive region the menu acts on.",
6288
+ "DON'T put primary, always-visible actions only behind a context menu \u2014 right-click is a discoverability dead-end on touch and for new users. Mirror critical actions in a visible `Button`/`DropdownMenu` and use ContextMenu as an accelerator.",
6289
+ 'DO mark irreversible items with `variant="destructive"` (\u524A\u9664 / \u53D6\u308A\u6D88\u3057) and group them under a `<ContextMenuSeparator>`; use `<ContextMenuShortcut>` to show the keyboard accelerator, `<ContextMenuSub>`/`<ContextMenuSubTrigger>` for nested submenus, and `<ContextMenuCheckboxItem>`/`<ContextMenuRadioItem>` for stateful toggles.',
6290
+ "DON'T hand-roll a positioned `<div>` + `onContextMenu={e => e.preventDefault()}` \u2014 the primitive already gives you keyboard navigation, focus trapping, typeahead, and WAI-ARIA menu semantics for free."
6034
6291
  ],
6035
6292
  useCases: [
6036
- "Right-click action menu",
6037
- "Contextual menus for rows and cards",
6038
- "Nested action rows with shortcuts"
6293
+ "Right-click actions on a DataTable/DataGrid row (\u8A73\u7D30 / \u8907\u88FD / \u524A\u9664) as a power-user accelerator alongside the visible row action button.",
6294
+ "Contextual menu on a file or document tile in an upload/asset manager (\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9 / \u540D\u524D\u5909\u66F4 / \u524A\u9664).",
6295
+ "Nested action menu with submenus and shortcuts (e.g. '\u30A8\u30AF\u30B9\u30DD\u30FC\u30C8 \u25B8 CSV / PDF') on a report card.",
6296
+ "Stateful toggles on a board/kanban card via ContextMenuCheckboxItem (e.g. \u30D4\u30F3\u7559\u3081, \u5B8C\u4E86\u3068\u3057\u3066\u30DE\u30FC\u30AF)."
6039
6297
  ],
6040
6298
  storyPath: "navigation/ContextMenu.stories.tsx",
6041
6299
  rules: [3, 6],
@@ -6059,21 +6317,34 @@ export default function PasswordBlock() {
6059
6317
  group: "navigation",
6060
6318
  tagline: "Application menubar primitives (menus, sub-menus, and check/radio items).",
6061
6319
  props: [
6320
+ {
6321
+ name: "value",
6322
+ type: "string",
6323
+ description: "Controlled value of the currently-open menu (pair with onValueChange)."
6324
+ },
6062
6325
  {
6063
6326
  name: "defaultValue",
6064
6327
  type: "string",
6065
- description: "Uncontrolled initial selected value."
6328
+ description: "Uncontrolled initial open menu."
6066
6329
  },
6067
6330
  {
6068
6331
  name: "onValueChange",
6069
6332
  type: "(value: string) => void",
6070
- description: "Selection callback."
6333
+ description: "Fires with the id of the menu that opened (or '' when all close)."
6071
6334
  }
6072
6335
  ],
6336
+ usage: [
6337
+ "DO reserve Menubar for a persistent, desktop-app-style command bar (\u30D5\u30A1\u30A4\u30EB / \u7DE8\u96C6 / \u8868\u793A \u2026) where multiple top-level menus sit side by side \u2014 moving the pointer across triggers opens the adjacent menu without an extra click.",
6338
+ "DON'T use Menubar for primary site/page navigation (links between pages) \u2014 that is `NavigationMenu`. Menubar items run *commands*; NavigationMenu items *navigate*.",
6339
+ "DON'T use Menubar when there is only one menu button \u2014 a single trigger that drops a list of actions is a `DropdownMenu`. Menubar earns its weight only with several coordinated menus.",
6340
+ "DO compose the full structure: `<Menubar>` \u203A `<MenubarMenu>` \u203A `<MenubarTrigger>` + `<MenubarContent>` with `<MenubarItem>`; use `<MenubarSeparator>` to group, `<MenubarShortcut>` for accelerators, `<MenubarSub>` for nested menus, and `<MenubarCheckboxItem>`/`<MenubarRadioItem>` for view toggles.",
6341
+ 'DO mark destructive commands with `variant="destructive"` and give every item an `onSelect` handler \u2014 items are commands, so they should *do* something, not just close.'
6342
+ ],
6073
6343
  useCases: [
6074
- "Top-bar application command menus",
6075
- "Workspace menus with nested items",
6076
- "Desktop-like navigation shells"
6344
+ "Top-bar command menu for a back-office editor (\u30D5\u30A1\u30A4\u30EB / \u7DE8\u96C6 / \u8868\u793A / \u30D8\u30EB\u30D7) with shortcuts and submenus.",
6345
+ "Workspace tool menus in an admin console where each menu groups a category of actions (\u30C7\u30FC\u30BF / \u30EC\u30DD\u30FC\u30C8 / \u8A2D\u5B9A).",
6346
+ "Desktop-like application shell (e.g. an internal POS or accounting workstation) that mirrors native menubar conventions.",
6347
+ "View-state toggles via MenubarCheckboxItem/MenubarRadioItem (e.g. \u8868\u793A \u203A \u30B0\u30EA\u30C3\u30C9\u7DDA\u3092\u8868\u793A, \u901A\u8CA8\u8868\u793A \u25B8 \xA5 / $)."
6077
6348
  ],
6078
6349
  storyPath: "navigation/Menubar.stories.tsx",
6079
6350
  rules: [3, 6],
@@ -6099,18 +6370,41 @@ export default function PasswordBlock() {
6099
6370
  defaultValue: '"horizontal"',
6100
6371
  description: "Main-axis arrangement for the nav menu."
6101
6372
  },
6373
+ {
6374
+ name: "value",
6375
+ type: "string",
6376
+ description: "Controlled value of the currently-open item (pair with onValueChange)."
6377
+ },
6102
6378
  {
6103
6379
  name: "defaultValue",
6104
6380
  type: "string",
6105
- description: "Uncontrolled initial selected value."
6381
+ description: "Uncontrolled initial open item."
6106
6382
  },
6107
6383
  {
6108
6384
  name: "onValueChange",
6109
6385
  type: "(value: string) => void",
6110
- description: "Selection callback."
6386
+ description: "Fires with the id of the item whose dropdown opened (or '' when all close)."
6387
+ },
6388
+ {
6389
+ name: "delayDuration",
6390
+ type: "number",
6391
+ defaultValue: "200",
6392
+ description: "Hover delay (ms) before a trigger's content opens."
6111
6393
  }
6112
6394
  ],
6113
- useCases: ["Primary app navigation", "Sectioned marketing navigation", "Nested link groups"],
6395
+ usage: [
6396
+ "DO use NavigationMenu for primary *navigation* between pages/sections \u2014 items wrap `<NavigationMenuLink>` (an `<a>`), not command buttons. For command bars (\u30D5\u30A1\u30A4\u30EB/\u7DE8\u96C6 \u2026) use `Menubar`; for a single action drop-down use `DropdownMenu`.",
6397
+ "DO render real links inside `<NavigationMenuLink asChild>` so SPA routers work: `<NavigationMenuLink asChild><Link href={route('reports.index')}>\u30EC\u30DD\u30FC\u30C8</Link></NavigationMenuLink>` \u2014 never nest a raw `<a>` directly with its own onClick navigation.",
6398
+ "DO use `<NavigationMenuTrigger>` + `<NavigationMenuContent>` only when an item needs a rich dropdown panel (link groups, featured cards). Top-level items that go straight to a page should be a bare `<NavigationMenuLink>` with NO trigger.",
6399
+ "DON'T use it as the app's left sidebar \u2014 for a persistent vertical app sidebar use `Sidebar`/`AppShell`. Set `orientation=\"vertical\"` only for an in-content vertical link menu, not the global shell.",
6400
+ "DON'T hand-roll the hover/focus dropdown timing \u2014 the primitive manages open-on-hover with `delayDuration`, keyboard navigation, and the animated viewport for you."
6401
+ ],
6402
+ useCases: [
6403
+ "Primary top navigation for an admin/portal app with dropdown panels grouping related pages (e.g. \u30EC\u30DD\u30FC\u30C8 \u25BE \u2192 \u58F2\u4E0A / \u7D4C\u8CBB / \u5165\u91D1).",
6404
+ "Sectioned marketing or docs navigation with featured link cards inside NavigationMenuContent.",
6405
+ "Nested link groups where one trigger reveals a multi-column panel of related destinations.",
6406
+ "Vertical in-content navigation (orientation='vertical') for a settings or documentation area \u2014 distinct from the global Sidebar shell."
6407
+ ],
6114
6408
  storyPath: "navigation/NavigationMenu.stories.tsx",
6115
6409
  rules: [3, 6],
6116
6410
  example: `import { NavigationMenu, NavigationMenuList, NavigationMenuItem, NavigationMenuTrigger } from "@godxjp/ui/navigation";
@@ -6128,12 +6422,53 @@ export default function PasswordBlock() {
6128
6422
  group: "layout",
6129
6423
  tagline: "Resizable panel group/child/handle primitives from react-resizable-panels.",
6130
6424
  props: [
6131
- { name: "id", type: "string", description: "Panel identifier for persistence." },
6132
- { name: "defaultSize", type: "number", description: "Initial panel size (percent/units)." },
6133
- { name: "minSize", type: "number", description: "Minimum size constraint." },
6134
- { name: "maxSize", type: "number", description: "Maximum size constraint." }
6425
+ {
6426
+ name: "orientation",
6427
+ type: '"horizontal" | "vertical"',
6428
+ defaultValue: '"horizontal"',
6429
+ description: "ResizablePanelGroup prop \u2014 axis the panels are laid out / resized along (horizontal = side-by-side)."
6430
+ },
6431
+ {
6432
+ name: "id",
6433
+ type: "string",
6434
+ description: "ResizablePanel identifier \u2014 required for collapse/expand control and for layout persistence."
6435
+ },
6436
+ {
6437
+ name: "defaultSize",
6438
+ type: "number",
6439
+ description: "ResizablePanel initial size as a percentage (0\u2013100) of the group."
6440
+ },
6441
+ {
6442
+ name: "minSize",
6443
+ type: "number",
6444
+ description: "ResizablePanel minimum size (%) \u2014 drag can't shrink below this."
6445
+ },
6446
+ { name: "maxSize", type: "number", description: "ResizablePanel maximum size (%)." },
6447
+ {
6448
+ name: "collapsible",
6449
+ type: "boolean",
6450
+ defaultValue: "false",
6451
+ description: "ResizablePanel \u2014 allow the panel to collapse to collapsedSize when dragged below minSize. Pair with onResize to react to collapse."
6452
+ },
6453
+ {
6454
+ name: "onResize",
6455
+ type: "(size: PanelSize, id, prevSize) => void",
6456
+ description: "ResizablePanel \u2014 fires while/after the panel is resized (e.g. to persist layout)."
6457
+ }
6458
+ ],
6459
+ usage: [
6460
+ 'DO put the layout on `<ResizablePanelGroup orientation="horizontal|vertical">`, the resizable regions in `<ResizablePanel>`, and a `<ResizableHandle>` BETWEEN every adjacent pair \u2014 a group of N panels needs N-1 handles or there is nothing to drag.',
6461
+ "DO size panels with `defaultSize`/`minSize`/`maxSize` as PERCENTAGES (the group totals 100), not pixels \u2014 don't fight this with a fixed `w-[280px]` className on the panel.",
6462
+ "DON'T reach for ResizablePanel when the split is fixed and never user-adjustable \u2014 use a plain `Flex`/`ResponsiveGrid`, or `SplitPane` for a simple two-pane layout. Resizable is for *user-draggable* boundaries only.",
6463
+ "DO give each panel a stable `id` and use `collapsible` + `collapsedSize` for a side panel the user can fully tuck away (e.g. a filters rail), reacting via `onResize`.",
6464
+ "DON'T hand-roll a draggable divider with mouse-move listeners \u2014 the primitive handles pointer + keyboard resizing, ARIA separator semantics, and min/max clamping. Always render `<ResizableHandle>`, never a bare styled `<div>`."
6465
+ ],
6466
+ useCases: [
6467
+ "Master\u2013detail admin layout: a draggable list pane on the left and a detail/preview pane on the right (e.g. \u4ED5\u8A33\u4E00\u89A7 | \u4ED5\u8A33\u8A73\u7D30).",
6468
+ "Collapsible filters or navigation rail beside a data table that operators can widen for long labels or tuck away to maximize the table.",
6469
+ "Stacked vertical split (orientation='vertical') such as a results table over a live JSON/log preview in a data-import tool.",
6470
+ "Three-pane workbench (nav | content | inspector) where each boundary is independently draggable and layout is persisted via id + onResize."
6135
6471
  ],
6136
- useCases: ["Split-pane layouts", "Resizable sidebars", "Code editors with adjustable zones"],
6137
6472
  storyPath: "layout/ResizablePanel.stories.tsx",
6138
6473
  rules: [3, 6],
6139
6474
  example: `import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from "@godxjp/ui/layout";
@@ -6162,10 +6497,23 @@ export default function PasswordBlock() {
6162
6497
  {
6163
6498
  name: "setApi",
6164
6499
  type: "(api: CarouselApi) => void",
6165
- description: "Receive carousel API for custom logic."
6500
+ description: "Receive the Embla api for custom logic (autoplay, external prev/next). NOT needed for dots \u2014 CarouselDots reads the api from context itself."
6166
6501
  }
6167
6502
  ],
6168
- useCases: ["Feature cards", "Image galleries", "Horizontal stepping lists"],
6503
+ usage: [
6504
+ "DO compose the full set: `<Carousel>` \u203A `<CarouselContent>` \u203A many `<CarouselItem>`, with `<CarouselPrevious>`/`<CarouselNext>` for arrows and `<CarouselDots>` for the indicator row. Don't render items outside `<CarouselContent>` \u2014 the track is the scroll container.",
6505
+ "DO use `<CarouselDots>` for the active-slide indicator instead of wiring `setApi` by hand \u2014 it reads `selectedIndex`/`scrollSnaps` from the Carousel context, renders one `aria-current` dot per snap, and auto-hides when there is \u22641 slide.",
6506
+ "DON'T use a Carousel where ALL items must be seen/compared at once or be keyboard-reachable in reading order (e.g. a list of selectable options, a data table, primary navigation) \u2014 hiding content behind a swipe is an anti-pattern there; use a Grid/`ResponsiveGrid`, `ScrollArea`, or `Tabs`.",
6507
+ "DON'T autoplay without a pause-on-hover/focus control and reduced-motion respect \u2014 pass the Embla autoplay plugin via `plugins` only for non-essential decorative content, never for content the user must read.",
6508
+ "DO set `opts={{ loop: true }}` for galleries that wrap, and rely on the built-in disabling: `CarouselPrevious`/`CarouselNext` auto-disable at the ends (via `canScrollPrev`/`canScrollNext`) \u2014 don't hide them, let them grey out.",
6509
+ "DO give each `<CarouselItem>` real, meaningful content; the component already injects an 'N of M' slide label for screen readers, so don't add a redundant one (a consumer `aria-label` on the item overrides the default)."
6510
+ ],
6511
+ useCases: [
6512
+ "Feature / onboarding highlight cards on a dashboard or landing surface, with CarouselDots showing position.",
6513
+ "Image or document thumbnail gallery (e.g. uploaded receipts / \u7269\u4EF6\u5199\u771F) with looping and prev/next arrows.",
6514
+ "Horizontal stepping list of compact KPI or announcement cards that overflow the viewport width.",
6515
+ "Product/plan comparison cards on a marketing page where swiping between a few options is acceptable (not the primary action)."
6516
+ ],
6169
6517
  storyPath: "data-display/Carousel.stories.tsx",
6170
6518
  rules: [3, 6],
6171
6519
  example: `import { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext, CarouselDots } from "@godxjp/ui/data-display";
@@ -6197,10 +6545,28 @@ export default function PasswordBlock() {
6197
6545
  type: "(value: string) => void",
6198
6546
  description: "Validated value callback."
6199
6547
  },
6200
- { name: "step", type: "number", defaultValue: "1", description: "Minute step." },
6548
+ {
6549
+ name: "step",
6550
+ type: "number",
6551
+ defaultValue: "1",
6552
+ description: "Minute step (clamped 1\u201359). Snaps the committed minute to the nearest lower multiple and sets the ArrowUp/ArrowDown increment."
6553
+ },
6201
6554
  { name: "name", type: "string", description: "Form field name." }
6202
6555
  ],
6203
- useCases: ["Time filters", "Schedule pickers (calendar-free)", "HH:mm-only forms"],
6556
+ usage: [
6557
+ "DO treat the value as a plain `HH:mm` string (24-hour, zero-padded) \u2014 TimeInput is calendar-free. For a date, or a date+time, use `DatePicker`/`Calendar`; for a richer dropdown time selector use `TimePicker`.",
6558
+ "DO drive it controlled with `value` + `onValueChange` (it follows the vocabulary \u2014 NOT `onChange`/`defaultValue` pairing for control). `onValueChange` fires only with a VALID, step-snapped `HH:mm`, so your state never holds a half-typed value.",
6559
+ "DON'T pass `value` without `onValueChange` \u2014 like every controlled @godxjp/ui input that freezes the field. Omit both for uncontrolled (use `defaultValue`).",
6560
+ "DO set `step` to your scheduling granularity (e.g. `15` or `30`) \u2014 the user can still type freely, but blur/Enter snaps the minute down to the nearest multiple, and ArrowUp/ArrowDown step by that amount (wrapping across midnight).",
6561
+ "DO let the built-in masking + validation work: digits auto-format to `HH:mm`, invalid input sets `aria-invalid` and is reverted on blur. Don't add your own regex/onChange masking on top.",
6562
+ "DON'T use it for a duration/elapsed time that can exceed 23:59 \u2014 it's a clock time-of-day input (00:00\u201323:59). Use a numeric Input for durations."
6563
+ ],
6564
+ useCases: [
6565
+ "\u52E4\u6020 (attendance) start/end time fields \u2014 \u51FA\u52E4 / \u9000\u52E4 HH:mm entry with step={1} or a rounding step for shift schedules.",
6566
+ "Business-hours / reservation slot editor where times snap to a 15- or 30-minute grid (step={15}).",
6567
+ "A from\u2013to time range filter on a report or log screen (two TimeInputs) with no date component.",
6568
+ "Any calendar-free HH:mm-only form field (e.g. a recurring daily batch time, a \u7DE0\u3081\u6642\u523B)."
6569
+ ],
6204
6570
  storyPath: "data-entry/TimeInput.stories.tsx",
6205
6571
  rules: [3, 6],
6206
6572
  example: `import { TimeInput } from "@godxjp/ui/data-entry";
@@ -6671,6 +7037,11 @@ var CARDINAL_RULES = [
6671
7037
  number: 41,
6672
7038
  title: "Drawer & dialog footer layout",
6673
7039
  body: 'Sheet/Dialog/AlertDialog footers are a pinned action bar (Ant Design Drawer footer): the footer sticks to the bottom, SheetFooter draws a full-bleed top border, and actions are RIGHT-aligned with the PRIMARY button rightmost (Cancel/secondary to its left). A destructive / clear / reset action goes far-LEFT \u2014 give that button `className="mr-auto"`. NEVER stack footer buttons full-width or center them.'
7040
+ },
7041
+ {
7042
+ number: 42,
7043
+ title: "Props & Tokens Before Customization",
7044
+ body: "Before reaching for a Tailwind class, inline `style`, or extra CSS, you MUST first check whether the component already supports the need via a PROP, a design TOKEN, or a layout/typography PRIMITIVE. godx-ui is meant to be enough on its own (Ant-Design-style): `className` is for genuine one-offs only \u2014 never to redo what an API already does. Specifically: (1) NEVER hand-roll typography \u2014 no `text-[13px]`/`text-[11px]` arbitrary px (bypasses the golden type scale), no `font-medium`/`font-semibold`/`text-muted-foreground` on a raw `<span>`; use `<Text size tone weight tabular mono>` / `<Heading level>`. (2) NEVER hand-roll a trivial flex/grid wrapper; use `<Flex>` / `<ResponsiveGrid>` / `<PageContainer>`. (3) NEVER set a control's radius/height/colour with a utility when a `shape`/`size`/`tone`/token exists. If a real need has NO prop/token/primitive, that is a library GAP \u2014 file it (draft_bug_report), don't paper over it with ad-hoc Tailwind."
6674
7045
  }
6675
7046
  ];
6676
7047
  function findRule(num) {
@@ -9633,7 +10004,10 @@ function lintJsx(jsx) {
9633
10004
  check(/<input[\s>]/, "Use `<Input>` instead of raw `<input>` (rule 29).");
9634
10005
  check(/<select[\s>]/, "Use `<Select>` instead of raw `<select>` (rule 29).");
9635
10006
  check(/<textarea[\s>]/, "Use `<Textarea>` instead of raw `<textarea>` (rule 29).");
9636
- check(/<(table|thead|tbody)[\s>]/, "Use `<DataTable>` instead of a hand-rolled `<table>` (rule 29).");
10007
+ check(
10008
+ /<(table|thead|tbody)[\s>]/,
10009
+ "Use `<DataTable>` instead of a hand-rolled `<table>` (rule 29)."
10010
+ );
9637
10011
  check(
9638
10012
  /bg-(red|blue|green|yellow|gray|slate|zinc|neutral|stone|orange|amber|lime|emerald|teal|cyan|sky|indigo|violet|purple|fuchsia|pink|rose)-\d{2,3}\b/,
9639
10013
  "Use semantic token utilities (`bg-primary`/`bg-destructive`) not raw color scales (rule 2)."
@@ -9646,6 +10020,10 @@ function lintJsx(jsx) {
9646
10020
  /size=["']default["']/,
9647
10021
  '`size="default"` is not in the controlled vocabulary \u2014 use `size` \u2208 xs|sm|md|lg.'
9648
10022
  );
10023
+ check(
10024
+ /\btext-\[[0-9.]+px\]/,
10025
+ "Arbitrary text size `text-[Npx]` bypasses the golden type scale \u2014 use `<Text size>` / `<Heading level>` (rule 42)."
10026
+ );
9649
10027
  check(
9650
10028
  /<Tag[\s\S]*?color=["']error["']/i,
9651
10029
  'Tag `color="error"` \u2192 `"destructive"` (v5.0, PR #60).'
@@ -9844,7 +10222,7 @@ ${c.example}
9844
10222
  // package.json
9845
10223
  var package_default = {
9846
10224
  name: "@godxjp/ui-mcp",
9847
- version: "0.19.1",
10225
+ version: "0.21.0",
9848
10226
  description: "Model Context Protocol server for @godxjp/ui \u2014 gives Claude Code / Codex CLI / Cursor / any MCP-aware agent live access to the component catalog, prop vocabulary, design tokens, 34 cardinal rules, copy-paste-ready patterns, 12 design / taste skills synthesised from Leonxlnx/taste-skill, 20+ anti-AI-tell patterns, and a 50-check redesign audit \u2014 token-efficient (list \u2192 drill-down).",
9849
10227
  type: "module",
9850
10228
  main: "./dist/index.js",