@lerianstudio/sindarian-ui 2.0.0-beta.13 → 2.0.0-beta.14

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 (53) hide show
  1. package/dist/components/form/select-field/index.d.ts +15 -1
  2. package/dist/components/form/select-field/index.d.ts.map +1 -1
  3. package/dist/components/form/select-field/index.js +28 -3
  4. package/dist/components/page/index.d.ts +35 -1
  5. package/dist/components/page/index.d.ts.map +1 -1
  6. package/dist/components/page/index.js +6 -2
  7. package/dist/components/table/id-table-cell.d.ts +9 -1
  8. package/dist/components/table/id-table-cell.d.ts.map +1 -1
  9. package/dist/components/table/id-table-cell.js +57 -9
  10. package/dist/components/table/index.d.ts +1 -0
  11. package/dist/components/table/index.d.ts.map +1 -1
  12. package/dist/components/table/index.js +1 -0
  13. package/dist/components/table/locked-table-actions.d.ts +24 -0
  14. package/dist/components/table/locked-table-actions.d.ts.map +1 -1
  15. package/dist/components/table/locked-table-actions.js +31 -1
  16. package/dist/components/table/name-table-cell.d.ts +57 -4
  17. package/dist/components/table/name-table-cell.d.ts.map +1 -1
  18. package/dist/components/table/name-table-cell.js +50 -2
  19. package/dist/components/ui/select/index.d.ts.map +1 -1
  20. package/dist/components/ui/select/index.js +8 -4
  21. package/dist/enterprise/data-table/index.d.ts +77 -7
  22. package/dist/enterprise/data-table/index.d.ts.map +1 -1
  23. package/dist/enterprise/data-table/index.js +56 -11
  24. package/dist/esm/components/form/select-field/index.d.ts +15 -1
  25. package/dist/esm/components/form/select-field/index.d.ts.map +1 -1
  26. package/dist/esm/components/form/select-field/index.js +29 -4
  27. package/dist/esm/components/page/index.d.ts +35 -1
  28. package/dist/esm/components/page/index.d.ts.map +1 -1
  29. package/dist/esm/components/page/index.js +6 -2
  30. package/dist/esm/components/table/id-table-cell.d.ts +9 -1
  31. package/dist/esm/components/table/id-table-cell.d.ts.map +1 -1
  32. package/dist/esm/components/table/id-table-cell.js +58 -10
  33. package/dist/esm/components/table/index.d.ts +1 -0
  34. package/dist/esm/components/table/index.d.ts.map +1 -1
  35. package/dist/esm/components/table/index.js +1 -0
  36. package/dist/esm/components/table/locked-table-actions.d.ts +24 -0
  37. package/dist/esm/components/table/locked-table-actions.d.ts.map +1 -1
  38. package/dist/esm/components/table/locked-table-actions.js +31 -1
  39. package/dist/esm/components/table/name-table-cell.d.ts +57 -4
  40. package/dist/esm/components/table/name-table-cell.d.ts.map +1 -1
  41. package/dist/esm/components/table/name-table-cell.js +50 -2
  42. package/dist/esm/components/ui/select/index.d.ts.map +1 -1
  43. package/dist/esm/components/ui/select/index.js +8 -4
  44. package/dist/esm/enterprise/data-table/index.d.ts +77 -7
  45. package/dist/esm/enterprise/data-table/index.d.ts.map +1 -1
  46. package/dist/esm/enterprise/data-table/index.js +57 -12
  47. package/dist/esm/index.d.ts +2 -0
  48. package/dist/esm/index.d.ts.map +1 -1
  49. package/dist/esm/index.js +2 -0
  50. package/dist/index.d.ts +2 -0
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +2 -0
  53. package/package.json +1 -1
@@ -15,6 +15,20 @@ type SelectFieldSharedProps<T extends FieldValues = FieldValues> = PropsWithChil
15
15
  required?: boolean;
16
16
  /** Copy for the "nothing to pick" row. Defaults to "No options found." */
17
17
  emptyMessage?: string;
