@flanksource/clicky-ui 0.3.47 → 0.3.49

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 (255) hide show
  1. package/README.md +49 -0
  2. package/dist/comments/CommentSidePanel.cjs +10 -4
  3. package/dist/comments/CommentSidePanel.cjs.map +1 -1
  4. package/dist/comments/CommentSidePanel.d.ts +2 -0
  5. package/dist/comments/CommentSidePanel.d.ts.map +1 -1
  6. package/dist/comments/CommentSidePanel.js +10 -4
  7. package/dist/comments/CommentSidePanel.js.map +1 -1
  8. package/dist/comments/CommentThread.cjs +2 -0
  9. package/dist/comments/CommentThread.cjs.map +1 -1
  10. package/dist/comments/CommentThread.d.ts +2 -1
  11. package/dist/comments/CommentThread.d.ts.map +1 -1
  12. package/dist/comments/CommentThread.js +2 -0
  13. package/dist/comments/CommentThread.js.map +1 -1
  14. package/dist/comments/CommentThreadList.cjs +3 -0
  15. package/dist/comments/CommentThreadList.cjs.map +1 -1
  16. package/dist/comments/CommentThreadList.d.ts +3 -1
  17. package/dist/comments/CommentThreadList.d.ts.map +1 -1
  18. package/dist/comments/CommentThreadList.js +3 -0
  19. package/dist/comments/CommentThreadList.js.map +1 -1
  20. package/dist/data/CodeLine.cjs +4 -2
  21. package/dist/data/CodeLine.cjs.map +1 -1
  22. package/dist/data/CodeLine.d.ts.map +1 -1
  23. package/dist/data/CodeLine.js +4 -2
  24. package/dist/data/CodeLine.js.map +1 -1
  25. package/dist/data/DataTable.cjs +18 -8
  26. package/dist/data/DataTable.cjs.map +1 -1
  27. package/dist/data/DataTable.d.ts +4 -0
  28. package/dist/data/DataTable.d.ts.map +1 -1
  29. package/dist/data/DataTable.js +18 -8
  30. package/dist/data/DataTable.js.map +1 -1
  31. package/dist/data/SymbolBadge.cjs +61 -0
  32. package/dist/data/SymbolBadge.cjs.map +1 -0
  33. package/dist/data/SymbolBadge.d.ts +21 -0
  34. package/dist/data/SymbolBadge.d.ts.map +1 -0
  35. package/dist/data/SymbolBadge.js +61 -0
  36. package/dist/data/SymbolBadge.js.map +1 -0
  37. package/dist/data/TaskManager.cjs +183 -159
  38. package/dist/data/TaskManager.cjs.map +1 -1
  39. package/dist/data/TaskManager.d.ts +3 -1
  40. package/dist/data/TaskManager.d.ts.map +1 -1
  41. package/dist/data/TaskManager.js +185 -161
  42. package/dist/data/TaskManager.js.map +1 -1
  43. package/dist/data/TaskManagerButton.cjs +1 -0
  44. package/dist/data/TaskManagerButton.cjs.map +1 -1
  45. package/dist/data/TaskManagerButton.js +1 -0
  46. package/dist/data/TaskManagerButton.js.map +1 -1
  47. package/dist/data/TaskProgress.cjs +13 -7
  48. package/dist/data/TaskProgress.cjs.map +1 -1
  49. package/dist/data/TaskProgress.d.ts +3 -1
  50. package/dist/data/TaskProgress.d.ts.map +1 -1
  51. package/dist/data/TaskProgress.js +13 -7
  52. package/dist/data/TaskProgress.js.map +1 -1
  53. package/dist/data/test-runner/TestDetailPanel.cjs +26 -12
  54. package/dist/data/test-runner/TestDetailPanel.cjs.map +1 -1
  55. package/dist/data/test-runner/TestDetailPanel.d.ts.map +1 -1
  56. package/dist/data/test-runner/TestDetailPanel.js +27 -13
  57. package/dist/data/test-runner/TestDetailPanel.js.map +1 -1
  58. package/dist/data/test-runner/TestRunner.cjs +6 -0
  59. package/dist/data/test-runner/TestRunner.cjs.map +1 -1
  60. package/dist/data/test-runner/TestRunner.d.ts +3 -1
  61. package/dist/data/test-runner/TestRunner.d.ts.map +1 -1
  62. package/dist/data/test-runner/TestRunner.js +6 -0
  63. package/dist/data/test-runner/TestRunner.js.map +1 -1
  64. package/dist/data/test-runner/TestTreeNode.cjs +2 -1
  65. package/dist/data/test-runner/TestTreeNode.cjs.map +1 -1
  66. package/dist/data/test-runner/TestTreeNode.d.ts.map +1 -1
  67. package/dist/data/test-runner/TestTreeNode.js +2 -1
  68. package/dist/data/test-runner/TestTreeNode.js.map +1 -1
  69. package/dist/data/test-runner/adapter.cjs +4 -0
  70. package/dist/data/test-runner/adapter.cjs.map +1 -1
  71. package/dist/data/test-runner/adapter.d.ts +9 -0
  72. package/dist/data/test-runner/adapter.d.ts.map +1 -1
  73. package/dist/data/test-runner/adapter.js +4 -0
  74. package/dist/data/test-runner/adapter.js.map +1 -1
  75. package/dist/data/test-runner/context.cjs.map +1 -1
  76. package/dist/data/test-runner/context.d.ts +2 -0
  77. package/dist/data/test-runner/context.d.ts.map +1 -1
  78. package/dist/data/test-runner/context.js.map +1 -1
  79. package/dist/data/test-runner/index.d.ts +2 -2
  80. package/dist/data/test-runner/index.d.ts.map +1 -1
  81. package/dist/data/test-runner/marks.cjs +42 -1
  82. package/dist/data/test-runner/marks.cjs.map +1 -1
  83. package/dist/data/test-runner/marks.d.ts +10 -0
  84. package/dist/data/test-runner/marks.d.ts.map +1 -1
  85. package/dist/data/test-runner/marks.js +42 -1
  86. package/dist/data/test-runner/marks.js.map +1 -1
  87. package/dist/data/traces/ProcessTraceTree.cjs +93 -0
  88. package/dist/data/traces/ProcessTraceTree.cjs.map +1 -0
  89. package/dist/data/traces/ProcessTraceTree.d.ts +24 -0
  90. package/dist/data/traces/ProcessTraceTree.d.ts.map +1 -0
  91. package/dist/data/traces/ProcessTraceTree.js +93 -0
  92. package/dist/data/traces/ProcessTraceTree.js.map +1 -0
  93. package/dist/data/traces/Sparkline.cjs +23 -0
  94. package/dist/data/traces/Sparkline.cjs.map +1 -0
  95. package/dist/data/traces/Sparkline.d.ts +13 -0
  96. package/dist/data/traces/Sparkline.d.ts.map +1 -0
  97. package/dist/data/traces/Sparkline.js +23 -0
  98. package/dist/data/traces/Sparkline.js.map +1 -0
  99. package/dist/data/traces/SqlTraceTable.cjs +104 -0
  100. package/dist/data/traces/SqlTraceTable.cjs.map +1 -0
  101. package/dist/data/traces/SqlTraceTable.d.ts +19 -0
  102. package/dist/data/traces/SqlTraceTable.d.ts.map +1 -0
  103. package/dist/data/traces/SqlTraceTable.js +104 -0
  104. package/dist/data/traces/SqlTraceTable.js.map +1 -0
  105. package/dist/data/traces/TerminalCastPlayer.cjs +162 -0
  106. package/dist/data/traces/TerminalCastPlayer.cjs.map +1 -0
  107. package/dist/data/traces/TerminalCastPlayer.d.ts +21 -0
  108. package/dist/data/traces/TerminalCastPlayer.d.ts.map +1 -0
  109. package/dist/data/traces/TerminalCastPlayer.js +162 -0
  110. package/dist/data/traces/TerminalCastPlayer.js.map +1 -0
  111. package/dist/data/traces/TraceHarView.cjs +15 -0
  112. package/dist/data/traces/TraceHarView.cjs.map +1 -0
  113. package/dist/data/traces/TraceHarView.d.ts +13 -0
  114. package/dist/data/traces/TraceHarView.d.ts.map +1 -0
  115. package/dist/data/traces/TraceHarView.js +15 -0
  116. package/dist/data/traces/TraceHarView.js.map +1 -0
  117. package/dist/data/traces/TraceView.cjs +54 -0
  118. package/dist/data/traces/TraceView.cjs.map +1 -0
  119. package/dist/data/traces/TraceView.d.ts +18 -0
  120. package/dist/data/traces/TraceView.d.ts.map +1 -0
  121. package/dist/data/traces/TraceView.js +54 -0
  122. package/dist/data/traces/TraceView.js.map +1 -0
  123. package/dist/data/traces/cast.cjs +81 -0
  124. package/dist/data/traces/cast.cjs.map +1 -0
  125. package/dist/data/traces/cast.d.ts +9 -0
  126. package/dist/data/traces/cast.d.ts.map +1 -0
  127. package/dist/data/traces/cast.js +81 -0
  128. package/dist/data/traces/cast.js.map +1 -0
  129. package/dist/data/traces/fixtures.d.ts +8 -0
  130. package/dist/data/traces/fixtures.d.ts.map +1 -0
  131. package/dist/data/traces/har-adapter.cjs +83 -0
  132. package/dist/data/traces/har-adapter.cjs.map +1 -0
  133. package/dist/data/traces/har-adapter.d.ts +13 -0
  134. package/dist/data/traces/har-adapter.d.ts.map +1 -0
  135. package/dist/data/traces/har-adapter.js +83 -0
  136. package/dist/data/traces/har-adapter.js.map +1 -0
  137. package/dist/data/traces/index.d.ts +12 -0
  138. package/dist/data/traces/index.d.ts.map +1 -0
  139. package/dist/data/traces/process-tree.cjs +53 -0
  140. package/dist/data/traces/process-tree.cjs.map +1 -0
  141. package/dist/data/traces/process-tree.d.ts +21 -0
  142. package/dist/data/traces/process-tree.d.ts.map +1 -0
  143. package/dist/data/traces/process-tree.js +53 -0
  144. package/dist/data/traces/process-tree.js.map +1 -0
  145. package/dist/data/traces/sql-trace.cjs +39 -0
  146. package/dist/data/traces/sql-trace.cjs.map +1 -0
  147. package/dist/data/traces/sql-trace.d.ts +31 -0
  148. package/dist/data/traces/sql-trace.d.ts.map +1 -0
  149. package/dist/data/traces/sql-trace.js +39 -0
  150. package/dist/data/traces/sql-trace.js.map +1 -0
  151. package/dist/data/traces/terminal-screen.cjs +270 -0
  152. package/dist/data/traces/terminal-screen.cjs.map +1 -0
  153. package/dist/data/traces/terminal-screen.d.ts +45 -0
  154. package/dist/data/traces/terminal-screen.d.ts.map +1 -0
  155. package/dist/data/traces/terminal-screen.js +270 -0
  156. package/dist/data/traces/terminal-screen.js.map +1 -0
  157. package/dist/data/traces/terminal-timeline.cjs +48 -0
  158. package/dist/data/traces/terminal-timeline.cjs.map +1 -0
  159. package/dist/data/traces/terminal-timeline.d.ts +26 -0
  160. package/dist/data/traces/terminal-timeline.d.ts.map +1 -0
  161. package/dist/data/traces/terminal-timeline.js +48 -0
  162. package/dist/data/traces/terminal-timeline.js.map +1 -0
  163. package/dist/data/traces/types.d.ts +139 -0
  164. package/dist/data/traces/types.d.ts.map +1 -0
  165. package/dist/data/verification/VerificationResults.cjs +4 -0
  166. package/dist/data/verification/VerificationResults.cjs.map +1 -1
  167. package/dist/data/verification/VerificationResults.d.ts +3 -1
  168. package/dist/data/verification/VerificationResults.d.ts.map +1 -1
  169. package/dist/data/verification/VerificationResults.js +4 -0
  170. package/dist/data/verification/VerificationResults.js.map +1 -1
  171. package/dist/data/verification/adapterViews.cjs +12 -3
  172. package/dist/data/verification/adapterViews.cjs.map +1 -1
  173. package/dist/data/verification/adapterViews.d.ts +4 -1
  174. package/dist/data/verification/adapterViews.d.ts.map +1 -1
  175. package/dist/data/verification/adapterViews.js +12 -3
  176. package/dist/data/verification/adapterViews.js.map +1 -1
  177. package/dist/data/verification/adapters.cjs +53 -10
  178. package/dist/data/verification/adapters.cjs.map +1 -1
  179. package/dist/data/verification/adapters.d.ts +7 -1
  180. package/dist/data/verification/adapters.d.ts.map +1 -1
  181. package/dist/data/verification/adapters.js +53 -10
  182. package/dist/data/verification/adapters.js.map +1 -1
  183. package/dist/data/verification/index.d.ts +4 -2
  184. package/dist/data/verification/index.d.ts.map +1 -1
  185. package/dist/data/verification/stepKind.cjs +217 -0
  186. package/dist/data/verification/stepKind.cjs.map +1 -0
  187. package/dist/data/verification/stepKind.d.ts +87 -0
  188. package/dist/data/verification/stepKind.d.ts.map +1 -0
  189. package/dist/data/verification/stepKind.js +217 -0
  190. package/dist/data/verification/stepKind.js.map +1 -0
  191. package/dist/data/verification/stepKindViews.cjs +111 -0
  192. package/dist/data/verification/stepKindViews.cjs.map +1 -0
  193. package/dist/data/verification/stepKindViews.d.ts +18 -0
  194. package/dist/data/verification/stepKindViews.d.ts.map +1 -0
  195. package/dist/data/verification/stepKindViews.js +111 -0
  196. package/dist/data/verification/stepKindViews.js.map +1 -0
  197. package/dist/data/verification/verifyReportTests.cjs +1 -0
  198. package/dist/data/verification/verifyReportTests.cjs.map +1 -1
  199. package/dist/data/verification/verifyReportTests.d.ts +2 -1
  200. package/dist/data/verification/verifyReportTests.d.ts.map +1 -1
  201. package/dist/data/verification/verifyReportTests.js +1 -0
  202. package/dist/data/verification/verifyReportTests.js.map +1 -1
  203. package/dist/data/version-info.cjs +3 -3
  204. package/dist/data/version-info.cjs.map +1 -1
  205. package/dist/data/version-info.js +3 -3
  206. package/dist/data/version-info.js.map +1 -1
  207. package/dist/data.cjs +36 -0
  208. package/dist/data.cjs.map +1 -1
  209. package/dist/data.d.ts +2 -0
  210. package/dist/data.d.ts.map +1 -1
  211. package/dist/data.js +38 -2
  212. package/dist/data.js.map +1 -1
  213. package/dist/index.cjs +38 -0
  214. package/dist/index.cjs.map +1 -1
  215. package/dist/index.js +40 -2
  216. package/dist/index.js.map +1 -1
  217. package/dist/rpc/FilterForm.cjs +9 -0
  218. package/dist/rpc/FilterForm.cjs.map +1 -1
  219. package/dist/rpc/FilterForm.d.ts +5 -1
  220. package/dist/rpc/FilterForm.d.ts.map +1 -1
  221. package/dist/rpc/FilterForm.js +9 -0
  222. package/dist/rpc/FilterForm.js.map +1 -1
  223. package/dist/rpc/OperationArgsForm.cjs +153 -0
  224. package/dist/rpc/OperationArgsForm.cjs.map +1 -0
  225. package/dist/rpc/OperationArgsForm.d.ts +16 -0
  226. package/dist/rpc/OperationArgsForm.d.ts.map +1 -0
  227. package/dist/rpc/OperationArgsForm.js +153 -0
  228. package/dist/rpc/OperationArgsForm.js.map +1 -0
  229. package/dist/rpc/OperationScheduleDialog.cjs +61 -17
  230. package/dist/rpc/OperationScheduleDialog.cjs.map +1 -1
  231. package/dist/rpc/OperationScheduleDialog.d.ts +3 -0
  232. package/dist/rpc/OperationScheduleDialog.d.ts.map +1 -1
  233. package/dist/rpc/OperationScheduleDialog.js +62 -18
  234. package/dist/rpc/OperationScheduleDialog.js.map +1 -1
  235. package/dist/rpc/OperationSchedules.cjs +6 -1
  236. package/dist/rpc/OperationSchedules.cjs.map +1 -1
  237. package/dist/rpc/OperationSchedules.d.ts.map +1 -1
  238. package/dist/rpc/OperationSchedules.js +6 -1
  239. package/dist/rpc/OperationSchedules.js.map +1 -1
  240. package/dist/rpc/operation-schedule-model.cjs +0 -7
  241. package/dist/rpc/operation-schedule-model.cjs.map +1 -1
  242. package/dist/rpc/operation-schedule-model.d.ts +0 -2
  243. package/dist/rpc/operation-schedule-model.d.ts.map +1 -1
  244. package/dist/rpc/operation-schedule-model.js +0 -7
  245. package/dist/rpc/operation-schedule-model.js.map +1 -1
  246. package/dist/rpc/operation-schedule-types.d.ts +6 -1
  247. package/dist/rpc/operation-schedule-types.d.ts.map +1 -1
  248. package/dist/rpc.cjs +2 -0
  249. package/dist/rpc.cjs.map +1 -1
  250. package/dist/rpc.d.ts +1 -0
  251. package/dist/rpc.d.ts.map +1 -1
  252. package/dist/rpc.js +2 -0
  253. package/dist/rpc.js.map +1 -1
  254. package/dist/styles.css +1 -1
  255. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"FilterForm.cjs","sources":["../../src/rpc/FilterForm.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, type FormEvent } from \"react\";\nimport { keepPreviousData, useQuery } from \"@tanstack/react-query\";\nimport { Button } from \"../components/button\";\nimport { FilterBar } from \"../components/FilterBar\";\nimport {\n packLookupParameterValues,\n parametersToFormConfig,\n pruneParameterValues,\n buildInitialParameterValues,\n titleCase,\n useDebouncedRecord,\n type ParameterValues,\n} from \"./formMetadata\";\nimport type { OpenAPIParameter } from \"./types\";\nimport { useOpenAPI, type OperationsApiClient } from \"./useOperations\";\nimport { ParameterGrid } from \"./FilterFormFields\";\n\nexport type FilterFormProps = {\n client: OperationsApiClient;\n path: string;\n method: string;\n parameters?: OpenAPIParameter[];\n initialValues?: ParameterValues | undefined;\n lockedValues?: ParameterValues | undefined;\n hideLocked?: boolean | undefined;\n enableLookup?: boolean | undefined;\n autoSubmit?: boolean | undefined;\n submitLabel?: string | undefined;\n submittingLabel?: string | undefined;\n emptyMessage?: string | undefined;\n isSubmitting?: boolean | undefined;\n className?: string | undefined;\n formId?: string | undefined;\n showSubmit?: boolean | undefined;\n excludedParameterNames?: string[] | undefined;\n /** Renders operation controls as form rows or the shared table FilterBar. */\n presentation?: \"form\" | \"filter-bar\" | undefined;\n onSubmit: (values: ParameterValues) => void | Promise<void>;\n};\n\nexport function FilterForm({\n client,\n path,\n method,\n parameters = [],\n initialValues = {},\n lockedValues = {},\n hideLocked = false,\n enableLookup = method.toUpperCase() === \"GET\",\n autoSubmit = false,\n submitLabel = \"Execute request\",\n submittingLabel = \"Executing…\",\n emptyMessage = \"This operation does not require input.\",\n isSubmitting = false,\n className,\n formId,\n showSubmit = true,\n excludedParameterNames = [],\n presentation = \"form\",\n onSubmit,\n}: FilterFormProps) {\n const resetKey = useMemo(\n () =>\n `${method}:${path}:${JSON.stringify(initialValues)}:${JSON.stringify(\n lockedValues\n )}`,\n [initialValues, lockedValues, method, path]\n );\n const visibleParameters = useMemo(\n () =>\n parameters.filter(\n (parameter) => !excludedParameterNames.includes(parameter.name)\n ),\n [excludedParameterNames, parameters]\n );\n const [values, setValues] = useState<ParameterValues>(() =>\n buildInitialParameterValues(parameters, method, lockedValues, initialValues)\n );\n const [error, setError] = useState(\"\");\n const debouncedValues = useDebouncedRecord(values, 250);\n const lastAutoSubmitted = useRef<string | null>(null);\n const lookupParameters = useMemo(\n () => packLookupParameterValues(debouncedValues, parameters),\n [debouncedValues, parameters]\n );\n\n const lookupQuery = useQuery({\n queryKey: [\"filter-form-lookup\", method, path, lookupParameters],\n queryFn: async () =>\n (await client.lookupFilters?.(path, method, lookupParameters, {\n Accept: \"application/json+clicky\",\n })) ?? { filters: {} },\n enabled:\n enableLookup &&\n !!client.lookupFilters &&\n parameters.some((param) => param.in === \"query\"),\n placeholderData: keepPreviousData,\n staleTime: 30_000,\n retry: 0,\n });\n\n // Shapes come from the spec so a control keeps its identity while the lookup\n // that fills it is in flight; see parametersToFormConfig.\n const { data: spec } = useOpenAPI(client);\n const filterShapes = spec?.components?.[\"x-clicky-filters\"];\n\n const formConfig = useMemo(\n () =>\n parametersToFormConfig(visibleParameters, values, setValues, {\n lookup: lookupQuery.data,\n components: filterShapes,\n lockedValues,\n hideLocked,\n }),\n [\n filterShapes,\n hideLocked,\n lockedValues,\n lookupQuery.data,\n visibleParameters,\n values,\n ]\n );\n\n const hasFields =\n formConfig.filters.length > 0 ||\n formConfig.search != null ||\n formConfig.timeRange != null;\n\n async function handleSubmit(event?: FormEvent) {\n event?.preventDefault();\n\n const missingRequired = parameters.filter((param) => {\n if (!param.required) return false;\n const value = lockedValues[param.name] ?? values[param.name] ?? \"\";\n return value.trim() === \"\";\n });\n\n if (missingRequired.length > 0) {\n setError(\n `Missing required fields: ${missingRequired\n .map((param) => titleCase(param.name))\n .join(\", \")}`\n );\n return;\n }\n\n setError(\"\");\n await onSubmit(pruneParameterValues(values));\n }\n\n useEffect(() => {\n setValues(\n buildInitialParameterValues(\n parameters,\n method,\n lockedValues,\n initialValues\n )\n );\n setError(\"\");\n lastAutoSubmitted.current = null;\n }, [resetKey]);\n\n useEffect(() => {\n if (!autoSubmit) {\n return;\n }\n\n const missingRequired = parameters.filter((param) => {\n if (!param.required) return false;\n const value =\n lockedValues[param.name] ?? debouncedValues[param.name] ?? \"\";\n return value.trim() === \"\";\n });\n if (missingRequired.length > 0) {\n return;\n }\n\n const submittedValues = pruneParameterValues(debouncedValues);\n const signature = JSON.stringify(submittedValues);\n if (lastAutoSubmitted.current === signature) {\n return;\n }\n\n lastAutoSubmitted.current = signature;\n setError(\"\");\n void onSubmit(submittedValues);\n }, [autoSubmit, debouncedValues, lockedValues, onSubmit, parameters]);\n\n return (\n <form id={formId} className=\"space-y-3\" onSubmit={handleSubmit}>\n {hasFields ? (\n presentation === \"filter-bar\" ? (\n <FilterBar\n autoSubmit={autoSubmit}\n filters={formConfig.filters}\n overflowMode=\"wrap\"\n {...(formConfig.search ? { search: formConfig.search } : {})}\n {...(formConfig.timeRange\n ? { timeRange: formConfig.timeRange }\n : {})}\n {...(!autoSubmit\n ? {\n onApply: () => void handleSubmit(),\n applyLabel: submitLabel,\n isPending: isSubmitting,\n }\n : {})}\n {...(className ? { className } : {})}\n />\n ) : (\n <ParameterGrid\n autoSubmit={autoSubmit}\n filters={formConfig.filters}\n isSubmitting={isSubmitting}\n submitLabel={submitLabel}\n submittingLabel={submittingLabel}\n showSubmit={showSubmit}\n {...(formConfig.timeRange\n ? { timeRange: formConfig.timeRange }\n : {})}\n {...(className ? { className } : {})}\n />\n )\n ) : (\n <div className=\"flex items-center justify-between gap-4\">\n <p className=\"text-sm text-muted-foreground\">{emptyMessage}</p>\n {showSubmit ? (\n <Button\n type=\"button\"\n onClick={handleSubmit}\n disabled={isSubmitting}\n >\n {isSubmitting ? submittingLabel : submitLabel}\n </Button>\n ) : null}\n </div>\n )}\n\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/5 p-3 text-sm text-destructive\">\n {error}\n </div>\n )}\n </form>\n );\n}\n"],"names":["useMemo","useState","buildInitialParameterValues","useDebouncedRecord","useRef","packLookupParameterValues","useQuery","_a","keepPreviousData","useOpenAPI","parametersToFormConfig","titleCase","pruneParameterValues","useEffect","jsx","FilterBar","ParameterGrid","jsxs","Button"],"mappings":"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"}
