@flanksource/clicky-ui 0.3.6 → 0.3.7

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 (215) hide show
  1. package/README.md +42 -0
  2. package/dist/components/DateField.d.ts +1 -1
  3. package/dist/components/DatePicker.d.ts +1 -1
  4. package/dist/components/DateTimePicker.d.ts +1 -1
  5. package/dist/components/FormLookupProvider.cjs +12 -0
  6. package/dist/components/FormLookupProvider.cjs.map +1 -0
  7. package/dist/components/FormLookupProvider.d.ts +7 -0
  8. package/dist/components/FormLookupProvider.d.ts.map +1 -0
  9. package/dist/components/FormLookupProvider.js +12 -0
  10. package/dist/components/FormLookupProvider.js.map +1 -0
  11. package/dist/components/JsonSchemaForm.cjs +2 -2
  12. package/dist/components/JsonSchemaForm.cjs.map +1 -1
  13. package/dist/components/JsonSchemaForm.js +1 -1
  14. package/dist/components/JsonSchemaForm.js.map +1 -1
  15. package/dist/components/ListMenu.cjs +145 -0
  16. package/dist/components/ListMenu.cjs.map +1 -0
  17. package/dist/components/ListMenu.d.ts +54 -0
  18. package/dist/components/ListMenu.d.ts.map +1 -0
  19. package/dist/components/ListMenu.js +145 -0
  20. package/dist/components/ListMenu.js.map +1 -0
  21. package/dist/components/MdxEditorField.cjs +128 -0
  22. package/dist/components/MdxEditorField.cjs.map +1 -0
  23. package/dist/components/MdxEditorField.d.ts +4 -0
  24. package/dist/components/MdxEditorField.d.ts.map +1 -0
  25. package/dist/components/MdxEditorField.js +106 -0
  26. package/dist/components/MdxEditorField.js.map +1 -0
  27. package/dist/components/MdxEditorToolbar.cjs +76 -0
  28. package/dist/components/MdxEditorToolbar.cjs.map +1 -0
  29. package/dist/components/MdxEditorToolbar.d.ts +8 -0
  30. package/dist/components/MdxEditorToolbar.d.ts.map +1 -0
  31. package/dist/components/MdxEditorToolbar.js +76 -0
  32. package/dist/components/MdxEditorToolbar.js.map +1 -0
  33. package/dist/components/button-variants.d.ts +1 -1
  34. package/dist/components/button.d.ts +1 -1
  35. package/dist/components/form-lookup-context.cjs +1 -8
  36. package/dist/components/form-lookup-context.cjs.map +1 -1
  37. package/dist/components/form-lookup-context.d.ts +1 -5
  38. package/dist/components/form-lookup-context.d.ts.map +1 -1
  39. package/dist/components/form-lookup-context.js +1 -8
  40. package/dist/components/form-lookup-context.js.map +1 -1
  41. package/dist/components/jotai-bindings-core.cjs +197 -0
  42. package/dist/components/jotai-bindings-core.cjs.map +1 -0
  43. package/dist/components/jotai-bindings-core.d.ts +45 -0
  44. package/dist/components/jotai-bindings-core.d.ts.map +1 -0
  45. package/dist/components/jotai-bindings-core.js +197 -0
  46. package/dist/components/jotai-bindings-core.js.map +1 -0
  47. package/dist/components/jotai-bindings.cjs +17 -0
  48. package/dist/components/jotai-bindings.cjs.map +1 -0
  49. package/dist/components/jotai-bindings.d.ts +4 -0
  50. package/dist/components/jotai-bindings.d.ts.map +1 -0
  51. package/dist/components/jotai-bindings.js +17 -0
  52. package/dist/components/jotai-bindings.js.map +1 -0
  53. package/dist/components/json-schema-form-fields.cjs +37 -0
  54. package/dist/components/json-schema-form-fields.cjs.map +1 -1
  55. package/dist/components/json-schema-form-fields.d.ts +6 -0
  56. package/dist/components/json-schema-form-fields.d.ts.map +1 -1
  57. package/dist/components/json-schema-form-fields.js +38 -1
  58. package/dist/components/json-schema-form-fields.js.map +1 -1
  59. package/dist/components/json-schema-form-render.cjs +3 -1
  60. package/dist/components/json-schema-form-render.cjs.map +1 -1
  61. package/dist/components/json-schema-form-render.d.ts.map +1 -1
  62. package/dist/components/json-schema-form-render.js +4 -2
  63. package/dist/components/json-schema-form-render.js.map +1 -1
  64. package/dist/components/json-schema-form-resolve.cjs +9 -0
  65. package/dist/components/json-schema-form-resolve.cjs.map +1 -1
  66. package/dist/components/json-schema-form-resolve.d.ts.map +1 -1
  67. package/dist/components/json-schema-form-resolve.js +9 -0
  68. package/dist/components/json-schema-form-resolve.js.map +1 -1
  69. package/dist/components/json-schema-form-types.d.ts +4 -1
  70. package/dist/components/json-schema-form-types.d.ts.map +1 -1
  71. package/dist/components/mdx-editor-field-plugins.cjs +124 -0
  72. package/dist/components/mdx-editor-field-plugins.cjs.map +1 -0
  73. package/dist/components/mdx-editor-field-plugins.d.ts +6 -0
  74. package/dist/components/mdx-editor-field-plugins.d.ts.map +1 -0
  75. package/dist/components/mdx-editor-field-plugins.js +124 -0
  76. package/dist/components/mdx-editor-field-plugins.js.map +1 -0
  77. package/dist/components/mdx-editor-options.d.ts +77 -0
  78. package/dist/components/mdx-editor-options.d.ts.map +1 -0
  79. package/dist/components/use-list-menu-selection.cjs +77 -0
  80. package/dist/components/use-list-menu-selection.cjs.map +1 -0
  81. package/dist/components/use-list-menu-selection.d.ts +40 -0
  82. package/dist/components/use-list-menu-selection.d.ts.map +1 -0
  83. package/dist/components/use-list-menu-selection.js +77 -0
  84. package/dist/components/use-list-menu-selection.js.map +1 -0
  85. package/dist/components.cjs +10 -1
  86. package/dist/components.cjs.map +1 -1
  87. package/dist/components.d.ts +4 -1
  88. package/dist/components.d.ts.map +1 -1
  89. package/dist/components.js +10 -1
  90. package/dist/components.js.map +1 -1
  91. package/dist/data/MarkdownEditor.model.d.ts +1 -1
  92. package/dist/data/PromptBanner.cjs +199 -0
  93. package/dist/data/PromptBanner.cjs.map +1 -0
  94. package/dist/data/PromptBanner.d.ts +17 -0
  95. package/dist/data/PromptBanner.d.ts.map +1 -0
  96. package/dist/data/PromptBanner.js +199 -0
  97. package/dist/data/PromptBanner.js.map +1 -0
  98. package/dist/data/PromptBanner.model.cjs +132 -0
  99. package/dist/data/PromptBanner.model.cjs.map +1 -0
  100. package/dist/data/PromptBanner.model.d.ts +23 -0
  101. package/dist/data/PromptBanner.model.d.ts.map +1 -0
  102. package/dist/data/PromptBanner.model.js +132 -0
  103. package/dist/data/PromptBanner.model.js.map +1 -0
  104. package/dist/data/PromptDialog.cjs +43 -0
  105. package/dist/data/PromptDialog.cjs.map +1 -0
  106. package/dist/data/PromptDialog.d.ts +14 -0
  107. package/dist/data/PromptDialog.d.ts.map +1 -0
  108. package/dist/data/PromptDialog.js +43 -0
  109. package/dist/data/PromptDialog.js.map +1 -0
  110. package/dist/data/TaskManager.cjs +8 -1
  111. package/dist/data/TaskManager.cjs.map +1 -1
  112. package/dist/data/TaskManager.d.ts.map +1 -1
  113. package/dist/data/TaskManager.js +8 -1
  114. package/dist/data/TaskManager.js.map +1 -1
  115. package/dist/data/TaskProgress.cjs +15 -2
  116. package/dist/data/TaskProgress.cjs.map +1 -1
  117. package/dist/data/TaskProgress.d.ts +0 -10
  118. package/dist/data/TaskProgress.d.ts.map +1 -1
  119. package/dist/data/TaskProgress.js +15 -2
  120. package/dist/data/TaskProgress.js.map +1 -1
  121. package/dist/data/ai/ChatWindow.cjs +53 -9
  122. package/dist/data/ai/ChatWindow.cjs.map +1 -1
  123. package/dist/data/ai/ChatWindow.d.ts.map +1 -1
  124. package/dist/data/ai/ChatWindow.js +53 -9
  125. package/dist/data/ai/ChatWindow.js.map +1 -1
  126. package/dist/data/ai/ToolPreferences.cjs +232 -39
  127. package/dist/data/ai/ToolPreferences.cjs.map +1 -1
  128. package/dist/data/ai/ToolPreferences.d.ts +4 -7
  129. package/dist/data/ai/ToolPreferences.d.ts.map +1 -1
  130. package/dist/data/ai/ToolPreferences.js +234 -41
  131. package/dist/data/ai/ToolPreferences.js.map +1 -1
  132. package/dist/data/chat/clickyOperationsToTools.cjs +89 -3
  133. package/dist/data/chat/clickyOperationsToTools.cjs.map +1 -1
  134. package/dist/data/chat/clickyOperationsToTools.d.ts +1 -1
  135. package/dist/data/chat/clickyOperationsToTools.d.ts.map +1 -1
  136. package/dist/data/chat/clickyOperationsToTools.js +89 -3
  137. package/dist/data/chat/clickyOperationsToTools.js.map +1 -1
  138. package/dist/data/chat/types.cjs.map +1 -1
  139. package/dist/data/chat/types.d.ts +7 -1
  140. package/dist/data/chat/types.d.ts.map +1 -1
  141. package/dist/data/chat/types.js.map +1 -1
  142. package/dist/data/task-status.cjs +19 -12
  143. package/dist/data/task-status.cjs.map +1 -1
  144. package/dist/data/task-status.d.ts +2 -1
  145. package/dist/data/task-status.d.ts.map +1 -1
  146. package/dist/data/task-status.js +19 -12
  147. package/dist/data/task-status.js.map +1 -1
  148. package/dist/data/test-runner/TestRunner.fixtures.d.ts +11 -4
  149. package/dist/data/test-runner/TestRunner.fixtures.d.ts.map +1 -1
  150. package/dist/data/version-info.cjs +3 -3
  151. package/dist/data/version-info.js +3 -3
  152. package/dist/data.cjs +7 -0
  153. package/dist/data.cjs.map +1 -1
  154. package/dist/data.d.ts +3 -0
  155. package/dist/data.d.ts.map +1 -1
  156. package/dist/data.js +7 -0
  157. package/dist/data.js.map +1 -1
  158. package/dist/hooks/use-prompts.cjs +83 -0
  159. package/dist/hooks/use-prompts.cjs.map +1 -0
  160. package/dist/hooks/use-prompts.d.ts +43 -0
  161. package/dist/hooks/use-prompts.d.ts.map +1 -0
  162. package/dist/hooks/use-prompts.js +83 -0
  163. package/dist/hooks/use-prompts.js.map +1 -0
  164. package/dist/hooks.cjs +3 -0
  165. package/dist/hooks.cjs.map +1 -1
  166. package/dist/hooks.d.ts +1 -0
  167. package/dist/hooks.d.ts.map +1 -1
  168. package/dist/hooks.js +3 -0
  169. package/dist/hooks.js.map +1 -1
  170. package/dist/index.cjs +17 -1
  171. package/dist/index.cjs.map +1 -1
  172. package/dist/index.js +17 -1
  173. package/dist/index.js.map +1 -1
  174. package/dist/jotai.cjs +9 -0
  175. package/dist/jotai.cjs.map +1 -0
  176. package/dist/jotai.d.ts +4 -0
  177. package/dist/jotai.d.ts.map +1 -0
  178. package/dist/jotai.js +9 -0
  179. package/dist/jotai.js.map +1 -0
  180. package/dist/lib/utils.cjs +18 -1
  181. package/dist/lib/utils.cjs.map +1 -1
  182. package/dist/lib/utils.d.ts.map +1 -1
  183. package/dist/lib/utils.js +18 -1
  184. package/dist/lib/utils.js.map +1 -1
  185. package/dist/mdx-editor.cjs +5 -0
  186. package/dist/mdx-editor.cjs.map +1 -0
  187. package/dist/mdx-editor.css +2 -0
  188. package/dist/mdx-editor.d.ts +2 -0
  189. package/dist/mdx-editor.d.ts.map +1 -0
  190. package/dist/mdx-editor.js +5 -0
  191. package/dist/mdx-editor.js.map +1 -0
  192. package/dist/rpc/apiClient.cjs +1 -1
  193. package/dist/rpc/apiClient.cjs.map +1 -1
  194. package/dist/rpc/apiClient.js +1 -1
  195. package/dist/rpc/apiClient.js.map +1 -1
  196. package/dist/rpc/surfaceIconMap.cjs +12 -1
  197. package/dist/rpc/surfaceIconMap.cjs.map +1 -1
  198. package/dist/rpc/surfaceIconMap.d.ts.map +1 -1
  199. package/dist/rpc/surfaceIconMap.js +12 -1
  200. package/dist/rpc/surfaceIconMap.js.map +1 -1
  201. package/dist/rpc/types.cjs.map +1 -1
  202. package/dist/rpc/types.d.ts +1 -0
  203. package/dist/rpc/types.d.ts.map +1 -1
  204. package/dist/rpc/types.js.map +1 -1
  205. package/dist/styles.css +1 -1
  206. package/oxlint-plugins/README.md +148 -0
  207. package/oxlint-plugins/clicky-ui-no-adhoc-overlay.js +88 -0
  208. package/oxlint-plugins/clicky-ui-prefer-components.js +46 -0
  209. package/oxlint-plugins/clicky-ui-prefer-icons.js +121 -0
  210. package/oxlint-plugins/clicky-ui-prefer-tailwind.js +98 -0
  211. package/oxlint-plugins/clicky-ui-prefer-theme.js +134 -0
  212. package/oxlint-plugins/clicky-ui-shared.js +132 -0
  213. package/oxlint-plugins/clicky-ui.js +34 -0
  214. package/oxlint-plugins/no-iconify-names.js +37 -0
  215. package/package.json +21 -1