18
+ /**
19
+ * Rendered inside the trigger, before the value — where a filter bar wants
20
+ * its glyph. On the single select it shares one flex box with the value so
21
+ * the trigger keeps exactly two children; on the multi select the trigger
22
+ * already groups them. The node itself is passed through untouched, with
23
+ * no `aria-hidden`: the prop takes an arbitrary node, and hiding it from
24
+ * the accessibility tree on the caller's behalf would silence a node the
25
+ * caller may have made meaningful on purpose. The trigger is named by its
26
+ * label and its value, so the node passed here should be decorative.
27
+ *
28
+ * Spacing is the caller's: the kit adds no margin, because a gap here
29
+ * would double with the one every existing call site already carries.
30
+ */
31
+ leadingIcon?: ReactNode;
18
32
  /**
19
33
  * Placed on the field's own root box, merged with its spacing rather than
20
34
  * replacing it. The root is the element a parent grid positions, so
@@ -47,6 +61,6 @@ type SelectFieldModeProps = {
47
61
  onChange?: (value: string) => void;
48
62
  };
49
63
  export type SelectFieldProps<T extends FieldValues = FieldValues> = SelectFieldSharedProps<T> & SelectFieldModeProps;
50
- export declare const SelectField: <T extends FieldValues = FieldValues>({ name, label, tooltip, labelExtra, required, placeholder, description, disabled, readOnly, multi, control, value, children, emptyMessage, className, onChange, ...others }: SelectFieldProps<T>) => React.JSX.Element;
64
+ export declare const SelectField: <T extends FieldValues = FieldValues>({ name, label, tooltip, labelExtra, required, placeholder, description, disabled, readOnly, multi, control, value, children, emptyMessage, leadingIcon, className, onChange, ...others }: SelectFieldProps<T>) => React.JSX.Element;
51
65
  export {};
52
66
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/form/select-field/index.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAC3D,OAAO,EACL,OAAO,EAEP,WAAW,EAEZ,MAAM,iBAAiB,CAAA;AAExB,KAAK,sBAAsB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAC7D,iBAAiB,GAAG;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC5B,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;qCACiC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAEH;;;;;;;;GAQG;AACH,KAAK,oBAAoB,GACrB;IACE,KAAK,EAAE,IAAI,CAAA;IACX;yEACqE;IACrE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;CACrC,GACD;IACE,KAAK,CAAC,EAAE,KAAK,CAAA;IACb;yEACqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC,CAAA;AAEL,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAC9D,sBAAsB,CAAC,CAAC,CAAC,GAAG,oBAAoB,CAAA;AAWlD,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,6KAkB9D,gBAAgB,CAAC,CAAC,CAAC,sBA6FrB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/form/select-field/index.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAC3D,OAAO,EACL,OAAO,EAEP,WAAW,EAEZ,MAAM,iBAAiB,CAAA;AAExB,KAAK,sBAAsB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAC7D,iBAAiB,GAAG;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC5B,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;qCACiC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;;;;;;;;;;OAYG;IACH,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAEH;;;;;;;;GAQG;AACH,KAAK,oBAAoB,GACrB;IACE,KAAK,EAAE,IAAI,CAAA;IACX;yEACqE;IACrE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;CACrC,GACD;IACE,KAAK,CAAC,EAAE,KAAK,CAAA;IACb;yEACqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC,CAAA;AAEL,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAC9D,sBAAsB,CAAC,CAAC,CAAC,GAAG,oBAAoB,CAAA;AAWlD,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,0LAmB9D,gBAAgB,CAAC,CAAC,CAAC,sBAgIrB,CAAA"}
@@ -6,20 +6,45 @@ const form_1 = require("../../../components/ui/form");
6
6
  const multiple_select_1 = require("../../../components/ui/multiple-select");
7
7
  const select_1 = require("../../../components/ui/select");
8
8
  const utils_1 = require("../../../lib/utils");
9
- const SelectField = ({ name, label, tooltip, labelExtra, required, placeholder, description, disabled, readOnly, multi, control, value, children, emptyMessage = 'No options found.', className, onChange, ...others }) => {
9
+ const SelectField = ({ name, label, tooltip, labelExtra, required, placeholder, description, disabled, readOnly, multi, control, value, children, emptyMessage = 'No options found.', leadingIcon, className, onChange, ...others }) => {
10
10
  // The props union already pairs `multi` with the callback shape that branch
11
11
  // emits, but destructuring an intersection loses that correlation — TS sees
12
12
  // two unrelated callbacks. Both branches report through this one seam, each
13
13
  // with the payload its own union member declared.
14
14
  const emitChange = onChange;
15
+ /**
16
+ * ⛔ THE GLYPH AND THE VALUE ARE ONE FLEX CHILD, NEVER TWO.
17
+ *
18
+ * `.select-trigger` is `flex w-full items-center justify-between`, so a
19
+ * third child makes it split the free space into TWO gaps: on any trigger
20
+ * wider than its content — which is every filter bar — the selected value
21
+ * drifted to the middle of the box instead of sitting beside the glyph.
22
+ * Wrapped, the trigger is back to two children, content and chevron,
23
+ * whatever the caller passes.
24
+ *
25
+ * `min-w-0` keeps the value truncating: the wrapper is the flex item now,
26
+ * and a flex item never shrinks below its own min-content width. The
27
+ * trigger's `VALUE_OVERFLOW_CLASS` reaches through it by descendant
28
+ * selector.
29
+ *
30
+ * No `gap`: the prop's contract is that spacing is the caller's (the Tracer
31
+ * filter bars carry their own `mr-2`), and a kit gap would double with it.
32
+ *
33
+ * The MULTI trigger needs none of this — it already renders its children
34
+ * inside one `flex grow flex-wrap` box, so the icon already shares a parent
35
+ * with the value and the trigger root already has exactly two children.
36
+ * Wrapping there would put the selected-value badges in a non-wrapping row
37
+ * and stop the trigger growing to a second line.
38
+ */
39
+ const withLeadingIcon = (value) => leadingIcon ? ((0, jsx_runtime_1.jsxs)("span", { className: "flex min-w-0 items-center", children: [leadingIcon, value] })) : (value);
15
40
  const renderItem = (field) => {
16
41
  return ((0, jsx_runtime_1.jsxs)(form_1.FormItem, { required: required, className: className, children: [label && ((0, jsx_runtime_1.jsx)(form_1.FormLabel, { extra: tooltip ? (0, jsx_runtime_1.jsx)(form_1.FormTooltip, { children: tooltip }) : labelExtra, children: label })), multi ? ((0, jsx_runtime_1.jsxs)(multiple_select_1.MultipleSelect, { onValueChange: (value) => {
17
42
  field.onChange(value);
18
43
  emitChange?.(value);
19
- }, disabled: disabled, ...field, children: [(0, jsx_runtime_1.jsx)(multiple_select_1.MultipleSelectTrigger, { readOnly: readOnly, children: (0, jsx_runtime_1.jsx)(multiple_select_1.MultipleSelectValue, { placeholder: placeholder }) }), (0, jsx_runtime_1.jsx)(multiple_select_1.MultipleSelectContent, { children: children })] })) : ((0, jsx_runtime_1.jsxs)(select_1.Select, { onValueChange: (value) => {
44
+ }, disabled: disabled, ...field, children: [(0, jsx_runtime_1.jsxs)(multiple_select_1.MultipleSelectTrigger, { readOnly: readOnly, children: [leadingIcon, (0, jsx_runtime_1.jsx)(multiple_select_1.MultipleSelectValue, { placeholder: placeholder })] }), (0, jsx_runtime_1.jsx)(multiple_select_1.MultipleSelectContent, { children: children })] })) : ((0, jsx_runtime_1.jsxs)(select_1.Select, { onValueChange: (value) => {
20
45
  field.onChange(value);
21
46
  emitChange?.(value);
22
- }, value: field.value, disabled: disabled, open: readOnly ? false : undefined, onOpenChange: readOnly ? () => { } : undefined, children: [(0, jsx_runtime_1.jsx)(form_1.FormControl, { children: (0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: (0, utils_1.cn)(disabled && 'bg-shadcn-100'), readOnly: readOnly, "data-testid": others['data-testid'], children: (0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: placeholder }) }) }), (0, jsx_runtime_1.jsxs)(select_1.SelectContent, { children: [(0, jsx_runtime_1.jsx)(select_1.SelectEmpty, { children: emptyMessage }), children] })] })), (0, jsx_runtime_1.jsx)(form_1.FormMessage, {}), description && (0, jsx_runtime_1.jsx)(form_1.FormDescription, { children: description })] }));
47
+ }, value: field.value, disabled: disabled, open: readOnly ? false : undefined, onOpenChange: readOnly ? () => { } : undefined, children: [(0, jsx_runtime_1.jsx)(form_1.FormControl, { children: (0, jsx_runtime_1.jsx)(select_1.SelectTrigger, { className: (0, utils_1.cn)(disabled && 'bg-shadcn-100'), readOnly: readOnly, "data-testid": others['data-testid'], children: withLeadingIcon((0, jsx_runtime_1.jsx)(select_1.SelectValue, { placeholder: placeholder })) }) }), (0, jsx_runtime_1.jsxs)(select_1.SelectContent, { children: [(0, jsx_runtime_1.jsx)(select_1.SelectEmpty, { children: emptyMessage }), children] })] })), (0, jsx_runtime_1.jsx)(form_1.FormMessage, {}), description && (0, jsx_runtime_1.jsx)(form_1.FormDescription, { children: description })] }));
23
48
  };