1
+ {"version":3,"file":"FilterForm.cjs","sources":["../../src/rpc/FilterForm.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, type FormEvent } from \"react\";\nimport { keepPreviousData, useQuery } from \"@tanstack/react-query\";\nimport { Button } from \"../components/button\";\nimport { FilterBar } from \"../components/FilterBar\";\nimport {\n packLookupParameterValues,\n parametersToFormConfig,\n pruneParameterValues,\n buildInitialParameterValues,\n titleCase,\n useDebouncedRecord,\n type ParameterValues,\n} from \"./formMetadata\";\nimport type { OpenAPIParameter } from \"./types\";\nimport { useOpenAPI, type OperationsApiClient } from \"./useOperations\";\nimport { ParameterGrid } from \"./FilterFormFields\";\n\nexport type FilterFormProps = {\n client: OperationsApiClient;\n path: string;\n method: string;\n parameters?: OpenAPIParameter[];\n initialValues?: ParameterValues | undefined;\n lockedValues?: ParameterValues | undefined;\n hideLocked?: boolean | undefined;\n enableLookup?: boolean | undefined;\n autoSubmit?: boolean | undefined;\n submitLabel?: string | undefined;\n submittingLabel?: string | undefined;\n emptyMessage?: string | undefined;\n isSubmitting?: boolean | undefined;\n className?: string | undefined;\n formId?: string | undefined;\n showSubmit?: boolean | undefined;\n excludedParameterNames?: string[] | undefined;\n /** Renders operation controls as form rows or the shared table FilterBar. */\n presentation?: \"form\" | \"filter-bar\" | undefined;\n /** Called with the current values whenever they change, starting with the\n * initial values, so the form can act as a controlled field set inside a\n * host form (pair with showSubmit={false}). */\n onChange?: ((values: ParameterValues) => void) | undefined;\n onSubmit: (values: ParameterValues) => void | Promise<void>;\n};\n\nexport function FilterForm({\n client,\n path,\n method,\n parameters = [],\n initialValues = {},\n lockedValues = {},\n hideLocked = false,\n enableLookup = method.toUpperCase() === \"GET\",\n autoSubmit = false,\n submitLabel = \"Execute request\",\n submittingLabel = \"Executing…\",\n emptyMessage = \"This operation does not require input.\",\n isSubmitting = false,\n className,\n formId,\n showSubmit = true,\n excludedParameterNames = [],\n presentation = \"form\",\n onChange,\n onSubmit,\n}: FilterFormProps) {\n const resetKey = useMemo(\n () =>\n `${method}:${path}:${JSON.stringify(initialValues)}:${JSON.stringify(\n lockedValues\n )}`,\n [initialValues, lockedValues, method, path]\n );\n const visibleParameters = useMemo(\n () =>\n parameters.filter(\n (parameter) => !excludedParameterNames.includes(parameter.name)\n ),\n [excludedParameterNames, parameters]\n );\n const [values, setValues] = useState<ParameterValues>(() =>\n buildInitialParameterValues(parameters, method, lockedValues, initialValues)\n );\n const [error, setError] = useState(\"\");\n const debouncedValues = useDebouncedRecord(values, 250);\n const lastAutoSubmitted = useRef<string | null>(null);\n // The latest callback is read from a ref so a host passing an inline\n // function is notified per value change, not per render.\n const onChangeRef = useRef(onChange);\n useEffect(() => {\n onChangeRef.current = onChange;\n });\n useEffect(() => {\n onChangeRef.current?.(values);\n }, [values]);\n const lookupParameters = useMemo(\n () => packLookupParameterValues(debouncedValues, parameters),\n [debouncedValues, parameters]\n );\n\n const lookupQuery = useQuery({\n queryKey: [\"filter-form-lookup\", method, path, lookupParameters],\n queryFn: async () =>\n (await client.lookupFilters?.(path, method, lookupParameters, {\n Accept: \"application/json+clicky\",\n })) ?? { filters: {} },\n enabled:\n enableLookup &&\n !!client.lookupFilters &&\n parameters.some((param) => param.in === \"query\"),\n placeholderData: keepPreviousData,\n staleTime: 30_000,\n retry: 0,\n });\n\n // Shapes come from the spec so a control keeps its identity while the lookup\n // that fills it is in flight; see parametersToFormConfig.\n const { data: spec } = useOpenAPI(client);\n const filterShapes = spec?.components?.[\"x-clicky-filters\"];\n\n const formConfig = useMemo(\n () =>\n parametersToFormConfig(visibleParameters, values, setValues, {\n lookup: lookupQuery.data,\n components: filterShapes,\n lockedValues,\n hideLocked,\n }),\n [\n filterShapes,\n hideLocked,\n lockedValues,\n lookupQuery.data,\n visibleParameters,\n values,\n ]\n );\n\n const hasFields =\n formConfig.filters.length > 0 ||\n formConfig.search != null ||\n formConfig.timeRange != null;\n\n async function handleSubmit(event?: FormEvent) {\n event?.preventDefault();\n\n const missingRequired = parameters.filter((param) => {\n if (!param.required) return false;\n const value = lockedValues[param.name] ?? values[param.name] ?? \"\";\n return value.trim() === \"\";\n });\n\n if (missingRequired.length > 0) {\n setError(\n `Missing required fields: ${missingRequired\n .map((param) => titleCase(param.name))\n .join(\", \")}`\n );\n return;\n }\n\n setError(\"\");\n await onSubmit(pruneParameterValues(values));\n }\n\n useEffect(() => {\n setValues(\n buildInitialParameterValues(\n parameters,\n method,\n lockedValues,\n initialValues\n )\n );\n setError(\"\");\n lastAutoSubmitted.current = null;\n }, [resetKey]);\n\n useEffect(() => {\n if (!autoSubmit) {\n return;\n }\n\n const missingRequired = parameters.filter((param) => {\n if (!param.required) return false;\n const value =\n lockedValues[param.name] ?? debouncedValues[param.name] ?? \"\";\n return value.trim() === \"\";\n });\n if (missingRequired.length > 0) {\n return;\n }\n\n const submittedValues = pruneParameterValues(debouncedValues);\n const signature = JSON.stringify(submittedValues);\n if (lastAutoSubmitted.current === signature) {\n return;\n }\n\n lastAutoSubmitted.current = signature;\n setError(\"\");\n void onSubmit(submittedValues);\n }, [autoSubmit, debouncedValues, lockedValues, onSubmit, parameters]);\n\n return (\n <form id={formId} className=\"space-y-3\" onSubmit={handleSubmit}>\n {hasFields ? (\n presentation === \"filter-bar\" ? (\n <FilterBar\n autoSubmit={autoSubmit}\n filters={formConfig.filters}\n overflowMode=\"wrap\"\n {...(formConfig.search ? { search: formConfig.search } : {})}\n {...(formConfig.timeRange\n ? { timeRange: formConfig.timeRange }\n : {})}\n {...(!autoSubmit\n ? {\n onApply: () => void handleSubmit(),\n applyLabel: submitLabel,\n isPending: isSubmitting,\n }\n : {})}\n {...(className ? { className } : {})}\n />\n ) : (\n <ParameterGrid\n autoSubmit={autoSubmit}\n filters={formConfig.filters}\n isSubmitting={isSubmitting}\n submitLabel={submitLabel}\n submittingLabel={submittingLabel}\n showSubmit={showSubmit}\n {...(formConfig.timeRange\n ? { timeRange: formConfig.timeRange }\n : {})}\n {...(className ? { className } : {})}\n />\n )\n ) : (\n <div className=\"flex items-center justify-between gap-4\">\n <p className=\"text-sm text-muted-foreground\">{emptyMessage}</p>\n {showSubmit ? (\n <Button\n type=\"button\"\n onClick={handleSubmit}\n disabled={isSubmitting}\n >\n {isSubmitting ? submittingLabel : submitLabel}\n </Button>\n ) : null}\n </div>\n )}\n\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/5 p-3 text-sm text-destructive\">\n {error}\n </div>\n )}\n </form>\n );\n}\n"],"names":["useMemo","useState","buildInitialParameterValues","useDebouncedRecord","useRef","useEffect","_a","packLookupParameterValues","useQuery","keepPreviousData","useOpenAPI","parametersToFormConfig","titleCase","pruneParameterValues","jsx","FilterBar","ParameterGrid","jsxs","Button"],"mappings":"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"}
@@ -21,7 +21,11 @@ export type FilterFormProps = {
21
21
  excludedParameterNames?: string[] | undefined;
22
22
  /** Renders operation controls as form rows or the shared table FilterBar. */
23
23
  presentation?: "form" | "filter-bar" | undefined;
24
+ /** Called with the current values whenever they change, starting with the
25
+ * initial values, so the form can act as a controlled field set inside a
26
+ * host form (pair with showSubmit={false}). */
27
+ onChange?: ((values: ParameterValues) => void) | undefined;
24
28
  onSubmit: (values: ParameterValues) => void | Promise<void>;
25
29
  };