package/README.md CHANGED
@@ -54,6 +54,17 @@ export function ClickyPanel() {
54
54
 
55
55
  `OperationCatalog` and `EntityExplorerApp` (both exported from `@flanksource/clicky-ui/rpc`) render an OpenAPI spec — fetched via an `OperationsApiClient` — as a navigable list of operations grouped by entity surface. They expect the spec to declare `x-clicky` surface metadata for the surfaces they should expose. See `apps/kitchen-sink/src/demos/OperationExplorerDemo.tsx` for a fake-client example.
56
56
 
57
+ ## Markdown editor field
58
+
59
+ `JsonSchemaForm` fields with `format: md` — and the standalone `MdxEditorField` exported from `@flanksource/clicky-ui/mdx-editor` — render an [MDXEditor](https://mdxeditor.dev/)-backed rich-text field. Its base styles ship as a **separate** stylesheet so apps that don't use the field don't pay its weight (the editor's JavaScript is also loaded lazily, on first render). Import it once at the app root, in addition to `styles.css`:
60
+
61
+ ```tsx
62
+ import "@flanksource/clicky-ui/styles.css";
63
+ import "@flanksource/clicky-ui/mdx-editor.css"; // only if you render the markdown field
64
+ ```
65
+
66
+ Without this import the field renders, but its toolbar and content are unstyled.
67
+
57
68
  ## Bundle size guidance
58
69
 
59
70
  Prefer subpath imports in production apps:
@@ -118,6 +129,37 @@ Both hooks persist their choice to `localStorage` under `clicky-ui-theme` / `cli
118
129
  </script>
119
130
  ```
120
131
 
132
+ ## Lint rules
133
+
134
+ The package ships opt-in [oxlint](https://oxc.rs/docs/guide/usage/linter/js-plugins.html)
135
+ rules that keep consuming projects on clicky-ui's components, overlays, theming
136
+ and icons instead of rebuilding them by hand. Enable them in your downstream
137
+ `.oxlintrc.json`:
138
+
139
+ ```json
140
+ {
141
+ "jsPlugins": [
142
+ {
143
+ "name": "clicky-ui",
144
+ "specifier": "@flanksource/clicky-ui/oxlint-plugins"
145
+ }
146
+ ],
147
+ "rules": {
148
+ "clicky-ui/prefer-clicky-components": "warn",
149
+ "clicky-ui/no-adhoc-overlay": "error",
150
+ "clicky-ui/prefer-tailwind-classes": "warn",
151
+ "clicky-ui/prefer-theme-tokens": "error",
152
+ "clicky-ui/prefer-clicky-icons": "warn"
153
+ }
154
+ }
155
+ ```
156
+
157
+ The rules flag rebuilt primitives (`<button>`, `<table>`, …), ad-hoc dialogs and
158
+ arbitrary z-index, hand-rolled inline styles, hardcoded colors / raw theme
159
+ storage access, and icons imported from unapproved libraries or faked with
160
+ emoji. See [`oxlint-plugins/README.md`](./oxlint-plugins/README.md) for the full
161
+ guide.
162
+
121
163
  ## License
122
164
 
123
165
  Apache-2.0
@@ -16,7 +16,7 @@ export type DateFieldProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type"
16
16
  /** Accessible label for the calendar/open button. */
17
17
  openButtonLabel?: string;
18
18
  };
19
- export declare const DateField: import('react').ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "type" | "onChange"> & {
19
+ export declare const DateField: import('react').ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "onChange" | "type"> & {
20
20
  /** Selects native date input or date-time input behavior. */
21
21
  mode?: DateFieldMode;
22
22
  /** ISO date or datetime-local string shown in the input. */
@@ -7,7 +7,7 @@ export type DatePickerProps = Omit<InputHTMLAttributes<HTMLInputElement>, "type"
7
7
  buttonClassName?: string;
8
8
  openButtonLabel?: string;
9
9
  };
10
- export declare const DatePicker: import('react').ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "type" | "onChange"> & {
10
+ export declare const DatePicker: import('react').ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "onChange" | "type"> & {
11
11
  value?: string;
12
12
  onChange?: (value: string) => void;
13
13
  className?: string;
@@ -11,7 +11,7 @@ export type DateTimePickerProps = Omit<InputHTMLAttributes<HTMLInputElement>, "t
11
11
  /** Leading in-field adornment, rendered at the left edge of the input. */
12
12
  prefix?: ReactNode;
13
13
  };
14
- export declare const DateTimePicker: import('react').ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "type" | "onChange" | "prefix"> & {
14
+ export declare const DateTimePicker: import('react').ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "prefix" | "value" | "onChange" | "type"> & {
15
15
  value?: string;
16
16
  onChange?: (value: string) => void;
17
17
  className?: string;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const formLookupContext = require("./form-lookup-context.cjs");
5
+ function FormLookupProvider({
6
+ fetcher,
7
+ children
8
+ }) {
9
+ return /* @__PURE__ */ jsxRuntime.jsx(formLookupContext.FormLookupContext.Provider, { value: fetcher, children });
10
+ }
11
+ exports.FormLookupProvider = FormLookupProvider;
12
+ //# sourceMappingURL=FormLookupProvider.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormLookupProvider.cjs","sources":["../../src/components/FormLookupProvider.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { LookupFetcher } from \"./json-schema-form-types\";\nimport { FormLookupContext } from \"./form-lookup-context\";\n\nexport function FormLookupProvider({\n fetcher,\n children,\n}: {\n fetcher?: LookupFetcher;\n children: ReactNode;\n}) {\n return <FormLookupContext.Provider value={fetcher}>{children}</FormLookupContext.Provider>;\n}\n"],"names":["FormLookupContext"],"mappings":";;;;AAIO,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AACF,GAGG;AACD,wCAAQA,kBAAAA,kBAAkB,UAAlB,EAA2B,OAAO,SAAU,UAAS;AAC/D;;"}
@@ -0,0 +1,7 @@
1
+ import { ReactNode } from 'react';
2
+ import { LookupFetcher } from './json-schema-form-types';
3
+ export declare function FormLookupProvider({ fetcher, children, }: {
4
+ fetcher?: LookupFetcher;
5
+ children: ReactNode;
6
+ }): import("react/jsx-runtime").JSX.Element;
7
+ //# sourceMappingURL=FormLookupProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormLookupProvider.d.ts","sourceRoot":"","sources":["../../src/components/FormLookupProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAG9D,wBAAgB,kBAAkB,CAAC,EACjC,OAAO,EACP,QAAQ,GACT,EAAE;IACD,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,QAAQ,EAAE,SAAS,CAAC;CACrB,2CAEA"}
@@ -0,0 +1,12 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { FormLookupContext } from "./form-lookup-context.js";
3
+ function FormLookupProvider({
4
+ fetcher,
5
+ children
6
+ }) {
7
+ return /* @__PURE__ */ jsx(FormLookupContext.Provider, { value: fetcher, children });
8
+ }
9
+ export {
10
+ FormLookupProvider
11
+ };
12
+ //# sourceMappingURL=FormLookupProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FormLookupProvider.js","sources":["../../src/components/FormLookupProvider.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { LookupFetcher } from \"./json-schema-form-types\";\nimport { FormLookupContext } from \"./form-lookup-context\";\n\nexport function FormLookupProvider({\n fetcher,\n children,\n}: {\n fetcher?: LookupFetcher;\n children: ReactNode;\n}) {\n return <FormLookupContext.Provider value={fetcher}>{children}</FormLookupContext.Provider>;\n}\n"],"names":[],"mappings":";;AAIO,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AACF,GAGG;AACD,6BAAQ,kBAAkB,UAAlB,EAA2B,OAAO,SAAU,UAAS;AAC/D;"}
@@ -6,7 +6,7 @@ const utils = require("../lib/utils.cjs");
6
6
  const Icon = require("../data/Icon.cjs");
7
7
  const DropdownMenu = require("../overlay/DropdownMenu.cjs");
8
8
  const jsonSchemaFormFields = require("./json-schema-form-fields.cjs");
9
- const formLookupContext = require("./form-lookup-context.cjs");
9
+ const FormLookupProvider = require("./FormLookupProvider.cjs");
10
10
  const jsonSchemaFormDiscriminator = require("./json-schema-form-discriminator.cjs");
11
11
  const jsonSchemaFormRefs = require("./json-schema-form-refs.cjs");
12
12
  const jsonSchemaFormRender = require("./json-schema-form-render.cjs");
@@ -73,7 +73,7 @@ function JsonSchemaForm({
73
73
  const resolvedSchema = react.useMemo(() => jsonSchemaFormRefs.rehydrateRefs(schema), [schema]);
74
74
  const discKey = typeof resolvedSchema["x-discriminator"] === "string" ? resolvedSchema["x-discriminator"] : void 0;
75
75
  const inPickerPhase = discKey != null && (value[discKey] == null || value[discKey] === "");
76
- return /* @__PURE__ */ jsxRuntime.jsx(formLookupContext.FormLookupProvider, { ...lookupFetcher ? { fetcher: lookupFetcher } : {}, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("relative flex flex-col", jsonSchemaFormSize.fieldInnerGapClass[effectiveSize]), children: [
76
+ return /* @__PURE__ */ jsxRuntime.jsx(FormLookupProvider.FormLookupProvider, { ...lookupFetcher ? { fetcher: lookupFetcher } : {}, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("relative flex flex-col", jsonSchemaFormSize.fieldInnerGapClass[effectiveSize]), children: [
77
77
  showPreferencesMenu && !inPickerPhase && /* @__PURE__ */ jsxRuntime.jsx(
78
78
  PreferencesMenu,
79
79
  {
@@ -1 +1 @@
1
- {"version":3,"file":"JsonSchemaForm.cjs","sources":["../../src/components/JsonSchemaForm.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../data/Icon\";\nimport { UiCheck, UiEllipsis } from \"../icons\";\nimport { DropdownMenu } from \"../overlay/DropdownMenu\";\nimport { FieldsGrid } from \"./json-schema-form-fields\";\nimport { FormLookupProvider } from \"./form-lookup-context\";\nimport { DiscriminatedForm } from \"./json-schema-form-discriminator\";\nimport { rehydrateRefs } from \"./json-schema-form-refs\";\nimport { renderApi, renderObjectFields } from \"./json-schema-form-render\";\nimport {\n DEFAULT_FORM_SIZE,\n fieldInnerGapClass,\n labelSizeClass,\n type FormSize,\n} from \"./json-schema-form-size\";\nimport {\n DEFAULT_PREFERENCES_STORAGE_KEY,\n readPreferences,\n writePreferences,\n type FormPreferences,\n type LayoutMode,\n type SortMode,\n} from \"./json-schema-form-preferences\";\nimport type { FormLayout, JsonSchemaFormProps, RenderContext } from \"./json-schema-form-types\";\n\nconst DEFAULT_LABEL_MAX_WIDTH = \"40ch\";\nconst DEFAULT_VALUE_MAX_WIDTH = \"600px\";\n\n// resolveFormLayout maps the `layout`/`inline` props plus an optional menu-driven\n// `modeOverride` to a single resolved FormLayout. An explicit `layout` sets the\n// base; `inline` is the shorthand for it; `modeOverride` (from the preferences\n// menu) wins over both but only flips the mode — width caps from `layout` are\n// preserved. Both modes fill valueMaxWidth (inline value column / stacked\n// label+value stack); the label cap only applies inline.\nfunction resolveFormLayout(\n layout: FormLayout | undefined,\n inline: boolean,\n modeOverride: LayoutMode | undefined,\n): FormLayout {\n const baseMode = layout?.mode ?? (inline ? \"inline\" : \"stacked\");\n const mode = modeOverride ?? baseMode;\n const valueMaxWidth = layout?.valueMaxWidth ?? DEFAULT_VALUE_MAX_WIDTH;\n if (mode !== \"inline\") return { mode: \"stacked\", valueMaxWidth };\n return {\n mode: \"inline\",\n labelMaxWidth: layout?.labelMaxWidth ?? DEFAULT_LABEL_MAX_WIDTH,\n valueMaxWidth,\n };\n}\n\n// JsonSchemaForm renders an object subschema as a form: one control per\n// (effective) property. It resolves if/then conditionals internally and recurses\n// through array items and object/map values; every other behaviour is layered on\n// by the pre/post extension functions the consumer supplies. The component holds\n// no domain knowledge.\nexport function JsonSchemaForm({\n schema,\n value,\n onChange,\n readOnly = false,\n inline = false,\n layout,\n size = DEFAULT_FORM_SIZE,\n idPrefix,\n hideReadOnlyFields = false,\n hiddenKeys,\n requiredFirst = false,\n title,\n pre,\n post,\n showPreferencesMenu = true,\n persistPreferences = true,\n preferencesStorageKey = DEFAULT_PREFERENCES_STORAGE_KEY,\n lookupFetcher,\n}: JsonSchemaFormProps) {\n // When the menu is hidden, never touch localStorage and start from an empty\n // override so behaviour is identical to before this feature existed.\n const [prefs, setPrefs] = useState<FormPreferences>(() =>\n showPreferencesMenu && persistPreferences ? readPreferences(preferencesStorageKey) : {},\n );\n\n const effectiveSize = prefs.size ?? size;\n const resolvedLayout = resolveFormLayout(layout, inline, prefs.layoutMode);\n // The `requiredFirst` prop sets the base sort; a menu selection overrides it.\n const effectiveSortMode = prefs.sortMode ?? (requiredFirst ? \"required-first\" : \"schema\");\n\n const applyPrefs = (next: FormPreferences) => {\n setPrefs(next);\n if (persistPreferences) writePreferences(preferencesStorageKey, next);\n };\n\n const ctx: RenderContext = {\n readOnly,\n hideReadOnlyFields,\n layout: resolvedLayout,\n size: effectiveSize,\n sortMode: effectiveSortMode,\n pre: pre ?? [],\n post: post ?? [],\n rootValue: value,\n depth: 0,\n render: renderApi,\n ...(idPrefix ? { idPrefix } : {}),\n };\n // A bundled schema (components under `$defs`, referenced by local `#/$defs`\n // pointers) is resolved once into a self-contained tree the renderer walks\n // directly; a non-bundled schema passes through untouched.\n const resolvedSchema = useMemo(() => rehydrateRefs(schema), [schema]);\n // A schema may name a discriminator property whose value selects a \"kind\"; the\n // form then runs a two-phase pick-then-fill flow (see DiscriminatedForm).\n const discKey =\n typeof resolvedSchema[\"x-discriminator\"] === \"string\" ? resolvedSchema[\"x-discriminator\"] : undefined;\n const inPickerPhase = discKey != null && (value[discKey] == null || value[discKey] === \"\");\n\n return (\n <FormLookupProvider {...(lookupFetcher ? { fetcher: lookupFetcher } : {})}>\n <div className={cn(\"relative flex flex-col\", fieldInnerGapClass[effectiveSize])}>\n {showPreferencesMenu && !inPickerPhase && (\n <PreferencesMenu\n size={effectiveSize}\n layoutMode={resolvedLayout.mode}\n sortMode={effectiveSortMode}\n onSelectSize={(next) => applyPrefs({ ...prefs, size: next })}\n onSelectLayout={(next) => applyPrefs({ ...prefs, layoutMode: next })}\n onSelectSort={(next) => applyPrefs({ ...prefs, sortMode: next })}\n />\n )}\n {title && <h3 className={cn(\"font-semibold\", labelSizeClass[effectiveSize])}>{title}</h3>}\n <FieldsGrid layout={resolvedLayout} size={effectiveSize}>\n {discKey ? (\n <DiscriminatedForm\n schema={resolvedSchema}\n value={value}\n onChange={onChange}\n ctx={ctx}\n discKey={discKey}\n />\n ) : (\n renderObjectFields(resolvedSchema, value, onChange, ctx, hiddenKeys ? { hiddenKeys } : undefined)\n )}\n </FieldsGrid>\n </div>\n </FormLookupProvider>\n );\n}\n\nconst SIZE_OPTIONS: { value: FormSize; label: string }[] = [\n { value: \"xs\", label: \"Extra small\" },\n { value: \"sm\", label: \"Small\" },\n { value: \"md\", label: \"Medium\" },\n { value: \"lg\", label: \"Large\" },\n { value: \"xl\", label: \"Extra large\" },\n];\n\nconst LAYOUT_OPTIONS: { value: LayoutMode; label: string }[] = [\n { value: \"stacked\", label: \"Stacked\" },\n { value: \"inline\", label: \"Inline\" },\n];\n\nconst SORT_OPTIONS: { value: SortMode; label: string }[] = [\n { value: \"schema\", label: \"Schema order\" },\n { value: \"required-first\", label: \"Required first\" },\n { value: \"priority\", label: \"Required & filled first\" },\n];\n\n// PreferencesMenu is the compact top-right ellipsis menu controlling this form's\n// size and layout mode. Selecting an item fires the matching callback and closes\n// the menu; the parent decides whether to persist.\nfunction PreferencesMenu({\n size,\n layoutMode,\n sortMode,\n onSelectSize,\n onSelectLayout,\n onSelectSort,\n}: {\n size: FormSize;\n layoutMode: LayoutMode;\n sortMode: SortMode;\n onSelectSize: (size: FormSize) => void;\n onSelectLayout: (mode: LayoutMode) => void;\n onSelectSort: (mode: SortMode) => void;\n}) {\n return (\n <DropdownMenu\n align=\"right\"\n menuLabel=\"Form display options\"\n className=\"absolute right-0 top-0 z-10\"\n trigger={\n <button\n type=\"button\"\n aria-label=\"Form display options\"\n aria-haspopup=\"menu\"\n className=\"inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <Icon icon={UiEllipsis} className=\"text-sm\" />\n </button>\n }\n >\n {(closeMenu) => (\n <>\n <PreferenceSection title=\"Size\" />\n {SIZE_OPTIONS.map((opt) => (\n <PreferenceItem\n key={opt.value}\n label={opt.label}\n selected={opt.value === size}\n onSelect={() => {\n onSelectSize(opt.value);\n closeMenu();\n }}\n />\n ))}\n <PreferenceSection title=\"Layout\" />\n {LAYOUT_OPTIONS.map((opt) => (\n <PreferenceItem\n key={opt.value}\n label={opt.label}\n selected={opt.value === layoutMode}\n onSelect={() => {\n onSelectLayout(opt.value);\n closeMenu();\n }}\n />\n ))}\n <PreferenceSection title=\"Sort\" />\n {SORT_OPTIONS.map((opt) => (\n <PreferenceItem\n key={opt.value}\n label={opt.label}\n selected={opt.value === sortMode}\n onSelect={() => {\n onSelectSort(opt.value);\n closeMenu();\n }}\n />\n ))}\n </>\n )}\n </DropdownMenu>\n );\n}\n\nfunction PreferenceSection({ title }: { title: string }) {\n return (\n <div className=\"px-3 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground\">\n {title}\n </div>\n );\n}\n\nfunction PreferenceItem({\n label,\n selected,\n onSelect,\n}: {\n label: string;\n selected: boolean;\n onSelect: () => void;\n}) {\n return (\n <button\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={selected}\n className=\"flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-popover-foreground hover:bg-accent hover:text-accent-foreground\"\n onClick={onSelect}\n >\n <span className=\"flex w-3.5 shrink-0 justify-center\">\n {selected && <Icon icon={UiCheck} className=\"text-xs\" />}\n </span>\n {label}\n </button>\n );\n}\n"],"names":["DEFAULT_FORM_SIZE","DEFAULT_PREFERENCES_STORAGE_KEY","useState","readPreferences","writePreferences","renderApi","useMemo","rehydrateRefs","FormLookupProvider","jsxs","cn","fieldInnerGapClass","jsx","labelSizeClass","FieldsGrid","DiscriminatedForm","renderObjectFields","DropdownMenu","Icon","UiEllipsis","Fragment","UiCheck"],"mappings":"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"}
1
+ {"version":3,"file":"JsonSchemaForm.cjs","sources":["../../src/components/JsonSchemaForm.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../data/Icon\";\nimport { UiCheck, UiEllipsis } from \"../icons\";\nimport { DropdownMenu } from \"../overlay/DropdownMenu\";\nimport { FieldsGrid } from \"./json-schema-form-fields\";\nimport { FormLookupProvider } from \"./FormLookupProvider\";\nimport { DiscriminatedForm } from \"./json-schema-form-discriminator\";\nimport { rehydrateRefs } from \"./json-schema-form-refs\";\nimport { renderApi, renderObjectFields } from \"./json-schema-form-render\";\nimport {\n DEFAULT_FORM_SIZE,\n fieldInnerGapClass,\n labelSizeClass,\n type FormSize,\n} from \"./json-schema-form-size\";\nimport {\n DEFAULT_PREFERENCES_STORAGE_KEY,\n readPreferences,\n writePreferences,\n type FormPreferences,\n type LayoutMode,\n type SortMode,\n} from \"./json-schema-form-preferences\";\nimport type { FormLayout, JsonSchemaFormProps, RenderContext } from \"./json-schema-form-types\";\n\nconst DEFAULT_LABEL_MAX_WIDTH = \"40ch\";\nconst DEFAULT_VALUE_MAX_WIDTH = \"600px\";\n\n// resolveFormLayout maps the `layout`/`inline` props plus an optional menu-driven\n// `modeOverride` to a single resolved FormLayout. An explicit `layout` sets the\n// base; `inline` is the shorthand for it; `modeOverride` (from the preferences\n// menu) wins over both but only flips the mode — width caps from `layout` are\n// preserved. Both modes fill valueMaxWidth (inline value column / stacked\n// label+value stack); the label cap only applies inline.\nfunction resolveFormLayout(\n layout: FormLayout | undefined,\n inline: boolean,\n modeOverride: LayoutMode | undefined,\n): FormLayout {\n const baseMode = layout?.mode ?? (inline ? \"inline\" : \"stacked\");\n const mode = modeOverride ?? baseMode;\n const valueMaxWidth = layout?.valueMaxWidth ?? DEFAULT_VALUE_MAX_WIDTH;\n if (mode !== \"inline\") return { mode: \"stacked\", valueMaxWidth };\n return {\n mode: \"inline\",\n labelMaxWidth: layout?.labelMaxWidth ?? DEFAULT_LABEL_MAX_WIDTH,\n valueMaxWidth,\n };\n}\n\n// JsonSchemaForm renders an object subschema as a form: one control per\n// (effective) property. It resolves if/then conditionals internally and recurses\n// through array items and object/map values; every other behaviour is layered on\n// by the pre/post extension functions the consumer supplies. The component holds\n// no domain knowledge.\nexport function JsonSchemaForm({\n schema,\n value,\n onChange,\n readOnly = false,\n inline = false,\n layout,\n size = DEFAULT_FORM_SIZE,\n idPrefix,\n hideReadOnlyFields = false,\n hiddenKeys,\n requiredFirst = false,\n title,\n pre,\n post,\n showPreferencesMenu = true,\n persistPreferences = true,\n preferencesStorageKey = DEFAULT_PREFERENCES_STORAGE_KEY,\n lookupFetcher,\n}: JsonSchemaFormProps) {\n // When the menu is hidden, never touch localStorage and start from an empty\n // override so behaviour is identical to before this feature existed.\n const [prefs, setPrefs] = useState<FormPreferences>(() =>\n showPreferencesMenu && persistPreferences ? readPreferences(preferencesStorageKey) : {},\n );\n\n const effectiveSize = prefs.size ?? size;\n const resolvedLayout = resolveFormLayout(layout, inline, prefs.layoutMode);\n // The `requiredFirst` prop sets the base sort; a menu selection overrides it.\n const effectiveSortMode = prefs.sortMode ?? (requiredFirst ? \"required-first\" : \"schema\");\n\n const applyPrefs = (next: FormPreferences) => {\n setPrefs(next);\n if (persistPreferences) writePreferences(preferencesStorageKey, next);\n };\n\n const ctx: RenderContext = {\n readOnly,\n hideReadOnlyFields,\n layout: resolvedLayout,\n size: effectiveSize,\n sortMode: effectiveSortMode,\n pre: pre ?? [],\n post: post ?? [],\n rootValue: value,\n depth: 0,\n render: renderApi,\n ...(idPrefix ? { idPrefix } : {}),\n };\n // A bundled schema (components under `$defs`, referenced by local `#/$defs`\n // pointers) is resolved once into a self-contained tree the renderer walks\n // directly; a non-bundled schema passes through untouched.\n const resolvedSchema = useMemo(() => rehydrateRefs(schema), [schema]);\n // A schema may name a discriminator property whose value selects a \"kind\"; the\n // form then runs a two-phase pick-then-fill flow (see DiscriminatedForm).\n const discKey =\n typeof resolvedSchema[\"x-discriminator\"] === \"string\" ? resolvedSchema[\"x-discriminator\"] : undefined;\n const inPickerPhase = discKey != null && (value[discKey] == null || value[discKey] === \"\");\n\n return (\n <FormLookupProvider {...(lookupFetcher ? { fetcher: lookupFetcher } : {})}>\n <div className={cn(\"relative flex flex-col\", fieldInnerGapClass[effectiveSize])}>\n {showPreferencesMenu && !inPickerPhase && (\n <PreferencesMenu\n size={effectiveSize}\n layoutMode={resolvedLayout.mode}\n sortMode={effectiveSortMode}\n onSelectSize={(next) => applyPrefs({ ...prefs, size: next })}\n onSelectLayout={(next) => applyPrefs({ ...prefs, layoutMode: next })}\n onSelectSort={(next) => applyPrefs({ ...prefs, sortMode: next })}\n />\n )}\n {title && <h3 className={cn(\"font-semibold\", labelSizeClass[effectiveSize])}>{title}</h3>}\n <FieldsGrid layout={resolvedLayout} size={effectiveSize}>\n {discKey ? (\n <DiscriminatedForm\n schema={resolvedSchema}\n value={value}\n onChange={onChange}\n ctx={ctx}\n discKey={discKey}\n />\n ) : (\n renderObjectFields(resolvedSchema, value, onChange, ctx, hiddenKeys ? { hiddenKeys } : undefined)\n )}\n </FieldsGrid>\n </div>\n </FormLookupProvider>\n );\n}\n\nconst SIZE_OPTIONS: { value: FormSize; label: string }[] = [\n { value: \"xs\", label: \"Extra small\" },\n { value: \"sm\", label: \"Small\" },\n { value: \"md\", label: \"Medium\" },\n { value: \"lg\", label: \"Large\" },\n { value: \"xl\", label: \"Extra large\" },\n];\n\nconst LAYOUT_OPTIONS: { value: LayoutMode; label: string }[] = [\n { value: \"stacked\", label: \"Stacked\" },\n { value: \"inline\", label: \"Inline\" },\n];\n\nconst SORT_OPTIONS: { value: SortMode; label: string }[] = [\n { value: \"schema\", label: \"Schema order\" },\n { value: \"required-first\", label: \"Required first\" },\n { value: \"priority\", label: \"Required & filled first\" },\n];\n\n// PreferencesMenu is the compact top-right ellipsis menu controlling this form's\n// size and layout mode. Selecting an item fires the matching callback and closes\n// the menu; the parent decides whether to persist.\nfunction PreferencesMenu({\n size,\n layoutMode,\n sortMode,\n onSelectSize,\n onSelectLayout,\n onSelectSort,\n}: {\n size: FormSize;\n layoutMode: LayoutMode;\n sortMode: SortMode;\n onSelectSize: (size: FormSize) => void;\n onSelectLayout: (mode: LayoutMode) => void;\n onSelectSort: (mode: SortMode) => void;\n}) {\n return (\n <DropdownMenu\n align=\"right\"\n menuLabel=\"Form display options\"\n className=\"absolute right-0 top-0 z-10\"\n trigger={\n <button\n type=\"button\"\n aria-label=\"Form display options\"\n aria-haspopup=\"menu\"\n className=\"inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <Icon icon={UiEllipsis} className=\"text-sm\" />\n </button>\n }\n >\n {(closeMenu) => (\n <>\n <PreferenceSection title=\"Size\" />\n {SIZE_OPTIONS.map((opt) => (\n <PreferenceItem\n key={opt.value}\n label={opt.label}\n selected={opt.value === size}\n onSelect={() => {\n onSelectSize(opt.value);\n closeMenu();\n }}\n />\n ))}\n <PreferenceSection title=\"Layout\" />\n {LAYOUT_OPTIONS.map((opt) => (\n <PreferenceItem\n key={opt.value}\n label={opt.label}\n selected={opt.value === layoutMode}\n onSelect={() => {\n onSelectLayout(opt.value);\n closeMenu();\n }}\n />\n ))}\n <PreferenceSection title=\"Sort\" />\n {SORT_OPTIONS.map((opt) => (\n <PreferenceItem\n key={opt.value}\n label={opt.label}\n selected={opt.value === sortMode}\n onSelect={() => {\n onSelectSort(opt.value);\n closeMenu();\n }}\n />\n ))}\n </>\n )}\n </DropdownMenu>\n );\n}\n\nfunction PreferenceSection({ title }: { title: string }) {\n return (\n <div className=\"px-3 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground\">\n {title}\n </div>\n );\n}\n\nfunction PreferenceItem({\n label,\n selected,\n onSelect,\n}: {\n label: string;\n selected: boolean;\n onSelect: () => void;\n}) {\n return (\n <button\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={selected}\n className=\"flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-popover-foreground hover:bg-accent hover:text-accent-foreground\"\n onClick={onSelect}\n >\n <span className=\"flex w-3.5 shrink-0 justify-center\">\n {selected && <Icon icon={UiCheck} className=\"text-xs\" />}\n </span>\n {label}\n </button>\n );\n}\n"],"names":["DEFAULT_FORM_SIZE","DEFAULT_PREFERENCES_STORAGE_KEY","useState","readPreferences","writePreferences","renderApi","useMemo","rehydrateRefs","FormLookupProvider","jsxs","cn","fieldInnerGapClass","jsx","labelSizeClass","FieldsGrid","DiscriminatedForm","renderObjectFields","DropdownMenu","Icon","UiEllipsis","Fragment","UiCheck"],"mappings":"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"}
@@ -4,7 +4,7 @@ import { cn } from "../lib/utils.js";
4
4
  import { Icon } from "../data/Icon.js";
5
5
  import { DropdownMenu } from "../overlay/DropdownMenu.js";
6
6
  import { FieldsGrid } from "./json-schema-form-fields.js";
7
- import { FormLookupProvider } from "./form-lookup-context.js";
7
+ import { FormLookupProvider } from "./FormLookupProvider.js";
8
8
  import { DiscriminatedForm } from "./json-schema-form-discriminator.js";
9
9
  import { rehydrateRefs } from "./json-schema-form-refs.js";
10
10
  import { renderApi, renderObjectFields } from "./json-schema-form-render.js";
@@ -1 +1 @@
1
- {"version":3,"file":"JsonSchemaForm.js","sources":["../../src/components/JsonSchemaForm.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../data/Icon\";\nimport { UiCheck, UiEllipsis } from \"../icons\";\nimport { DropdownMenu } from \"../overlay/DropdownMenu\";\nimport { FieldsGrid } from \"./json-schema-form-fields\";\nimport { FormLookupProvider } from \"./form-lookup-context\";\nimport { DiscriminatedForm } from \"./json-schema-form-discriminator\";\nimport { rehydrateRefs } from \"./json-schema-form-refs\";\nimport { renderApi, renderObjectFields } from \"./json-schema-form-render\";\nimport {\n DEFAULT_FORM_SIZE,\n fieldInnerGapClass,\n labelSizeClass,\n type FormSize,\n} from \"./json-schema-form-size\";\nimport {\n DEFAULT_PREFERENCES_STORAGE_KEY,\n readPreferences,\n writePreferences,\n type FormPreferences,\n type LayoutMode,\n type SortMode,\n} from \"./json-schema-form-preferences\";\nimport type { FormLayout, JsonSchemaFormProps, RenderContext } from \"./json-schema-form-types\";\n\nconst DEFAULT_LABEL_MAX_WIDTH = \"40ch\";\nconst DEFAULT_VALUE_MAX_WIDTH = \"600px\";\n\n// resolveFormLayout maps the `layout`/`inline` props plus an optional menu-driven\n// `modeOverride` to a single resolved FormLayout. An explicit `layout` sets the\n// base; `inline` is the shorthand for it; `modeOverride` (from the preferences\n// menu) wins over both but only flips the mode — width caps from `layout` are\n// preserved. Both modes fill valueMaxWidth (inline value column / stacked\n// label+value stack); the label cap only applies inline.\nfunction resolveFormLayout(\n layout: FormLayout | undefined,\n inline: boolean,\n modeOverride: LayoutMode | undefined,\n): FormLayout {\n const baseMode = layout?.mode ?? (inline ? \"inline\" : \"stacked\");\n const mode = modeOverride ?? baseMode;\n const valueMaxWidth = layout?.valueMaxWidth ?? DEFAULT_VALUE_MAX_WIDTH;\n if (mode !== \"inline\") return { mode: \"stacked\", valueMaxWidth };\n return {\n mode: \"inline\",\n labelMaxWidth: layout?.labelMaxWidth ?? DEFAULT_LABEL_MAX_WIDTH,\n valueMaxWidth,\n };\n}\n\n// JsonSchemaForm renders an object subschema as a form: one control per\n// (effective) property. It resolves if/then conditionals internally and recurses\n// through array items and object/map values; every other behaviour is layered on\n// by the pre/post extension functions the consumer supplies. The component holds\n// no domain knowledge.\nexport function JsonSchemaForm({\n schema,\n value,\n onChange,\n readOnly = false,\n inline = false,\n layout,\n size = DEFAULT_FORM_SIZE,\n idPrefix,\n hideReadOnlyFields = false,\n hiddenKeys,\n requiredFirst = false,\n title,\n pre,\n post,\n showPreferencesMenu = true,\n persistPreferences = true,\n preferencesStorageKey = DEFAULT_PREFERENCES_STORAGE_KEY,\n lookupFetcher,\n}: JsonSchemaFormProps) {\n // When the menu is hidden, never touch localStorage and start from an empty\n // override so behaviour is identical to before this feature existed.\n const [prefs, setPrefs] = useState<FormPreferences>(() =>\n showPreferencesMenu && persistPreferences ? readPreferences(preferencesStorageKey) : {},\n );\n\n const effectiveSize = prefs.size ?? size;\n const resolvedLayout = resolveFormLayout(layout, inline, prefs.layoutMode);\n // The `requiredFirst` prop sets the base sort; a menu selection overrides it.\n const effectiveSortMode = prefs.sortMode ?? (requiredFirst ? \"required-first\" : \"schema\");\n\n const applyPrefs = (next: FormPreferences) => {\n setPrefs(next);\n if (persistPreferences) writePreferences(preferencesStorageKey, next);\n };\n\n const ctx: RenderContext = {\n readOnly,\n hideReadOnlyFields,\n layout: resolvedLayout,\n size: effectiveSize,\n sortMode: effectiveSortMode,\n pre: pre ?? [],\n post: post ?? [],\n rootValue: value,\n depth: 0,\n render: renderApi,\n ...(idPrefix ? { idPrefix } : {}),\n };\n // A bundled schema (components under `$defs`, referenced by local `#/$defs`\n // pointers) is resolved once into a self-contained tree the renderer walks\n // directly; a non-bundled schema passes through untouched.\n const resolvedSchema = useMemo(() => rehydrateRefs(schema), [schema]);\n // A schema may name a discriminator property whose value selects a \"kind\"; the\n // form then runs a two-phase pick-then-fill flow (see DiscriminatedForm).\n const discKey =\n typeof resolvedSchema[\"x-discriminator\"] === \"string\" ? resolvedSchema[\"x-discriminator\"] : undefined;\n const inPickerPhase = discKey != null && (value[discKey] == null || value[discKey] === \"\");\n\n return (\n <FormLookupProvider {...(lookupFetcher ? { fetcher: lookupFetcher } : {})}>\n <div className={cn(\"relative flex flex-col\", fieldInnerGapClass[effectiveSize])}>\n {showPreferencesMenu && !inPickerPhase && (\n <PreferencesMenu\n size={effectiveSize}\n layoutMode={resolvedLayout.mode}\n sortMode={effectiveSortMode}\n onSelectSize={(next) => applyPrefs({ ...prefs, size: next })}\n onSelectLayout={(next) => applyPrefs({ ...prefs, layoutMode: next })}\n onSelectSort={(next) => applyPrefs({ ...prefs, sortMode: next })}\n />\n )}\n {title && <h3 className={cn(\"font-semibold\", labelSizeClass[effectiveSize])}>{title}</h3>}\n <FieldsGrid layout={resolvedLayout} size={effectiveSize}>\n {discKey ? (\n <DiscriminatedForm\n schema={resolvedSchema}\n value={value}\n onChange={onChange}\n ctx={ctx}\n discKey={discKey}\n />\n ) : (\n renderObjectFields(resolvedSchema, value, onChange, ctx, hiddenKeys ? { hiddenKeys } : undefined)\n )}\n </FieldsGrid>\n </div>\n </FormLookupProvider>\n );\n}\n\nconst SIZE_OPTIONS: { value: FormSize; label: string }[] = [\n { value: \"xs\", label: \"Extra small\" },\n { value: \"sm\", label: \"Small\" },\n { value: \"md\", label: \"Medium\" },\n { value: \"lg\", label: \"Large\" },\n { value: \"xl\", label: \"Extra large\" },\n];\n\nconst LAYOUT_OPTIONS: { value: LayoutMode; label: string }[] = [\n { value: \"stacked\", label: \"Stacked\" },\n { value: \"inline\", label: \"Inline\" },\n];\n\nconst SORT_OPTIONS: { value: SortMode; label: string }[] = [\n { value: \"schema\", label: \"Schema order\" },\n { value: \"required-first\", label: \"Required first\" },\n { value: \"priority\", label: \"Required & filled first\" },\n];\n\n// PreferencesMenu is the compact top-right ellipsis menu controlling this form's\n// size and layout mode. Selecting an item fires the matching callback and closes\n// the menu; the parent decides whether to persist.\nfunction PreferencesMenu({\n size,\n layoutMode,\n sortMode,\n onSelectSize,\n onSelectLayout,\n onSelectSort,\n}: {\n size: FormSize;\n layoutMode: LayoutMode;\n sortMode: SortMode;\n onSelectSize: (size: FormSize) => void;\n onSelectLayout: (mode: LayoutMode) => void;\n onSelectSort: (mode: SortMode) => void;\n}) {\n return (\n <DropdownMenu\n align=\"right\"\n menuLabel=\"Form display options\"\n className=\"absolute right-0 top-0 z-10\"\n trigger={\n <button\n type=\"button\"\n aria-label=\"Form display options\"\n aria-haspopup=\"menu\"\n className=\"inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <Icon icon={UiEllipsis} className=\"text-sm\" />\n </button>\n }\n >\n {(closeMenu) => (\n <>\n <PreferenceSection title=\"Size\" />\n {SIZE_OPTIONS.map((opt) => (\n <PreferenceItem\n key={opt.value}\n label={opt.label}\n selected={opt.value === size}\n onSelect={() => {\n onSelectSize(opt.value);\n closeMenu();\n }}\n />\n ))}\n <PreferenceSection title=\"Layout\" />\n {LAYOUT_OPTIONS.map((opt) => (\n <PreferenceItem\n key={opt.value}\n label={opt.label}\n selected={opt.value === layoutMode}\n onSelect={() => {\n onSelectLayout(opt.value);\n closeMenu();\n }}\n />\n ))}\n <PreferenceSection title=\"Sort\" />\n {SORT_OPTIONS.map((opt) => (\n <PreferenceItem\n key={opt.value}\n label={opt.label}\n selected={opt.value === sortMode}\n onSelect={() => {\n onSelectSort(opt.value);\n closeMenu();\n }}\n />\n ))}\n </>\n )}\n </DropdownMenu>\n );\n}\n\nfunction PreferenceSection({ title }: { title: string }) {\n return (\n <div className=\"px-3 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground\">\n {title}\n </div>\n );\n}\n\nfunction PreferenceItem({\n label,\n selected,\n onSelect,\n}: {\n label: string;\n selected: boolean;\n onSelect: () => void;\n}) {\n return (\n <button\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={selected}\n className=\"flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-popover-foreground hover:bg-accent hover:text-accent-foreground\"\n onClick={onSelect}\n >\n <span className=\"flex w-3.5 shrink-0 justify-center\">\n {selected && <Icon icon={UiCheck} className=\"text-xs\" />}\n </span>\n {label}\n </button>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"JsonSchemaForm.js","sources":["../../src/components/JsonSchemaForm.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Icon } from \"../data/Icon\";\nimport { UiCheck, UiEllipsis } from \"../icons\";\nimport { DropdownMenu } from \"../overlay/DropdownMenu\";\nimport { FieldsGrid } from \"./json-schema-form-fields\";\nimport { FormLookupProvider } from \"./FormLookupProvider\";\nimport { DiscriminatedForm } from \"./json-schema-form-discriminator\";\nimport { rehydrateRefs } from \"./json-schema-form-refs\";\nimport { renderApi, renderObjectFields } from \"./json-schema-form-render\";\nimport {\n DEFAULT_FORM_SIZE,\n fieldInnerGapClass,\n labelSizeClass,\n type FormSize,\n} from \"./json-schema-form-size\";\nimport {\n DEFAULT_PREFERENCES_STORAGE_KEY,\n readPreferences,\n writePreferences,\n type FormPreferences,\n type LayoutMode,\n type SortMode,\n} from \"./json-schema-form-preferences\";\nimport type { FormLayout, JsonSchemaFormProps, RenderContext } from \"./json-schema-form-types\";\n\nconst DEFAULT_LABEL_MAX_WIDTH = \"40ch\";\nconst DEFAULT_VALUE_MAX_WIDTH = \"600px\";\n\n// resolveFormLayout maps the `layout`/`inline` props plus an optional menu-driven\n// `modeOverride` to a single resolved FormLayout. An explicit `layout` sets the\n// base; `inline` is the shorthand for it; `modeOverride` (from the preferences\n// menu) wins over both but only flips the mode — width caps from `layout` are\n// preserved. Both modes fill valueMaxWidth (inline value column / stacked\n// label+value stack); the label cap only applies inline.\nfunction resolveFormLayout(\n layout: FormLayout | undefined,\n inline: boolean,\n modeOverride: LayoutMode | undefined,\n): FormLayout {\n const baseMode = layout?.mode ?? (inline ? \"inline\" : \"stacked\");\n const mode = modeOverride ?? baseMode;\n const valueMaxWidth = layout?.valueMaxWidth ?? DEFAULT_VALUE_MAX_WIDTH;\n if (mode !== \"inline\") return { mode: \"stacked\", valueMaxWidth };\n return {\n mode: \"inline\",\n labelMaxWidth: layout?.labelMaxWidth ?? DEFAULT_LABEL_MAX_WIDTH,\n valueMaxWidth,\n };\n}\n\n// JsonSchemaForm renders an object subschema as a form: one control per\n// (effective) property. It resolves if/then conditionals internally and recurses\n// through array items and object/map values; every other behaviour is layered on\n// by the pre/post extension functions the consumer supplies. The component holds\n// no domain knowledge.\nexport function JsonSchemaForm({\n schema,\n value,\n onChange,\n readOnly = false,\n inline = false,\n layout,\n size = DEFAULT_FORM_SIZE,\n idPrefix,\n hideReadOnlyFields = false,\n hiddenKeys,\n requiredFirst = false,\n title,\n pre,\n post,\n showPreferencesMenu = true,\n persistPreferences = true,\n preferencesStorageKey = DEFAULT_PREFERENCES_STORAGE_KEY,\n lookupFetcher,\n}: JsonSchemaFormProps) {\n // When the menu is hidden, never touch localStorage and start from an empty\n // override so behaviour is identical to before this feature existed.\n const [prefs, setPrefs] = useState<FormPreferences>(() =>\n showPreferencesMenu && persistPreferences ? readPreferences(preferencesStorageKey) : {},\n );\n\n const effectiveSize = prefs.size ?? size;\n const resolvedLayout = resolveFormLayout(layout, inline, prefs.layoutMode);\n // The `requiredFirst` prop sets the base sort; a menu selection overrides it.\n const effectiveSortMode = prefs.sortMode ?? (requiredFirst ? \"required-first\" : \"schema\");\n\n const applyPrefs = (next: FormPreferences) => {\n setPrefs(next);\n if (persistPreferences) writePreferences(preferencesStorageKey, next);\n };\n\n const ctx: RenderContext = {\n readOnly,\n hideReadOnlyFields,\n layout: resolvedLayout,\n size: effectiveSize,\n sortMode: effectiveSortMode,\n pre: pre ?? [],\n post: post ?? [],\n rootValue: value,\n depth: 0,\n render: renderApi,\n ...(idPrefix ? { idPrefix } : {}),\n };\n // A bundled schema (components under `$defs`, referenced by local `#/$defs`\n // pointers) is resolved once into a self-contained tree the renderer walks\n // directly; a non-bundled schema passes through untouched.\n const resolvedSchema = useMemo(() => rehydrateRefs(schema), [schema]);\n // A schema may name a discriminator property whose value selects a \"kind\"; the\n // form then runs a two-phase pick-then-fill flow (see DiscriminatedForm).\n const discKey =\n typeof resolvedSchema[\"x-discriminator\"] === \"string\" ? resolvedSchema[\"x-discriminator\"] : undefined;\n const inPickerPhase = discKey != null && (value[discKey] == null || value[discKey] === \"\");\n\n return (\n <FormLookupProvider {...(lookupFetcher ? { fetcher: lookupFetcher } : {})}>\n <div className={cn(\"relative flex flex-col\", fieldInnerGapClass[effectiveSize])}>\n {showPreferencesMenu && !inPickerPhase && (\n <PreferencesMenu\n size={effectiveSize}\n layoutMode={resolvedLayout.mode}\n sortMode={effectiveSortMode}\n onSelectSize={(next) => applyPrefs({ ...prefs, size: next })}\n onSelectLayout={(next) => applyPrefs({ ...prefs, layoutMode: next })}\n onSelectSort={(next) => applyPrefs({ ...prefs, sortMode: next })}\n />\n )}\n {title && <h3 className={cn(\"font-semibold\", labelSizeClass[effectiveSize])}>{title}</h3>}\n <FieldsGrid layout={resolvedLayout} size={effectiveSize}>\n {discKey ? (\n <DiscriminatedForm\n schema={resolvedSchema}\n value={value}\n onChange={onChange}\n ctx={ctx}\n discKey={discKey}\n />\n ) : (\n renderObjectFields(resolvedSchema, value, onChange, ctx, hiddenKeys ? { hiddenKeys } : undefined)\n )}\n </FieldsGrid>\n </div>\n </FormLookupProvider>\n );\n}\n\nconst SIZE_OPTIONS: { value: FormSize; label: string }[] = [\n { value: \"xs\", label: \"Extra small\" },\n { value: \"sm\", label: \"Small\" },\n { value: \"md\", label: \"Medium\" },\n { value: \"lg\", label: \"Large\" },\n { value: \"xl\", label: \"Extra large\" },\n];\n\nconst LAYOUT_OPTIONS: { value: LayoutMode; label: string }[] = [\n { value: \"stacked\", label: \"Stacked\" },\n { value: \"inline\", label: \"Inline\" },\n];\n\nconst SORT_OPTIONS: { value: SortMode; label: string }[] = [\n { value: \"schema\", label: \"Schema order\" },\n { value: \"required-first\", label: \"Required first\" },\n { value: \"priority\", label: \"Required & filled first\" },\n];\n\n// PreferencesMenu is the compact top-right ellipsis menu controlling this form's\n// size and layout mode. Selecting an item fires the matching callback and closes\n// the menu; the parent decides whether to persist.\nfunction PreferencesMenu({\n size,\n layoutMode,\n sortMode,\n onSelectSize,\n onSelectLayout,\n onSelectSort,\n}: {\n size: FormSize;\n layoutMode: LayoutMode;\n sortMode: SortMode;\n onSelectSize: (size: FormSize) => void;\n onSelectLayout: (mode: LayoutMode) => void;\n onSelectSort: (mode: SortMode) => void;\n}) {\n return (\n <DropdownMenu\n align=\"right\"\n menuLabel=\"Form display options\"\n className=\"absolute right-0 top-0 z-10\"\n trigger={\n <button\n type=\"button\"\n aria-label=\"Form display options\"\n aria-haspopup=\"menu\"\n className=\"inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n >\n <Icon icon={UiEllipsis} className=\"text-sm\" />\n </button>\n }\n >\n {(closeMenu) => (\n <>\n <PreferenceSection title=\"Size\" />\n {SIZE_OPTIONS.map((opt) => (\n <PreferenceItem\n key={opt.value}\n label={opt.label}\n selected={opt.value === size}\n onSelect={() => {\n onSelectSize(opt.value);\n closeMenu();\n }}\n />\n ))}\n <PreferenceSection title=\"Layout\" />\n {LAYOUT_OPTIONS.map((opt) => (\n <PreferenceItem\n key={opt.value}\n label={opt.label}\n selected={opt.value === layoutMode}\n onSelect={() => {\n onSelectLayout(opt.value);\n closeMenu();\n }}\n />\n ))}\n <PreferenceSection title=\"Sort\" />\n {SORT_OPTIONS.map((opt) => (\n <PreferenceItem\n key={opt.value}\n label={opt.label}\n selected={opt.value === sortMode}\n onSelect={() => {\n onSelectSort(opt.value);\n closeMenu();\n }}\n />\n ))}\n </>\n )}\n </DropdownMenu>\n );\n}\n\nfunction PreferenceSection({ title }: { title: string }) {\n return (\n <div className=\"px-3 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground\">\n {title}\n </div>\n );\n}\n\nfunction PreferenceItem({\n label,\n selected,\n onSelect,\n}: {\n label: string;\n selected: boolean;\n onSelect: () => void;\n}) {\n return (\n <button\n type=\"button\"\n role=\"menuitemradio\"\n aria-checked={selected}\n className=\"flex w-full items-center gap-2 px-3 py-1.5 text-left text-xs text-popover-foreground hover:bg-accent hover:text-accent-foreground\"\n onClick={onSelect}\n >\n <span className=\"flex w-3.5 shrink-0 justify-center\">\n {selected && <Icon icon={UiCheck} className=\"text-xs\" />}\n </span>\n {label}\n </button>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const utils = require("../lib/utils.cjs");
5
+ const button = require("./button.cjs");
6
+ const useListMenuSelection = require("./use-list-menu-selection.cjs");
7
+ function ListMenu({
8
+ className,
9
+ selection,
10
+ showCheckboxes = true,
11
+ children,
12
+ ...props
13
+ }) {
14
+ const menu = /* @__PURE__ */ jsxRuntime.jsx("div", { className: utils.cn("divide-y divide-border", className), ...props, children });
15
+ if (!selection) return menu;
16
+ return /* @__PURE__ */ jsxRuntime.jsx(useListMenuSelection.ListMenuSelectionContext.Provider, { value: { selection, showCheckboxes }, children: menu });
17
+ }
18
+ function ListMenuSection({ className, ...props }) {
19
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, ...props });
20
+ }
21
+ function ListMenuHeader({ className, ...props }) {
22
+ return /* @__PURE__ */ jsxRuntime.jsx(
23
+ "div",
24
+ {
25
+ className: utils.cn(
26
+ "sticky top-0 z-10 flex w-full items-center gap-2 border-b border-border bg-muted px-3 py-1.5",
27
+ className
28
+ ),
29
+ ...props
30
+ }
31
+ );
32
+ }
33
+ function ListMenuItem({
34
+ active = false,
35
+ selected = false,
36
+ accentClassName = "border-transparent",
37
+ interactive = true,
38
+ itemKey,
39
+ hideCheckbox = false,
40
+ className,
41
+ onClick,
42
+ children,
43
+ ...props
44
+ }) {
45
+ const ctx = useListMenuSelection.useListMenuSelectionContext();
46
+ const selectable = ctx !== null && itemKey !== void 0;
47
+ const rowSelected = selectable ? ctx.selection.isSelected(itemKey) : selected;
48
+ const handleClick = (event) => {
49
+ if (selectable && event.shiftKey) {
50
+ event.preventDefault();
51
+ ctx.selection.toggle(itemKey, { shiftKey: true });
52
+ return;
53
+ }
54
+ onClick == null ? void 0 : onClick(event);
55
+ };
56
+ const showCheckbox = selectable && ctx.showCheckboxes && !hideCheckbox;
57
+ const body = selectable && showCheckbox ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
58
+ /* @__PURE__ */ jsxRuntime.jsx(
59
+ "input",
60
+ {
61
+ type: "checkbox",
62
+ className: "mt-0.5 size-4 shrink-0 rounded border-input",
63
+ checked: rowSelected,
64
+ "aria-label": "Select item",
65
+ onClick: (event) => {
66
+ event.stopPropagation();
67
+ if (event.shiftKey) {
68
+ event.preventDefault();
69
+ ctx.selection.toggle(itemKey, { shiftKey: true });
70
+ }
71
+ },
72
+ onChange: () => ctx.selection.toggle(itemKey)
73
+ }
74
+ ),
75
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children })
76
+ ] }) : children;
77
+ return /* @__PURE__ */ jsxRuntime.jsx(
78
+ "div",
79
+ {
80
+ className: utils.cn(
81
+ "min-w-0 overflow-hidden border-l-2 transition-colors",
82
+ interactive && "cursor-pointer",
83
+ active ? "border-primary bg-primary/10" : rowSelected ? "border-primary/40 bg-primary/5 hover:bg-primary/10" : utils.cn(accentClassName, interactive && "hover:bg-muted"),
84
+ className
85
+ ),
86
+ onClick: handleClick,
87
+ ...props,
88
+ children: body
89
+ }
90
+ );
91
+ }
92
+ function ListMenuActionBar({
93
+ selection: selectionProp,
94
+ actions = [],
95
+ renderLabel,
96
+ hideWhenEmpty = true,
97
+ clearLabel = "Clear",
98
+ className,
99
+ children,
100
+ ...props
101
+ }) {
102
+ const ctx = useListMenuSelection.useListMenuSelectionContext();
103
+ const selection = selectionProp ?? (ctx == null ? void 0 : ctx.selection);
104
+ if (!selection) return null;
105
+ const { count } = selection;
106
+ if (hideWhenEmpty && count === 0) return null;
107
+ return /* @__PURE__ */ jsxRuntime.jsxs(
108
+ "div",
109
+ {
110
+ className: utils.cn(
111
+ "flex items-center gap-2 border-b border-border bg-muted px-3 py-1.5",
112
+ className
113
+ ),
114
+ ...props,
115
+ children: [
116
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-foreground", children: renderLabel ? renderLabel(count) : `${count} selected` }),
117
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-auto flex items-center gap-1", children: [
118
+ actions.map((action, i) => /* @__PURE__ */ jsxRuntime.jsxs(
119
+ button.Button,
120
+ {
121
+ type: "button",
122
+ size: "sm",
123
+ variant: action.variant ?? "outline",
124
+ disabled: action.disabled || count === 0,
125
+ onClick: () => action.onClick(selection.selectedKeys),
126
+ children: [
127
+ action.icon,
128
+ action.label
129
+ ]
130
+ },
131
+ i
132
+ )),
133
+ children,
134
+ clearLabel !== null && /* @__PURE__ */ jsxRuntime.jsx(button.Button, { type: "button", size: "sm", variant: "ghost", onClick: () => selection.clear(), children: clearLabel })
135
+ ] })
136
+ ]
137
+ }
138
+ );
139
+ }
140
+ exports.ListMenu = ListMenu;
141
+ exports.ListMenuActionBar = ListMenuActionBar;
142
+ exports.ListMenuHeader = ListMenuHeader;
143
+ exports.ListMenuItem = ListMenuItem;
144
+ exports.ListMenuSection = ListMenuSection;
145
+ //# sourceMappingURL=ListMenu.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ListMenu.cjs","sources":["../../src/components/ListMenu.tsx"],"sourcesContent":["import type { HTMLAttributes, MouseEvent, ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { Button, type ButtonProps } from \"./button\";\nimport {\n ListMenuSelectionContext,\n useListMenuSelectionContext,\n type ListMenuSelection,\n} from \"./use-list-menu-selection\";\n\nexport type ListMenuProps = HTMLAttributes<HTMLDivElement> & {\n /**\n * Enables multi-select. Pass the object returned by `useListMenuSelection`;\n * rows that set `itemKey` then become selectable via checkbox or Shift+Click.\n */\n selection?: ListMenuSelection;\n /** Render a leading checkbox on selectable rows (default true when `selection` is set). */\n showCheckboxes?: boolean;\n};\n\nexport function ListMenu({\n className,\n selection,\n showCheckboxes = true,\n children,\n ...props\n}: ListMenuProps) {\n const menu = (\n <div className={cn(\"divide-y divide-border\", className)} {...props}>\n {children}\n </div>\n );\n if (!selection) return menu;\n return (\n <ListMenuSelectionContext.Provider value={{ selection, showCheckboxes }}>\n {menu}\n </ListMenuSelectionContext.Provider>\n );\n}\n\nexport type ListMenuSectionProps = HTMLAttributes<HTMLDivElement>;\n\nexport function ListMenuSection({ className, ...props }: ListMenuSectionProps) {\n return <div className={className} {...props} />;\n}\n\nexport type ListMenuHeaderProps = HTMLAttributes<HTMLDivElement>;\n\nexport function ListMenuHeader({ className, ...props }: ListMenuHeaderProps) {\n return (\n <div\n className={cn(\n \"sticky top-0 z-10 flex w-full items-center gap-2 border-b border-border bg-muted px-3 py-1.5\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport type ListMenuItemProps = HTMLAttributes<HTMLDivElement> & {\n /** Applies the primary selected-row treatment used for the open/detail row. */\n active?: boolean;\n /** Applies the softer selected treatment used for checkbox or multi-select rows. */\n selected?: boolean;\n /** Left-border class when neither active nor selected. */\n accentClassName?: string;\n /** Whether the row gets pointer and hover affordances. */\n interactive?: boolean;\n /** Stable key that opts the row into `ListMenu` multi-select. */\n itemKey?: string;\n /** Hide the leading checkbox for this row even when selection is enabled. */\n hideCheckbox?: boolean;\n};\n\nexport function ListMenuItem({\n active = false,\n selected = false,\n accentClassName = \"border-transparent\",\n interactive = true,\n itemKey,\n hideCheckbox = false,\n className,\n onClick,\n children,\n ...props\n}: ListMenuItemProps) {\n const ctx = useListMenuSelectionContext();\n const selectable = ctx !== null && itemKey !== undefined;\n const rowSelected = selectable ? ctx.selection.isSelected(itemKey) : selected;\n\n const handleClick = (event: MouseEvent<HTMLDivElement>) => {\n if (selectable && event.shiftKey) {\n event.preventDefault();\n ctx.selection.toggle(itemKey, { shiftKey: true });\n return;\n }\n onClick?.(event);\n };\n\n const showCheckbox = selectable && ctx.showCheckboxes && !hideCheckbox;\n const body =\n selectable && showCheckbox ? (\n <div className=\"flex items-start gap-2\">\n <input\n type=\"checkbox\"\n className=\"mt-0.5 size-4 shrink-0 rounded border-input\"\n checked={rowSelected}\n aria-label=\"Select item\"\n onClick={(event) => {\n event.stopPropagation();\n if (event.shiftKey) {\n event.preventDefault();\n ctx.selection.toggle(itemKey, { shiftKey: true });\n }\n }}\n onChange={() => ctx.selection.toggle(itemKey)}\n />\n <div className=\"min-w-0 flex-1\">{children}</div>\n </div>\n ) : (\n children\n );\n\n return (\n <div\n className={cn(\n \"min-w-0 overflow-hidden border-l-2 transition-colors\",\n interactive && \"cursor-pointer\",\n active\n ? \"border-primary bg-primary/10\"\n : rowSelected\n ? \"border-primary/40 bg-primary/5 hover:bg-primary/10\"\n : cn(accentClassName, interactive && \"hover:bg-muted\"),\n className,\n )}\n onClick={handleClick}\n {...props}\n >\n {body}\n </div>\n );\n}\n\nexport type ListMenuAction = {\n label: ReactNode;\n /** Receives the currently selected keys (ordered as in the menu). */\n onClick: (selectedKeys: string[]) => void;\n icon?: ReactNode;\n disabled?: boolean;\n variant?: ButtonProps[\"variant\"];\n};\n\nexport type ListMenuActionBarProps = HTMLAttributes<HTMLDivElement> & {\n /** Selection to act on; falls back to the enclosing `ListMenu` selection context. */\n selection?: ListMenuSelection;\n /** Bulk actions executed across every selected key. */\n actions?: ListMenuAction[];\n /** Override the \"{n} selected\" summary. */\n renderLabel?: (count: number) => ReactNode;\n /** Hide the whole bar when nothing is selected (default true). */\n hideWhenEmpty?: boolean;\n /** Label for the clear button; pass null to hide it. */\n clearLabel?: ReactNode;\n};\n\nexport function ListMenuActionBar({\n selection: selectionProp,\n actions = [],\n renderLabel,\n hideWhenEmpty = true,\n clearLabel = \"Clear\",\n className,\n children,\n ...props\n}: ListMenuActionBarProps) {\n const ctx = useListMenuSelectionContext();\n const selection = selectionProp ?? ctx?.selection;\n if (!selection) return null;\n const { count } = selection;\n if (hideWhenEmpty && count === 0) return null;\n\n return (\n <div\n className={cn(\n \"flex items-center gap-2 border-b border-border bg-muted px-3 py-1.5\",\n className,\n )}\n {...props}\n >\n <span className=\"text-sm font-medium text-foreground\">\n {renderLabel ? renderLabel(count) : `${count} selected`}\n </span>\n <div className=\"ml-auto flex items-center gap-1\">\n {actions.map((action, i) => (\n <Button\n key={i}\n type=\"button\"\n size=\"sm\"\n variant={action.variant ?? \"outline\"}\n disabled={action.disabled || count === 0}\n onClick={() => action.onClick(selection.selectedKeys)}\n >\n {action.icon}\n {action.label}\n </Button>\n ))}\n {children}\n {clearLabel !== null && (\n <Button type=\"button\" size=\"sm\" variant=\"ghost\" onClick={() => selection.clear()}>\n {clearLabel}\n </Button>\n )}\n </div>\n </div>\n );\n}\n"],"names":["jsx","cn","ListMenuSelectionContext","useListMenuSelectionContext","jsxs","Button"],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import { HTMLAttributes, ReactNode } from 'react';
2
+ import { ButtonProps } from './button';
3
+ import { ListMenuSelection } from './use-list-menu-selection';
4
+ export type ListMenuProps = HTMLAttributes<HTMLDivElement> & {
5
+ /**
6
+ * Enables multi-select. Pass the object returned by `useListMenuSelection`;
7
+ * rows that set `itemKey` then become selectable via checkbox or Shift+Click.
8
+ */
9
+ selection?: ListMenuSelection;
10
+ /** Render a leading checkbox on selectable rows (default true when `selection` is set). */
11
+ showCheckboxes?: boolean;
12
+ };
13
+ export declare function ListMenu({ className, selection, showCheckboxes, children, ...props }: ListMenuProps): import("react/jsx-runtime").JSX.Element;
14
+ export type ListMenuSectionProps = HTMLAttributes<HTMLDivElement>;
15
+ export declare function ListMenuSection({ className, ...props }: ListMenuSectionProps): import("react/jsx-runtime").JSX.Element;
16
+ export type ListMenuHeaderProps = HTMLAttributes<HTMLDivElement>;
17
+ export declare function ListMenuHeader({ className, ...props }: ListMenuHeaderProps): import("react/jsx-runtime").JSX.Element;
18
+ export type ListMenuItemProps = HTMLAttributes<HTMLDivElement> & {
19
+ /** Applies the primary selected-row treatment used for the open/detail row. */
20
+ active?: boolean;
21
+ /** Applies the softer selected treatment used for checkbox or multi-select rows. */
22
+ selected?: boolean;
23
+ /** Left-border class when neither active nor selected. */
24
+ accentClassName?: string;
25
+ /** Whether the row gets pointer and hover affordances. */
26
+ interactive?: boolean;
27
+ /** Stable key that opts the row into `ListMenu` multi-select. */
28
+ itemKey?: string;
29
+ /** Hide the leading checkbox for this row even when selection is enabled. */
30
+ hideCheckbox?: boolean;
31
+ };
32
+ export declare function ListMenuItem({ active, selected, accentClassName, interactive, itemKey, hideCheckbox, className, onClick, children, ...props }: ListMenuItemProps): import("react/jsx-runtime").JSX.Element;
33
+ export type ListMenuAction = {
34
+ label: ReactNode;
35
+ /** Receives the currently selected keys (ordered as in the menu). */
36
+ onClick: (selectedKeys: string[]) => void;
37
+ icon?: ReactNode;
38
+ disabled?: boolean;
39
+ variant?: ButtonProps["variant"];
40
+ };
41
+ export type ListMenuActionBarProps = HTMLAttributes<HTMLDivElement> & {
42
+ /** Selection to act on; falls back to the enclosing `ListMenu` selection context. */
43
+ selection?: ListMenuSelection;
44
+ /** Bulk actions executed across every selected key. */
45
+ actions?: ListMenuAction[];
46
+ /** Override the "{n} selected" summary. */
47
+ renderLabel?: (count: number) => ReactNode;
48
+ /** Hide the whole bar when nothing is selected (default true). */
49
+ hideWhenEmpty?: boolean;
50
+ /** Label for the clear button; pass null to hide it. */
51
+ clearLabel?: ReactNode;
52
+ };
53
+ export declare function ListMenuActionBar({ selection: selectionProp, actions, renderLabel, hideWhenEmpty, clearLabel, className, children, ...props }: ListMenuActionBarProps): import("react/jsx-runtime").JSX.Element | null;
54
+ //# sourceMappingURL=ListMenu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ListMenu.d.ts","sourceRoot":"","sources":["../../src/components/ListMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAc,SAAS,EAAE,MAAM,OAAO,CAAC;AAEnE,OAAO,EAAU,KAAK,WAAW,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,aAAa,GAAG,cAAc,CAAC,cAAc,CAAC,GAAG;IAC3D;;;OAGG;IACH,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,2FAA2F;IAC3F,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EACvB,SAAS,EACT,SAAS,EACT,cAAqB,EACrB,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,aAAa,2CAYf;AAED,MAAM,MAAM,oBAAoB,GAAG,cAAc,CAAC,cAAc,CAAC,CAAC;AAElE,wBAAgB,eAAe,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,oBAAoB,2CAE5E;AAED,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,cAAc,CAAC,CAAC;AAEjE,wBAAgB,cAAc,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,mBAAmB,2CAU1E;AAED,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,cAAc,CAAC,GAAG;IAC/D,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0DAA0D;IAC1D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,wBAAgB,YAAY,CAAC,EAC3B,MAAc,EACd,QAAgB,EAChB,eAAsC,EACtC,WAAkB,EAClB,OAAO,EACP,YAAoB,EACpB,SAAS,EACT,OAAO,EACP,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,iBAAiB,2CAwDnB;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,SAAS,CAAC;IACjB,qEAAqE;IACrE,OAAO,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC1C,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,cAAc,CAAC,cAAc,CAAC,GAAG;IACpE,qFAAqF;IACrF,SAAS,CAAC,EAAE,iBAAiB,CAAC;IAC9B,uDAAuD;IACvD,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,2CAA2C;IAC3C,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IAC3C,kEAAkE;IAClE,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,wDAAwD;IACxD,UAAU,CAAC,EAAE,SAAS,CAAC;CACxB,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,EAChC,SAAS,EAAE,aAAa,EACxB,OAAY,EACZ,WAAW,EACX,aAAoB,EACpB,UAAoB,EACpB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,sBAAsB,kDAyCxB"}