24
49
  if (!control) {
25
50
  // `value` undefined leaves Radix to keep its own state, so an uncontrolled
@@ -1,4 +1,38 @@
1
1
  export declare function PageRoot({ className, ...props }: React.ComponentProps<'div'>): import("react").JSX.Element;
2
2
  export declare function PageView({ className, ...props }: React.ComponentProps<'div'>): import("react").JSX.Element;
3
- export declare function PageContent({ className, ...props }: React.ComponentProps<'div'>): import("react").JSX.Element;
3
+ export type PageContentProps = React.ComponentProps<'div'> & {
4
+ /**
5
+ * How much room the container spends on padding.
6
+ *
7
+ * `'default'` is 64px a side at every width, and it is what every consumer
8
+ * that does not ask for anything gets, class for class.
9
+ *
10
+ * `'compact'` adds 16px a side below `sm` (640px) and changes nothing above
11
+ * it. On a 390px phone the default spends 128px — a third of the screen —
12
+ * before the page has drawn anything: measured at 390 on ten Product Console
13
+ * screens across Midaz, Pix, Payments, Reporter, Tracer, Flowker and
14
+ * Settings, each was left with 262px of content, and the pages adding their
15
+ * own `px-24` inside it were down to 70px, which is where a transaction
16
+ * receipt's amount ran off its card.
17
+ *
18
+ * WHY `max-sm:p-4` AND NOT THE MOBILE-FIRST `p-4 sm:p-16`. A base `p-4`
19
+ * REPLACES `p-16` for every caller, and `sm:p-16` then re-imposes 64px at
20
+ * desktop on the pages that deliberately asked for something else — a
21
+ * full-bleed page would grow a 64px frame at 1440. `max-sm:` only ADDS a rule
22
+ * below `sm`, so every call site renders identically from 640px up, whatever
23
+ * padding it passes.
24
+ *
25
+ * HOW A CALLER OPTS OUT. Pass the phone value too: `className="p-0
26
+ * max-sm:p-0"`. `cn` is tailwind-merge, so the caller's `max-sm:p-0` replaces
27
+ * this rule rather than stacking with it.
28
+ *
29
+ * AND AN AXIS PADDING HAS TO REPEAT ITSELF: `className="px-6 max-sm:px-6"`,
30
+ * never `px-6` alone. tailwind-merge does not treat `max-sm:p-4` and `px-6`
31
+ * as the same group, so it drops neither, and CSS then gives the media-query
32
+ * rule the last word — a lone `px-6` is silently 16px on both axes on a
33
+ * phone.
34
+ */
35
+ padding?: 'default' | 'compact';
36
+ };
37
+ export declare function PageContent({ className, padding, ...props }: PageContentProps): import("react").JSX.Element;
4
38
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/page/index.tsx"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,+BAW5E;AAED,wBAAgB,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,+BAQ5E;AAED,wBAAgB,WAAW,CAAC,EAC1B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,+BAQ7B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/page/index.tsx"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,+BAW5E;AAED,wBAAgB,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,+BAQ5E;AAED,MAAM,MAAM,gBAAgB,GAAG,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG;IAC3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;CAChC,CAAA;AAED,wBAAgB,WAAW,CAAC,EAC1B,SAAS,EACT,OAAmB,EACnB,GAAG,KAAK,EACT,EAAE,gBAAgB,+BAelB"}
@@ -11,6 +11,10 @@ function PageRoot({ className, ...props }) {
11
11
  function PageView({ className, ...props }) {
12
12
  return ((0, jsx_runtime_1.jsx)("div", { "data-slot": "page-view", className: (0, utils_1.cn)('flex min-h-0 flex-1', className), ...props }));
13
13
  }
14
- function PageContent({ className, ...props }) {
15
- return ((0, jsx_runtime_1.jsx)("div", { "data-slot": "page-content", className: (0, utils_1.cn)('flex grow flex-col overflow-y-auto p-16', className), ...props }));
14
+ function PageContent({ className, padding = 'default', ...props }) {
15
+ return ((0, jsx_runtime_1.jsx)("div", { "data-slot": "page-content",
16
+ // Order is the contract, not a style: `className` last is what lets a
17
+ // caller's `p-0` beat `p-16` and a caller's `max-sm:p-0` replace the
18
+ // compact rule instead of stacking with it.
19
+ className: (0, utils_1.cn)('flex grow flex-col overflow-y-auto p-16', padding === 'compact' && 'max-sm:p-4', className), ...props }));
16
20
  }
@@ -6,6 +6,14 @@ export type IdTableCellProps = Omit<React.ComponentProps<typeof TableCell>, 'onC
6
6
  head?: number;
7
7
  /** Characters kept from the end of the id. */
8
8
  tail?: number;
9
+ /**
10
+ * Accessible name for the copy button. Override for localization; the kit
11
+ * ships no i18n. The id is deliberately NOT interpolated into it — a column
12
+ * of thirty rows would announce thirty different names for the same action,
13
+ * and the id is already the cell's text and its `title`.
14
+ * @defaultValue 'Copy id'
15
+ */
16
+ copyLabel?: string;
9
17
  };
10
- export declare const IdTableCell: ({ id, onCopy, head, tail, ...others }: IdTableCellProps) => import("react").JSX.Element;
18
+ export declare const IdTableCell: ({ id, onCopy, head, tail, copyLabel, ...others }: IdTableCellProps) => import("react").JSX.Element;
11
19
  //# sourceMappingURL=id-table-cell.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"id-table-cell.d.ts","sourceRoot":"","sources":["../../../src/components/table/id-table-cell.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAGV,MAAM,uBAAuB,CAAA;AAS9B,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,KAAK,CAAC,cAAc,CAAC,OAAO,SAAS,CAAC,EACtC,QAAQ,CACT,GAAG;IACF,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AA6CD,eAAO,MAAM,WAAW,GAAI,uCAMzB,gBAAgB,gCAiClB,CAAA"}
1
+ {"version":3,"file":"id-table-cell.d.ts","sourceRoot":"","sources":["../../../src/components/table/id-table-cell.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAoB,MAAM,uBAAuB,CAAA;AASnE,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,KAAK,CAAC,cAAc,CAAC,OAAO,SAAS,CAAC,EACtC,QAAQ,CACT,GAAG;IACF,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AA6CD,eAAO,MAAM,WAAW,GAAI,kDAOzB,gBAAgB,gCAqFlB,CAAA"}
@@ -44,18 +44,66 @@ function truncateMiddle(id, head, tail) {
44
44
  ? id
45
45
  : `${id.slice(0, start)}…${id.slice(id.length - end)}`;
46
46
  }
47
- const IdTableCell = ({ id, onCopy, head = DEFAULT_HEAD, tail = DEFAULT_TAIL, ...others }) => {
48
- const handleCopyToClipboard = () => {
49
- // The WHOLE id, never what is rendered. The cell is the only place most
50
- // consoles expose it, and a truncated id pasted into a query matches
51
- // nothing.
52
- navigator.clipboard.writeText(id);
53
- onCopy?.(id);
47
+ const IdTableCell = ({ id, onCopy, head = DEFAULT_HEAD, tail = DEFAULT_TAIL, copyLabel = 'Copy id', ...others }) => {
48
+ /**
49
+ * ⛔ THE COPY ACTION IS A NAMED BUTTON, NEVER A HANDLER ON THE `<td>`.
50
+ *
51
+ * On the cell it had no role, no tab stop and no accessible name: an
52
+ * operator who does not use a pointer could not copy an id from any table
53
+ * built on this kit, and a screen reader announced the cell as plain text.
54
+ * It also made the id column the one cell in a row-activating table that
55
+ * silently swallowed the click that opens the record.
56
+ */
57
+ const handleCopyToClipboard = async (event) => {
58
+ // A row that opens a record on click must not also open it on a copy.
59
+ event.stopPropagation();
60
+ // The same guard `CopyField` uses: an insecure context, and a browser
61
+ // without the API, both leave `navigator.clipboard` undefined, and the
62
+ // only thing this cell would otherwise do about it is throw an unhandled
63
+ // rejection out of a click handler. There is no input here to select as a
64
+ // fallback, so a silent no-op is the whole of what it can honestly offer.
65
+ if (!id || typeof navigator.clipboard?.writeText !== 'function') {
66
+ return;
67
+ }
68
+ try {
69
+ // The WHOLE id, never what is rendered. The cell is the only place most
70
+ // consoles expose it, and a truncated id pasted into a query matches
71
+ // nothing.
72
+ await navigator.clipboard.writeText(id);
73
+ onCopy?.(id);
74
+ }
75
+ catch {
76
+ // The browser refused the write: nothing reached the clipboard, so
77
+ // `onCopy` — which consumers hang their "copied" toast off — must not
78
+ // claim otherwise.
79
+ }
54
80
  };
55
- return ((0, jsx_runtime_1.jsx)(table_1.TableCell, { onClick: handleCopyToClipboard,
81
+ return ((0, jsx_runtime_1.jsx)(table_1.TableCell
82
+ // The tooltip is the styled affordance; `title` is the fallback that
83
+ // survives without it — a printed page, a portal that has not mounted,
84
+ // and the cell padding outside the trigger.
85
+ , {
56
86
  // The tooltip is the styled affordance; `title` is the fallback that
57
87
  // survives without it — a printed page, a portal that has not mounted,
58
88
  // and the cell padding outside the trigger.
59
- title: id, ...others, children: (0, jsx_runtime_1.jsxs)(table_1.TableCellWrapper, { children: [(0, jsx_runtime_1.jsx)(tooltip_1.TooltipProvider, { children: (0, jsx_runtime_1.jsxs)(tooltip_1.Tooltip, { delayDuration: 300, children: [(0, jsx_runtime_1.jsx)(tooltip_1.TooltipTrigger, { children: id ? truncateMiddle(id, head, tail) : id }), (0, jsx_runtime_1.jsx)(tooltip_1.TooltipContent, { children: id })] }) }), (0, jsx_runtime_1.jsx)(table_1.TableCellAction, { children: (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { className: "size-3.5" }) })] }) }));
89
+ title: id, ...others, children: (0, jsx_runtime_1.jsxs)(table_1.TableCellWrapper, { children: [(0, jsx_runtime_1.jsx)(tooltip_1.TooltipProvider, { children: (0, jsx_runtime_1.jsxs)(tooltip_1.Tooltip, { delayDuration: 300, children: [(0, jsx_runtime_1.jsx)(tooltip_1.TooltipTrigger, { children: id ? truncateMiddle(id, head, tail) : id }), (0, jsx_runtime_1.jsx)(tooltip_1.TooltipContent, { children: id })] }) }), id ? ((0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": copyLabel, onClick: handleCopyToClipboard,
90
+ // ⛔ THREE RULES HERE, AND EVERY ONE IS A WAY THE CONTROL
91
+ // DISAPPEARS RATHER THAN A DECORATION.
92
+ //
93
+ // `focus-visible:opacity-100`: a keyboard stop the operator cannot
94
+ // see is the same dead end the `<td>` handler was.
95
+ //
96
+ // `[@media(hover:none)]:opacity-100`: a phone or a tablet has no
97
+ // pointer to hover with, so `group-hover` never fires and the only
98
+ // control that copies an id stayed fully transparent — invisible
99
+ // AND unexplained on the one device where the id is hardest to
100
+ // read off the screen. Written as an arbitrary variant because the
101
+ // kit's Tailwind theme defines no named one.
102
+ //
103
+ // `size-6` with `-my-0.5`: WCAG 2.5.8 wants 24×24 CSS px, and the
104
+ // glyph alone is 14. The negative margin pulls the button's outer
105
+ // box back to the 20px line box of the cell's `text-sm` text, so
106
+ // the target grows without the row growing under it.
107
+ className: "focus-visible:ring-ring -my-0.5 ml-4 inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-sm opacity-0 transition-opacity group-hover/table-cell:opacity-100 focus-visible:opacity-100 focus-visible:ring-2 focus-visible:outline-none [@media(hover:none)]:opacity-100", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Copy, { className: "size-3.5" }) })) : null] }) }));
60
108
  };
61
109
  exports.IdTableCell = IdTableCell;
@@ -1,3 +1,4 @@
1
1
  export * from './id-table-cell';
2
+ export * from './locked-table-actions';
2
3
  export * from './name-table-cell';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/table/index.tsx"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/table/index.tsx"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AACtC,cAAc,mBAAmB,CAAA"}
@@ -15,4 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./id-table-cell"), exports);
18
+ __exportStar(require("./locked-table-actions"), exports);
18
19
  __exportStar(require("./name-table-cell"), exports);
@@ -1,7 +1,31 @@
1
1
  import React from 'react';
2
2
  export type LockedTableActionsProps = {
3
+ /** Why this record cannot be changed. It is the control's accessible name as well as the tooltip's text. */
3
4
  message: string;
4
5
  className?: string;
5
6
  };
7
+ /**
8
+ * Where a row's action menu would be, on a record the ledger will not let
9
+ * anyone change — an external account, in double-entry terms.
10
+ *
11
+ * ⛔ THE TRIGGER IS A BUTTON. It used to be a `<div>`, so the ONLY explanation
12
+ * of why the menu is gone lived in a tooltip that a keyboard could not open and
13
+ * a screen reader had nothing to attach to: the row simply had one control
14
+ * fewer, unexplained.
15
+ *
16
+ * `aria-disabled`, not the `disabled` attribute: a truly disabled button takes
17
+ * no focus and fires no pointer events, so it would announce as unavailable and
18
+ * then refuse to open the tooltip that says WHY — the same dead end in a
19
+ * different shape. This one is reachable, announces its reason as its name,
20
+ * opens the tooltip on focus as well as hover, and does nothing when pressed.
21
+ *
22
+ * ⛔ DOING NOTHING TAKES A HANDLER. `aria-disabled` is a promise to the
23
+ * accessibility tree, not to the DOM: the press really does dispatch a click,
24
+ * and a click bubbles. Consumers put this where a row's action menu would be,
25
+ * in rows that open the record — so with no handler of its own the one control
26
+ * that says "you cannot change this" would open the record it refuses to
27
+ * change, from a pointer and, because a button turns Enter and Space into
28
+ * clicks, from a keyboard too.
29
+ */
6
30
  export declare const LockedTableActions: ({ message, className }: LockedTableActionsProps) => React.JSX.Element;
7
31
  //# sourceMappingURL=locked-table-actions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"locked-table-actions.d.ts","sourceRoot":"","sources":["../../../src/components/table/locked-table-actions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAUzB,MAAM,MAAM,uBAAuB,GAAG;IACpC,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,wBAGhC,uBAAuB,sBAkBzB,CAAA"}
1
+ {"version":3,"file":"locked-table-actions.d.ts","sourceRoot":"","sources":["../../../src/components/table/locked-table-actions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAUzB,MAAM,MAAM,uBAAuB,GAAG;IACpC,4GAA4G;IAC5G,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,kBAAkB,GAAI,wBAGhC,uBAAuB,sBA6BzB,CAAA"}
@@ -5,7 +5,37 @@ const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const lucide_react_1 = require("lucide-react");
6
6
  const tooltip_1 = require("../../components/ui/tooltip");
7
7
  const utils_1 = require("../../lib/utils");
8
+ /**
9
+ * Where a row's action menu would be, on a record the ledger will not let
10
+ * anyone change — an external account, in double-entry terms.
11
+ *
12
+ * ⛔ THE TRIGGER IS A BUTTON. It used to be a `<div>`, so the ONLY explanation
13
+ * of why the menu is gone lived in a tooltip that a keyboard could not open and
14
+ * a screen reader had nothing to attach to: the row simply had one control
15
+ * fewer, unexplained.
16
+ *
17
+ * `aria-disabled`, not the `disabled` attribute: a truly disabled button takes
18
+ * no focus and fires no pointer events, so it would announce as unavailable and
19
+ * then refuse to open the tooltip that says WHY — the same dead end in a
20
+ * different shape. This one is reachable, announces its reason as its name,
21
+ * opens the tooltip on focus as well as hover, and does nothing when pressed.
22
+ *
23
+ * ⛔ DOING NOTHING TAKES A HANDLER. `aria-disabled` is a promise to the
24
+ * accessibility tree, not to the DOM: the press really does dispatch a click,
25
+ * and a click bubbles. Consumers put this where a row's action menu would be,
26
+ * in rows that open the record — so with no handler of its own the one control
27
+ * that says "you cannot change this" would open the record it refuses to
28
+ * change, from a pointer and, because a button turns Enter and Space into
29
+ * clicks, from a keyboard too.
30
+ */
8
31
  const LockedTableActions = ({ message, className }) => {
9
- return ((0, jsx_runtime_1.jsx)(tooltip_1.TooltipProvider, { children: (0, jsx_runtime_1.jsxs)(tooltip_1.Tooltip, { children: [(0, jsx_runtime_1.jsx)(tooltip_1.TooltipTrigger, { asChild: true, children: (0, jsx_runtime_1.jsx)("div", { className: (0, utils_1.cn)('border-border bg-muted flex size-9 items-center justify-center rounded-md border', className), children: (0, jsx_runtime_1.jsx)(lucide_react_1.LockIcon, { size: 14, className: "text-muted-foreground" }) }) }), (0, jsx_runtime_1.jsx)(tooltip_1.TooltipContent, { side: "left", children: message })] }) }));
32
+ return ((0, jsx_runtime_1.jsx)(tooltip_1.TooltipProvider, { children: (0, jsx_runtime_1.jsxs)(tooltip_1.Tooltip, { children: [(0, jsx_runtime_1.jsx)(tooltip_1.TooltipTrigger, { asChild: true, children: (0, jsx_runtime_1.jsx)("button", { type: "button", "aria-disabled": "true", "aria-label": message, onClick: (event) => {
33
+ event.preventDefault();
34
+ event.stopPropagation();
35
+ }, className: (0, utils_1.cn)('border-border bg-muted flex size-9 items-center justify-center rounded-md border',
36
+ // Reachable is half the fix: a `border` over a `bg-muted` can
37
+ // swallow the browser's default outline entirely, and the other
38
+ // two cells in this table carry the same ring.
39
+ 'focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none', className), children: (0, jsx_runtime_1.jsx)(lucide_react_1.LockIcon, { size: 14, className: "text-muted-foreground" }) }) }), (0, jsx_runtime_1.jsx)(tooltip_1.TooltipContent, { side: "left", children: message })] }) }));
10
40
  };
11
41
  exports.LockedTableActions = LockedTableActions;
@@ -1,6 +1,59 @@
1
1
  import { TableCell } from '../ui/table';
2
- export type NameTableCellProps = React.ComponentProps<typeof TableCell> & {
3
- name?: string | React.ReactNode;
4
- };
5
- export declare const NameTableCell: ({ name, ...props }: NameTableCellProps) => import("react").JSX.Element;
2
+ /** Opens the record. Lands on a real button, never on the `<td>`. */
3
+ type OpenRecord = React.MouseEventHandler<HTMLButtonElement>;
4
+ export type NameTableCellProps = Omit<React.ComponentProps<typeof TableCell>, 'onClick'> &
5
+ /**
6
+ * Three shapes, and the only one missing is an interactive cell that can
7
+ * carry no accessible name.
8
+ *
9
+ * 1. A text `name` IS the button's accessible name, so the handler is free
10
+ * and `buttonLabel` merely overrides what the button announces.
11
+ * 2. Anything else rendered inside the button — a badge, an icon, an italic
12
+ * placeholder — may carry no text at all, so it has to name its button.
13
+ * 3. No handler, no control: text in a table has nothing to name.
14
+ */
15
+ ({
16
+ name: string;
17
+ onClick?: OpenRecord;
18
+ buttonLabel?: string;
19
+ } | {
20
+ name?: React.ReactNode;
21
+ onClick: OpenRecord;
22
+ buttonLabel: string;
23
+ } | {
24
+ name?: React.ReactNode;
25
+ onClick?: never;
26
+ buttonLabel?: never;
27
+ });
28
+ /**
29
+ * The name column, which is how an operator opens a record from a list.
30
+ *
31
+ * ⛔ THE CLICK IS A BUTTON, NEVER THE `<td>`. The cell had no click prop at
32
+ * all, so a consumer wanting a clickable name put `onClick` on the `<td>`
33
+ * through the spread props: a pointer user saw an affordance under a
34
+ * `<p class="cursor-pointer">`, and a keyboard or screen-reader user got a
35
+ * table cell with no control in it — the row's only route to the record was
36
+ * unreachable. `onClick` is now `Omit`ed from the inherited cell props, so the
37
+ * `<td>` handler is unrepresentable and the compiler says so at the call site.
38
+ *
39
+ * And with no handler there is no pointer cursor: text that says it is
40
+ * clickable and is not is worse than plain text.
41
+ *
42
+ * ⛔ AN INTERACTIVE CELL CANNOT BE ANONYMOUS. `name` renders INSIDE the button,
43
+ * so whatever it renders is the button's accessible name — and a `ReactNode` is
44
+ * free to render no text at all, which announces as a bare "button" and leaves
45
+ * the row's only route to its record as unreachable as the `<td>` handler
46
+ * above. The props are a union rather than a comment: a text `name` names the
47
+ * button by itself, anything else has to pass `buttonLabel`, and a cell with no
48
+ * handler is exempt because it holds no control.
49
+ *
50
+ * `buttonLabel` and not an inherited `aria-label`, which the spread puts on the
51
+ * `<td>` — naming the CELL, never the control inside it.
52
+ *
53
+ * ⛔ AND THE UNION STILL LETS THE EMPTY STRING THROUGH, because every string
54
+ * type does. `name=""` satisfies the text arm and `buttonLabel=" "` satisfies
55
+ * the labelled one, so the last word is the component's, below.
56
+ */
57
+ export declare const NameTableCell: ({ name, onClick, buttonLabel, ...props }: NameTableCellProps) => import("react").JSX.Element;
58
+ export {};
6
59
  //# sourceMappingURL=name-table-cell.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"name-table-cell.d.ts","sourceRoot":"","sources":["../../../src/components/table/name-table-cell.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAqC,MAAM,aAAa,CAAA;AAE1E,MAAM,MAAM,kBAAkB,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,SAAS,CAAC,GAAG;IACxE,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAA;CAChC,CAAA;AAED,eAAO,MAAM,aAAa,GAAI,oBAAoB,kBAAkB,gCAWnE,CAAA"}
1
+ {"version":3,"file":"name-table-cell.d.ts","sourceRoot":"","sources":["../../../src/components/table/name-table-cell.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAqC,MAAM,aAAa,CAAA;AAE1E,qEAAqE;AACrE,KAAK,UAAU,GAAG,KAAK,CAAC,iBAAiB,CAAC,iBAAiB,CAAC,CAAA;AAE5D,MAAM,MAAM,kBAAkB,GAAG,IAAI,CACnC,KAAK,CAAC,cAAc,CAAC,OAAO,SAAS,CAAC,EACtC,SAAS,CACV;AACC;;;;;;;;;GASG;AACH,CACI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,UAAU,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAAC,OAAO,EAAE,UAAU,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACpE;IAAE,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAAC,OAAO,CAAC,EAAE,KAAK,CAAC;IAAC,WAAW,CAAC,EAAE,KAAK,CAAA;CAAE,CACnE,CAAA;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,aAAa,GAAI,0CAK3B,kBAAkB,gCAgDpB,CAAA"}
@@ -4,7 +4,55 @@ exports.NameTableCell = void 0;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const lucide_react_1 = require("lucide-react");
6
6
  const table_1 = require("../ui/table");
7
- const NameTableCell = ({ name, ...props }) => {
8
- return ((0, jsx_runtime_1.jsx)(table_1.TableCell, { ...props, children: (0, jsx_runtime_1.jsxs)(table_1.TableCellWrapper, { children: [(0, jsx_runtime_1.jsx)("p", { className: "cursor-pointer", children: name }), (0, jsx_runtime_1.jsx)(table_1.TableCellAction, { children: (0, jsx_runtime_1.jsx)(lucide_react_1.Search, { className: "size-3.5" }) })] }) }));
7
+ /**
8
+ * The name column, which is how an operator opens a record from a list.
9
+ *
10
+ * ⛔ THE CLICK IS A BUTTON, NEVER THE `<td>`. The cell had no click prop at
11
+ * all, so a consumer wanting a clickable name put `onClick` on the `<td>`
12
+ * through the spread props: a pointer user saw an affordance under a
13
+ * `<p class="cursor-pointer">`, and a keyboard or screen-reader user got a
14
+ * table cell with no control in it — the row's only route to the record was
15
+ * unreachable. `onClick` is now `Omit`ed from the inherited cell props, so the
16
+ * `<td>` handler is unrepresentable and the compiler says so at the call site.
17
+ *
18
+ * And with no handler there is no pointer cursor: text that says it is
19
+ * clickable and is not is worse than plain text.
20
+ *
21
+ * ⛔ AN INTERACTIVE CELL CANNOT BE ANONYMOUS. `name` renders INSIDE the button,
22
+ * so whatever it renders is the button's accessible name — and a `ReactNode` is
23
+ * free to render no text at all, which announces as a bare "button" and leaves
24
+ * the row's only route to its record as unreachable as the `<td>` handler
25
+ * above. The props are a union rather than a comment: a text `name` names the
26
+ * button by itself, anything else has to pass `buttonLabel`, and a cell with no
27
+ * handler is exempt because it holds no control.
28
+ *
29
+ * `buttonLabel` and not an inherited `aria-label`, which the spread puts on the
30
+ * `<td>` — naming the CELL, never the control inside it.
31
+ *
32
+ * ⛔ AND THE UNION STILL LETS THE EMPTY STRING THROUGH, because every string
33
+ * type does. `name=""` satisfies the text arm and `buttonLabel=" "` satisfies
34
+ * the labelled one, so the last word is the component's, below.
35
+ */
36
+ const NameTableCell = ({ name, onClick, buttonLabel, ...props }) => {
37
+ // Whitespace names nothing. An `aria-label` of " " is worse than none: it
38
+ // OVERRIDES the text inside the button, so a perfectly good name is replaced
39
+ // by silence. Drop it and the text content names the control again.
40
+ const label = buttonLabel?.trim() ? buttonLabel : undefined;
41
+ // Only a text `name` can name the button by itself. Any other node is opaque
42
+ // here — which is why the type demands a label for it in the first place.
43
+ const textName = typeof name === 'string' ? name.trim() : '';
44
+ const named = Boolean(label) || textName !== '';
45
+ // ⛔ AN UNNAMED BUTTON LOSES ITS HANDLER, IT DOES NOT SHIP. The two failures
46
+ // are not symmetric: a button announcing "" is invisible to the developer who
47
+ // wrote it and fatal to the operator who cannot use a mouse, while a name
48
+ // that does not open its record shows itself on the very first render. So the
49
+ // cell degrades to the plain text it can still display correctly, and says so
50
+ // where a developer will see it. Never throws: one nameless row must not take
51
+ // the whole table down.
52
+ if (process.env.NODE_ENV !== 'production' && onClick && !named) {
53
+ console.error(`NameTableCell has no accessible name: \`${typeof name === 'string' ? 'name' : 'buttonLabel'}\` is empty, so its click handler was dropped rather than render a button announcing "".`);
54
+ }
55
+ const opensRecord = onClick && named;
56
+ return ((0, jsx_runtime_1.jsx)(table_1.TableCell, { ...props, children: (0, jsx_runtime_1.jsxs)(table_1.TableCellWrapper, { children: [opensRecord ? ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: onClick, "aria-label": label, className: "focus-visible:ring-ring cursor-pointer rounded-sm text-left focus-visible:ring-2 focus-visible:outline-none", children: name })) : ((0, jsx_runtime_1.jsx)("p", { children: name })), (0, jsx_runtime_1.jsx)(table_1.TableCellAction, { children: (0, jsx_runtime_1.jsx)(lucide_react_1.Search, { className: "size-3.5" }) })] }) }));
9
57
  };
10
58
  exports.NameTableCell = NameTableCell;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/select/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAA;AAKzD,iBAAS,MAAM,CAAC,EACd,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,qBAEnD;AAED,iBAAS,WAAW,CAAC,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,qBAEpD;AAED;;;;GAIG;AACH,iBAAS,WAAW,CAAC,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,qBAEpD;AAED,KAAK,kBAAkB,GAAG,KAAK,CAAC,cAAc,CAC5C,OAAO,eAAe,CAAC,OAAO,CAC/B,GAAG;IACF,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAsBD,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,kBAAkB,qBAsBpB;AAED,iBAAS,oBAAoB,CAAC,EAC5B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,cAAc,CAAC,qBAU7D;AACD,iBAAS,sBAAsB,CAAC,EAC9B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,gBAAgB,CAAC,qBAU/D;AAED,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,QAAQ,EACR,QAAmB,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC,qBA0CtD;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,wBAAwB,CAAC,KAAK,CAAC,qBAQvC;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,qBAQpD;AAED,iBAAS,UAAU,CAAC,EAClB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,qBAgBnD;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,SAAS,CAAC,qBAQxD;AAED,OAAO,EACL,MAAM,EACN,WAAW,EACX,WAAW,EACX,aAAa,EACb,aAAa,EACb,WAAW,EACX,WAAW,EACX,UAAU,EACV,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACvB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/select/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,eAAe,MAAM,wBAAwB,CAAA;AAKzD,iBAAS,MAAM,CAAC,EACd,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,qBAEnD;AAED,iBAAS,WAAW,CAAC,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,qBAEpD;AAED;;;;GAIG;AACH,iBAAS,WAAW,CAAC,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,qBAEpD;AAED,KAAK,kBAAkB,GAAG,KAAK,CAAC,cAAc,CAC5C,OAAO,eAAe,CAAC,OAAO,CAC/B,GAAG;IACF,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AA0BD,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,kBAAkB,qBAsBpB;AAED,iBAAS,oBAAoB,CAAC,EAC5B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,cAAc,CAAC,qBAU7D;AACD,iBAAS,sBAAsB,CAAC,EAC9B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,gBAAgB,CAAC,qBAU/D;AAED,iBAAS,aAAa,CAAC,EACrB,SAAS,EACT,QAAQ,EACR,QAAmB,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,OAAO,CAAC,qBA0CtD;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,wBAAwB,CAAC,KAAK,CAAC,qBAQvC;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC,qBAQpD;AAED,iBAAS,UAAU,CAAC,EAClB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,qBAgBnD;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,SAAS,CAAC,qBAQxD;AAED,OAAO,EACL,MAAM,EACN,WAAW,EACX,WAAW,EACX,aAAa,EACb,aAAa,EACb,WAAW,EACX,WAAW,EACX,UAAU,EACV,eAAe,EACf,oBAAoB,EACpB,sBAAsB,EACvB,CAAA"}
@@ -73,15 +73,19 @@ function SelectValue({ ...props }) {
73
73
  * slot shrink, `truncate` ellipsises what no longer fits — the chevron keeps its
74
74
  * place because the value absorbs all the shrinkage.
75
75
  *
76
- * Written as a child-scoped variant on the TRIGGER because Radix strips
77
- * `className` off `Select.Value` itself; the `>` combinator holds because Radix
78
- * renders that span as the trigger's direct child.
76
+ * Written as a descendant-scoped variant on the TRIGGER because Radix strips
77
+ * `className` off `Select.Value` itself. DESCENDANT, not `>`: a trigger that
78
+ * carries a leading glyph groups the glyph and the value into one flex child
79
+ * (see `SelectField`'s `leadingIcon`), so the value span is no longer the
80
+ * trigger's direct child and a `>` combinator stopped truncating exactly where
81
+ * the trigger has the least room. The wrapper carries `min-w-0` itself, which
82
+ * is what lets the shrinkage reach the value.
79
83
  *
80
84
  * Truncation hides text, so a trigger showing a long value should also carry
81
85
  * `title` — on the trigger, never on the value slot, which Radix renders
82
86
  * `pointer-events: none`.
83
87
  */
84
- const VALUE_OVERFLOW_CLASS = '[&>[data-slot=select-value]]:min-w-0 [&>[data-slot=select-value]]:truncate';
88
+ const VALUE_OVERFLOW_CLASS = '[&_[data-slot=select-value]]:min-w-0 [&_[data-slot=select-value]]:truncate';
85
89
  function SelectTrigger({ className, readOnly, children, ...props }) {
86
90
  return ((0, jsx_runtime_1.jsxs)(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": "default", className: (0, utils_1.cn)('select-trigger select-read-only select-disabled', VALUE_OVERFLOW_CLASS, className), "aria-readonly": readOnly, "data-read-only": readOnly ? '' : undefined, ...props, children: [children, !readOnly && ((0, jsx_runtime_1.jsx)(SelectPrimitive.Icon, { asChild: true, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "select-chevron" }) }))] }));
87
91
  }