26
- export declare function FilterForm({ client, path, method, parameters, initialValues, lockedValues, hideLocked, enableLookup, autoSubmit, submitLabel, submittingLabel, emptyMessage, isSubmitting, className, formId, showSubmit, excludedParameterNames, presentation, onSubmit, }: FilterFormProps): import("react").JSX.Element;
30
+ export declare function FilterForm({ client, path, method, parameters, initialValues, lockedValues, hideLocked, enableLookup, autoSubmit, submitLabel, submittingLabel, emptyMessage, isSubmitting, className, formId, showSubmit, excludedParameterNames, presentation, onChange, onSubmit, }: FilterFormProps): import("react").JSX.Element;
27
31
  //# sourceMappingURL=FilterForm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"FilterForm.d.ts","sourceRoot":"","sources":["../../src/rpc/FilterForm.tsx"],"names":[],"mappings":"AAIA,OAAO,EAOL,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAc,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAGvE,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAChC,aAAa,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IAC5C,YAAY,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IAC3C,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,sBAAsB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC9C,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;IACjD,QAAQ,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D,CAAC;AAEF,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,IAAI,EACJ,MAAM,EACN,UAAe,EACf,aAAkB,EAClB,YAAiB,EACjB,UAAkB,EAClB,YAA6C,EAC7C,UAAkB,EAClB,WAA+B,EAC/B,eAA8B,EAC9B,YAAuD,EACvD,YAAoB,EACpB,SAAS,EACT,MAAM,EACN,UAAiB,EACjB,sBAA2B,EAC3B,YAAqB,EACrB,QAAQ,GACT,EAAE,eAAe,+BA2LjB"}
