@script-it/json-views-react 0.1.0-beta.1

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 (172) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +301 -0
  3. package/THIRD_PARTY_NOTICES.txt +4917 -0
  4. package/dist/browser-device.d.ts +21 -0
  5. package/dist/browser-device.js +22 -0
  6. package/dist/file-content-edit-state.d.ts +10 -0
  7. package/dist/file-content-edit-state.js +1 -0
  8. package/dist/file-content-view.d.ts +8 -0
  9. package/dist/file-content-view.js +7 -0
  10. package/dist/index.d.ts +11 -0
  11. package/dist/index.js +8 -0
  12. package/dist/json/copy-json.d.ts +15 -0
  13. package/dist/json/copy-json.js +52 -0
  14. package/dist/json/html-preview.d.ts +5 -0
  15. package/dist/json/html-preview.js +12 -0
  16. package/dist/json/html-view/native-control.d.ts +44 -0
  17. package/dist/json/html-view/native-control.js +96 -0
  18. package/dist/json/html-view/settings.d.ts +2 -0
  19. package/dist/json/html-view/settings.js +58 -0
  20. package/dist/json/html-view/template.d.ts +12 -0
  21. package/dist/json/html-view/template.js +55 -0
  22. package/dist/json/html-view.d.ts +9 -0
  23. package/dist/json/html-view.js +163 -0
  24. package/dist/json/json-content.d.ts +61 -0
  25. package/dist/json/json-content.js +433 -0
  26. package/dist/json/json-source-editor.d.ts +36 -0
  27. package/dist/json/json-source-editor.js +203 -0
  28. package/dist/json/json-view.d.ts +3 -0
  29. package/dist/json/json-view.js +322 -0
  30. package/dist/json/kanban-view.d.ts +20 -0
  31. package/dist/json/kanban-view.js +328 -0
  32. package/dist/json/long-text.d.ts +3 -0
  33. package/dist/json/long-text.js +6 -0
  34. package/dist/json/record-view.d.ts +38 -0
  35. package/dist/json/record-view.js +85 -0
  36. package/dist/json/row-mutations.d.ts +7 -0
  37. package/dist/json/row-mutations.js +93 -0
  38. package/dist/json/schema-value.d.ts +57 -0
  39. package/dist/json/schema-value.js +294 -0
  40. package/dist/json/source-default.d.ts +1 -0
  41. package/dist/json/source-default.js +1 -0
  42. package/dist/json/source-edit-capabilities.d.ts +8 -0
  43. package/dist/json/source-edit-capabilities.js +22 -0
  44. package/dist/json/source-literals.d.ts +15 -0
  45. package/dist/json/source-literals.js +24 -0
  46. package/dist/json/source-recovery.d.ts +32 -0
  47. package/dist/json/source-recovery.js +38 -0
  48. package/dist/json/source-selection.d.ts +3 -0
  49. package/dist/json/source-selection.js +16 -0
  50. package/dist/json/table-view.d.ts +20 -0
  51. package/dist/json/table-view.js +300 -0
  52. package/dist/json/use-kanban-card-drag.d.ts +13 -0
  53. package/dist/json/use-kanban-card-drag.js +255 -0
  54. package/dist/json/use-kanban-column-drag.d.ts +14 -0
  55. package/dist/json/use-kanban-column-drag.js +179 -0
  56. package/dist/json/view-diagnostics.d.ts +4 -0
  57. package/dist/json/view-diagnostics.js +26 -0
  58. package/dist/json/view-model.d.ts +27 -0
  59. package/dist/json/view-model.js +180 -0
  60. package/dist/json/view-save-status.d.ts +12 -0
  61. package/dist/json/view-save-status.js +38 -0
  62. package/dist/json/view-settings.d.ts +36 -0
  63. package/dist/json/view-settings.js +661 -0
  64. package/dist/json/view-types.d.ts +47 -0
  65. package/dist/json/view-types.js +4 -0
  66. package/dist/json/viewers/general/general-json-view.d.ts +46 -0
  67. package/dist/json/viewers/general/general-json-view.js +163 -0
  68. package/dist/json/viewers/general/json-flatten.d.ts +33 -0
  69. package/dist/json/viewers/general/json-flatten.js +51 -0
  70. package/dist/json/viewers/general/json-node-view.d.ts +43 -0
  71. package/dist/json/viewers/general/json-node-view.js +215 -0
  72. package/dist/lib/clipboard.d.ts +1 -0
  73. package/dist/lib/clipboard.js +24 -0
  74. package/dist/lib/cn.d.ts +2 -0
  75. package/dist/lib/cn.js +5 -0
  76. package/dist/lib/file-data.d.ts +7 -0
  77. package/dist/lib/file-data.js +5 -0
  78. package/dist/lib/floating-portal-selectors.d.ts +4 -0
  79. package/dist/lib/floating-portal-selectors.js +4 -0
  80. package/dist/lib/time.d.ts +24 -0
  81. package/dist/lib/time.js +60 -0
  82. package/dist/lib/ui-size.d.ts +28 -0
  83. package/dist/lib/ui-size.js +39 -0
  84. package/dist/lib/use-anchored-position.d.ts +5 -0
  85. package/dist/lib/use-anchored-position.js +65 -0
  86. package/dist/lib/use-dismiss.d.ts +31 -0
  87. package/dist/lib/use-dismiss.js +65 -0
  88. package/dist/primitives/boolean-toggle.d.ts +10 -0
  89. package/dist/primitives/boolean-toggle.js +11 -0
  90. package/dist/primitives/button.d.ts +11 -0
  91. package/dist/primitives/button.js +34 -0
  92. package/dist/primitives/checkbox.d.ts +4 -0
  93. package/dist/primitives/checkbox.js +8 -0
  94. package/dist/primitives/choice-control.d.ts +29 -0
  95. package/dist/primitives/choice-control.js +131 -0
  96. package/dist/primitives/dialog.d.ts +29 -0
  97. package/dist/primitives/dialog.js +25 -0
  98. package/dist/primitives/dropdown-menu.d.ts +37 -0
  99. package/dist/primitives/dropdown-menu.js +39 -0
  100. package/dist/primitives/inline-feedback-action.d.ts +13 -0
  101. package/dist/primitives/inline-feedback-action.js +6 -0
  102. package/dist/primitives/menu-select.d.ts +12 -0
  103. package/dist/primitives/menu-select.js +7 -0
  104. package/dist/primitives/multi-select.d.ts +18 -0
  105. package/dist/primitives/multi-select.js +7 -0
  106. package/dist/primitives/number-input.d.ts +10 -0
  107. package/dist/primitives/number-input.js +8 -0
  108. package/dist/primitives/option-pill.d.ts +13 -0
  109. package/dist/primitives/option-pill.js +50 -0
  110. package/dist/primitives/tabs.d.ts +7 -0
  111. package/dist/primitives/tabs.js +12 -0
  112. package/dist/primitives/textarea.d.ts +4 -0
  113. package/dist/primitives/textarea.js +8 -0
  114. package/dist/primitives/tooltip.d.ts +31 -0
  115. package/dist/primitives/tooltip.js +37 -0
  116. package/dist/standalone.css +113 -0
  117. package/dist/structured-data/atomic-value-editor.d.ts +33 -0
  118. package/dist/structured-data/atomic-value-editor.js +195 -0
  119. package/dist/structured-data/atomic-value.d.ts +3 -0
  120. package/dist/structured-data/atomic-value.js +6 -0
  121. package/dist/structured-data/column-controls.d.ts +21 -0
  122. package/dist/structured-data/column-controls.js +180 -0
  123. package/dist/structured-data/copy-json-action.d.ts +10 -0
  124. package/dist/structured-data/copy-json-action.js +87 -0
  125. package/dist/structured-data/delete-row-action.d.ts +10 -0
  126. package/dist/structured-data/delete-row-action.js +37 -0
  127. package/dist/structured-data/edit-base.d.ts +3 -0
  128. package/dist/structured-data/edit-base.js +8 -0
  129. package/dist/structured-data/filter-value.d.ts +2 -0
  130. package/dist/structured-data/filter-value.js +28 -0
  131. package/dist/structured-data/row-selection-icon.d.ts +9 -0
  132. package/dist/structured-data/row-selection-icon.js +6 -0
  133. package/dist/structured-data/structured-cell-fill-context.d.ts +6 -0
  134. package/dist/structured-data/structured-cell-fill-context.js +7 -0
  135. package/dist/structured-data/structured-data-content.d.ts +1 -0
  136. package/dist/structured-data/structured-data-content.js +1 -0
  137. package/dist/structured-data/table-types.d.ts +108 -0
  138. package/dist/structured-data/table-types.js +1 -0
  139. package/dist/structured-data/use-metadata-persistence.d.ts +15 -0
  140. package/dist/structured-data/use-metadata-persistence.js +18 -0
  141. package/dist/structured-data/use-optimistic-text-document.d.ts +22 -0
  142. package/dist/structured-data/use-optimistic-text-document.js +30 -0
  143. package/dist/structured-data/value-cell.d.ts +15 -0
  144. package/dist/structured-data/value-cell.js +81 -0
  145. package/dist/styles.css +2 -0
  146. package/dist/surface.d.ts +14 -0
  147. package/dist/surface.js +73 -0
  148. package/dist/tabular-data-view.d.ts +5 -0
  149. package/dist/tabular-data-view.js +493 -0
  150. package/dist/viewer-state.d.ts +72 -0
  151. package/dist/viewer-state.js +79 -0
  152. package/dist/widget-registry.d.ts +60 -0
  153. package/dist/widget-registry.js +126 -0
  154. package/dist/widgets/boolean-widget.d.ts +2 -0
  155. package/dist/widgets/boolean-widget.js +15 -0
  156. package/dist/widgets/date-picker-widget.d.ts +18 -0
  157. package/dist/widgets/date-picker-widget.js +265 -0
  158. package/dist/widgets/dropdown-widget.d.ts +10 -0
  159. package/dist/widgets/dropdown-widget.js +19 -0
  160. package/dist/widgets/multi-select-widget.d.ts +11 -0
  161. package/dist/widgets/multi-select-widget.js +46 -0
  162. package/dist/widgets/number-widget.d.ts +2 -0
  163. package/dist/widgets/number-widget.js +19 -0
  164. package/dist/widgets/read-only-field.d.ts +36 -0
  165. package/dist/widgets/read-only-field.js +39 -0
  166. package/dist/widgets/text-widget.d.ts +2 -0
  167. package/dist/widgets/text-widget.js +19 -0
  168. package/dist/widgets/textarea-widget.d.ts +2 -0
  169. package/dist/widgets/textarea-widget.js +34 -0
  170. package/dist/widgets/types.d.ts +53 -0
  171. package/dist/widgets/types.js +1 -0
  172. package/package.json +83 -0
