@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,46 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import yaml, { JSON_SCHEMA } from 'js-yaml';
3
+ import { MultiSelect } from '../primitives/multi-select.js';
4
+ import { ReadOnlyChip, ReadOnlyEmpty } from './read-only-field.js';
5
+ function parseSelectedValues(value) {
6
+ try {
7
+ if (value.startsWith('[')) {
8
+ const parsed = JSON.parse(value);
9
+ return Array.isArray(parsed) ? parsed.map(String) : [];
10
+ }
11
+ if (value.startsWith('-')) {
12
+ const parsed = yaml.load(value, { schema: JSON_SCHEMA });
13
+ if (Array.isArray(parsed)) {
14
+ return parsed.map(String);
15
+ }
16
+ return value ? [value] : [];
17
+ }
18
+ if (value.includes(',')) {
19
+ return value.split(',').map((s) => s.trim()).filter(Boolean);
20
+ }
21
+ return value ? [value] : [];
22
+ }
23
+ catch {
24
+ return value ? [value] : [];
25
+ }
26
+ }
27
+ function optionValue(option) {
28
+ return typeof option === 'object' ? option.value : String(option);
29
+ }
30
+ function optionLabel(option) {
31
+ return typeof option === 'object' ? option.label : String(option);
32
+ }
33
+ export function MultiSelectWidget({ value, onChange, onDirectSave, onBlur, ariaLabel, ariaDescribedBy, invalid = false, options: optionOverrides, profile = 'plain', creatable = false, openOnMount, onDismiss, onOptionColorChange, readOnly = false, disabled = false, uiHint, }) {
34
+ const options = optionOverrides ?? (uiHint && 'options' in uiHint && Array.isArray(uiHint.options)
35
+ ? uiHint.options
36
+ : []);
37
+ const selectedValues = parseSelectedValues(value);
38
+ if (readOnly && profile === 'plain') {
39
+ return (_jsx("div", { "aria-readonly": "true", children: selectedValues.length > 0 ? (_jsx("div", { className: "flex flex-wrap gap-2", children: selectedValues.map((selected, idx) => (_jsx(ReadOnlyChip, { emphasis: true, className: "px-3 py-1.5", children: optionLabel(options.find((option) => optionValue(option) === selected) ?? selected) }, idx))) })) : (_jsx(ReadOnlyEmpty, {})) }));
40
+ }
41
+ const handleChange = (next) => {
42
+ onChange(JSON.stringify(next));
43
+ onDirectSave?.(next);
44
+ };
45
+ return (_jsx(MultiSelect, { "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, invalid: invalid, openOnMount: openOnMount, onDismiss: onDismiss, value: selectedValues, onChange: handleChange, onSelectionBlur: onBlur, options: options, disabled: disabled, readOnly: readOnly, profile: profile, creatable: creatable, onOptionColorChange: onOptionColorChange }));
46
+ }
@@ -0,0 +1,2 @@
1
+ import type { WidgetProps } from './types.js';
2
+ export declare function NumberWidget({ value, onChange, onBlur, ariaLabel, ariaDescribedBy, invalid, readOnly, disabled, surface, metadata, uiHint, }: WidgetProps): import("react").JSX.Element;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { NumberInput } from '../primitives/number-input.js';
3
+ import { READONLY_SURFACE_CLASSES } from './read-only-field.js';
4
+ import { cn } from '../lib/cn.js';
5
+ export function NumberWidget({ value, onChange, onBlur, ariaLabel, ariaDescribedBy, invalid = false, readOnly = false, disabled = false, surface = 'default', metadata, uiHint, }) {
6
+ const uiHintType = uiHint?.type;
7
+ const min = uiHint && uiHintType === 'number' && 'min' in uiHint && typeof uiHint.min === 'number'
8
+ ? uiHint.min
9
+ : undefined;
10
+ const max = uiHint && uiHintType === 'number' && 'max' in uiHint && typeof uiHint.max === 'number'
11
+ ? uiHint.max
12
+ : undefined;
13
+ const step = uiHint && uiHintType === 'number' && 'step' in uiHint && typeof uiHint.step === 'number'
14
+ ? uiHint.step
15
+ : metadata?.type === 'float'
16
+ ? 0.01
17
+ : 1;
18
+ return (_jsx(NumberInput, { autoFocus: !readOnly, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-invalid": invalid || undefined, value: value, onChange: onChange, onBlur: onBlur, min: min, max: max, step: step, readOnly: readOnly, disabled: disabled, placeholder: readOnly ? '—' : 'Enter a number', className: cn(surface === 'inline' && !readOnly && 'h-7 rounded-sm border-transparent bg-transparent px-1 py-0 text-xs shadow-none outline-none hover:bg-accent/40 focus:border-transparent focus:ring-1 focus:ring-inset focus:ring-ring', readOnly && READONLY_SURFACE_CLASSES, invalid && 'border-destructive focus:border-destructive focus:ring-destructive/20') }));
19
+ }
@@ -0,0 +1,36 @@
1
+ import type { ReactNode } from 'react';
2
+ /**
3
+ * Shared read-only presentation for the input-editor widgets.
4
+ *
5
+ * Read-only is a mode of each widget, not a substitute component — these are
6
+ * style helpers the widgets compose in their `readOnly` branch, never entries
7
+ * in the widget registry. The look is built entirely from semantic tokens
8
+ * (`muted` / `border` / `foreground`) so it themes in light and dark.
9
+ */
10
+ /**
11
+ * Muted, non-interactive boxed field for read-only widgets that render their
12
+ * own container (text, dropdown, date). Matches the editable field footprint
13
+ * (`w-full rounded-md px-3 py-2 text-sm`) so the layout doesn't shift.
14
+ */
15
+ export declare const READONLY_FIELD_CLASSES = "w-full rounded-md border border-border bg-muted px-3 py-2 text-sm text-foreground focus:outline-none";
16
+ /**
17
+ * Colour-only override appended to a primitive's `className` when the primitive
18
+ * keeps its own layout but must present as read-only (textarea, number). Relies
19
+ * on the primitives merging `className` last via `cn`, so these win over the
20
+ * editable `border-border bg-card focus:ring-ring` defaults.
21
+ */
22
+ export declare const READONLY_SURFACE_CLASSES = "cursor-default border-border bg-muted text-foreground focus:border-border focus:ring-0";
23
+ /** The neutral placeholder shown where a read-only value is empty. */
24
+ export declare const READONLY_EMPTY = "\u2014";
25
+ /** Renders `—` in muted ink for an empty read-only value. */
26
+ export declare function ReadOnlyEmpty(): import("react").JSX.Element;
27
+ /**
28
+ * Muted, non-interactive chip used by the boolean and multi-select read-only
29
+ * renders. `emphasis` marks a selected option (a filled-but-muted pill) versus
30
+ * an unselected one (quiet outline).
31
+ */
32
+ export declare function ReadOnlyChip({ children, emphasis, className, }: {
33
+ children: ReactNode;
34
+ emphasis?: boolean;
35
+ className?: string;
36
+ }): import("react").JSX.Element;
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from '../lib/cn.js';
3
+ /**
4
+ * Shared read-only presentation for the input-editor widgets.
5
+ *
6
+ * Read-only is a mode of each widget, not a substitute component — these are
7
+ * style helpers the widgets compose in their `readOnly` branch, never entries
8
+ * in the widget registry. The look is built entirely from semantic tokens
9
+ * (`muted` / `border` / `foreground`) so it themes in light and dark.
10
+ */
11
+ /**
12
+ * Muted, non-interactive boxed field for read-only widgets that render their
13
+ * own container (text, dropdown, date). Matches the editable field footprint
14
+ * (`w-full rounded-md px-3 py-2 text-sm`) so the layout doesn't shift.
15
+ */
16
+ export const READONLY_FIELD_CLASSES = 'w-full rounded-md border border-border bg-muted px-3 py-2 text-sm text-foreground focus:outline-none';
17
+ /**
18
+ * Colour-only override appended to a primitive's `className` when the primitive
19
+ * keeps its own layout but must present as read-only (textarea, number). Relies
20
+ * on the primitives merging `className` last via `cn`, so these win over the
21
+ * editable `border-border bg-card focus:ring-ring` defaults.
22
+ */
23
+ export const READONLY_SURFACE_CLASSES = 'cursor-default border-border bg-muted text-foreground focus:border-border focus:ring-0';
24
+ /** The neutral placeholder shown where a read-only value is empty. */
25
+ export const READONLY_EMPTY = '—';
26
+ /** Renders `—` in muted ink for an empty read-only value. */
27
+ export function ReadOnlyEmpty() {
28
+ return _jsx("span", { className: "text-muted-foreground", children: READONLY_EMPTY });
29
+ }
30
+ /**
31
+ * Muted, non-interactive chip used by the boolean and multi-select read-only
32
+ * renders. `emphasis` marks a selected option (a filled-but-muted pill) versus
33
+ * an unselected one (quiet outline).
34
+ */
35
+ export function ReadOnlyChip({ children, emphasis = false, className, }) {
36
+ return (_jsx("span", { className: cn('inline-flex items-center rounded-md border px-2.5 py-1 text-sm', emphasis
37
+ ? 'border-border bg-muted text-foreground'
38
+ : 'border-border bg-transparent text-muted-foreground', className), children: children }));
39
+ }
@@ -0,0 +1,2 @@
1
+ import type { WidgetProps } from './types.js';
2
+ export declare function TextWidget({ value, onChange, onBlur, ariaLabel, ariaDescribedBy, invalid, readOnly, disabled, surface, uiHint, }: WidgetProps): import("react").JSX.Element;
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { READONLY_FIELD_CLASSES } from './read-only-field.js';
3
+ import { cn } from '../lib/cn.js';
4
+ const EDITABLE_CLASSES = 'w-full rounded-md border border-border bg-card px-3 py-2 text-sm transition-colors focus:border-transparent focus:outline-none focus:ring-2 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50';
5
+ export function TextWidget({ value, onChange, onBlur, ariaLabel, ariaDescribedBy, invalid = false, readOnly = false, disabled = false, surface = 'default', uiHint, }) {
6
+ const placeholder = readOnly
7
+ ? '—'
8
+ : uiHint?.type === 'text' &&
9
+ 'placeholder' in uiHint &&
10
+ typeof uiHint.placeholder === 'string' &&
11
+ uiHint.placeholder.length > 0
12
+ ? uiHint.placeholder
13
+ : 'Enter value';
14
+ return (_jsx("input", { autoFocus: !readOnly, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-invalid": invalid || undefined, type: "text", value: value, onChange: (e) => onChange(e.target.value), onBlur: onBlur, readOnly: readOnly, disabled: disabled, placeholder: placeholder, className: cn(readOnly
15
+ ? READONLY_FIELD_CLASSES
16
+ : surface === 'inline'
17
+ ? 'h-7 w-full rounded-sm border border-transparent bg-transparent px-1 py-0 text-xs outline-none hover:bg-accent/40 focus:border-transparent focus:ring-1 focus:ring-inset focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50'
18
+ : EDITABLE_CLASSES, invalid && 'border-destructive focus:border-destructive focus:ring-destructive/20') }));
19
+ }
@@ -0,0 +1,2 @@
1
+ import type { WidgetProps } from './types.js';
2
+ export declare function TextareaWidget({ value, onChange, onBlur, ariaLabel, ariaDescribedBy, invalid, minHeight, autoSize, readOnly, disabled, surface, metadata, uiHint, }: WidgetProps): import("react").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useLayoutEffect, useRef } from 'react';
3
+ import { Textarea } from '../primitives/textarea.js';
4
+ import { cn } from '../lib/cn.js';
5
+ import { READONLY_SURFACE_CLASSES } from './read-only-field.js';
6
+ export function TextareaWidget({ value, onChange, onBlur, ariaLabel, ariaDescribedBy, invalid = false, minHeight, autoSize = false, readOnly = false, disabled = false, surface = 'default', metadata, uiHint, }) {
7
+ const textareaRef = useRef(null);
8
+ const isObjectOrArray = metadata?.type === 'array';
9
+ const rows = isObjectOrArray ? 8 : 4;
10
+ const placeholder = readOnly
11
+ ? '—'
12
+ : isObjectOrArray
13
+ ? 'Enter YAML'
14
+ : uiHint &&
15
+ 'placeholder' in uiHint &&
16
+ typeof uiHint.placeholder === 'string' &&
17
+ uiHint.placeholder.length > 0
18
+ ? uiHint.placeholder
19
+ : 'Enter text';
20
+ useLayoutEffect(() => {
21
+ if (!autoSize || !textareaRef.current)
22
+ return;
23
+ const textarea = textareaRef.current;
24
+ textarea.style.height = 'auto';
25
+ textarea.style.height = `${Math.max(minHeight ?? 0, textarea.scrollHeight)}px`;
26
+ if (!readOnly)
27
+ textarea.focus({ preventScroll: true });
28
+ }, [autoSize, minHeight, readOnly, value]);
29
+ return (_jsx(Textarea, { ref: textareaRef, autoFocus: !readOnly && !autoSize, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, "aria-invalid": invalid || undefined, value: value, onChange: (e) => onChange(e.target.value), onBlur: onBlur, rows: rows, readOnly: readOnly, disabled: disabled, placeholder: placeholder, style: minHeight ? { minHeight } : undefined,
30
+ // Soft wrapping already folds prose at spaces; `break-words` additionally
31
+ // folds a token too long to fit on its own — a URL, a long `${{ }}`
32
+ // expression — instead of letting it scroll out of the card sideways.
33
+ className: cn('break-words', surface === 'inline' && !readOnly && 'rounded-sm border-transparent bg-transparent px-1 py-1 text-xs shadow-none outline-none hover:bg-accent/40 focus:border-transparent focus:ring-1 focus:ring-inset focus:ring-ring', autoSize ? '[font:inherit]' : 'font-mono', readOnly && READONLY_SURFACE_CLASSES, invalid && 'border-destructive focus:border-destructive focus:ring-destructive/20') }));
34
+ }
@@ -0,0 +1,53 @@
1
+ export interface WidgetOption {
2
+ value: string;
3
+ label: string;
4
+ color?: string;
5
+ }
6
+ export type WidgetOptions = ReadonlyArray<string | number | WidgetOption>;
7
+ export type UiHint = {
8
+ type: 'text';
9
+ placeholder?: string;
10
+ multiline?: boolean;
11
+ } | {
12
+ type: 'number';
13
+ min?: number;
14
+ max?: number;
15
+ step?: number;
16
+ } | {
17
+ type: 'select';
18
+ options: string[];
19
+ } | {
20
+ type: 'multi-select';
21
+ options: string[];
22
+ } | {
23
+ type: 'checkbox';
24
+ } | {
25
+ type: 'date-picker';
26
+ } | null;
27
+ export interface InputMetadata {
28
+ type: 'string' | 'float' | 'boolean' | 'array';
29
+ title?: string;
30
+ description?: string;
31
+ required?: boolean;
32
+ ui_hint?: UiHint;
33
+ }
34
+ export interface WidgetProps {
35
+ value: string;
36
+ onChange: (value: string) => void;
37
+ onBlur: () => void;
38
+ ariaLabel?: string;
39
+ ariaDescribedBy?: string;
40
+ invalid?: boolean;
41
+ options?: WidgetOptions;
42
+ emptyOptionLabel?: string;
43
+ minHeight?: number;
44
+ autoSize?: boolean;
45
+ surface?: 'default' | 'inline';
46
+ readOnly?: boolean;
47
+ disabled?: boolean;
48
+ metadata?: InputMetadata;
49
+ uiHint?: UiHint;
50
+ }
51
+ export interface DirectSaveWidgetProps extends WidgetProps {
52
+ onDirectSave: (value: unknown) => void;
53
+ }
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "@script-it/json-views-react",
3
+ "version": "0.1.0-beta.1",
4
+ "description": "Extensible React viewer and editor for JSON Views",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./styles.css": "./dist/styles.css",
15
+ "./standalone.css": "./dist/standalone.css"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE",
21
+ "THIRD_PARTY_NOTICES.txt"
22
+ ],
23
+ "scripts": {
24
+ "build": "node ../../scripts/clean-dist.mjs && tsc -p tsconfig.json && tailwindcss -i src/styles.css -o dist/styles.css --minify && node ../../scripts/scope-styles.mjs",
25
+ "check": "tsc -p tsconfig.json --noEmit",
26
+ "test": "vitest run",
27
+ "prepack": "node ../../scripts/prepare-package.mjs"
28
+ },
29
+ "dependencies": {
30
+ "@codemirror/lang-json": "^6.0.2",
31
+ "@codemirror/language": "^6.12.4",
32
+ "@codemirror/state": "^6.7.4",
33
+ "@codemirror/view": "^6.43.11",
34
+ "@lezer/highlight": "^1.2.3",
35
+ "@radix-ui/react-checkbox": "^1.3.3",
36
+ "@radix-ui/react-dialog": "^1.1.15",
37
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
38
+ "@radix-ui/react-slot": "^1.2.4",
39
+ "@radix-ui/react-tabs": "^1.1.13",
40
+ "@radix-ui/react-tooltip": "^1.2.8",
41
+ "@script-it/json-views-core": "0.1.0-beta.1",
42
+ "@tanstack/react-virtual": "3.14.10",
43
+ "class-variance-authority": "^0.7.1",
44
+ "clsx": "^2.1.1",
45
+ "codemirror": "^6.0.2",
46
+ "js-yaml": "^4.1.0",
47
+ "lucide-react": "^0.561.0",
48
+ "tailwind-merge": "^3.3.1"
49
+ },
50
+ "peerDependencies": {
51
+ "react": "^18.0.0 || ^19.0.0",
52
+ "react-dom": "^18.0.0 || ^19.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "@tailwindcss/cli": "^4.1.13",
56
+ "@types/js-yaml": "^4.0.9",
57
+ "@types/react": "^19.1.16",
58
+ "@types/react-dom": "^19.1.9",
59
+ "jsdom": "29.0.2",
60
+ "react": "^19.1.1",
61
+ "react-dom": "^19.1.1",
62
+ "typescript": "^5.9.3",
63
+ "vitest": "3.2.7"
64
+ },
65
+ "repository": {
66
+ "type": "git",
67
+ "url": "git+https://github.com/script-it/json-views.git",
68
+ "directory": "packages/react"
69
+ },
70
+ "homepage": "https://github.com/script-it/json-views#readme",
71
+ "bugs": {
72
+ "url": "https://github.com/script-it/json-views/issues"
73
+ },
74
+ "engines": {
75
+ "node": "^22.13.0 || >=24.0.0"
76
+ },
77
+ "sideEffects": [
78
+ "**/*.css"
79
+ ],
80
+ "publishConfig": {
81
+ "access": "public"
82
+ }
83
+ }