1
+ {"version":3,"file":"FilterForm.d.ts","sourceRoot":"","sources":["../../src/rpc/FilterForm.tsx"],"names":[],"mappings":"AAIA,OAAO,EAOL,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAc,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAGvE,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAChC,aAAa,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IAC5C,YAAY,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IAC3C,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,sBAAsB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC9C,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;IACjD;;oDAEgD;IAChD,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC3D,QAAQ,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D,CAAC;AAEF,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,IAAI,EACJ,MAAM,EACN,UAAe,EACf,aAAkB,EAClB,YAAiB,EACjB,UAAkB,EAClB,YAA6C,EAC7C,UAAkB,EAClB,WAA+B,EAC/B,eAA8B,EAC9B,YAAuD,EACvD,YAAoB,EACpB,SAAS,EACT,MAAM,EACN,UAAiB,EACjB,sBAA2B,EAC3B,YAAqB,EACrB,QAAQ,EACR,QAAQ,GACT,EAAE,eAAe,+BAoMjB"}
@@ -25,6 +25,7 @@ function FilterForm({
25
25
  showSubmit = true,
26
26
  excludedParameterNames = [],
27
27
  presentation = "form",
28
+ onChange,
28
29
  onSubmit
29
30
  }) {
30
31
  var _a;
@@ -46,6 +47,14 @@ function FilterForm({
46
47
  const [error, setError] = useState("");
47
48
  const debouncedValues = useDebouncedRecord(values, 250);
48
49
  const lastAutoSubmitted = useRef(null);
50
+ const onChangeRef = useRef(onChange);
51
+ useEffect(() => {
52
+ onChangeRef.current = onChange;
53
+ });
54
+ useEffect(() => {
55
+ var _a2;
56
+ (_a2 = onChangeRef.current) == null ? void 0 : _a2.call(onChangeRef, values);
57
+ }, [values]);
49
58
  const lookupParameters = useMemo(
50
59
  () => packLookupParameterValues(debouncedValues, parameters),
51
60
  [debouncedValues, parameters]
@@ -1 +1 @@
1
- {"version":3,"file":"FilterForm.js","sources":["../../src/rpc/FilterForm.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, type FormEvent } from \"react\";\nimport { keepPreviousData, useQuery } from \"@tanstack/react-query\";\nimport { Button } from \"../components/button\";\nimport { FilterBar } from \"../components/FilterBar\";\nimport {\n packLookupParameterValues,\n parametersToFormConfig,\n pruneParameterValues,\n buildInitialParameterValues,\n titleCase,\n useDebouncedRecord,\n type ParameterValues,\n} from \"./formMetadata\";\nimport type { OpenAPIParameter } from \"./types\";\nimport { useOpenAPI, type OperationsApiClient } from \"./useOperations\";\nimport { ParameterGrid } from \"./FilterFormFields\";\n\nexport type FilterFormProps = {\n client: OperationsApiClient;\n path: string;\n method: string;\n parameters?: OpenAPIParameter[];\n initialValues?: ParameterValues | undefined;\n lockedValues?: ParameterValues | undefined;\n hideLocked?: boolean | undefined;\n enableLookup?: boolean | undefined;\n autoSubmit?: boolean | undefined;\n submitLabel?: string | undefined;\n submittingLabel?: string | undefined;\n emptyMessage?: string | undefined;\n isSubmitting?: boolean | undefined;\n className?: string | undefined;\n formId?: string | undefined;\n showSubmit?: boolean | undefined;\n excludedParameterNames?: string[] | undefined;\n /** Renders operation controls as form rows or the shared table FilterBar. */\n presentation?: \"form\" | \"filter-bar\" | undefined;\n onSubmit: (values: ParameterValues) => void | Promise<void>;\n};\n\nexport function FilterForm({\n client,\n path,\n method,\n parameters = [],\n initialValues = {},\n lockedValues = {},\n hideLocked = false,\n enableLookup = method.toUpperCase() === \"GET\",\n autoSubmit = false,\n submitLabel = \"Execute request\",\n submittingLabel = \"Executing…\",\n emptyMessage = \"This operation does not require input.\",\n isSubmitting = false,\n className,\n formId,\n showSubmit = true,\n excludedParameterNames = [],\n presentation = \"form\",\n onSubmit,\n}: FilterFormProps) {\n const resetKey = useMemo(\n () =>\n `${method}:${path}:${JSON.stringify(initialValues)}:${JSON.stringify(\n lockedValues\n )}`,\n [initialValues, lockedValues, method, path]\n );\n const visibleParameters = useMemo(\n () =>\n parameters.filter(\n (parameter) => !excludedParameterNames.includes(parameter.name)\n ),\n [excludedParameterNames, parameters]\n );\n const [values, setValues] = useState<ParameterValues>(() =>\n buildInitialParameterValues(parameters, method, lockedValues, initialValues)\n );\n const [error, setError] = useState(\"\");\n const debouncedValues = useDebouncedRecord(values, 250);\n const lastAutoSubmitted = useRef<string | null>(null);\n const lookupParameters = useMemo(\n () => packLookupParameterValues(debouncedValues, parameters),\n [debouncedValues, parameters]\n );\n\n const lookupQuery = useQuery({\n queryKey: [\"filter-form-lookup\", method, path, lookupParameters],\n queryFn: async () =>\n (await client.lookupFilters?.(path, method, lookupParameters, {\n Accept: \"application/json+clicky\",\n })) ?? { filters: {} },\n enabled:\n enableLookup &&\n !!client.lookupFilters &&\n parameters.some((param) => param.in === \"query\"),\n placeholderData: keepPreviousData,\n staleTime: 30_000,\n retry: 0,\n });\n\n // Shapes come from the spec so a control keeps its identity while the lookup\n // that fills it is in flight; see parametersToFormConfig.\n const { data: spec } = useOpenAPI(client);\n const filterShapes = spec?.components?.[\"x-clicky-filters\"];\n\n const formConfig = useMemo(\n () =>\n parametersToFormConfig(visibleParameters, values, setValues, {\n lookup: lookupQuery.data,\n components: filterShapes,\n lockedValues,\n hideLocked,\n }),\n [\n filterShapes,\n hideLocked,\n lockedValues,\n lookupQuery.data,\n visibleParameters,\n values,\n ]\n );\n\n const hasFields =\n formConfig.filters.length > 0 ||\n formConfig.search != null ||\n formConfig.timeRange != null;\n\n async function handleSubmit(event?: FormEvent) {\n event?.preventDefault();\n\n const missingRequired = parameters.filter((param) => {\n if (!param.required) return false;\n const value = lockedValues[param.name] ?? values[param.name] ?? \"\";\n return value.trim() === \"\";\n });\n\n if (missingRequired.length > 0) {\n setError(\n `Missing required fields: ${missingRequired\n .map((param) => titleCase(param.name))\n .join(\", \")}`\n );\n return;\n }\n\n setError(\"\");\n await onSubmit(pruneParameterValues(values));\n }\n\n useEffect(() => {\n setValues(\n buildInitialParameterValues(\n parameters,\n method,\n lockedValues,\n initialValues\n )\n );\n setError(\"\");\n lastAutoSubmitted.current = null;\n }, [resetKey]);\n\n useEffect(() => {\n if (!autoSubmit) {\n return;\n }\n\n const missingRequired = parameters.filter((param) => {\n if (!param.required) return false;\n const value =\n lockedValues[param.name] ?? debouncedValues[param.name] ?? \"\";\n return value.trim() === \"\";\n });\n if (missingRequired.length > 0) {\n return;\n }\n\n const submittedValues = pruneParameterValues(debouncedValues);\n const signature = JSON.stringify(submittedValues);\n if (lastAutoSubmitted.current === signature) {\n return;\n }\n\n lastAutoSubmitted.current = signature;\n setError(\"\");\n void onSubmit(submittedValues);\n }, [autoSubmit, debouncedValues, lockedValues, onSubmit, parameters]);\n\n return (\n <form id={formId} className=\"space-y-3\" onSubmit={handleSubmit}>\n {hasFields ? (\n presentation === \"filter-bar\" ? (\n <FilterBar\n autoSubmit={autoSubmit}\n filters={formConfig.filters}\n overflowMode=\"wrap\"\n {...(formConfig.search ? { search: formConfig.search } : {})}\n {...(formConfig.timeRange\n ? { timeRange: formConfig.timeRange }\n : {})}\n {...(!autoSubmit\n ? {\n onApply: () => void handleSubmit(),\n applyLabel: submitLabel,\n isPending: isSubmitting,\n }\n : {})}\n {...(className ? { className } : {})}\n />\n ) : (\n <ParameterGrid\n autoSubmit={autoSubmit}\n filters={formConfig.filters}\n isSubmitting={isSubmitting}\n submitLabel={submitLabel}\n submittingLabel={submittingLabel}\n showSubmit={showSubmit}\n {...(formConfig.timeRange\n ? { timeRange: formConfig.timeRange }\n : {})}\n {...(className ? { className } : {})}\n />\n )\n ) : (\n <div className=\"flex items-center justify-between gap-4\">\n <p className=\"text-sm text-muted-foreground\">{emptyMessage}</p>\n {showSubmit ? (\n <Button\n type=\"button\"\n onClick={handleSubmit}\n disabled={isSubmitting}\n >\n {isSubmitting ? submittingLabel : submitLabel}\n </Button>\n ) : null}\n </div>\n )}\n\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/5 p-3 text-sm text-destructive\">\n {error}\n </div>\n )}\n </form>\n );\n}\n"],"names":["_a"],"mappings":"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"}
1
+ {"version":3,"file":"FilterForm.js","sources":["../../src/rpc/FilterForm.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState, type FormEvent } from \"react\";\nimport { keepPreviousData, useQuery } from \"@tanstack/react-query\";\nimport { Button } from \"../components/button\";\nimport { FilterBar } from \"../components/FilterBar\";\nimport {\n packLookupParameterValues,\n parametersToFormConfig,\n pruneParameterValues,\n buildInitialParameterValues,\n titleCase,\n useDebouncedRecord,\n type ParameterValues,\n} from \"./formMetadata\";\nimport type { OpenAPIParameter } from \"./types\";\nimport { useOpenAPI, type OperationsApiClient } from \"./useOperations\";\nimport { ParameterGrid } from \"./FilterFormFields\";\n\nexport type FilterFormProps = {\n client: OperationsApiClient;\n path: string;\n method: string;\n parameters?: OpenAPIParameter[];\n initialValues?: ParameterValues | undefined;\n lockedValues?: ParameterValues | undefined;\n hideLocked?: boolean | undefined;\n enableLookup?: boolean | undefined;\n autoSubmit?: boolean | undefined;\n submitLabel?: string | undefined;\n submittingLabel?: string | undefined;\n emptyMessage?: string | undefined;\n isSubmitting?: boolean | undefined;\n className?: string | undefined;\n formId?: string | undefined;\n showSubmit?: boolean | undefined;\n excludedParameterNames?: string[] | undefined;\n /** Renders operation controls as form rows or the shared table FilterBar. */\n presentation?: \"form\" | \"filter-bar\" | undefined;\n /** Called with the current values whenever they change, starting with the\n * initial values, so the form can act as a controlled field set inside a\n * host form (pair with showSubmit={false}). */\n onChange?: ((values: ParameterValues) => void) | undefined;\n onSubmit: (values: ParameterValues) => void | Promise<void>;\n};\n\nexport function FilterForm({\n client,\n path,\n method,\n parameters = [],\n initialValues = {},\n lockedValues = {},\n hideLocked = false,\n enableLookup = method.toUpperCase() === \"GET\",\n autoSubmit = false,\n submitLabel = \"Execute request\",\n submittingLabel = \"Executing…\",\n emptyMessage = \"This operation does not require input.\",\n isSubmitting = false,\n className,\n formId,\n showSubmit = true,\n excludedParameterNames = [],\n presentation = \"form\",\n onChange,\n onSubmit,\n}: FilterFormProps) {\n const resetKey = useMemo(\n () =>\n `${method}:${path}:${JSON.stringify(initialValues)}:${JSON.stringify(\n lockedValues\n )}`,\n [initialValues, lockedValues, method, path]\n );\n const visibleParameters = useMemo(\n () =>\n parameters.filter(\n (parameter) => !excludedParameterNames.includes(parameter.name)\n ),\n [excludedParameterNames, parameters]\n );\n const [values, setValues] = useState<ParameterValues>(() =>\n buildInitialParameterValues(parameters, method, lockedValues, initialValues)\n );\n const [error, setError] = useState(\"\");\n const debouncedValues = useDebouncedRecord(values, 250);\n const lastAutoSubmitted = useRef<string | null>(null);\n // The latest callback is read from a ref so a host passing an inline\n // function is notified per value change, not per render.\n const onChangeRef = useRef(onChange);\n useEffect(() => {\n onChangeRef.current = onChange;\n });\n useEffect(() => {\n onChangeRef.current?.(values);\n }, [values]);\n const lookupParameters = useMemo(\n () => packLookupParameterValues(debouncedValues, parameters),\n [debouncedValues, parameters]\n );\n\n const lookupQuery = useQuery({\n queryKey: [\"filter-form-lookup\", method, path, lookupParameters],\n queryFn: async () =>\n (await client.lookupFilters?.(path, method, lookupParameters, {\n Accept: \"application/json+clicky\",\n })) ?? { filters: {} },\n enabled:\n enableLookup &&\n !!client.lookupFilters &&\n parameters.some((param) => param.in === \"query\"),\n placeholderData: keepPreviousData,\n staleTime: 30_000,\n retry: 0,\n });\n\n // Shapes come from the spec so a control keeps its identity while the lookup\n // that fills it is in flight; see parametersToFormConfig.\n const { data: spec } = useOpenAPI(client);\n const filterShapes = spec?.components?.[\"x-clicky-filters\"];\n\n const formConfig = useMemo(\n () =>\n parametersToFormConfig(visibleParameters, values, setValues, {\n lookup: lookupQuery.data,\n components: filterShapes,\n lockedValues,\n hideLocked,\n }),\n [\n filterShapes,\n hideLocked,\n lockedValues,\n lookupQuery.data,\n visibleParameters,\n values,\n ]\n );\n\n const hasFields =\n formConfig.filters.length > 0 ||\n formConfig.search != null ||\n formConfig.timeRange != null;\n\n async function handleSubmit(event?: FormEvent) {\n event?.preventDefault();\n\n const missingRequired = parameters.filter((param) => {\n if (!param.required) return false;\n const value = lockedValues[param.name] ?? values[param.name] ?? \"\";\n return value.trim() === \"\";\n });\n\n if (missingRequired.length > 0) {\n setError(\n `Missing required fields: ${missingRequired\n .map((param) => titleCase(param.name))\n .join(\", \")}`\n );\n return;\n }\n\n setError(\"\");\n await onSubmit(pruneParameterValues(values));\n }\n\n useEffect(() => {\n setValues(\n buildInitialParameterValues(\n parameters,\n method,\n lockedValues,\n initialValues\n )\n );\n setError(\"\");\n lastAutoSubmitted.current = null;\n }, [resetKey]);\n\n useEffect(() => {\n if (!autoSubmit) {\n return;\n }\n\n const missingRequired = parameters.filter((param) => {\n if (!param.required) return false;\n const value =\n lockedValues[param.name] ?? debouncedValues[param.name] ?? \"\";\n return value.trim() === \"\";\n });\n if (missingRequired.length > 0) {\n return;\n }\n\n const submittedValues = pruneParameterValues(debouncedValues);\n const signature = JSON.stringify(submittedValues);\n if (lastAutoSubmitted.current === signature) {\n return;\n }\n\n lastAutoSubmitted.current = signature;\n setError(\"\");\n void onSubmit(submittedValues);\n }, [autoSubmit, debouncedValues, lockedValues, onSubmit, parameters]);\n\n return (\n <form id={formId} className=\"space-y-3\" onSubmit={handleSubmit}>\n {hasFields ? (\n presentation === \"filter-bar\" ? (\n <FilterBar\n autoSubmit={autoSubmit}\n filters={formConfig.filters}\n overflowMode=\"wrap\"\n {...(formConfig.search ? { search: formConfig.search } : {})}\n {...(formConfig.timeRange\n ? { timeRange: formConfig.timeRange }\n : {})}\n {...(!autoSubmit\n ? {\n onApply: () => void handleSubmit(),\n applyLabel: submitLabel,\n isPending: isSubmitting,\n }\n : {})}\n {...(className ? { className } : {})}\n />\n ) : (\n <ParameterGrid\n autoSubmit={autoSubmit}\n filters={formConfig.filters}\n isSubmitting={isSubmitting}\n submitLabel={submitLabel}\n submittingLabel={submittingLabel}\n showSubmit={showSubmit}\n {...(formConfig.timeRange\n ? { timeRange: formConfig.timeRange }\n : {})}\n {...(className ? { className } : {})}\n />\n )\n ) : (\n <div className=\"flex items-center justify-between gap-4\">\n <p className=\"text-sm text-muted-foreground\">{emptyMessage}</p>\n {showSubmit ? (\n <Button\n type=\"button\"\n onClick={handleSubmit}\n disabled={isSubmitting}\n >\n {isSubmitting ? submittingLabel : submitLabel}\n </Button>\n ) : null}\n </div>\n )}\n\n {error && (\n <div className=\"rounded-md border border-destructive/40 bg-destructive/5 p-3 text-sm text-destructive\">\n {error}\n </div>\n )}\n </form>\n );\n}\n"],"names":["_a"],"mappings":"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"}
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const react = require("react");
5
+ const FilterForm = require("./FilterForm.cjs");
6
+ const clickyMetadata = require("./clickyMetadata.cjs");
7
+ const formMetadata = require("./formMetadata.cjs");
8
+ const NO_PARAMETERS = [];
9
+ function OperationArgsForm({
10
+ client,
11
+ operation,
12
+ args,
13
+ onChange,
14
+ onValidityChange,
15
+ className
16
+ }) {
17
+ var _a;
18
+ const parameters = operation.operation.parameters ?? NO_PARAMETERS;
19
+ const [initial] = react.useState(
20
+ () => storedArgsToValues(args, parameters, operation.method)
21
+ );
22
+ const [values, setValues] = react.useState(initial.values);
23
+ const problems = react.useMemo(
24
+ () => parameterProblems(parameters, values),
25
+ [parameters, values]
26
+ );
27
+ const valid = problems.length === 0;
28
+ react.useEffect(() => {
29
+ onValidityChange == null ? void 0 : onValidityChange(valid);
30
+ }, [onValidityChange, valid]);
31
+ const handleChange = (next) => {
32
+ setValues(next);
33
+ onChange(
34
+ valuesToArgs({
35
+ parameters,
36
+ method: operation.method,
37
+ values: next,
38
+ stored: initial.names
39
+ })
40
+ );
41
+ };
42
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
43
+ /* @__PURE__ */ jsxRuntime.jsx(
44
+ FilterForm.FilterForm,
45
+ {
46
+ client,
47
+ path: operation.path,
48
+ method: operation.method,
49
+ parameters,
50
+ initialValues: initial.values,
51
+ enableLookup: Boolean((_a = clickyMetadata.getOperationClickyMeta(operation)) == null ? void 0 : _a.supportsLookup),
52
+ showSubmit: false,
53
+ onChange: handleChange,
54
+ onSubmit: () => void 0,
55
+ ...className ? { className } : {}
56
+ }
57
+ ),
58
+ initial.dropped.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
59
+ "Dropped arguments this operation no longer accepts:",
60
+ " ",
61
+ initial.dropped.join(", ")
62
+ ] }) : null,
63
+ problems.map((problem) => /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: problem }, problem))
64
+ ] });
65
+ }
66
+ function storedArgsToValues(args, parameters, method) {
67
+ const known = new Set(parameters.map((parameter) => parameter.name));
68
+ const stored = {};
69
+ const dropped = [];
70
+ for (const [name, value] of Object.entries(args)) {
71
+ if (!known.has(name)) {
72
+ dropped.push(name);
73
+ continue;
74
+ }
75
+ stored[name] = storedValueToString(name, value);
76
+ }
77
+ return {
78
+ values: formMetadata.buildInitialParameterValues(parameters, method, {}, stored),
79
+ names: new Set(Object.keys(stored)),
80
+ dropped
81
+ };
82
+ }
83
+ function storedValueToString(name, value) {
84
+ if (Array.isArray(value)) return value.map(String).join(",");
85
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
86
+ return String(value);
87
+ }
88
+ throw new Error(
89
+ `Stored argument ${name} has an unsupported value: ${JSON.stringify(value)}`
90
+ );
91
+ }
92
+ function valuesToArgs({
93
+ parameters,
94
+ method,
95
+ values,
96
+ stored
97
+ }) {
98
+ const args = {};
99
+ for (const parameter of parameters) {
100
+ const raw = values[parameter.name] ?? "";
101
+ if (raw.trim() === "") continue;
102
+ const isDefault = raw === effectiveDefault(parameter, method);
103
+ if (isDefault && !parameter.required && !stored.has(parameter.name)) continue;
104
+ args[parameter.name] = typedValue(parameter, raw);
105
+ }
106
+ return args;
107
+ }
108
+ function effectiveDefault(parameter, method) {
109
+ var _a;
110
+ const value = formMetadata.defaultValueForParameter(parameter, method);
111
+ return ((_a = parameter.schema) == null ? void 0 : _a.type) === "boolean" && value === "" ? "false" : value;
112
+ }
113
+ function typedValue(parameter, raw) {
114
+ var _a;
115
+ switch ((_a = parameter.schema) == null ? void 0 : _a.type) {
116
+ case "boolean":
117
+ return raw === "true";
118
+ case "integer":
119
+ return isWholeNumber(raw) ? Number(raw) : raw;
120
+ case "number":
121
+ return isNumber(raw) ? Number(raw) : raw;
122
+ case "array":
123
+ return raw.split(",").map((item) => item.trim()).filter(Boolean);
124
+ default:
125
+ return raw;
126
+ }
127
+ }
128
+ function parameterProblems(parameters, values) {
129
+ var _a, _b;
130
+ const missing = parameters.filter(
131
+ (parameter) => parameter.required && (values[parameter.name] ?? "").trim() === ""
132
+ ).map((parameter) => formMetadata.titleCase(parameter.name));
133
+ const problems = missing.length > 0 ? [`Missing required fields: ${missing.join(", ")}`] : [];
134
+ for (const parameter of parameters) {
135
+ const raw = (values[parameter.name] ?? "").trim();
136
+ if (raw === "") continue;
137
+ const label = formMetadata.titleCase(parameter.name);
138
+ if (((_a = parameter.schema) == null ? void 0 : _a.type) === "integer" && !isWholeNumber(raw)) {
139
+ problems.push(`${label} must be a whole number.`);
140
+ } else if (((_b = parameter.schema) == null ? void 0 : _b.type) === "number" && !isNumber(raw)) {
141
+ problems.push(`${label} must be a number.`);
142
+ }
143
+ }
144
+ return problems;
145
+ }
146
+ function isWholeNumber(raw) {
147
+ return /^-?\d+$/.test(raw.trim());
148
+ }
149
+ function isNumber(raw) {
150
+ return raw.trim() !== "" && Number.isFinite(Number(raw));
151
+ }
152
+ exports.OperationArgsForm = OperationArgsForm;
153
+ //# sourceMappingURL=OperationArgsForm.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OperationArgsForm.cjs","sources":["../../src/rpc/OperationArgsForm.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport { FilterForm } from \"./FilterForm\";\nimport { getOperationClickyMeta } from \"./clickyMetadata\";\nimport {\n buildInitialParameterValues,\n defaultValueForParameter,\n titleCase,\n type ParameterValues,\n} from \"./formMetadata\";\nimport type { OpenAPIParameter, ResolvedOperation } from \"./types\";\nimport type { OperationsApiClient } from \"./useOperations\";\n\nexport type OperationArgs = Record<string, unknown>;\n\nexport type OperationArgsFormProps = {\n client: OperationsApiClient;\n operation: ResolvedOperation;\n /** Stored args. Read when the form mounts; remount it (key) to load others. */\n args: OperationArgs;\n /** Called with the typed args (booleans, numbers, lists) on every change. */\n onChange: (args: OperationArgs) => void;\n /** Called with false while a required parameter is missing or a value is malformed. */\n onValidityChange?: ((valid: boolean) => void) | undefined;\n className?: string | undefined;\n};\n\nconst NO_PARAMETERS: OpenAPIParameter[] = [];\n\n// OperationArgsForm renders an operation's own parameter form — the FilterForm\n// its action button opens, with the operation's lookups — as a field set whose\n// value is a stored args object, for surfaces that save or queue a run (the\n// schedule dialog, a Run now confirmation) rather than execute it.\nexport function OperationArgsForm({\n client,\n operation,\n args,\n onChange,\n onValidityChange,\n className,\n}: OperationArgsFormProps) {\n const parameters = operation.operation.parameters ?? NO_PARAMETERS;\n const [initial] = useState(() =>\n storedArgsToValues(args, parameters, operation.method),\n );\n const [values, setValues] = useState(initial.values);\n const problems = useMemo(\n () => parameterProblems(parameters, values),\n [parameters, values],\n );\n const valid = problems.length === 0;\n\n useEffect(() => {\n onValidityChange?.(valid);\n }, [onValidityChange, valid]);\n\n const handleChange = (next: ParameterValues) => {\n setValues(next);\n onChange(\n valuesToArgs({\n parameters,\n method: operation.method,\n values: next,\n stored: initial.names,\n }),\n );\n };\n\n return (\n <div className=\"space-y-2\">\n <FilterForm\n client={client}\n path={operation.path}\n method={operation.method}\n parameters={parameters}\n initialValues={initial.values}\n enableLookup={Boolean(getOperationClickyMeta(operation)?.supportsLookup)}\n showSubmit={false}\n onChange={handleChange}\n onSubmit={() => undefined}\n {...(className ? { className } : {})}\n />\n {initial.dropped.length > 0 ? (\n <p className=\"text-xs text-muted-foreground\">\n Dropped arguments this operation no longer accepts:{\" \"}\n {initial.dropped.join(\", \")}\n </p>\n ) : null}\n {problems.map((problem) => (\n <p key={problem} className=\"text-sm text-destructive\">\n {problem}\n </p>\n ))}\n </div>\n );\n}\n\nfunction storedArgsToValues(\n args: OperationArgs,\n parameters: OpenAPIParameter[],\n method: string,\n) {\n const known = new Set(parameters.map((parameter) => parameter.name));\n const stored: ParameterValues = {};\n const dropped: string[] = [];\n for (const [name, value] of Object.entries(args)) {\n if (!known.has(name)) {\n dropped.push(name);\n continue;\n }\n stored[name] = storedValueToString(name, value);\n }\n return {\n values: buildInitialParameterValues(parameters, method, {}, stored),\n names: new Set(Object.keys(stored)),\n dropped,\n };\n}\n\nfunction storedValueToString(name: string, value: unknown): string {\n if (Array.isArray(value)) return value.map(String).join(\",\");\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return String(value);\n }\n throw new Error(\n `Stored argument ${name} has an unsupported value: ${JSON.stringify(value)}`,\n );\n}\n\n// valuesToArgs keeps what the schedule names: values that differ from the\n// parameter's default, required ones, and ones the stored args already held.\n// Untouched defaults stay out so the server keeps applying its own default.\nfunction valuesToArgs({\n parameters,\n method,\n values,\n stored,\n}: {\n parameters: OpenAPIParameter[];\n method: string;\n values: ParameterValues;\n stored: ReadonlySet<string>;\n}): OperationArgs {\n const args: OperationArgs = {};\n for (const parameter of parameters) {\n const raw = values[parameter.name] ?? \"\";\n if (raw.trim() === \"\") continue;\n const isDefault = raw === effectiveDefault(parameter, method);\n if (isDefault && !parameter.required && !stored.has(parameter.name)) continue;\n args[parameter.name] = typedValue(parameter, raw);\n }\n return args;\n}\n\nfunction effectiveDefault(parameter: OpenAPIParameter, method: string): string {\n const value = defaultValueForParameter(parameter, method);\n return parameter.schema?.type === \"boolean\" && value === \"\" ? \"false\" : value;\n}\n\nfunction typedValue(parameter: OpenAPIParameter, raw: string): unknown {\n switch (parameter.schema?.type) {\n case \"boolean\":\n return raw === \"true\";\n case \"integer\":\n return isWholeNumber(raw) ? Number(raw) : raw;\n case \"number\":\n return isNumber(raw) ? Number(raw) : raw;\n case \"array\":\n return raw\n .split(\",\")\n .map((item) => item.trim())\n .filter(Boolean);\n default:\n return raw;\n }\n}\n\nfunction parameterProblems(\n parameters: OpenAPIParameter[],\n values: ParameterValues,\n): string[] {\n const missing = parameters\n .filter(\n (parameter) =>\n parameter.required && (values[parameter.name] ?? \"\").trim() === \"\",\n )\n .map((parameter) => titleCase(parameter.name));\n const problems =\n missing.length > 0 ? [`Missing required fields: ${missing.join(\", \")}`] : [];\n for (const parameter of parameters) {\n const raw = (values[parameter.name] ?? \"\").trim();\n if (raw === \"\") continue;\n const label = titleCase(parameter.name);\n if (parameter.schema?.type === \"integer\" && !isWholeNumber(raw)) {\n problems.push(`${label} must be a whole number.`);\n } else if (parameter.schema?.type === \"number\" && !isNumber(raw)) {\n problems.push(`${label} must be a number.`);\n }\n }\n return problems;\n}\n\nfunction isWholeNumber(raw: string): boolean {\n return /^-?\\d+$/.test(raw.trim());\n}\n\nfunction isNumber(raw: string): boolean {\n return raw.trim() !== \"\" && Number.isFinite(Number(raw));\n}\n"],"names":["useState","useMemo","useEffect","jsxs","jsx","FilterForm","getOperationClickyMeta","buildInitialParameterValues","defaultValueForParameter","titleCase"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { ResolvedOperation } from './types.js';
2
+ import { OperationsApiClient } from './useOperations.js';
3
+ export type OperationArgs = Record<string, unknown>;
4
+ export type OperationArgsFormProps = {
5
+ client: OperationsApiClient;
6
+ operation: ResolvedOperation;
7
+ /** Stored args. Read when the form mounts; remount it (key) to load others. */
8
+ args: OperationArgs;
9
+ /** Called with the typed args (booleans, numbers, lists) on every change. */
10
+ onChange: (args: OperationArgs) => void;
11
+ /** Called with false while a required parameter is missing or a value is malformed. */
12
+ onValidityChange?: ((valid: boolean) => void) | undefined;
13
+ className?: string | undefined;
14
+ };
15
+ export declare function OperationArgsForm({ client, operation, args, onChange, onValidityChange, className, }: OperationArgsFormProps): import("react").JSX.Element;
16
+ //# sourceMappingURL=OperationArgsForm.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OperationArgsForm.d.ts","sourceRoot":"","sources":["../../src/rpc/OperationArgsForm.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAoB,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,+EAA+E;IAC/E,IAAI,EAAE,aAAa,CAAC;IACpB,6EAA6E;IAC7E,QAAQ,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;IACxC,uFAAuF;IACvF,gBAAgB,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IAC1D,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAQF,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,SAAS,EACT,IAAI,EACJ,QAAQ,EACR,gBAAgB,EAChB,SAAS,GACV,EAAE,sBAAsB,+BAuDxB"}
@@ -0,0 +1,153 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { useState, useMemo, useEffect } from "react";
3
+ import { FilterForm } from "./FilterForm.js";
4
+ import { getOperationClickyMeta } from "./clickyMetadata.js";
5
+ import { buildInitialParameterValues, titleCase, defaultValueForParameter } from "./formMetadata.js";
6
+ const NO_PARAMETERS = [];
7
+ function OperationArgsForm({
8
+ client,
9
+ operation,
10
+ args,
11
+ onChange,
12
+ onValidityChange,
13
+ className
14
+ }) {
15
+ var _a;
16
+ const parameters = operation.operation.parameters ?? NO_PARAMETERS;
17
+ const [initial] = useState(
18
+ () => storedArgsToValues(args, parameters, operation.method)
19
+ );
20
+ const [values, setValues] = useState(initial.values);
21
+ const problems = useMemo(
22
+ () => parameterProblems(parameters, values),
23
+ [parameters, values]
24
+ );
25
+ const valid = problems.length === 0;
26
+ useEffect(() => {
27
+ onValidityChange == null ? void 0 : onValidityChange(valid);
28
+ }, [onValidityChange, valid]);
29
+ const handleChange = (next) => {
30
+ setValues(next);
31
+ onChange(
32
+ valuesToArgs({
33
+ parameters,
34
+ method: operation.method,
35
+ values: next,
36
+ stored: initial.names
37
+ })
38
+ );
39
+ };
40
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
41
+ /* @__PURE__ */ jsx(
42
+ FilterForm,
43
+ {
44
+ client,
45
+ path: operation.path,
46
+ method: operation.method,
47
+ parameters,
48
+ initialValues: initial.values,
49
+ enableLookup: Boolean((_a = getOperationClickyMeta(operation)) == null ? void 0 : _a.supportsLookup),
50
+ showSubmit: false,
51
+ onChange: handleChange,
52
+ onSubmit: () => void 0,
53
+ ...className ? { className } : {}
54
+ }
55
+ ),
56
+ initial.dropped.length > 0 ? /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
57
+ "Dropped arguments this operation no longer accepts:",
58
+ " ",
59
+ initial.dropped.join(", ")
60
+ ] }) : null,
61
+ problems.map((problem) => /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: problem }, problem))
62
+ ] });
63
+ }
64
+ function storedArgsToValues(args, parameters, method) {
65
+ const known = new Set(parameters.map((parameter) => parameter.name));
66
+ const stored = {};
67
+ const dropped = [];
68
+ for (const [name, value] of Object.entries(args)) {
69
+ if (!known.has(name)) {
70
+ dropped.push(name);
71
+ continue;
72
+ }
73
+ stored[name] = storedValueToString(name, value);
74
+ }
75
+ return {
76
+ values: buildInitialParameterValues(parameters, method, {}, stored),
77
+ names: new Set(Object.keys(stored)),
78
+ dropped
79
+ };
80
+ }
81
+ function storedValueToString(name, value) {
82
+ if (Array.isArray(value)) return value.map(String).join(",");
83
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
84
+ return String(value);
85
+ }
86
+ throw new Error(
87
+ `Stored argument ${name} has an unsupported value: ${JSON.stringify(value)}`
88
+ );
89
+ }
90
+ function valuesToArgs({
91
+ parameters,
92
+ method,
93
+ values,
94
+ stored
95
+ }) {
96
+ const args = {};
97
+ for (const parameter of parameters) {
98
+ const raw = values[parameter.name] ?? "";
99
+ if (raw.trim() === "") continue;
100
+ const isDefault = raw === effectiveDefault(parameter, method);
101
+ if (isDefault && !parameter.required && !stored.has(parameter.name)) continue;
102
+ args[parameter.name] = typedValue(parameter, raw);
103
+ }
104
+ return args;
105
+ }
106
+ function effectiveDefault(parameter, method) {
107
+ var _a;
108
+ const value = defaultValueForParameter(parameter, method);
109
+ return ((_a = parameter.schema) == null ? void 0 : _a.type) === "boolean" && value === "" ? "false" : value;
110
+ }
111
+ function typedValue(parameter, raw) {
112
+ var _a;
113
+ switch ((_a = parameter.schema) == null ? void 0 : _a.type) {
114
+ case "boolean":
115
+ return raw === "true";
116
+ case "integer":
117
+ return isWholeNumber(raw) ? Number(raw) : raw;
118
+ case "number":
119
+ return isNumber(raw) ? Number(raw) : raw;
120
+ case "array":
121
+ return raw.split(",").map((item) => item.trim()).filter(Boolean);
122
+ default:
123
+ return raw;
124
+ }
125
+ }
126
+ function parameterProblems(parameters, values) {
127
+ var _a, _b;
128
+ const missing = parameters.filter(
129
+ (parameter) => parameter.required && (values[parameter.name] ?? "").trim() === ""
130
+ ).map((parameter) => titleCase(parameter.name));
131
+ const problems = missing.length > 0 ? [`Missing required fields: ${missing.join(", ")}`] : [];
132
+ for (const parameter of parameters) {
133
+ const raw = (values[parameter.name] ?? "").trim();
134
+ if (raw === "") continue;
135
+ const label = titleCase(parameter.name);
136
+ if (((_a = parameter.schema) == null ? void 0 : _a.type) === "integer" && !isWholeNumber(raw)) {
137
+ problems.push(`${label} must be a whole number.`);
138
+ } else if (((_b = parameter.schema) == null ? void 0 : _b.type) === "number" && !isNumber(raw)) {
139
+ problems.push(`${label} must be a number.`);
140
+ }
141
+ }
142
+ return problems;
143
+ }
144
+ function isWholeNumber(raw) {
145
+ return /^-?\d+$/.test(raw.trim());
146
+ }
147
+ function isNumber(raw) {
148
+ return raw.trim() !== "" && Number.isFinite(Number(raw));
149
+ }
150
+ export {
151
+ OperationArgsForm
152
+ };
153
+ //# sourceMappingURL=OperationArgsForm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OperationArgsForm.js","sources":["../../src/rpc/OperationArgsForm.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport { FilterForm } from \"./FilterForm\";\nimport { getOperationClickyMeta } from \"./clickyMetadata\";\nimport {\n buildInitialParameterValues,\n defaultValueForParameter,\n titleCase,\n type ParameterValues,\n} from \"./formMetadata\";\nimport type { OpenAPIParameter, ResolvedOperation } from \"./types\";\nimport type { OperationsApiClient } from \"./useOperations\";\n\nexport type OperationArgs = Record<string, unknown>;\n\nexport type OperationArgsFormProps = {\n client: OperationsApiClient;\n operation: ResolvedOperation;\n /** Stored args. Read when the form mounts; remount it (key) to load others. */\n args: OperationArgs;\n /** Called with the typed args (booleans, numbers, lists) on every change. */\n onChange: (args: OperationArgs) => void;\n /** Called with false while a required parameter is missing or a value is malformed. */\n onValidityChange?: ((valid: boolean) => void) | undefined;\n className?: string | undefined;\n};\n\nconst NO_PARAMETERS: OpenAPIParameter[] = [];\n\n// OperationArgsForm renders an operation's own parameter form — the FilterForm\n// its action button opens, with the operation's lookups — as a field set whose\n// value is a stored args object, for surfaces that save or queue a run (the\n// schedule dialog, a Run now confirmation) rather than execute it.\nexport function OperationArgsForm({\n client,\n operation,\n args,\n onChange,\n onValidityChange,\n className,\n}: OperationArgsFormProps) {\n const parameters = operation.operation.parameters ?? NO_PARAMETERS;\n const [initial] = useState(() =>\n storedArgsToValues(args, parameters, operation.method),\n );\n const [values, setValues] = useState(initial.values);\n const problems = useMemo(\n () => parameterProblems(parameters, values),\n [parameters, values],\n );\n const valid = problems.length === 0;\n\n useEffect(() => {\n onValidityChange?.(valid);\n }, [onValidityChange, valid]);\n\n const handleChange = (next: ParameterValues) => {\n setValues(next);\n onChange(\n valuesToArgs({\n parameters,\n method: operation.method,\n values: next,\n stored: initial.names,\n }),\n );\n };\n\n return (\n <div className=\"space-y-2\">\n <FilterForm\n client={client}\n path={operation.path}\n method={operation.method}\n parameters={parameters}\n initialValues={initial.values}\n enableLookup={Boolean(getOperationClickyMeta(operation)?.supportsLookup)}\n showSubmit={false}\n onChange={handleChange}\n onSubmit={() => undefined}\n {...(className ? { className } : {})}\n />\n {initial.dropped.length > 0 ? (\n <p className=\"text-xs text-muted-foreground\">\n Dropped arguments this operation no longer accepts:{\" \"}\n {initial.dropped.join(\", \")}\n </p>\n ) : null}\n {problems.map((problem) => (\n <p key={problem} className=\"text-sm text-destructive\">\n {problem}\n </p>\n ))}\n </div>\n );\n}\n\nfunction storedArgsToValues(\n args: OperationArgs,\n parameters: OpenAPIParameter[],\n method: string,\n) {\n const known = new Set(parameters.map((parameter) => parameter.name));\n const stored: ParameterValues = {};\n const dropped: string[] = [];\n for (const [name, value] of Object.entries(args)) {\n if (!known.has(name)) {\n dropped.push(name);\n continue;\n }\n stored[name] = storedValueToString(name, value);\n }\n return {\n values: buildInitialParameterValues(parameters, method, {}, stored),\n names: new Set(Object.keys(stored)),\n dropped,\n };\n}\n\nfunction storedValueToString(name: string, value: unknown): string {\n if (Array.isArray(value)) return value.map(String).join(\",\");\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n return String(value);\n }\n throw new Error(\n `Stored argument ${name} has an unsupported value: ${JSON.stringify(value)}`,\n );\n}\n\n// valuesToArgs keeps what the schedule names: values that differ from the\n// parameter's default, required ones, and ones the stored args already held.\n// Untouched defaults stay out so the server keeps applying its own default.\nfunction valuesToArgs({\n parameters,\n method,\n values,\n stored,\n}: {\n parameters: OpenAPIParameter[];\n method: string;\n values: ParameterValues;\n stored: ReadonlySet<string>;\n}): OperationArgs {\n const args: OperationArgs = {};\n for (const parameter of parameters) {\n const raw = values[parameter.name] ?? \"\";\n if (raw.trim() === \"\") continue;\n const isDefault = raw === effectiveDefault(parameter, method);\n if (isDefault && !parameter.required && !stored.has(parameter.name)) continue;\n args[parameter.name] = typedValue(parameter, raw);\n }\n return args;\n}\n\nfunction effectiveDefault(parameter: OpenAPIParameter, method: string): string {\n const value = defaultValueForParameter(parameter, method);\n return parameter.schema?.type === \"boolean\" && value === \"\" ? \"false\" : value;\n}\n\nfunction typedValue(parameter: OpenAPIParameter, raw: string): unknown {\n switch (parameter.schema?.type) {\n case \"boolean\":\n return raw === \"true\";\n case \"integer\":\n return isWholeNumber(raw) ? Number(raw) : raw;\n case \"number\":\n return isNumber(raw) ? Number(raw) : raw;\n case \"array\":\n return raw\n .split(\",\")\n .map((item) => item.trim())\n .filter(Boolean);\n default:\n return raw;\n }\n}\n\nfunction parameterProblems(\n parameters: OpenAPIParameter[],\n values: ParameterValues,\n): string[] {\n const missing = parameters\n .filter(\n (parameter) =>\n parameter.required && (values[parameter.name] ?? \"\").trim() === \"\",\n )\n .map((parameter) => titleCase(parameter.name));\n const problems =\n missing.length > 0 ? [`Missing required fields: ${missing.join(\", \")}`] : [];\n for (const parameter of parameters) {\n const raw = (values[parameter.name] ?? \"\").trim();\n if (raw === \"\") continue;\n const label = titleCase(parameter.name);\n if (parameter.schema?.type === \"integer\" && !isWholeNumber(raw)) {\n problems.push(`${label} must be a whole number.`);\n } else if (parameter.schema?.type === \"number\" && !isNumber(raw)) {\n problems.push(`${label} must be a number.`);\n }\n }\n return problems;\n}\n\nfunction isWholeNumber(raw: string): boolean {\n return /^-?\\d+$/.test(raw.trim());\n}\n\nfunction isNumber(raw: string): boolean {\n return raw.trim() !== \"\" && Number.isFinite(Number(raw));\n}\n"],"names":[],"mappings":"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"}