@@ -0,0 +1,113 @@
1
+ .json-views-app {
2
+ --background: var(--json-views-background, oklch(0.988 0.003 280));
3
+ --foreground: var(--json-views-foreground, oklch(0.27 0.018 280));
4
+ --card: var(--json-views-card, oklch(1 0 0));
5
+ --card-foreground: var(--json-views-card-foreground, oklch(0.27 0.018 280));
6
+ --muted: var(--json-views-muted, oklch(0.957 0.008 280));
7
+ --muted-foreground: var(--json-views-muted-foreground, oklch(0.49 0.022 280));
8
+ --accent: var(--json-views-accent, oklch(0.94 0.019 285));
9
+ --accent-foreground: var(--json-views-accent-foreground, oklch(0.36 0.065 285));
10
+ --primary: var(--json-views-primary, oklch(0.49 0.15 285));
11
+ --primary-foreground: var(--json-views-primary-foreground, oklch(0.99 0.003 285));
12
+ --border: var(--json-views-border, oklch(0.901 0.009 280));
13
+ --input: var(--json-views-input, oklch(0.901 0.009 280));
14
+ --ring: var(--json-views-ring, oklch(0.55 0.15 285));
15
+ --link: var(--json-views-link, oklch(0.48 0.14 250));
16
+ --warning: var(--json-views-warning, oklch(0.48 0.09 70));
17
+ --destructive: var(--json-views-destructive, oklch(0.49 0.16 25));
18
+ --destructive-border: var(--json-views-destructive-border, oklch(0.86 0.055 25));
19
+ --destructive-surface: var(--json-views-destructive-surface, oklch(0.965 0.015 25));
20
+ --destructive-foreground: var(--json-views-destructive-foreground, oklch(0.43 0.12 25));
21
+ }
22
+
23
+ .json-views-app[data-json-views-theme="dark"] {
24
+ --background: var(--json-views-background, oklch(0.205 0.012 280));
25
+ --foreground: var(--json-views-foreground, oklch(0.93 0.01 280));
26
+ --card: var(--json-views-card, oklch(0.245 0.015 280));
27
+ --card-foreground: var(--json-views-card-foreground, oklch(0.93 0.01 280));
28
+ --muted: var(--json-views-muted, oklch(0.27 0.014 280));
29
+ --muted-foreground: var(--json-views-muted-foreground, oklch(0.71 0.025 280));
30
+ --accent: var(--json-views-accent, oklch(0.31 0.035 285));
31
+ --accent-foreground: var(--json-views-accent-foreground, oklch(0.87 0.035 285));
32
+ --primary: var(--json-views-primary, oklch(0.79 0.105 285));
33
+ --primary-foreground: var(--json-views-primary-foreground, oklch(0.23 0.04 285));
34
+ --border: var(--json-views-border, oklch(0.355 0.019 280));
35
+ --input: var(--json-views-input, oklch(0.355 0.019 280));
36
+ --ring: var(--json-views-ring, oklch(0.73 0.12 285));
37
+ --link: var(--json-views-link, oklch(0.77 0.1 250));
38
+ --warning: var(--json-views-warning, oklch(0.81 0.11 80));
39
+ --destructive: var(--json-views-destructive, oklch(0.77 0.13 25));
40
+ --destructive-border: var(--json-views-destructive-border, oklch(0.46 0.08 25));
41
+ --destructive-surface: var(--json-views-destructive-surface, oklch(0.28 0.03 25));
42
+ --destructive-foreground: var(--json-views-destructive-foreground, oklch(0.86 0.07 25));
43
+ }
44
+
45
+ .json-views-app .json-views-inline-feedback-action {
46
+ display: inline-flex;
47
+ width: auto;
48
+ min-width: 24px;
49
+ max-width: 24px;
50
+ height: 24px;
51
+ flex-shrink: 0;
52
+ align-items: center;
53
+ justify-content: center;
54
+ gap: 0;
55
+ overflow: hidden;
56
+ padding: 0;
57
+ border: 0;
58
+ border-radius: 5px;
59
+ color: var(--muted-foreground);
60
+ background: transparent;
61
+ font-family: inherit;
62
+ font-size: 13px;
63
+ font-weight: 400;
64
+ line-height: 1;
65
+ letter-spacing: normal;
66
+ white-space: nowrap;
67
+ cursor: pointer;
68
+ transition: max-width 220ms cubic-bezier(.2, .8, .2, 1), padding 220ms ease, gap 220ms ease, color 150ms ease, background-color 150ms ease;
69
+ }
70
+ .json-views-app .json-views-inline-feedback-action:hover,
71
+ .json-views-app .json-views-inline-feedback-action.is-feedback {
72
+ color: var(--foreground);
73
+ background: var(--accent);
74
+ }
75
+ .json-views-app .json-views-inline-feedback-action.is-strong { color: var(--foreground); }
76
+ .json-views-app .json-views-inline-feedback-action.is-destructive { color: var(--destructive); }
77
+ .json-views-app .json-views-inline-feedback-action.is-feedback {
78
+ max-width: 150px;
79
+ gap: 5px;
80
+ padding-inline: 7px;
81
+ }
82
+ .json-views-app .json-views-inline-feedback-action:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
83
+ .json-views-app .json-views-inline-feedback-action:disabled { cursor: default; opacity: .5; }
84
+ .json-views-app .json-views-inline-feedback-action > svg { flex-shrink: 0; }
85
+ .json-views-app .json-views-inline-feedback-action.is-feedback > svg { animation: json-views-action-confirm 220ms cubic-bezier(.2, .8, .2, 1); }
86
+ .json-views-app .json-views-inline-feedback-label {
87
+ max-width: 0;
88
+ overflow: hidden;
89
+ opacity: 0;
90
+ transform: translateX(-4px);
91
+ transition: max-width 220ms cubic-bezier(.2, .8, .2, 1), opacity 150ms ease 50ms, transform 220ms ease;
92
+ }
93
+ .json-views-app .json-views-inline-feedback-action.is-feedback .json-views-inline-feedback-label {
94
+ max-width: 120px;
95
+ opacity: 1;
96
+ transform: translateX(0);
97
+ }
98
+ @keyframes json-views-action-confirm {
99
+ from { opacity: .4; transform: scale(.72); }
100
+ to { opacity: 1; transform: scale(1); }
101
+ }
102
+ @media (prefers-reduced-motion: reduce) {
103
+ .json-views-app .json-views-inline-feedback-action,
104
+ .json-views-app .json-views-inline-feedback-label { transition: none; }
105
+ .json-views-app .json-views-inline-feedback-action.is-feedback > svg { animation: none; }
106
+ }
107
+
108
+
109
+ .json-views-app {
110
+ color: var(--foreground);
111
+ background: var(--background);
112
+ font-family: var(--json-views-font-family, Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif);
113
+ }
@@ -0,0 +1,33 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { AtomicValue } from './atomic-value.js';
3
+ interface AtomicValueEditorProps {
4
+ actions?: ReactNode;
5
+ children: ReactNode;
6
+ className?: string;
7
+ editorClassName?: string;
8
+ label: string;
9
+ multiline?: boolean;
10
+ onCommit: (value: AtomicValue) => Promise<void>;
11
+ preserveLayout?: boolean;
12
+ saving?: boolean;
13
+ value: AtomicValue;
14
+ }
15
+ export declare function EditValueAction({ disabled, label, onClick, }: {
16
+ disabled?: boolean;
17
+ label: string;
18
+ onClick: () => void;
19
+ }): import("react").JSX.Element;
20
+ export declare function AtomicValueEditor({ actions, children, className, editorClassName, label, multiline, onCommit, preserveLayout, saving, value, }: AtomicValueEditorProps): import("react").JSX.Element;
21
+ export declare function StructuredValueCellFrame({ activationDisabled, activationLabel, activationRole, actions, children, leading, className, onActivate, overlayActions, }: {
22
+ activationDisabled?: boolean;
23
+ activationLabel?: string;
24
+ /** Use a group when rendered content contains its own interactive descendants. */
25
+ activationRole?: 'button' | 'group';
26
+ actions?: ReactNode;
27
+ children: ReactNode;
28
+ leading?: ReactNode;
29
+ className?: string;
30
+ onActivate?: () => void;
31
+ overlayActions?: boolean;
32
+ }): import("react").JSX.Element;
33
+ export {};
@@ -0,0 +1,195 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useContext, useEffect, useLayoutEffect, useRef, useState, } from 'react';
3
+ import { Pencil } from 'lucide-react';
4
+ import { cn } from '../lib/cn.js';
5
+ import { StructuredCellFillContext } from './structured-cell-fill-context.js';
6
+ import { useEditBaseChanged } from './edit-base.js';
7
+ const CONTROL_CLASS = 'min-h-8 w-full min-w-0 rounded-md border border-border bg-background px-2 [font:inherit] text-foreground outline-none transition-colors placeholder:text-muted-foreground hover:border-input focus:border-ring focus:ring-2 focus:ring-inset focus:ring-ring/20';
8
+ export function EditValueAction({ disabled = false, label, onClick, }) {
9
+ return (_jsx("button", { type: "button", "data-id": "structured-edit-value", "aria-label": `Edit ${label}`, disabled: disabled, className: "grid h-6 w-6 place-items-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-40", onClick: (event) => {
10
+ event.stopPropagation();
11
+ onClick();
12
+ }, children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }));
13
+ }
14
+ export function AtomicValueEditor({ actions, children, className, editorClassName, label, multiline = false, onCommit, preserveLayout = false, saving = false, value, }) {
15
+ const [editing, setEditing] = useState(false);
16
+ const fillCell = useContext(StructuredCellFillContext);
17
+ const frameRef = useRef(null);
18
+ const [editorMinHeight, setEditorMinHeight] = useState();
19
+ const stableLayout = multiline || preserveLayout;
20
+ const preserveHeight = () => {
21
+ if (!stableLayout)
22
+ return;
23
+ const height = frameRef.current?.getBoundingClientRect().height ?? 0;
24
+ if (height > 0)
25
+ setEditorMinHeight(height);
26
+ };
27
+ const stopEditing = () => {
28
+ setEditorMinHeight(undefined);
29
+ setEditing(false);
30
+ };
31
+ if (editing) {
32
+ const editor = (_jsx(AtomicValueDirectEditor, { controlClassName: editorClassName, label: label, disabled: saving, minHeight: editorMinHeight, multiline: multiline, onCancel: stopEditing, onCommit: async (nextValue) => {
33
+ await onCommit(nextValue);
34
+ stopEditing();
35
+ }, value: value }));
36
+ // A zero-padding host cell would press the input against the cell
37
+ // borders. Deliberately tighter than the idle frame's px-3/py-2: the
38
+ // 32px control plus py-1.5 matches the 44px row exactly (py-2 would grow
39
+ // the row on every edit toggle), and the control's own ~8px text inset
40
+ // on top of px-1.5 lands the edited text near the idle text position.
41
+ const paddedEditor = fillCell ? _jsx("div", { className: "px-1.5 py-1.5", children: editor }) : editor;
42
+ return stableLayout
43
+ ? _jsx("div", { ref: frameRef, "data-id": "atomic-value-editor-frame", className: "h-full min-w-0", style: editorMinHeight ? { minHeight: editorMinHeight } : undefined, children: paddedEditor })
44
+ : paddedEditor;
45
+ }
46
+ const display = (_jsx(StructuredValueCellFrame, { actions: actions, activationDisabled: saving, activationLabel: `Edit ${label}`, className: className, onActivate: () => {
47
+ preserveHeight();
48
+ setEditing(true);
49
+ }, children: children }));
50
+ return stableLayout
51
+ ? _jsx("div", { ref: frameRef, "data-id": "atomic-value-editor-frame", className: "h-full min-w-0", style: editorMinHeight ? { minHeight: editorMinHeight } : undefined, children: display })
52
+ : display;
53
+ }
54
+ export function StructuredValueCellFrame({ activationDisabled = false, activationLabel, activationRole = 'button', actions, children, leading, className, onActivate, overlayActions = false, }) {
55
+ const fillCell = useContext(StructuredCellFillContext);
56
+ // An editable cell advertises itself with a primary-tinted hover — the
57
+ // neutral accent tone sits on white/card surfaces where it is barely
58
+ // perceptible. A low alpha reads on white but is swallowed by the
59
+ // near-black dark surfaces, so dark raises it.
60
+ return (_jsxs("div", { "data-id": "structured-value-cell", className: cn('group/structured-cell json-viewer-hovercell relative flex min-h-7 w-full min-w-0 items-start transition-colors',
61
+ // Inside a zero-padding table cell the frame IS the cell face:
62
+ // square corners, and the padding the cell would otherwise carry.
63
+ // Table values use the entire width up to the column divider. The
64
+ // right edge is deliberately flush so clipped content makes the
65
+ // boundary of its column unmistakable.
66
+ fillCell ? 'h-full items-center rounded-none py-2 pl-3 pr-0' : 'rounded-md px-1.5', onActivate && !activationDisabled
67
+ ? fillCell
68
+ ? 'before:absolute before:inset-y-0 before:-left-5 before:right-0 hover:before:bg-primary/10 dark:hover:before:bg-primary/25'
69
+ : 'hover:bg-primary/10 dark:hover:bg-primary/25'
70
+ : fillCell
71
+ ? 'before:absolute before:inset-y-0 before:-left-5 before:right-0 hover:before:bg-accent/70'
72
+ : 'hover:bg-accent/70', fillCell && onActivate && !activationDisabled && 'cursor-text', className), onClick: (event) => {
73
+ // The table cell's padding belongs to the same action as its value.
74
+ // Child actions keep their own handlers and must not activate twice.
75
+ if (fillCell && event.target === event.currentTarget && !activationDisabled)
76
+ onActivate?.();
77
+ }, children: [leading && _jsx("span", { className: "mr-1.5 flex min-h-7 shrink-0 items-center", children: leading }), _jsx("div", { "data-id": onActivate ? 'atomic-edit-value' : undefined, "aria-disabled": onActivate ? activationDisabled : undefined, "aria-label": onActivate ? activationLabel : undefined, role: onActivate ? activationRole : undefined, tabIndex: onActivate && !activationDisabled ? 0 : undefined, title: onActivate ? activationLabel : undefined, className: cn('flex min-h-7 min-w-0 flex-1 items-center', fillCell && 'self-stretch',
78
+ // The actions overlay the trailing edge, so the value ends before
79
+ // their lane: truncation stops short of the chip rather than being
80
+ // covered by it. Reserved unconditionally — sizing it on hover
81
+ // would re-truncate the text under the pointer.
82
+ actions && !fillCell && !overlayActions && 'pr-6', onActivate && !activationDisabled && 'cursor-text focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring'), onClick: (event) => {
83
+ if (!onActivate || activationDisabled)
84
+ return;
85
+ const interactiveTarget = event.target instanceof Element
86
+ ? event.target.closest('a[href], button, input, textarea, select, summary, [controls], [contenteditable]:not([contenteditable="false"]), [role="button"], [role="link"], [role="checkbox"], [role="radio"], [role="switch"], [role="slider"], [role="tab"], [role="menuitem"], [role="option"]')
87
+ : null;
88
+ if (interactiveTarget && interactiveTarget !== event.currentTarget)
89
+ return;
90
+ onActivate();
91
+ }, onKeyDown: (event) => {
92
+ if (!onActivate || activationDisabled)
93
+ return;
94
+ if (event.target !== event.currentTarget)
95
+ return;
96
+ if (event.key !== 'Enter' && event.key !== ' ')
97
+ return;
98
+ event.preventDefault();
99
+ onActivate();
100
+ }, children: children }), actions && (
101
+ // No `z-index`: a rank here escapes the cell and bids against
102
+ // whatever else the surface stacks — a table's sticky header sits in
103
+ // a different subtree, so an equal rank hands the win to this one on
104
+ // DOM order alone. On touch there is no `hover:hover` to fall back
105
+ // on, so these are visible on every row at once, including rows
106
+ // scrolled under a sticky header.
107
+ _jsx("div", { "data-id": "structured-value-cell-actions", className: cn('absolute right-1 flex opacity-100 transition-opacity [@media(hover:hover)]:opacity-0 [@media(hover:hover)]:group-hover/structured-cell:opacity-100 [@media(hover:hover)]:group-focus-within/structured-cell:opacity-100',
108
+ // No surface of its own: the layer is exactly the chip, so it
109
+ // never has to guess the colour of the cell face it sits on —
110
+ // which the row wash and the frame's own hover both repaint.
111
+ // A table row is a uniform height, so the chip centres in it;
112
+ // a free-standing cell can be a tall block of prose, where the
113
+ // chip belongs beside its first line.
114
+ overlayActions ? 'top-2 right-2 rounded-md bg-background/90 shadow-sm' : fillCell ? 'top-1/2 -translate-y-1/2' : 'top-0.5'), children: actions }))] }));
115
+ }
116
+ function AtomicValueDirectEditor({ controlClassName, disabled = false, label, minHeight, multiline, onCancel, onCommit, value, }) {
117
+ const [draft, setDraft] = useState(typeof value === 'boolean' ? value : String(value));
118
+ const [error, setError] = useState();
119
+ const containerRef = useRef(null);
120
+ const textareaRef = useRef(null);
121
+ const committingRef = useRef(false);
122
+ const originalValueRef = useRef(value);
123
+ const attemptedCommitRef = useRef(false);
124
+ const baseChanged = useEditBaseChanged();
125
+ useEffect(() => {
126
+ containerRef.current?.querySelector('input, textarea')?.focus({ preventScroll: multiline });
127
+ }, [multiline]);
128
+ useLayoutEffect(() => {
129
+ if (!multiline || !textareaRef.current)
130
+ return;
131
+ const textarea = textareaRef.current;
132
+ textarea.style.height = 'auto';
133
+ textarea.style.height = `${Math.max(minHeight ?? 0, textarea.scrollHeight)}px`;
134
+ }, [draft, minHeight, multiline]);
135
+ const commit = async (nextBoolean) => {
136
+ if (committingRef.current)
137
+ return;
138
+ if (baseChanged || (!attemptedCommitRef.current && !Object.is(value, originalValueRef.current))) {
139
+ setError('This document changed outside this editor. Cancel and reopen it before saving.');
140
+ return;
141
+ }
142
+ let nextValue;
143
+ if (typeof value === 'boolean') {
144
+ nextValue = nextBoolean ?? Boolean(draft);
145
+ }
146
+ else if (typeof value === 'number') {
147
+ const parsed = Number(draft);
148
+ if (typeof draft !== 'string' || draft.trim() === '' || !Number.isFinite(parsed)) {
149
+ setError('Enter a valid number');
150
+ return;
151
+ }
152
+ nextValue = parsed;
153
+ }
154
+ else {
155
+ nextValue = String(draft);
156
+ }
157
+ if (Object.is(nextValue, value) && !error) {
158
+ onCancel();
159
+ return;
160
+ }
161
+ committingRef.current = true;
162
+ attemptedCommitRef.current = true;
163
+ setError(undefined);
164
+ try {
165
+ await onCommit(nextValue);
166
+ }
167
+ catch (commitError) {
168
+ setError(commitError instanceof Error ? commitError.message : 'Could not save this value');
169
+ }
170
+ finally {
171
+ committingRef.current = false;
172
+ }
173
+ };
174
+ return (_jsxs("div", { ref: containerRef, "data-id": "atomic-direct-editor", className: "relative min-w-0", onBlur: (event) => {
175
+ if (event.currentTarget.contains(event.relatedTarget))
176
+ return;
177
+ void commit();
178
+ }, onKeyDown: (event) => {
179
+ if (event.key === 'Escape') {
180
+ event.preventDefault();
181
+ event.stopPropagation();
182
+ onCancel();
183
+ return;
184
+ }
185
+ const isTextarea = event.target instanceof HTMLTextAreaElement;
186
+ if (event.key === 'Enter' && (!isTextarea || event.metaKey || event.ctrlKey)) {
187
+ event.preventDefault();
188
+ void commit();
189
+ }
190
+ }, children: [typeof value === 'boolean' ? (_jsxs("label", { className: cn('flex min-h-8 items-center gap-2 rounded-md border border-border bg-background px-2 text-xs text-foreground', controlClassName), children: [_jsx("input", { "aria-label": `Edit ${label}`, type: "checkbox", disabled: disabled, checked: draft === true, className: "h-4 w-4 accent-primary", onChange: (event) => {
191
+ const next = event.target.checked;
192
+ setDraft(next);
193
+ void commit(next);
194
+ } }), _jsx("span", { children: draft === true ? 'True' : 'False' })] })) : multiline ? (_jsx("textarea", { ref: textareaRef, "aria-label": `Edit ${label}`, disabled: disabled, className: cn(CONTROL_CLASS, 'min-h-24 resize-y py-2', controlClassName), style: minHeight ? { minHeight } : undefined, value: draft, onChange: (event) => setDraft(event.target.value) })) : (_jsx("input", { "aria-label": `Edit ${label}`, type: "text", disabled: disabled, inputMode: typeof value === 'number' ? 'decimal' : undefined, className: cn(CONTROL_CLASS, controlClassName), value: draft, onChange: (event) => setDraft(event.target.value) })), error && _jsx("p", { role: "alert", className: "mt-1 text-[0.6875rem] text-destructive", children: error })] }));
195
+ }
@@ -0,0 +1,3 @@
1
+ export type AtomicValue = string | number | boolean;
2
+ /** Values whose editor type is unambiguous from the source value itself. */
3
+ export declare function isEditableAtomicValue(value: unknown): value is AtomicValue;
@@ -0,0 +1,6 @@
1
+ /** Values whose editor type is unambiguous from the source value itself. */
2
+ export function isEditableAtomicValue(value) {
3
+ return typeof value === 'string'
4
+ || typeof value === 'boolean'
5
+ || (typeof value === 'number' && Number.isFinite(value));
6
+ }
@@ -0,0 +1,21 @@
1
+ import type { ColumnFilterOperatorOption, ColumnFilterRule, SortConfig, SortDirection, TabularObjectArrayViewProps } from './table-types.js';
2
+ export declare function AddColumnHeader({ onAdd }: {
3
+ onAdd: NonNullable<TabularObjectArrayViewProps['onAddColumn']>;
4
+ }): import("react").JSX.Element;
5
+ export declare function ColumnHeaderMenu({ activeFilter, activeSort, canHide, column, draggable, dragging, filterOperators, filterValues, label, onFilter, onHide, onMoveFirst, onDelete, onRename, onSort, }: {
6
+ activeFilter?: ColumnFilterRule;
7
+ activeSort: SortConfig;
8
+ canHide: boolean;
9
+ column: string;
10
+ draggable?: boolean;
11
+ dragging?: boolean;
12
+ filterOperators?: readonly ColumnFilterOperatorOption[];
13
+ filterValues?: readonly unknown[];
14
+ label: string;
15
+ onFilter?: (column: string, filter: ColumnFilterRule | null) => void | Promise<void>;
16
+ onMoveFirst?: () => void | Promise<void>;
17
+ onHide?: (column: string) => void | Promise<void>;
18
+ onDelete?: (column: string) => void | Promise<void>;
19
+ onRename?: (column: string, label: string) => void | Promise<void>;
20
+ onSort?: (column: string, direction: SortDirection | null) => void | Promise<void>;
21
+ }): import("react").JSX.Element;
@@ -0,0 +1,180 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsonValueText } from '@script-it/json-views-core';
3
+ import { useEffect, useId, useRef, useState } from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { ArrowLeftToLine, ArrowUpDown, ChevronDown, ChevronRight, ChevronUp, EyeOff, Filter, Plus, Trash2, X } from 'lucide-react';
6
+ import { useDismiss } from '../lib/use-dismiss.js';
7
+ import { useAnchoredPosition } from '../lib/use-anchored-position.js';
8
+ import { MenuSelect } from '../primitives/menu-select.js';
9
+ import { useJsonViewsPortalContainer } from '../surface.js';
10
+ const DEFAULT_FILTER_OPERATORS = [
11
+ { value: 'eq', label: 'is' },
12
+ { value: 'neq', label: 'is not' },
13
+ { value: 'in', label: 'is any of' },
14
+ { value: 'notIn', label: 'is none of' },
15
+ { value: 'gt', label: 'is greater than' },
16
+ { value: 'gte', label: 'is at least' },
17
+ { value: 'lt', label: 'is less than' },
18
+ { value: 'lte', label: 'is at most' },
19
+ { value: 'contains', label: 'contains' },
20
+ { value: 'notContains', label: 'does not contain' },
21
+ { value: 'isEmpty', label: 'is empty' },
22
+ { value: 'isNotEmpty', label: 'is not empty' },
23
+ ];
24
+ function filterValueText(value, operator) {
25
+ if ((operator === 'in' || operator === 'notIn') && Array.isArray(value))
26
+ return value.map(jsonValueText).join(', ');
27
+ if (typeof value === 'string')
28
+ return value;
29
+ if (value === undefined)
30
+ return '';
31
+ try {
32
+ return JSON.stringify(value);
33
+ }
34
+ catch {
35
+ return jsonValueText(value);
36
+ }
37
+ }
38
+ export function AddColumnHeader({ onAdd }) {
39
+ const portalContainer = useJsonViewsPortalContainer();
40
+ const [open, setOpen] = useState(false);
41
+ const [label, setLabel] = useState('');
42
+ const [type, setType] = useState('text');
43
+ const [saving, setSaving] = useState(false);
44
+ const [error, setError] = useState();
45
+ const triggerRef = useRef(null);
46
+ const dialogRef = useRef(null);
47
+ const position = useAnchoredPosition(open, triggerRef, dialogRef);
48
+ const close = () => { if (!saving) {
49
+ setOpen(false);
50
+ setError(undefined);
51
+ } };
52
+ const show = () => {
53
+ setOpen(true);
54
+ };
55
+ useDismiss({ enabled: open, onDismiss: close, insideRefs: [triggerRef, dialogRef] });
56
+ return _jsxs(_Fragment, { children: [_jsxs("button", { ref: triggerRef, type: "button", "data-id": "tabular-add-column", className: "flex h-full min-h-9 w-full items-center gap-1.5 px-3 py-2 text-left text-muted-foreground hover:bg-accent hover:text-foreground", onClick: show, children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), " Add property"] }), open && typeof document !== 'undefined' && createPortal(_jsxs("form", { ref: dialogRef, "aria-label": "Add property", className: "z-[100] grid w-64 gap-3 rounded-md border border-border bg-card p-3 text-foreground shadow-lg", style: position, onSubmit: (event) => {
57
+ event.preventDefault();
58
+ const nextLabel = label.trim();
59
+ if (!nextLabel)
60
+ return;
61
+ setSaving(true);
62
+ setError(undefined);
63
+ void Promise.resolve(onAdd({ label: nextLabel, type })).then(() => {
64
+ setLabel('');
65
+ setType('text');
66
+ setOpen(false);
67
+ }).catch((cause) => setError(cause instanceof Error ? cause.message : 'Could not add this property')).finally(() => setSaving(false));
68
+ }, children: [_jsxs("label", { className: "grid gap-1 text-xs font-medium", children: ["Name", _jsx("input", { autoFocus: true, value: label, onChange: (event) => setLabel(event.target.value), className: "h-8 rounded-md border border-input bg-background px-2 text-sm font-normal outline-none focus-visible:ring-1 focus-visible:ring-ring" })] }), _jsxs("label", { className: "grid gap-1 text-xs font-medium", children: ["Type", _jsx(MenuSelect, { ariaLabel: "Property type", options: [
69
+ { value: 'text', label: 'Text' },
70
+ { value: 'markdown', label: 'Markdown' },
71
+ { value: 'html', label: 'HTML' },
72
+ { value: 'number', label: 'Number' },
73
+ { value: 'checkbox', label: 'Checkbox' },
74
+ { value: 'date', label: 'Date' },
75
+ { value: 'select', label: 'Select' },
76
+ { value: 'multi-select', label: 'Multi-select' },
77
+ ], value: type, onChange: (next) => { if (next)
78
+ setType(next); } })] }), error && _jsx("p", { role: "alert", className: "m-0 text-xs text-destructive", children: error }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx("button", { type: "button", className: "h-8 rounded-md px-2 text-xs hover:bg-accent", onClick: close, children: "Cancel" }), _jsx("button", { type: "submit", disabled: saving || !label.trim(), className: "h-8 rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground disabled:opacity-50", children: "Add property" })] })] }), portalContainer ?? document.body)] });
79
+ }
80
+ export function ColumnHeaderMenu({ activeFilter, activeSort, canHide, column, draggable = false, dragging = false, filterOperators = DEFAULT_FILTER_OPERATORS, filterValues = [], label, onFilter, onHide, onMoveFirst, onDelete, onRename, onSort, }) {
81
+ const portalContainer = useJsonViewsPortalContainer();
82
+ const [open, setOpen] = useState(false);
83
+ const [draftLabel, setDraftLabel] = useState(label);
84
+ const [saving, setSaving] = useState(false);
85
+ const [error, setError] = useState();
86
+ const [confirmDelete, setConfirmDelete] = useState(false);
87
+ const [editingFilter, setEditingFilter] = useState(false);
88
+ const [editingSort, setEditingSort] = useState(false);
89
+ const [draftFilterOperator, setDraftFilterOperator] = useState(activeFilter?.operator ?? filterOperators[0]?.value ?? 'eq');
90
+ const [draftFilterValue, setDraftFilterValue] = useState(() => filterValueText(activeFilter?.value, activeFilter?.operator));
91
+ const filterValueListId = useId();
92
+ const triggerRef = useRef(null);
93
+ const menuRef = useRef(null);
94
+ const sortTriggerRef = useRef(null);
95
+ const sortMenuRef = useRef(null);
96
+ const menuPosition = useAnchoredPosition(open, triggerRef, menuRef);
97
+ const sortPosition = useAnchoredPosition(open && editingSort, sortTriggerRef, sortMenuRef, 'right');
98
+ const renameTimerRef = useRef(undefined);
99
+ const renameVersionRef = useRef(0);
100
+ const sort = activeSort?.column === column ? activeSort.direction : undefined;
101
+ useEffect(() => setDraftLabel(label), [label]);
102
+ useEffect(() => {
103
+ if (dragging)
104
+ setOpen(false);
105
+ }, [dragging]);
106
+ useEffect(() => () => {
107
+ if (renameTimerRef.current !== undefined)
108
+ clearTimeout(renameTimerRef.current);
109
+ }, []);
110
+ const run = async (action) => {
111
+ setSaving(true);
112
+ setError(undefined);
113
+ try {
114
+ await action();
115
+ setOpen(false);
116
+ }
117
+ catch (actionError) {
118
+ setError(actionError instanceof Error ? actionError.message : 'Could not update this column');
119
+ }
120
+ finally {
121
+ setSaving(false);
122
+ }
123
+ };
124
+ const saveRename = (value, delay = 0) => {
125
+ if (renameTimerRef.current !== undefined)
126
+ clearTimeout(renameTimerRef.current);
127
+ const next = value.trim();
128
+ if (!onRename || !next || next === label)
129
+ return;
130
+ const version = ++renameVersionRef.current;
131
+ const save = () => {
132
+ renameTimerRef.current = undefined;
133
+ setSaving(true);
134
+ void Promise.resolve(onRename(column, next)).then(() => {
135
+ if (version === renameVersionRef.current)
136
+ setError(undefined);
137
+ }).catch((renameError) => {
138
+ if (version === renameVersionRef.current)
139
+ setError(renameError instanceof Error ? renameError.message : 'Could not rename this column');
140
+ }).finally(() => {
141
+ if (version === renameVersionRef.current)
142
+ setSaving(false);
143
+ });
144
+ };
145
+ if (delay > 0)
146
+ renameTimerRef.current = setTimeout(save, delay);
147
+ else
148
+ save();
149
+ };
150
+ const openMenu = () => {
151
+ setDraftLabel(label);
152
+ setError(undefined);
153
+ setConfirmDelete(false);
154
+ setEditingFilter(false);
155
+ setEditingSort(false);
156
+ setDraftFilterOperator(activeFilter?.operator ?? filterOperators[0]?.value ?? 'eq');
157
+ setDraftFilterValue(filterValueText(activeFilter?.value, activeFilter?.operator));
158
+ setOpen(true);
159
+ };
160
+ const closeMenu = () => {
161
+ setOpen(false);
162
+ saveRename(draftLabel);
163
+ };
164
+ useDismiss({ enabled: open, onDismiss: closeMenu, insideRefs: [triggerRef, menuRef] });
165
+ return (_jsxs(_Fragment, { children: [_jsxs("button", { ref: triggerRef, type: "button", "data-id": "tabular-column-menu", "aria-label": `Column options for ${label}`, "aria-expanded": open, "aria-haspopup": "menu", draggable: draggable, className: "flex h-full min-h-9 w-full min-w-0 items-center px-3 py-2 text-left transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", title: label, onClick: openMenu, children: [_jsx("span", { className: "block min-w-0 flex-1 overflow-hidden whitespace-nowrap", children: label }), activeFilter && _jsx(Filter, { "data-id": "tabular-column-filter-active", "aria-hidden": true, className: "ml-1.5 h-3 w-3 shrink-0 text-primary" })] }), open && typeof document !== 'undefined' && createPortal(_jsxs("div", { ref: menuRef, role: "menu", className: "z-[100] w-60 rounded-md border border-border bg-card p-1.5 text-foreground shadow-lg", style: menuPosition, children: [confirmDelete ? _jsxs("div", { className: "grid gap-2 p-1", children: [_jsxs("p", { className: "text-xs text-muted-foreground", children: ["Delete ", label, " from every row?"] }), _jsxs("div", { className: "flex justify-end gap-1.5", children: [_jsx("button", { type: "button", className: "rounded-md px-2 py-1.5 text-xs hover:bg-accent", onClick: () => setConfirmDelete(false), children: "Cancel" }), _jsx("button", { type: "button", "data-id": "tabular-confirm-delete-column", disabled: saving, className: "rounded-md border border-destructive-border bg-destructive-surface px-2 py-1.5 text-xs font-medium text-destructive-foreground hover:brightness-95 focus-visible:outline focus-visible:outline-2 focus-visible:outline-ring disabled:opacity-50", onClick: () => { if (onDelete)
166
+ void run(() => onDelete(column)); }, children: "Delete property" })] })] }) : editingFilter ? _jsxs("form", { className: "grid gap-2 p-1", "aria-label": `Filter ${label}`, onSubmit: (event) => {
167
+ event.preventDefault();
168
+ if (!onFilter)
169
+ return;
170
+ const presenceOnly = draftFilterOperator === 'isEmpty' || draftFilterOperator === 'isNotEmpty';
171
+ void run(() => onFilter(column, {
172
+ operator: draftFilterOperator,
173
+ ...(presenceOnly ? {} : { value: draftFilterValue }),
174
+ }));
175
+ }, children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Filter, { className: "h-3.5 w-3.5 text-muted-foreground" }), _jsxs("span", { className: "min-w-0 flex-1 truncate text-xs font-medium", children: ["Filter ", label] })] }), _jsxs("label", { className: "grid gap-1 text-[0.6875rem] text-muted-foreground", children: ["Condition", _jsx(MenuSelect, { ariaLabel: `Filter ${label} condition`, options: filterOperators, value: draftFilterOperator, onChange: setDraftFilterOperator })] }), draftFilterOperator !== 'isEmpty' && draftFilterOperator !== 'isNotEmpty' && _jsxs("label", { className: "grid gap-1 text-[0.6875rem] text-muted-foreground", children: ["Value", _jsx("input", { autoFocus: true, "aria-label": `Filter ${label} value`, list: filterValueListId, className: "h-8 w-full rounded-md border border-input bg-background px-2 text-xs text-foreground outline-none focus-visible:ring-1 focus-visible:ring-ring", placeholder: draftFilterOperator === 'in' || draftFilterOperator === 'notIn' ? 'Comma-separated values' : 'Value', value: draftFilterValue, onChange: (event) => setDraftFilterValue(event.target.value) }), filterValues.length > 0 && _jsx("datalist", { id: filterValueListId, children: filterValues.map((value, index) => _jsx("option", { value: filterValueText(value) }, `${filterValueText(value)}:${index}`)) })] }), _jsxs("div", { className: "flex justify-between gap-2 pt-1", children: [_jsx("button", { type: "button", "data-id": "tabular-clear-filter", disabled: saving || !activeFilter, className: "h-8 rounded-md px-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:opacity-40", onClick: () => { if (onFilter)
176
+ void run(() => onFilter(column, null)); }, children: "Clear" }), _jsxs("div", { className: "flex gap-1", children: [_jsx("button", { type: "button", className: "h-8 rounded-md px-2 text-xs hover:bg-accent", onClick: () => setEditingFilter(false), children: "Back" }), _jsx("button", { type: "submit", "data-id": "tabular-apply-filter", disabled: saving || ((draftFilterOperator !== 'isEmpty' && draftFilterOperator !== 'isNotEmpty') && !draftFilterValue.trim()), className: "h-8 rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground disabled:opacity-50", children: "Apply" })] })] })] }) : _jsxs(_Fragment, { children: [onRename && (_jsx("div", { className: "mb-1", children: _jsx("input", { "aria-label": `Rename ${label}`, className: "h-8 w-full rounded-md border border-input bg-background px-2 text-xs text-foreground outline-none focus-visible:ring-1 focus-visible:ring-ring", value: draftLabel, onChange: (event) => {
177
+ setDraftLabel(event.target.value);
178
+ saveRename(event.target.value, 300);
179
+ }, onBlur: (event) => saveRename(event.target.value) }) })), onRename && _jsx("div", { className: "-mx-1.5 my-1 h-px bg-border" }), onSort && (_jsxs("div", { role: "none", className: "relative", children: [_jsxs("button", { ref: sortTriggerRef, type: "button", role: "menuitem", "data-id": "tabular-sort-column", "aria-haspopup": "menu", "aria-expanded": editingSort, disabled: saving, className: "flex w-full cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-accent focus:bg-accent disabled:pointer-events-none disabled:opacity-50", onClick: () => setEditingSort((current) => !current), children: [_jsx(ArrowUpDown, { className: "mr-2 h-3.5 w-3.5" }), " Sort ", _jsx(ChevronRight, { className: "ml-auto h-3.5 w-3.5 text-muted-foreground" })] }), editingSort && (_jsxs("div", { ref: sortMenuRef, role: "menu", "aria-label": `Sort ${label}`, className: "z-[101] w-52 rounded-md border border-border bg-card p-1.5 text-foreground shadow-lg", style: sortPosition, children: [_jsxs("button", { type: "button", role: "menuitem", "data-id": "tabular-sort-ascending", disabled: saving, className: "flex w-full cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-accent focus:bg-accent disabled:pointer-events-none disabled:opacity-50", onClick: () => { void run(() => onSort(column, 'asc')); }, children: [_jsx(ChevronUp, { className: "mr-2 h-3.5 w-3.5" }), " Sort ascending"] }), _jsxs("button", { type: "button", role: "menuitem", "data-id": "tabular-sort-descending", disabled: saving, className: "flex w-full cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-accent focus:bg-accent disabled:pointer-events-none disabled:opacity-50", onClick: () => { void run(() => onSort(column, 'desc')); }, children: [_jsx(ChevronDown, { className: "mr-2 h-3.5 w-3.5" }), " Sort descending"] }), sort && _jsxs(_Fragment, { children: [_jsx("div", { className: "-mx-1.5 my-1 h-px bg-border" }), _jsxs("button", { type: "button", role: "menuitem", "data-id": "tabular-clear-sort", disabled: saving, className: "flex w-full cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-accent focus:bg-accent disabled:pointer-events-none disabled:opacity-50", onClick: () => { void run(() => onSort(column, null)); }, children: [_jsx(X, { className: "mr-2 h-3.5 w-3.5" }), " Clear sort"] })] })] }))] })), onFilter && (_jsxs(_Fragment, { children: [_jsx("div", { className: "-mx-1.5 my-1 h-px bg-border" }), _jsxs("button", { type: "button", role: "menuitem", "data-id": "tabular-filter-column", disabled: saving, className: "flex w-full cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-accent focus:bg-accent disabled:pointer-events-none disabled:opacity-50", onClick: () => { setEditingSort(false); setEditingFilter(true); }, children: [_jsx(Filter, { className: "mr-2 h-3.5 w-3.5" }), " ", activeFilter ? 'Edit filter' : 'Filter'] })] })), onMoveFirst && _jsxs("button", { type: "button", role: "menuitem", disabled: saving, className: "flex w-full cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-accent focus:bg-accent disabled:pointer-events-none disabled:opacity-50", onClick: () => { void run(onMoveFirst); }, children: [_jsx(ArrowLeftToLine, { className: "mr-2 h-3.5 w-3.5" }), " Move first"] }), onHide && (_jsxs(_Fragment, { children: [_jsx("div", { className: "-mx-1.5 my-1 h-px bg-border" }), _jsxs("button", { type: "button", role: "menuitem", "data-id": "tabular-hide-column", disabled: saving || !canHide, className: "flex w-full cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-left text-sm outline-none transition-colors hover:bg-accent focus:bg-accent disabled:pointer-events-none disabled:opacity-50", onClick: () => { void run(() => onHide(column)); }, children: [_jsx(EyeOff, { className: "mr-2 h-3.5 w-3.5" }), " Hide column"] })] })), onDelete && _jsxs(_Fragment, { children: [_jsx("div", { className: "-mx-1.5 my-1 h-px bg-border" }), _jsxs("button", { type: "button", role: "menuitem", "data-id": "tabular-delete-column", disabled: saving, className: "flex w-full cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-left text-sm text-destructive outline-none transition-colors hover:bg-accent focus:bg-accent disabled:pointer-events-none disabled:opacity-50", onClick: () => setConfirmDelete(true), children: [_jsx(Trash2, { className: "mr-2 h-3.5 w-3.5" }), " Delete property"] })] })] }), error && _jsx("p", { role: "alert", className: "px-2 py-1 text-xs text-destructive", children: error })] }), portalContainer ?? document.body)] }));
180
+ }
@@ -0,0 +1,10 @@
1
+ export declare function CopyJsonAction({ className, dataId, getPathText, getRecordText, label, pathLabel, recordLabel, tone, }: {
2
+ className?: string;
3
+ dataId: string;
4
+ getPathText: () => string;
5
+ getRecordText: () => string;
6
+ label: string;
7
+ pathLabel: 'path' | 'paths';
8
+ recordLabel: 'record' | 'records';
9
+ tone?: 'default' | 'strong';
10
+ }): import("react").JSX.Element;