@pipe0/react 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/components/compound/pipe-form/errors.d.mts.map +1 -1
  3. package/dist/components/compound/pipe-form/errors.mjs +5 -4
  4. package/dist/components/compound/pipe-form/errors.mjs.map +1 -1
  5. package/dist/components/compound/pipe-form/root.d.mts +3 -1
  6. package/dist/components/compound/pipe-form/root.d.mts.map +1 -1
  7. package/dist/components/compound/pipe-form/root.mjs +5 -3
  8. package/dist/components/compound/pipe-form/root.mjs.map +1 -1
  9. package/dist/components/compound/search-form/errors.d.mts.map +1 -1
  10. package/dist/components/compound/search-form/errors.mjs +5 -4
  11. package/dist/components/compound/search-form/errors.mjs.map +1 -1
  12. package/dist/components/compound/search-form/root.d.mts +3 -1
  13. package/dist/components/compound/search-form/root.d.mts.map +1 -1
  14. package/dist/components/compound/search-form/root.mjs +5 -3
  15. package/dist/components/compound/search-form/root.mjs.map +1 -1
  16. package/dist/components/defaults/adapters/context-select-input.mjs +1 -1
  17. package/dist/components/defaults/adapters/context-select-input.mjs.map +1 -1
  18. package/dist/components/defaults/adapters/index.d.mts.map +1 -1
  19. package/dist/components/defaults/adapters/index.mjs +8 -5
  20. package/dist/components/defaults/adapters/index.mjs.map +1 -1
  21. package/dist/components/defaults/adapters/int-input.mjs.map +1 -1
  22. package/dist/components/defaults/adapters/loose-object-input.mjs +111 -0
  23. package/dist/components/defaults/adapters/loose-object-input.mjs.map +1 -0
  24. package/dist/components/defaults/adapters/pipes-run-if-input.mjs +68 -55
  25. package/dist/components/defaults/adapters/pipes-run-if-input.mjs.map +1 -1
  26. package/dist/components/defaults/adapters/providers-input.mjs.map +1 -1
  27. package/dist/components/defaults/adapters/search-payload-input.mjs +18 -0
  28. package/dist/components/defaults/adapters/search-payload-input.mjs.map +1 -0
  29. package/dist/components/defaults/adapters/select-input.mjs +46 -27
  30. package/dist/components/defaults/adapters/select-input.mjs.map +1 -1
  31. package/dist/components/defaults/catalog/card-derived.d.mts +1 -1
  32. package/dist/components/defaults/catalog/card-derived.d.mts.map +1 -1
  33. package/dist/components/defaults/catalog/card-derived.mjs +6 -2
  34. package/dist/components/defaults/catalog/card-derived.mjs.map +1 -1
  35. package/dist/components/defaults/layout/field-wrapper.d.mts.map +1 -1
  36. package/dist/components/defaults/layout/field-wrapper.mjs +11 -5
  37. package/dist/components/defaults/layout/field-wrapper.mjs.map +1 -1
  38. package/dist/components/defaults/layout/group.mjs +1 -1
  39. package/dist/components/internal/LiquidEditor/LiquidEditor.mjs.map +1 -1
  40. package/dist/components/internal/form-level-errors.mjs +4 -3
  41. package/dist/components/internal/form-level-errors.mjs.map +1 -1
  42. package/dist/components/internal/icons.mjs +27 -1
  43. package/dist/components/internal/icons.mjs.map +1 -1
  44. package/dist/components/ui/alert.d.mts +47 -0
  45. package/dist/components/ui/alert.d.mts.map +1 -0
  46. package/dist/components/ui/alert.mjs +66 -0
  47. package/dist/components/ui/alert.mjs.map +1 -0
  48. package/dist/context/form-context.d.mts +21 -0
  49. package/dist/context/form-context.d.mts.map +1 -0
  50. package/dist/context/form-context.mjs +11 -1
  51. package/dist/context/form-context.mjs.map +1 -1
  52. package/dist/context/form-provider.d.mts +3 -1
  53. package/dist/context/form-provider.d.mts.map +1 -1
  54. package/dist/context/form-provider.mjs +8 -2
  55. package/dist/context/form-provider.mjs.map +1 -1
  56. package/dist/hooks/use-effect-catalog-table.d.mts.map +1 -1
  57. package/dist/hooks/use-effect-catalog-table.mjs +2 -2
  58. package/dist/hooks/use-effect-catalog-table.mjs.map +1 -1
  59. package/dist/hooks/use-form-core.mjs +7 -4
  60. package/dist/hooks/use-form-core.mjs.map +1 -1
  61. package/dist/hooks/use-pipe-catalog-table.d.mts +8 -8
  62. package/dist/hooks/use-pipe-catalog-table.d.mts.map +1 -1
  63. package/dist/hooks/use-pipe-catalog-table.mjs +2 -2
  64. package/dist/hooks/use-pipe-catalog-table.mjs.map +1 -1
  65. package/dist/hooks/use-pipe-form.d.mts.map +1 -1
  66. package/dist/hooks/use-pipe-form.mjs +18 -19
  67. package/dist/hooks/use-pipe-form.mjs.map +1 -1
  68. package/dist/hooks/use-search-catalog-table.d.mts +6 -6
  69. package/dist/hooks/use-search-form.d.mts.map +1 -1
  70. package/dist/hooks/use-search-form.mjs +18 -18
  71. package/dist/hooks/use-search-form.mjs.map +1 -1
  72. package/dist/hooks/use-sheet-effect-form.d.mts.map +1 -1
  73. package/dist/hooks/use-sheet-effect-form.mjs +18 -19
  74. package/dist/hooks/use-sheet-effect-form.mjs.map +1 -1
  75. package/dist/index.d.mts +4 -2
  76. package/dist/index.mjs +3 -1
  77. package/dist/styles/pipe0-form.css +1 -1
  78. package/dist/types/adapters.d.mts +22 -1
  79. package/dist/types/adapters.d.mts.map +1 -1
  80. package/dist/types/catalog-adapters.d.mts +1 -1
  81. package/dist/types/field-props.d.mts +15 -13
  82. package/dist/types/field-props.d.mts.map +1 -1
  83. package/dist/types/form-customization.d.mts +2 -25
  84. package/dist/utils/build-section-handlers.mjs +7 -73
  85. package/dist/utils/build-section-handlers.mjs.map +1 -1
  86. package/dist/widgets/token-pricing-badge.d.mts +1 -0
  87. package/dist/widgets/token-pricing-badge.mjs +55 -0
  88. package/dist/widgets/token-pricing-badge.mjs.map +1 -0
  89. package/dist/widgets/widget-strip.d.mts.map +1 -1
  90. package/dist/widgets/widget-strip.mjs +1 -0
  91. package/dist/widgets/widget-strip.mjs.map +1 -1
  92. package/dist/widgets/widget-view.d.mts.map +1 -1
  93. package/dist/widgets/widget-view.mjs +6 -0
  94. package/dist/widgets/widget-view.mjs.map +1 -1
  95. package/package.json +2 -2
  96. package/dist/components/defaults/adapters/key-value-list-input.mjs +0 -102
  97. package/dist/components/defaults/adapters/key-value-list-input.mjs.map +0 -1
  98. package/dist/types/form-customization.d.mts.map +0 -1
@@ -37,8 +37,8 @@ const EMPTY_CONDITION = {
37
37
  value: ""
38
38
  };
39
39
  const CONNECTOR_GAP = 32;
40
- function ConditionRow({ condition, index, eligibleFields, fieldsByName, isFirst, isLast, logic, form, basePath, onUpdate, onRemove, onAddCondition, onLogicChange }) {
41
- const conditionPath = `${basePath}.when.conditions.${index}`;
40
+ function ConditionRow({ condition, index, eligibleFields, fieldsByName, isFirst, isLast, logic, form, conditionsPath, onUpdate, onRemove, onAddCondition, onLogicChange }) {
41
+ const conditionPath = `${conditionsPath}.${index}`;
42
42
  const valueError = useFieldError(form, `${conditionPath}.value`);
43
43
  const fieldNameError = useFieldError(form, `${conditionPath}.field_name`);
44
44
  const operatorError = useFieldError(form, `${conditionPath}.operator`);
@@ -282,91 +282,79 @@ function ConditionRow({ condition, index, eligibleFields, fieldsByName, isFirst,
282
282
  })
283
283
  })] });
284
284
  }
285
- function PipesRunIfInputAdapter(field) {
286
- const pipeRunIf = field.value;
287
- const meta = field.meta;
288
- const eligibleFields = useMemo(() => meta.fields.filter((f) => OPERATORS_BY_TYPE[f.type].length > 0), [meta.fields]);
289
- const fieldsByName = useMemo(() => new Map(meta.fields.map((f) => [f.fieldName, f])), [meta.fields]);
290
- const addRunIf = useCallback(() => {
291
- field.setValue({
292
- action: "run",
293
- when: {
294
- logic: "and",
295
- conditions: [{ ...EMPTY_CONDITION }]
296
- }
285
+ /**
286
+ * Presentational condition-block editor shared by the pipe run-if gate and by
287
+ * effects that filter rows by a condition (e.g. `rows:remove@1`'s `where`).
288
+ * Operates on a bare `{logic, conditions}` block via `value`/`onChange`; the
289
+ * wrapping adapters own where that block lives in their payload.
290
+ */
291
+ function ConditionBlockEditor({ value, onChange, fields, form, conditionsPath }) {
292
+ const eligibleFields = useMemo(() => fields.filter((f) => OPERATORS_BY_TYPE[f.type].length > 0), [fields]);
293
+ const fieldsByName = useMemo(() => new Map(fields.map((f) => [f.fieldName, f])), [fields]);
294
+ const addBlock = useCallback(() => {
295
+ onChange({
296
+ logic: "and",
297
+ conditions: [{ ...EMPTY_CONDITION }]
297
298
  });
298
- }, [field]);
299
+ }, [onChange]);
299
300
  const updateCondition = (index, condition) => {
300
- if (!pipeRunIf) return;
301
- const newConditions = [...pipeRunIf.when.conditions];
301
+ if (!value) return;
302
+ const newConditions = [...value.conditions];
302
303
  newConditions[index] = condition;
303
- field.setValue({
304
- ...pipeRunIf,
305
- when: {
306
- ...pipeRunIf.when,
307
- conditions: newConditions
308
- }
304
+ onChange({
305
+ ...value,
306
+ conditions: newConditions
309
307
  });
310
308
  };
311
309
  const removeCondition = (index) => {
312
- if (!pipeRunIf) return;
313
- if (pipeRunIf.when.conditions.length <= 1) {
314
- field.setValue(null);
310
+ if (!value) return;
311
+ if (value.conditions.length <= 1) {
312
+ onChange(null);
315
313
  return;
316
314
  }
317
- const newConditions = pipeRunIf.when.conditions.filter((_, i) => i !== index);
318
- field.setValue({
319
- ...pipeRunIf,
320
- when: {
321
- ...pipeRunIf.when,
322
- conditions: newConditions
323
- }
315
+ onChange({
316
+ ...value,
317
+ conditions: value.conditions.filter((_, i) => i !== index)
324
318
  });
325
319
  };
326
320
  const addCondition = () => {
327
- if (!pipeRunIf) return;
328
- field.setValue({
329
- ...pipeRunIf,
330
- when: {
331
- ...pipeRunIf.when,
332
- conditions: [...pipeRunIf.when.conditions, { ...EMPTY_CONDITION }]
333
- }
321
+ if (!value) return;
322
+ onChange({
323
+ ...value,
324
+ conditions: [...value.conditions, { ...EMPTY_CONDITION }]
334
325
  });
335
326
  };
336
327
  const changeLogic = (logic) => {
337
- if (!pipeRunIf) return;
338
- field.setValue({
339
- ...pipeRunIf,
340
- when: {
341
- ...pipeRunIf.when,
342
- logic
343
- }
328
+ if (!value) return;
329
+ onChange({
330
+ ...value,
331
+ logic
344
332
  });
345
333
  };
346
334
  return /* @__PURE__ */ jsx("div", {
347
335
  "data-p0": "input",
348
- children: !pipeRunIf ? /* @__PURE__ */ jsx("div", {
336
+ children: !value ? /* @__PURE__ */ jsx("div", {
349
337
  className: "pz:flex pz:items-center",
350
338
  children: /* @__PURE__ */ jsxs(Button, {
351
339
  type: "button",
352
340
  variant: "link",
353
341
  size: "xs",
354
342
  className: "pz:px-0",
355
- onClick: addRunIf,
343
+ onClick: addBlock,
356
344
  children: [/* @__PURE__ */ jsx(IconPlus, {}), "New rule"]
357
345
  })
358
346
  }) : /* @__PURE__ */ jsx("div", {
359
347
  className: "pz:flex pz:flex-col pz:gap-8",
360
- children: pipeRunIf.when.conditions.map((condition, idx) => /* @__PURE__ */ jsx(ConditionRow, {
348
+ children: value.conditions.map((condition, idx) => /* @__PURE__ */ jsx(ConditionRow, {
361
349
  condition,
362
350
  index: idx,
363
351
  eligibleFields,
364
352
  fieldsByName,
365
353
  isFirst: idx === 0,
366
- isLast: idx === pipeRunIf.when.conditions.length - 1,
367
- logic: pipeRunIf.when.logic,
368
- form: field.form,
369
- basePath: field.path,
354
+ isLast: idx === value.conditions.length - 1,
355
+ logic: value.logic,
356
+ form,
357
+ conditionsPath,
370
358
  onUpdate: updateCondition,
371
359
  onRemove: removeCondition,
372
360
  onAddCondition: addCondition,
@@ -375,7 +363,32 @@ function PipesRunIfInputAdapter(field) {
375
363
  })
376
364
  });
377
365
  }
366
+ function PipesRunIfInputAdapter(field) {
367
+ const pipeRunIf = field.value;
368
+ const meta = field.meta;
369
+ return /* @__PURE__ */ jsx(ConditionBlockEditor, {
370
+ value: pipeRunIf?.when ?? null,
371
+ onChange: (block) => field.setValue(block ? {
372
+ action: "run",
373
+ when: block
374
+ } : null),
375
+ fields: meta.fields,
376
+ form: field.form,
377
+ conditionsPath: `${field.path}.when.conditions`
378
+ });
379
+ }
380
+ function ConditionBlockInputAdapter(field) {
381
+ const block = field.value;
382
+ const meta = field.meta;
383
+ return /* @__PURE__ */ jsx(ConditionBlockEditor, {
384
+ value: block,
385
+ onChange: (next) => field.setValue(next),
386
+ fields: meta.fields,
387
+ form: field.form,
388
+ conditionsPath: `${field.path}.conditions`
389
+ });
390
+ }
378
391
 
379
392
  //#endregion
380
- export { PipesRunIfInputAdapter };
393
+ export { ConditionBlockInputAdapter, PipesRunIfInputAdapter };
381
394
  //# sourceMappingURL=pipes-run-if-input.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pipes-run-if-input.mjs","names":[],"sources":["../../../../src/components/defaults/adapters/pipes-run-if-input.tsx"],"sourcesContent":["import {\n type ConditionOperators,\n type PipeRunIf,\n type PipesRunIfMeta,\n type RecordFieldType,\n RUN_IF_OPERATOR_LABELS,\n RUN_IF_STATUS_OPERATORS,\n RUN_IF_VALUE_OPERATORS,\n} from \"@pipe0/base\";\nimport { useCallback, useMemo } from \"react\";\nimport { useFieldError } from \"../../../hooks/use-field-error.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport type { FieldHandle } from \"../../../types/field-handle.js\";\nimport type { FormHandle } from \"../../../types/form-handle.js\";\nimport { HoverInfo } from \"../../hover-info.js\";\nimport { IconPlus } from \"../../internal/icons.js\";\nimport { Badge } from \"../../ui/badge.js\";\nimport { Button } from \"../../ui/button.js\";\nimport { Input } from \"../../ui/input.js\";\nimport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"../../ui/select.js\";\nimport { FieldSectionEnumeration } from \"../field-section-enumeration.js\";\n\n// Use a loose type for internal manipulation since the discriminated union\n// is strict about property/operator/value combinations.\ntype Condition = {\n field_name: string;\n property: string;\n operator: string;\n value: unknown;\n};\n\nconst OPERATORS_BY_TYPE: Record<RecordFieldType, ConditionOperators[]> = {\n string: [\"eq\", \"neq\", \"contains\"],\n number: [\"eq\", \"neq\", \"gt\", \"gte\", \"lt\", \"lte\"],\n boolean: [\"eq\", \"neq\"],\n json: [],\n unknown: [],\n};\n\nconst EMPTY_CONDITION: Condition = {\n field_name: \"\",\n property: \"value\",\n operator: \"eq\",\n value: \"\",\n};\n\nconst CONNECTOR_GAP = 32;\n\ntype MetaField = {\n fieldName: string;\n fieldLabel: string;\n type: RecordFieldType;\n format: string | null;\n};\n\nfunction ConditionRow({\n condition,\n index,\n eligibleFields,\n fieldsByName,\n isFirst,\n isLast,\n logic,\n form,\n basePath,\n onUpdate,\n onRemove,\n onAddCondition,\n onLogicChange,\n}: {\n condition: Condition;\n index: number;\n eligibleFields: MetaField[];\n fieldsByName: ReadonlyMap<string, MetaField>;\n isFirst: boolean;\n isLast: boolean;\n logic: PipeRunIf[\"when\"][\"logic\"];\n form: FormHandle;\n basePath: string;\n onUpdate: (index: number, condition: Condition) => void;\n onRemove: (index: number) => void;\n onAddCondition: () => void;\n onLogicChange: (logic: PipeRunIf[\"when\"][\"logic\"]) => void;\n}) {\n const conditionPath = `${basePath}.when.conditions.${index}`;\n const valueError = useFieldError(form, `${conditionPath}.value`);\n const fieldNameError = useFieldError(form, `${conditionPath}.field_name`);\n const operatorError = useFieldError(form, `${conditionPath}.operator`);\n const propertyError = useFieldError(form, `${conditionPath}.property`);\n\n const showConnector = !(isFirst && isLast);\n const hasEligibleFields = eligibleFields.length > 0;\n const selectedField = condition.field_name ? fieldsByName.get(condition.field_name) : undefined;\n\n const availableOperators = useMemo(() => {\n if (condition.property === \"status\") {\n return RUN_IF_STATUS_OPERATORS.map((op) => ({\n value: op,\n label: RUN_IF_OPERATOR_LABELS[op],\n }));\n }\n if (!selectedField?.type) return [];\n return RUN_IF_VALUE_OPERATORS.map((op) => ({\n value: op,\n label: RUN_IF_OPERATOR_LABELS[op],\n })).filter((op) => OPERATORS_BY_TYPE[selectedField.type].includes(op.value));\n }, [condition.property, selectedField]);\n\n const renderValueInput = () => {\n if (!condition.field_name || !selectedField) {\n return <Input className=\"pz:w-32\" placeholder=\"\" disabled aria-invalid={!!valueError} />;\n }\n\n if (condition.property === \"status\") {\n return (\n <Select\n value={String(condition.value) || null}\n onValueChange={(v) => onUpdate(index, { ...condition, value: v })}\n >\n <SelectTrigger className=\"pz:w-32\" aria-invalid={!!valueError}>\n <SelectValue placeholder=\"--status--\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"completed\">completed</SelectItem>\n <SelectItem value=\"skipped\">skipped</SelectItem>\n <SelectItem value=\"failed\">failed</SelectItem>\n <SelectItem value=\"no_result\">no_result</SelectItem>\n </SelectContent>\n </Select>\n );\n }\n\n if (selectedField.type === \"boolean\") {\n return (\n <Select\n value={String(condition.value)}\n onValueChange={(v) => onUpdate(index, { ...condition, value: v })}\n >\n <SelectTrigger className=\"pz:w-24\" aria-invalid={!!valueError}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"true\">true</SelectItem>\n <SelectItem value=\"false\">false</SelectItem>\n </SelectContent>\n </Select>\n );\n }\n\n if (selectedField.type === \"number\") {\n return (\n <Input\n type=\"number\"\n className=\"pz:w-32\"\n aria-invalid={!!valueError}\n value={String(Number(condition.value ?? 0))}\n onChange={(e) =>\n onUpdate(index, {\n ...condition,\n value: Number(e.target.value) || 0,\n })\n }\n placeholder=\"\"\n />\n );\n }\n\n return (\n <Input\n className=\"pz:w-32\"\n aria-invalid={!!valueError}\n value={String(condition.value ?? \"\")}\n onChange={(e) => onUpdate(index, { ...condition, value: e.target.value })}\n placeholder=\"\"\n />\n );\n };\n\n const errorMessage = valueError || fieldNameError || operatorError || propertyError;\n\n return (\n <div>\n <div className={cn(\"pz:relative\", showConnector && \"pz:pl-17\")}>\n {showConnector && (\n <>\n {isFirst ? (\n <div\n className=\"pz:absolute pz:left-7 pz:w-10 pz:border-l-2 pz:border-t-2 pz:border-input pz:rounded-tl-lg pz:pointer-events-none\"\n style={{ top: \"50%\", bottom: -CONNECTOR_GAP }}\n />\n ) : (\n <div\n className=\"pz:absolute pz:left-7 pz:w-10 pz:border-l-2 pz:border-b-2 pz:border-input pz:rounded-bl-lg pz:pointer-events-none\"\n style={{ top: -CONNECTOR_GAP, bottom: \"50%\" }}\n />\n )}\n {!isFirst && !isLast && (\n <div\n className=\"pz:absolute pz:left-7 pz:border-l-2 pz:border-input pz:pointer-events-none\"\n style={{ top: \"50%\", bottom: -CONNECTOR_GAP }}\n />\n )}\n {!isFirst && (\n <div\n className=\"pz:absolute pz:left-7 pz:z-10\"\n style={{\n top: -CONNECTOR_GAP / 2,\n transform: \"translate(-50%, -50%)\",\n }}\n >\n <Select value={logic} onValueChange={(v) => onLogicChange(v as \"and\" | \"or\")}>\n <SelectTrigger className=\"pz:w-17 pz:h-6 pz:text-xs pz:bg-background pz:hover:bg-accent\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectGroup>\n <SelectItem value=\"and\">AND</SelectItem>\n <SelectItem value=\"or\">OR</SelectItem>\n </SelectGroup>\n </SelectContent>\n </Select>\n </div>\n )}\n </>\n )}\n <div className=\"pz:flex pz:flex-wrap pz:items-center pz:gap-2\">\n <FieldSectionEnumeration index={index} onRemove={onRemove} />\n <Select\n value={condition.field_name || null}\n disabled={!hasEligibleFields}\n onValueChange={(v) => {\n if (v === null) return;\n onUpdate(index, {\n ...condition,\n field_name: v,\n operator: \"eq\",\n value: \"\",\n });\n }}\n >\n <SelectTrigger className=\"pz:w-40\" aria-invalid={!!fieldNameError}>\n <SelectValue placeholder=\"Select field\" />\n </SelectTrigger>\n <SelectContent>\n {eligibleFields.map((f) => (\n <SelectItem key={f.fieldName} value={f.fieldName}>\n {f.fieldLabel || f.fieldName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {!hasEligibleFields && (\n <span className=\"pz:text-xs pz:text-muted-foreground\">\n No fields available{\" \"}\n <HoverInfo>Conditions require plain fields. JSON fields are not allowed.</HoverInfo>\n </span>\n )}\n\n {selectedField?.type && (\n <>\n <Badge\n variant=\"secondary\"\n className=\"pz:bg-accent pz:border pz:border-input pz:font-normal pz:text-muted-foreground pz:h-8\"\n >\n &apos;s\n </Badge>\n <Select\n defaultValue=\"value\"\n value={condition.property}\n onValueChange={(v) => {\n if (v === null) return;\n onUpdate(index, {\n ...condition,\n property: v,\n operator: \"eq\",\n value: \"\",\n });\n }}\n >\n <SelectTrigger className=\"pz:w-28\" aria-invalid={!!propertyError}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"value\">value</SelectItem>\n <SelectItem value=\"status\">status</SelectItem>\n </SelectContent>\n </Select>\n\n <Select\n value={condition.operator}\n onValueChange={(v) =>\n onUpdate(index, {\n ...condition,\n operator: v as ConditionOperators,\n })\n }\n >\n <SelectTrigger className=\"pz:w-28\" aria-invalid={!!operatorError}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {availableOperators.map((op) => (\n <SelectItem key={op.value} value={op.value}>\n {op.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {renderValueInput()}\n </>\n )}\n </div>\n {errorMessage && (\n <span\n className=\"pz:text-destructive pz:text-xs pz:font-medium pz:mt-1 pz:block\"\n role=\"alert\"\n >\n {errorMessage}\n </span>\n )}\n </div>\n {isLast && (\n <div className=\"pz:flex pz:items-center pz:gap-2\">\n <Button type=\"button\" variant=\"link\" size=\"xs\" onClick={onAddCondition}>\n <IconPlus />\n New rule\n </Button>\n </div>\n )}\n </div>\n );\n}\n\nexport function PipesRunIfInputAdapter(field: FieldHandle<\"pipes_run_if_input\">) {\n const pipeRunIf = field.value as PipeRunIf | null;\n const meta = field.meta as PipesRunIfMeta;\n\n // Built once per render of the adapter and reused across every condition\n // row, instead of each row scanning `meta.fields` linearly. With N rows and\n // M fields this turns O(N·M) lookups into O(N+M).\n const eligibleFields = useMemo(\n () => meta.fields.filter((f) => OPERATORS_BY_TYPE[f.type].length > 0),\n [meta.fields],\n );\n const fieldsByName = useMemo(\n () => new Map(meta.fields.map((f) => [f.fieldName, f])),\n [meta.fields],\n );\n\n const addRunIf = useCallback(() => {\n field.setValue({\n action: \"run\",\n when: {\n logic: \"and\",\n conditions: [{ ...EMPTY_CONDITION }],\n },\n });\n }, [field]);\n\n const updateCondition = (index: number, condition: Condition) => {\n if (!pipeRunIf) return;\n const newConditions = [...pipeRunIf.when.conditions] as Condition[];\n newConditions[index] = condition;\n field.setValue({\n ...pipeRunIf,\n when: { ...pipeRunIf.when, conditions: newConditions },\n });\n };\n\n const removeCondition = (index: number) => {\n if (!pipeRunIf) return;\n if (pipeRunIf.when.conditions.length <= 1) {\n field.setValue(null);\n return;\n }\n const newConditions = pipeRunIf.when.conditions.filter((_, i) => i !== index);\n field.setValue({\n ...pipeRunIf,\n when: { ...pipeRunIf.when, conditions: newConditions },\n });\n };\n\n const addCondition = () => {\n if (!pipeRunIf) return;\n field.setValue({\n ...pipeRunIf,\n when: {\n ...pipeRunIf.when,\n conditions: [...pipeRunIf.when.conditions, { ...EMPTY_CONDITION }],\n },\n });\n };\n\n const changeLogic = (logic: \"and\" | \"or\") => {\n if (!pipeRunIf) return;\n field.setValue({\n ...pipeRunIf,\n when: { ...pipeRunIf.when, logic },\n });\n };\n\n return (\n <div data-p0=\"input\">\n {!pipeRunIf ? (\n <div className=\"pz:flex pz:items-center\">\n <Button type=\"button\" variant=\"link\" size=\"xs\" className=\"pz:px-0\" onClick={addRunIf}>\n <IconPlus />\n New rule\n </Button>\n </div>\n ) : (\n <div className=\"pz:flex pz:flex-col pz:gap-8\">\n {pipeRunIf.when.conditions.map((condition, idx) => (\n <ConditionRow\n key={idx}\n condition={condition}\n index={idx}\n eligibleFields={eligibleFields}\n fieldsByName={fieldsByName}\n isFirst={idx === 0}\n isLast={idx === pipeRunIf.when.conditions.length - 1}\n logic={pipeRunIf.when.logic}\n form={field.form}\n basePath={field.path}\n onUpdate={updateCondition}\n onRemove={removeCondition}\n onAddCondition={addCondition}\n onLogicChange={changeLogic}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"pipes-run-if-input.mjs","names":[],"sources":["../../../../src/components/defaults/adapters/pipes-run-if-input.tsx"],"sourcesContent":["import {\n type ConditionBlockMeta,\n type ConditionOperators,\n type PipeRunIf,\n type PipesRunIfMeta,\n type RecordFieldType,\n RUN_IF_OPERATOR_LABELS,\n RUN_IF_STATUS_OPERATORS,\n RUN_IF_VALUE_OPERATORS,\n} from \"@pipe0/base\";\nimport { useCallback, useMemo } from \"react\";\nimport { useFieldError } from \"../../../hooks/use-field-error.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport type { FieldHandle } from \"../../../types/field-handle.js\";\nimport type { FormHandle } from \"../../../types/form-handle.js\";\nimport { HoverInfo } from \"../../hover-info.js\";\nimport { IconPlus } from \"../../internal/icons.js\";\nimport { Badge } from \"../../ui/badge.js\";\nimport { Button } from \"../../ui/button.js\";\nimport { Input } from \"../../ui/input.js\";\nimport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"../../ui/select.js\";\nimport { FieldSectionEnumeration } from \"../field-section-enumeration.js\";\n\n// Use a loose type for internal manipulation since the discriminated union\n// is strict about property/operator/value combinations.\ntype Condition = {\n field_name: string;\n property: string;\n operator: string;\n value: unknown;\n};\n\n// Loose shape of a bare condition block ({logic, conditions}) — the run-if\n// `when` block, and an effect's `config.where`. The editor below is shared by\n// both; the strict discriminated-union types live in @pipe0/base.\ntype ConditionBlockValue = {\n logic: \"and\" | \"or\";\n conditions: Condition[];\n};\n\nconst OPERATORS_BY_TYPE: Record<RecordFieldType, ConditionOperators[]> = {\n string: [\"eq\", \"neq\", \"contains\"],\n number: [\"eq\", \"neq\", \"gt\", \"gte\", \"lt\", \"lte\"],\n boolean: [\"eq\", \"neq\"],\n json: [],\n unknown: [],\n};\n\nconst EMPTY_CONDITION: Condition = {\n field_name: \"\",\n property: \"value\",\n operator: \"eq\",\n value: \"\",\n};\n\nconst CONNECTOR_GAP = 32;\n\ntype MetaField = {\n fieldName: string;\n fieldLabel: string;\n type: RecordFieldType;\n format: string | null;\n};\n\nfunction ConditionRow({\n condition,\n index,\n eligibleFields,\n fieldsByName,\n isFirst,\n isLast,\n logic,\n form,\n conditionsPath,\n onUpdate,\n onRemove,\n onAddCondition,\n onLogicChange,\n}: {\n condition: Condition;\n index: number;\n eligibleFields: MetaField[];\n fieldsByName: ReadonlyMap<string, MetaField>;\n isFirst: boolean;\n isLast: boolean;\n logic: PipeRunIf[\"when\"][\"logic\"];\n form: FormHandle;\n conditionsPath: string;\n onUpdate: (index: number, condition: Condition) => void;\n onRemove: (index: number) => void;\n onAddCondition: () => void;\n onLogicChange: (logic: PipeRunIf[\"when\"][\"logic\"]) => void;\n}) {\n const conditionPath = `${conditionsPath}.${index}`;\n const valueError = useFieldError(form, `${conditionPath}.value`);\n const fieldNameError = useFieldError(form, `${conditionPath}.field_name`);\n const operatorError = useFieldError(form, `${conditionPath}.operator`);\n const propertyError = useFieldError(form, `${conditionPath}.property`);\n\n const showConnector = !(isFirst && isLast);\n const hasEligibleFields = eligibleFields.length > 0;\n const selectedField = condition.field_name ? fieldsByName.get(condition.field_name) : undefined;\n\n const availableOperators = useMemo(() => {\n if (condition.property === \"status\") {\n return RUN_IF_STATUS_OPERATORS.map((op) => ({\n value: op,\n label: RUN_IF_OPERATOR_LABELS[op],\n }));\n }\n if (!selectedField?.type) return [];\n return RUN_IF_VALUE_OPERATORS.map((op) => ({\n value: op,\n label: RUN_IF_OPERATOR_LABELS[op],\n })).filter((op) => OPERATORS_BY_TYPE[selectedField.type].includes(op.value));\n }, [condition.property, selectedField]);\n\n const renderValueInput = () => {\n if (!condition.field_name || !selectedField) {\n return <Input className=\"pz:w-32\" placeholder=\"\" disabled aria-invalid={!!valueError} />;\n }\n\n if (condition.property === \"status\") {\n return (\n <Select\n value={String(condition.value) || null}\n onValueChange={(v) => onUpdate(index, { ...condition, value: v })}\n >\n <SelectTrigger className=\"pz:w-32\" aria-invalid={!!valueError}>\n <SelectValue placeholder=\"--status--\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"completed\">completed</SelectItem>\n <SelectItem value=\"skipped\">skipped</SelectItem>\n <SelectItem value=\"failed\">failed</SelectItem>\n <SelectItem value=\"no_result\">no_result</SelectItem>\n </SelectContent>\n </Select>\n );\n }\n\n if (selectedField.type === \"boolean\") {\n return (\n <Select\n value={String(condition.value)}\n onValueChange={(v) => onUpdate(index, { ...condition, value: v })}\n >\n <SelectTrigger className=\"pz:w-24\" aria-invalid={!!valueError}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"true\">true</SelectItem>\n <SelectItem value=\"false\">false</SelectItem>\n </SelectContent>\n </Select>\n );\n }\n\n if (selectedField.type === \"number\") {\n return (\n <Input\n type=\"number\"\n className=\"pz:w-32\"\n aria-invalid={!!valueError}\n value={String(Number(condition.value ?? 0))}\n onChange={(e) =>\n onUpdate(index, {\n ...condition,\n value: Number(e.target.value) || 0,\n })\n }\n placeholder=\"\"\n />\n );\n }\n\n return (\n <Input\n className=\"pz:w-32\"\n aria-invalid={!!valueError}\n value={String(condition.value ?? \"\")}\n onChange={(e) => onUpdate(index, { ...condition, value: e.target.value })}\n placeholder=\"\"\n />\n );\n };\n\n const errorMessage = valueError || fieldNameError || operatorError || propertyError;\n\n return (\n <div>\n <div className={cn(\"pz:relative\", showConnector && \"pz:pl-17\")}>\n {showConnector && (\n <>\n {isFirst ? (\n <div\n className=\"pz:absolute pz:left-7 pz:w-10 pz:border-l-2 pz:border-t-2 pz:border-input pz:rounded-tl-lg pz:pointer-events-none\"\n style={{ top: \"50%\", bottom: -CONNECTOR_GAP }}\n />\n ) : (\n <div\n className=\"pz:absolute pz:left-7 pz:w-10 pz:border-l-2 pz:border-b-2 pz:border-input pz:rounded-bl-lg pz:pointer-events-none\"\n style={{ top: -CONNECTOR_GAP, bottom: \"50%\" }}\n />\n )}\n {!isFirst && !isLast && (\n <div\n className=\"pz:absolute pz:left-7 pz:border-l-2 pz:border-input pz:pointer-events-none\"\n style={{ top: \"50%\", bottom: -CONNECTOR_GAP }}\n />\n )}\n {!isFirst && (\n <div\n className=\"pz:absolute pz:left-7 pz:z-10\"\n style={{\n top: -CONNECTOR_GAP / 2,\n transform: \"translate(-50%, -50%)\",\n }}\n >\n <Select value={logic} onValueChange={(v) => onLogicChange(v as \"and\" | \"or\")}>\n <SelectTrigger className=\"pz:w-17 pz:h-6 pz:text-xs pz:bg-background pz:hover:bg-accent\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectGroup>\n <SelectItem value=\"and\">AND</SelectItem>\n <SelectItem value=\"or\">OR</SelectItem>\n </SelectGroup>\n </SelectContent>\n </Select>\n </div>\n )}\n </>\n )}\n <div className=\"pz:flex pz:flex-wrap pz:items-center pz:gap-2\">\n <FieldSectionEnumeration index={index} onRemove={onRemove} />\n <Select\n value={condition.field_name || null}\n disabled={!hasEligibleFields}\n onValueChange={(v) => {\n if (v === null) return;\n onUpdate(index, {\n ...condition,\n field_name: v,\n operator: \"eq\",\n value: \"\",\n });\n }}\n >\n <SelectTrigger className=\"pz:w-40\" aria-invalid={!!fieldNameError}>\n <SelectValue placeholder=\"Select field\" />\n </SelectTrigger>\n <SelectContent>\n {eligibleFields.map((f) => (\n <SelectItem key={f.fieldName} value={f.fieldName}>\n {f.fieldLabel || f.fieldName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n {!hasEligibleFields && (\n <span className=\"pz:text-xs pz:text-muted-foreground\">\n No fields available{\" \"}\n <HoverInfo>Conditions require plain fields. JSON fields are not allowed.</HoverInfo>\n </span>\n )}\n\n {selectedField?.type && (\n <>\n <Badge\n variant=\"secondary\"\n className=\"pz:bg-accent pz:border pz:border-input pz:font-normal pz:text-muted-foreground pz:h-8\"\n >\n &apos;s\n </Badge>\n <Select\n defaultValue=\"value\"\n value={condition.property}\n onValueChange={(v) => {\n if (v === null) return;\n onUpdate(index, {\n ...condition,\n property: v,\n operator: \"eq\",\n value: \"\",\n });\n }}\n >\n <SelectTrigger className=\"pz:w-28\" aria-invalid={!!propertyError}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"value\">value</SelectItem>\n <SelectItem value=\"status\">status</SelectItem>\n </SelectContent>\n </Select>\n\n <Select\n value={condition.operator}\n onValueChange={(v) =>\n onUpdate(index, {\n ...condition,\n operator: v as ConditionOperators,\n })\n }\n >\n <SelectTrigger className=\"pz:w-28\" aria-invalid={!!operatorError}>\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {availableOperators.map((op) => (\n <SelectItem key={op.value} value={op.value}>\n {op.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {renderValueInput()}\n </>\n )}\n </div>\n {errorMessage && (\n <span\n className=\"pz:text-destructive pz:text-xs pz:font-medium pz:mt-1 pz:block\"\n role=\"alert\"\n >\n {errorMessage}\n </span>\n )}\n </div>\n {isLast && (\n <div className=\"pz:flex pz:items-center pz:gap-2\">\n <Button type=\"button\" variant=\"link\" size=\"xs\" onClick={onAddCondition}>\n <IconPlus />\n New rule\n </Button>\n </div>\n )}\n </div>\n );\n}\n\n/**\n * Presentational condition-block editor shared by the pipe run-if gate and by\n * effects that filter rows by a condition (e.g. `rows:remove@1`'s `where`).\n * Operates on a bare `{logic, conditions}` block via `value`/`onChange`; the\n * wrapping adapters own where that block lives in their payload.\n */\nexport function ConditionBlockEditor({\n value,\n onChange,\n fields,\n form,\n conditionsPath,\n}: {\n value: ConditionBlockValue | null;\n onChange: (value: ConditionBlockValue | null) => void;\n fields: MetaField[];\n form: FormHandle;\n // Field-error path of the conditions array (e.g. `run_if.when.conditions` or\n // `config.where.conditions`).\n conditionsPath: string;\n}) {\n // Built once and reused across every condition row, instead of each row\n // scanning `fields` linearly. With N rows and M fields this turns O(N·M)\n // lookups into O(N+M).\n const eligibleFields = useMemo(\n () => fields.filter((f) => OPERATORS_BY_TYPE[f.type].length > 0),\n [fields],\n );\n const fieldsByName = useMemo(() => new Map(fields.map((f) => [f.fieldName, f])), [fields]);\n\n const addBlock = useCallback(() => {\n onChange({ logic: \"and\", conditions: [{ ...EMPTY_CONDITION }] });\n }, [onChange]);\n\n const updateCondition = (index: number, condition: Condition) => {\n if (!value) return;\n const newConditions = [...value.conditions];\n newConditions[index] = condition;\n onChange({ ...value, conditions: newConditions });\n };\n\n const removeCondition = (index: number) => {\n if (!value) return;\n if (value.conditions.length <= 1) {\n onChange(null);\n return;\n }\n onChange({ ...value, conditions: value.conditions.filter((_, i) => i !== index) });\n };\n\n const addCondition = () => {\n if (!value) return;\n onChange({ ...value, conditions: [...value.conditions, { ...EMPTY_CONDITION }] });\n };\n\n const changeLogic = (logic: \"and\" | \"or\") => {\n if (!value) return;\n onChange({ ...value, logic });\n };\n\n return (\n <div data-p0=\"input\">\n {!value ? (\n <div className=\"pz:flex pz:items-center\">\n <Button type=\"button\" variant=\"link\" size=\"xs\" className=\"pz:px-0\" onClick={addBlock}>\n <IconPlus />\n New rule\n </Button>\n </div>\n ) : (\n <div className=\"pz:flex pz:flex-col pz:gap-8\">\n {value.conditions.map((condition, idx) => (\n <ConditionRow\n key={idx}\n condition={condition}\n index={idx}\n eligibleFields={eligibleFields}\n fieldsByName={fieldsByName}\n isFirst={idx === 0}\n isLast={idx === value.conditions.length - 1}\n logic={value.logic}\n form={form}\n conditionsPath={conditionsPath}\n onUpdate={updateCondition}\n onRemove={removeCondition}\n onAddCondition={addCondition}\n onLogicChange={changeLogic}\n />\n ))}\n </div>\n )}\n </div>\n );\n}\n\nexport function PipesRunIfInputAdapter(field: FieldHandle<\"pipes_run_if_input\">) {\n const pipeRunIf = field.value as PipeRunIf | null;\n const meta = field.meta as PipesRunIfMeta;\n\n return (\n <ConditionBlockEditor\n value={(pipeRunIf?.when as ConditionBlockValue | undefined) ?? null}\n onChange={(block) => field.setValue(block ? { action: \"run\", when: block } : null)}\n fields={meta.fields}\n form={field.form}\n conditionsPath={`${field.path}.when.conditions`}\n />\n );\n}\n\nexport function ConditionBlockInputAdapter(field: FieldHandle<\"condition_block_input\">) {\n const block = field.value as ConditionBlockValue | null;\n const meta = field.meta as ConditionBlockMeta;\n\n return (\n <ConditionBlockEditor\n value={block}\n onChange={(next) => field.setValue(next)}\n fields={meta.fields}\n form={field.form}\n conditionsPath={`${field.path}.conditions`}\n />\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"providers-input.mjs","names":["XIcon","SearchIcon"],"sources":["../../../../src/components/defaults/adapters/providers-input.tsx"],"sourcesContent":["import {\n closestCenter,\n DndContext,\n type DragEndEvent,\n KeyboardSensor,\n PointerSensor,\n useSensor,\n useSensors,\n} from \"@dnd-kit/core\";\nimport { SortableContext, useSortable, verticalListSortingStrategy } from \"@dnd-kit/sortable\";\nimport type { ProviderName, WidgetsByKind } from \"@pipe0/base\";\nimport { Search as SearchIcon, X as XIcon } from \"lucide-react\";\nimport { type CSSProperties, useMemo, useState } from \"react\";\nimport { cn } from \"../../../lib/utils.js\";\nimport type { FieldHandle } from \"../../../types/field-handle.js\";\nimport { AvatarGroup } from \"../../../widgets/avatar-group.js\";\nimport { PricingBadge } from \"../../../widgets/pricing-badge.js\";\nimport { WidgetStrip } from \"../../../widgets/widget-strip.js\";\nimport { IconGripVertical, IconPlus } from \"../../internal/icons.js\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../../ui/popover.js\";\n\ntype ProviderOption = {\n value: string;\n label: string;\n widgets?: WidgetsByKind;\n};\n\nfunction splitWidgets(widgets: WidgetsByKind | undefined): {\n providerName: ProviderName | undefined;\n leading: WidgetsByKind | undefined;\n pricing: WidgetsByKind[\"pricing\"] | undefined;\n} {\n if (!widgets) return { providerName: undefined, leading: undefined, pricing: undefined };\n const { pricing, provider_logo, ...rest } = widgets;\n const hasLeading = Object.values(rest).some((v) => v != null);\n return {\n providerName: provider_logo?.provider,\n leading: hasLeading ? rest : undefined,\n pricing,\n };\n}\n\nfunction LeadingAvatar({\n providerName,\n leading,\n}: {\n providerName: ProviderName | undefined;\n leading: WidgetsByKind | undefined;\n}) {\n if (providerName) {\n return <AvatarGroup providers={[providerName]} size=\"sm\" className=\"pz:shrink-0\" />;\n }\n if (leading) {\n return (\n <span className=\"pz:flex pz:items-center pz:shrink-0\">\n <WidgetStrip widgets={leading} size={18} />\n </span>\n );\n }\n return null;\n}\n\n/**\n * Provider waterfall — vertical, ordered list of providers tried sequentially\n * until a valid result is returned. The list makes the running order legible\n * (numeric badge per row, drag-to-reorder), and the credit widget per\n * provider stays visible.\n */\nexport function ProvidersInputAdapter(field: FieldHandle<\"providers_input\">) {\n const selected: string[] = useMemo(\n () => (field.value ?? []).map((p: { provider: string }) => p.provider),\n [field.value],\n );\n\n const options = field.options ?? [];\n const optionByValue = useMemo(() => {\n const map = new Map<string, ProviderOption>();\n for (const o of options) map.set(o.value, o);\n return map;\n }, [options]);\n\n const maxItems = field.meta.maxItems;\n const atMax = maxItems !== undefined && selected.length >= maxItems;\n\n const setValue = (next: string[]) =>\n field.setValue(next.map((provider) => ({ provider })));\n\n const handleRemove = (value: string) => setValue(selected.filter((v) => v !== value));\n const handleAdd = (value: string) => {\n if (selected.includes(value)) return;\n setValue([...selected, value]);\n };\n\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor),\n );\n\n const handleDragEnd = (event: DragEndEvent) => {\n const { active, over } = event;\n if (!over || active.id === over.id) return;\n const from = selected.indexOf(String(active.id));\n const to = selected.indexOf(String(over.id));\n if (from === -1 || to === -1) return;\n const next = [...selected];\n const [moved] = next.splice(from, 1);\n next.splice(to, 0, moved);\n setValue(next);\n };\n\n return (\n <div data-p0=\"input\" className=\"pz:flex pz:flex-col pz:gap-1\">\n {selected.length > 0 && (\n <DndContext\n sensors={sensors}\n collisionDetection={closestCenter}\n onDragEnd={handleDragEnd}\n >\n <SortableContext items={selected} strategy={verticalListSortingStrategy}>\n <ul className=\"pz:flex pz:flex-col pz:gap-0.5 pz:list-none pz:m-0 pz:p-0\">\n {selected.map((value, index) => {\n const option = optionByValue.get(value);\n return (\n <ProviderRow\n key={value}\n value={value}\n index={index}\n label={option?.label ?? value}\n widgets={option?.widgets}\n onRemove={() => handleRemove(value)}\n />\n );\n })}\n </ul>\n </SortableContext>\n </DndContext>\n )}\n {!atMax && (\n <AddProviderControl\n options={options}\n excluded={selected}\n onAdd={handleAdd}\n ariaInvalid={!!field.error}\n />\n )}\n </div>\n );\n}\n\nfunction ProviderRow({\n value,\n index,\n label,\n widgets,\n onRemove,\n}: {\n value: string;\n index: number;\n label: string;\n widgets?: WidgetsByKind;\n onRemove: () => void;\n}) {\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({\n id: value,\n });\n const style: CSSProperties = {\n transform: transform\n ? `translate3d(${Math.round(transform.x)}px, ${Math.round(transform.y)}px, 0)`\n : undefined,\n transition,\n opacity: isDragging ? 0.6 : 1,\n };\n\n const { providerName, leading, pricing } = splitWidgets(widgets);\n const orderLabel = String(index + 1);\n\n return (\n <li\n ref={setNodeRef}\n style={style}\n className={cn(\n \"pz:group/prov pz:flex pz:items-center pz:gap-2.5 pz:rounded-md pz:px-1.5 pz:py-1.5\",\n \"pz:hover:bg-muted/60 pz:transition-colors\",\n )}\n {...attributes}\n >\n <button\n type=\"button\"\n aria-label=\"Drag to reorder\"\n className={cn(\n \"pz:flex pz:items-center pz:justify-center pz:size-4 pz:cursor-grab pz:touch-none\",\n \"pz:text-muted-foreground/60 pz:hover:text-foreground pz:transition-colors\",\n )}\n {...listeners}\n >\n <IconGripVertical width={11} height={14} />\n </button>\n <LeadingAvatar providerName={providerName} leading={leading} />\n <span className=\"pz:text-sm pz:font-medium pz:text-foreground pz:flex-1 pz:truncate\">\n {label}\n </span>\n {pricing && <PricingBadge credits={pricing.credits} />}\n <span\n aria-hidden\n className={cn(\n \"pz:inline-flex pz:items-center pz:justify-center pz:min-w-7 pz:h-5 pz:px-1.5\",\n \"pz:rounded-full pz:bg-muted pz:text-muted-foreground pz:text-[10.5px]\",\n \"pz:font-medium pz:tabular-nums\",\n )}\n >\n {orderLabel}\n </span>\n <button\n type=\"button\"\n aria-label={`Remove ${label}`}\n onClick={(e) => {\n e.stopPropagation();\n onRemove();\n }}\n className={cn(\n \"pz:flex pz:items-center pz:justify-center pz:size-5 pz:rounded\",\n \"pz:text-muted-foreground/70\",\n \"pz:hover:bg-destructive/10 pz:hover:text-destructive pz:transition-colors\",\n )}\n >\n <XIcon className=\"pz:size-3\" />\n </button>\n </li>\n );\n}\n\nfunction AddProviderControl({\n options,\n excluded,\n onAdd,\n ariaInvalid,\n}: {\n options: ProviderOption[];\n excluded: string[];\n onAdd: (value: string) => void;\n ariaInvalid?: boolean;\n}) {\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n\n const excludedSet = useMemo(() => new Set(excluded), [excluded]);\n const available = useMemo(\n () => options.filter((o) => !excludedSet.has(o.value)),\n [options, excludedSet],\n );\n\n const filtered = useMemo(() => {\n const q = query.trim().toLowerCase();\n if (!q) return available;\n return available.filter(\n (o) => o.label.toLowerCase().includes(q) || o.value.toLowerCase().includes(q),\n );\n }, [available, query]);\n\n if (available.length === 0 && excluded.length === 0) {\n return (\n <div className=\"pz:text-xs pz:text-muted-foreground pz:px-1.5 pz:py-2\">\n No providers available\n </div>\n );\n }\n\n if (available.length === 0) return null;\n\n return (\n <Popover\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) setQuery(\"\");\n }}\n >\n <PopoverTrigger\n aria-invalid={ariaInvalid || undefined}\n className={cn(\n \"pz:flex pz:items-center pz:justify-center pz:gap-1.5 pz:w-full\",\n \"pz:px-2.5 pz:py-1.5 pz:mt-1 pz:rounded-md\",\n \"pz:border pz:border-dashed pz:border-input pz:bg-transparent\",\n \"pz:text-xs pz:text-muted-foreground\",\n \"pz:hover:bg-muted/60 pz:hover:text-foreground pz:hover:border-muted-foreground/40\",\n \"pz:transition-colors pz:cursor-pointer\",\n )}\n >\n <IconPlus width={12} height={12} />\n Add provider\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"pz:w-(--anchor-width) pz:p-0 pz:gap-0\">\n <div className=\"pz:flex pz:items-center pz:gap-1.5 pz:px-2.5 pz:py-2 pz:border-b pz:border-border\">\n <SearchIcon className=\"pz:size-3.5 pz:text-muted-foreground pz:shrink-0\" />\n <input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search providers…\"\n // biome-ignore lint/a11y/noAutofocus: popover-scoped picker\n autoFocus\n className=\"pz:flex-1 pz:bg-transparent pz:outline-none pz:text-sm pz:placeholder:text-muted-foreground/70\"\n />\n </div>\n <ul className=\"pz:flex pz:flex-col pz:p-1 pz:max-h-72 pz:overflow-auto pz:list-none pz:m-0\">\n {filtered.length === 0 && (\n <li className=\"pz:px-2 pz:py-3 pz:text-center pz:text-xs pz:text-muted-foreground\">\n No providers\n </li>\n )}\n {filtered.map((option) => {\n const { providerName, leading, pricing } = splitWidgets(option.widgets);\n return (\n <li key={option.value} className=\"pz:contents\">\n <button\n type=\"button\"\n onClick={() => {\n onAdd(option.value);\n setOpen(false);\n setQuery(\"\");\n }}\n className={cn(\n \"pz:flex pz:items-center pz:gap-2 pz:w-full pz:text-left\",\n \"pz:px-2 pz:py-1.5 pz:rounded-md pz:text-sm\",\n \"pz:hover:bg-accent pz:hover:text-accent-foreground\",\n \"pz:transition-colors pz:cursor-pointer\",\n )}\n >\n <LeadingAvatar providerName={providerName} leading={leading} />\n <span className=\"pz:flex-1 pz:truncate\">{option.label}</span>\n {pricing && <PricingBadge credits={pricing.credits} />}\n </button>\n </li>\n );\n })}\n </ul>\n </PopoverContent>\n </Popover>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"providers-input.mjs","names":["XIcon","SearchIcon"],"sources":["../../../../src/components/defaults/adapters/providers-input.tsx"],"sourcesContent":["import {\n closestCenter,\n DndContext,\n type DragEndEvent,\n KeyboardSensor,\n PointerSensor,\n useSensor,\n useSensors,\n} from \"@dnd-kit/core\";\nimport { SortableContext, useSortable, verticalListSortingStrategy } from \"@dnd-kit/sortable\";\nimport type { ProviderName, WidgetsByKind } from \"@pipe0/base\";\nimport { Search as SearchIcon, X as XIcon } from \"lucide-react\";\nimport { type CSSProperties, useMemo, useState } from \"react\";\nimport { cn } from \"../../../lib/utils.js\";\nimport type { FieldHandle } from \"../../../types/field-handle.js\";\nimport { AvatarGroup } from \"../../../widgets/avatar-group.js\";\nimport { PricingBadge } from \"../../../widgets/pricing-badge.js\";\nimport { WidgetStrip } from \"../../../widgets/widget-strip.js\";\nimport { IconGripVertical, IconPlus } from \"../../internal/icons.js\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"../../ui/popover.js\";\n\ntype ProviderOption = {\n value: string;\n label: string;\n widgets?: WidgetsByKind;\n};\n\nfunction splitWidgets(widgets: WidgetsByKind | undefined): {\n providerName: ProviderName | undefined;\n leading: WidgetsByKind | undefined;\n pricing: WidgetsByKind[\"pricing\"] | undefined;\n} {\n if (!widgets) return { providerName: undefined, leading: undefined, pricing: undefined };\n const { pricing, provider_logo, ...rest } = widgets;\n const hasLeading = Object.values(rest).some((v) => v != null);\n return {\n providerName: provider_logo?.provider,\n leading: hasLeading ? rest : undefined,\n pricing,\n };\n}\n\nfunction LeadingAvatar({\n providerName,\n leading,\n}: {\n providerName: ProviderName | undefined;\n leading: WidgetsByKind | undefined;\n}) {\n if (providerName) {\n return <AvatarGroup providers={[providerName]} size=\"sm\" className=\"pz:shrink-0\" />;\n }\n if (leading) {\n return (\n <span className=\"pz:flex pz:items-center pz:shrink-0\">\n <WidgetStrip widgets={leading} size={18} />\n </span>\n );\n }\n return null;\n}\n\n/**\n * Provider waterfall — vertical, ordered list of providers tried sequentially\n * until a valid result is returned. The list makes the running order legible\n * (numeric badge per row, drag-to-reorder), and the credit widget per\n * provider stays visible.\n */\nexport function ProvidersInputAdapter(field: FieldHandle<\"providers_input\">) {\n const selected: string[] = useMemo(\n () => (field.value ?? []).map((p: { provider: string }) => p.provider),\n [field.value],\n );\n\n const options = field.options ?? [];\n const optionByValue = useMemo(() => {\n const map = new Map<string, ProviderOption>();\n for (const o of options) map.set(o.value, o);\n return map;\n }, [options]);\n\n const maxItems = field.meta.maxItems;\n const atMax = maxItems !== undefined && selected.length >= maxItems;\n\n const setValue = (next: string[]) => field.setValue(next.map((provider) => ({ provider })));\n\n const handleRemove = (value: string) => setValue(selected.filter((v) => v !== value));\n const handleAdd = (value: string) => {\n if (selected.includes(value)) return;\n setValue([...selected, value]);\n };\n\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor),\n );\n\n const handleDragEnd = (event: DragEndEvent) => {\n const { active, over } = event;\n if (!over || active.id === over.id) return;\n const from = selected.indexOf(String(active.id));\n const to = selected.indexOf(String(over.id));\n if (from === -1 || to === -1) return;\n const next = [...selected];\n const [moved] = next.splice(from, 1);\n next.splice(to, 0, moved);\n setValue(next);\n };\n\n return (\n <div data-p0=\"input\" className=\"pz:flex pz:flex-col pz:gap-1\">\n {selected.length > 0 && (\n <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>\n <SortableContext items={selected} strategy={verticalListSortingStrategy}>\n <ul className=\"pz:flex pz:flex-col pz:gap-0.5 pz:list-none pz:m-0 pz:p-0\">\n {selected.map((value, index) => {\n const option = optionByValue.get(value);\n return (\n <ProviderRow\n key={value}\n value={value}\n index={index}\n label={option?.label ?? value}\n widgets={option?.widgets}\n onRemove={() => handleRemove(value)}\n />\n );\n })}\n </ul>\n </SortableContext>\n </DndContext>\n )}\n {!atMax && (\n <AddProviderControl\n options={options}\n excluded={selected}\n onAdd={handleAdd}\n ariaInvalid={!!field.error}\n />\n )}\n </div>\n );\n}\n\nfunction ProviderRow({\n value,\n index,\n label,\n widgets,\n onRemove,\n}: {\n value: string;\n index: number;\n label: string;\n widgets?: WidgetsByKind;\n onRemove: () => void;\n}) {\n const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({\n id: value,\n });\n const style: CSSProperties = {\n transform: transform\n ? `translate3d(${Math.round(transform.x)}px, ${Math.round(transform.y)}px, 0)`\n : undefined,\n transition,\n opacity: isDragging ? 0.6 : 1,\n };\n\n const { providerName, leading, pricing } = splitWidgets(widgets);\n const orderLabel = String(index + 1);\n\n return (\n <li\n ref={setNodeRef}\n style={style}\n className={cn(\n \"pz:group/prov pz:flex pz:items-center pz:gap-2.5 pz:rounded-md pz:px-1.5 pz:py-1.5\",\n \"pz:hover:bg-muted/60 pz:transition-colors\",\n )}\n {...attributes}\n >\n <button\n type=\"button\"\n aria-label=\"Drag to reorder\"\n className={cn(\n \"pz:flex pz:items-center pz:justify-center pz:size-4 pz:cursor-grab pz:touch-none\",\n \"pz:text-muted-foreground/60 pz:hover:text-foreground pz:transition-colors\",\n )}\n {...listeners}\n >\n <IconGripVertical width={11} height={14} />\n </button>\n <LeadingAvatar providerName={providerName} leading={leading} />\n <span className=\"pz:text-sm pz:font-medium pz:text-foreground pz:flex-1 pz:truncate\">\n {label}\n </span>\n {pricing && <PricingBadge credits={pricing.credits} />}\n <span\n aria-hidden\n className={cn(\n \"pz:inline-flex pz:items-center pz:justify-center pz:min-w-7 pz:h-5 pz:px-1.5\",\n \"pz:rounded-full pz:bg-muted pz:text-muted-foreground pz:text-[10.5px]\",\n \"pz:font-medium pz:tabular-nums\",\n )}\n >\n {orderLabel}\n </span>\n <button\n type=\"button\"\n aria-label={`Remove ${label}`}\n onClick={(e) => {\n e.stopPropagation();\n onRemove();\n }}\n className={cn(\n \"pz:flex pz:items-center pz:justify-center pz:size-5 pz:rounded\",\n \"pz:text-muted-foreground/70\",\n \"pz:hover:bg-destructive/10 pz:hover:text-destructive pz:transition-colors\",\n )}\n >\n <XIcon className=\"pz:size-3\" />\n </button>\n </li>\n );\n}\n\nfunction AddProviderControl({\n options,\n excluded,\n onAdd,\n ariaInvalid,\n}: {\n options: ProviderOption[];\n excluded: string[];\n onAdd: (value: string) => void;\n ariaInvalid?: boolean;\n}) {\n const [open, setOpen] = useState(false);\n const [query, setQuery] = useState(\"\");\n\n const excludedSet = useMemo(() => new Set(excluded), [excluded]);\n const available = useMemo(\n () => options.filter((o) => !excludedSet.has(o.value)),\n [options, excludedSet],\n );\n\n const filtered = useMemo(() => {\n const q = query.trim().toLowerCase();\n if (!q) return available;\n return available.filter(\n (o) => o.label.toLowerCase().includes(q) || o.value.toLowerCase().includes(q),\n );\n }, [available, query]);\n\n if (available.length === 0 && excluded.length === 0) {\n return (\n <div className=\"pz:text-xs pz:text-muted-foreground pz:px-1.5 pz:py-2\">\n No providers available\n </div>\n );\n }\n\n if (available.length === 0) return null;\n\n return (\n <Popover\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) setQuery(\"\");\n }}\n >\n <PopoverTrigger\n aria-invalid={ariaInvalid || undefined}\n className={cn(\n \"pz:flex pz:items-center pz:justify-center pz:gap-1.5 pz:w-full\",\n \"pz:px-2.5 pz:py-1.5 pz:mt-1 pz:rounded-md\",\n \"pz:border pz:border-dashed pz:border-input pz:bg-transparent\",\n \"pz:text-xs pz:text-muted-foreground\",\n \"pz:hover:bg-muted/60 pz:hover:text-foreground pz:hover:border-muted-foreground/40\",\n \"pz:transition-colors pz:cursor-pointer\",\n )}\n >\n <IconPlus width={12} height={12} />\n Add provider\n </PopoverTrigger>\n <PopoverContent align=\"start\" className=\"pz:w-(--anchor-width) pz:p-0 pz:gap-0\">\n <div className=\"pz:flex pz:items-center pz:gap-1.5 pz:px-2.5 pz:py-2 pz:border-b pz:border-border\">\n <SearchIcon className=\"pz:size-3.5 pz:text-muted-foreground pz:shrink-0\" />\n <input\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder=\"Search providers…\"\n // biome-ignore lint/a11y/noAutofocus: popover-scoped picker\n autoFocus\n className=\"pz:flex-1 pz:bg-transparent pz:outline-none pz:text-sm pz:placeholder:text-muted-foreground/70\"\n />\n </div>\n <ul className=\"pz:flex pz:flex-col pz:p-1 pz:max-h-72 pz:overflow-auto pz:list-none pz:m-0\">\n {filtered.length === 0 && (\n <li className=\"pz:px-2 pz:py-3 pz:text-center pz:text-xs pz:text-muted-foreground\">\n No providers\n </li>\n )}\n {filtered.map((option) => {\n const { providerName, leading, pricing } = splitWidgets(option.widgets);\n return (\n <li key={option.value} className=\"pz:contents\">\n <button\n type=\"button\"\n onClick={() => {\n onAdd(option.value);\n setOpen(false);\n setQuery(\"\");\n }}\n className={cn(\n \"pz:flex pz:items-center pz:gap-2 pz:w-full pz:text-left\",\n \"pz:px-2 pz:py-1.5 pz:rounded-md pz:text-sm\",\n \"pz:hover:bg-accent pz:hover:text-accent-foreground\",\n \"pz:transition-colors pz:cursor-pointer\",\n )}\n >\n <LeadingAvatar providerName={providerName} leading={leading} />\n <span className=\"pz:flex-1 pz:truncate\">{option.label}</span>\n {pricing && <PricingBadge credits={pricing.credits} />}\n </button>\n </li>\n );\n })}\n </ul>\n </PopoverContent>\n </Popover>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ //#region src/components/defaults/adapters/search-payload-input.tsx
2
+ /**
3
+ * Default adapter for the opaque `search_payload_input` slot.
4
+ *
5
+ * The shared library intentionally renders nothing for this kind — configuring
6
+ * a `SearchPayload` requires the full search catalog + search form UI, which
7
+ * lives in the consuming app (`apps/dash`), not in `@pipe0/react`. Consumers
8
+ * override this adapter via `<FormProvider adapters={...}>` / the `adapters`
9
+ * prop to render a real control (e.g. a button that opens the pick → configure
10
+ * flow). See `FieldExtras["search_payload_input"]`.
11
+ */
12
+ function SearchPayloadInputAdapter(_field) {
13
+ return null;
14
+ }
15
+
16
+ //#endregion
17
+ export { SearchPayloadInputAdapter };
18
+ //# sourceMappingURL=search-payload-input.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-payload-input.mjs","names":[],"sources":["../../../../src/components/defaults/adapters/search-payload-input.tsx"],"sourcesContent":["import type { FieldHandle } from \"../../../types/field-handle.js\";\n\n/**\n * Default adapter for the opaque `search_payload_input` slot.\n *\n * The shared library intentionally renders nothing for this kind — configuring\n * a `SearchPayload` requires the full search catalog + search form UI, which\n * lives in the consuming app (`apps/dash`), not in `@pipe0/react`. Consumers\n * override this adapter via `<FormProvider adapters={...}>` / the `adapters`\n * prop to render a real control (e.g. a button that opens the pick → configure\n * flow). See `FieldExtras[\"search_payload_input\"]`.\n */\nexport function SearchPayloadInputAdapter(_field: FieldHandle<\"search_payload_input\">) {\n return null;\n}\n"],"mappings":";;;;;;;;;;;AAYA,SAAgB,0BAA0B,QAA6C;AACrF,QAAO"}
@@ -1,6 +1,7 @@
1
1
  import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "../../ui/select.mjs";
2
2
  import { WidgetStrip } from "../../../widgets/widget-strip.mjs";
3
3
  import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { X } from "lucide-react";
4
5
 
5
6
  //#region src/components/defaults/adapters/select-input.tsx
6
7
  function OptionContent({ label, widgets }) {
@@ -10,38 +11,56 @@ function OptionContent({ label, widgets }) {
10
11
  });
11
12
  }
12
13
  function SelectInputAdapter(field) {
13
- const placeholder = field.meta.placeholder ?? "— Select —";
14
+ const placeholder = field.meta.placeholder ?? "";
14
15
  const selectedOption = field.options.find((o) => o.value === field.selectedValue);
15
16
  const hasOptions = field.options.length > 0;
17
+ const isClearable = !field.meta.required && !!field.selectedValue;
16
18
  return /* @__PURE__ */ jsxs("div", {
17
19
  "data-p0": "input",
18
20
  className: "pz:flex pz:flex-col pz:gap-1",
19
- children: [/* @__PURE__ */ jsxs(Select, {
20
- value: field.selectedValue || null,
21
- onValueChange: (v) => {
22
- if (v === null) return;
23
- field.onSelect(v);
24
- },
25
- name: field.path,
26
- disabled: !hasOptions,
27
- children: [/* @__PURE__ */ jsx(SelectTrigger, {
28
- id: field.id,
29
- "aria-invalid": !!field.error,
30
- className: "pz:w-full",
31
- children: /* @__PURE__ */ jsx(SelectValue, {
32
- placeholder,
33
- children: selectedOption ? /* @__PURE__ */ jsx(OptionContent, {
34
- label: selectedOption.label,
35
- widgets: selectedOption.widgets
36
- }) : void 0
37
- })
38
- }), /* @__PURE__ */ jsx(SelectContent, { children: /* @__PURE__ */ jsx(SelectGroup, { children: field.options.map((opt) => /* @__PURE__ */ jsx(SelectItem, {
39
- value: opt.value,
40
- children: /* @__PURE__ */ jsx(OptionContent, {
41
- label: opt.label,
42
- widgets: opt.widgets
43
- })
44
- }, opt.value)) }) })]
21
+ children: [/* @__PURE__ */ jsxs("div", {
22
+ className: "pz:relative",
23
+ children: [/* @__PURE__ */ jsxs(Select, {
24
+ value: field.selectedValue || null,
25
+ onValueChange: (v) => {
26
+ if (v === null) return;
27
+ field.onSelect(v);
28
+ },
29
+ name: field.path,
30
+ disabled: !hasOptions,
31
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
32
+ id: field.id,
33
+ "aria-invalid": !!field.error,
34
+ className: "pz:w-full",
35
+ children: /* @__PURE__ */ jsx(SelectValue, {
36
+ placeholder,
37
+ className: isClearable ? "pz:pr-6" : void 0,
38
+ children: selectedOption ? /* @__PURE__ */ jsx(OptionContent, {
39
+ label: selectedOption.label,
40
+ widgets: selectedOption.widgets
41
+ }) : void 0
42
+ })
43
+ }), /* @__PURE__ */ jsx(SelectContent, { children: /* @__PURE__ */ jsx(SelectGroup, { children: field.options.map((opt) => /* @__PURE__ */ jsx(SelectItem, {
44
+ value: opt.value,
45
+ children: /* @__PURE__ */ jsx(OptionContent, {
46
+ label: opt.label,
47
+ widgets: opt.widgets
48
+ })
49
+ }, opt.value)) }) })]
50
+ }), isClearable && /* @__PURE__ */ jsx("button", {
51
+ type: "button",
52
+ "aria-label": "Clear selection",
53
+ onClick: () => field.onSelect(""),
54
+ style: {
55
+ position: "absolute",
56
+ top: "50%",
57
+ right: "1.75rem",
58
+ transform: "translateY(-50%)",
59
+ zIndex: 1
60
+ },
61
+ className: "pz:flex pz:size-4 pz:cursor-pointer pz:items-center pz:justify-center pz:rounded pz:text-muted-foreground pz:transition-colors pz:hover:text-foreground",
62
+ children: /* @__PURE__ */ jsx(X, { className: "pz:size-3.5" })
63
+ })]
45
64
  }), !hasOptions && /* @__PURE__ */ jsx("span", {
46
65
  className: "pz:text-xs pz:text-muted-foreground",
47
66
  children: "No options available"
@@ -1 +1 @@
1
- {"version":3,"file":"select-input.mjs","names":[],"sources":["../../../../src/components/defaults/adapters/select-input.tsx"],"sourcesContent":["import type { WidgetsByKind } from \"@pipe0/base\";\nimport type { FieldHandle } from \"../../../types/field-handle.js\";\nimport { WidgetStrip } from \"../../../widgets/widget-strip.js\";\nimport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"../../ui/select.js\";\n\nfunction OptionContent({ label, widgets }: { label: string; widgets?: WidgetsByKind }) {\n return (\n <span className=\"pz:flex pz:items-center pz:gap-2\">\n <WidgetStrip widgets={widgets} />\n <span>{label}</span>\n </span>\n );\n}\n\nexport function SelectInputAdapter(field: FieldHandle<\"select_input\">) {\n const placeholder = field.meta.placeholder ?? \"— Select —\";\n const selectedOption = field.options.find((o) => o.value === field.selectedValue);\n const hasOptions = field.options.length > 0;\n\n return (\n <div data-p0=\"input\" className=\"pz:flex pz:flex-col pz:gap-1\">\n <Select\n // `null` (not `undefined`) keeps the Select controlled for its whole\n // lifetime — `undefined` makes it uncontrolled while empty and flips to\n // controlled on first selection, which warns.\n value={field.selectedValue || null}\n onValueChange={(v) => {\n if (v === null) return;\n field.onSelect(v);\n }}\n name={field.path}\n disabled={!hasOptions}\n >\n <SelectTrigger id={field.id} aria-invalid={!!field.error} className=\"pz:w-full\">\n <SelectValue placeholder={placeholder}>\n {selectedOption ? (\n <OptionContent label={selectedOption.label} widgets={selectedOption.widgets} />\n ) : undefined}\n </SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectGroup>\n {field.options.map((opt) => (\n <SelectItem key={opt.value} value={opt.value}>\n <OptionContent label={opt.label} widgets={opt.widgets} />\n </SelectItem>\n ))}\n </SelectGroup>\n </SelectContent>\n </Select>\n {!hasOptions && (\n <span className=\"pz:text-xs pz:text-muted-foreground\">No options available</span>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;AAYA,SAAS,cAAc,EAAE,OAAO,WAAuD;AACrF,QACE,qBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,oBAAC,aAAD,EAAsB,SAAW,GACjC,oBAAC,QAAD,YAAO,OAAa,EACf;;;AAIX,SAAgB,mBAAmB,OAAoC;CACrE,MAAM,cAAc,MAAM,KAAK,eAAe;CAC9C,MAAM,iBAAiB,MAAM,QAAQ,MAAM,MAAM,EAAE,UAAU,MAAM,cAAc;CACjF,MAAM,aAAa,MAAM,QAAQ,SAAS;AAE1C,QACE,qBAAC,OAAD;EAAK,WAAQ;EAAQ,WAAU;YAA/B,CACE,qBAAC,QAAD;GAIE,OAAO,MAAM,iBAAiB;GAC9B,gBAAgB,MAAM;AACpB,QAAI,MAAM,KAAM;AAChB,UAAM,SAAS,EAAE;;GAEnB,MAAM,MAAM;GACZ,UAAU,CAAC;aAVb,CAYE,oBAAC,eAAD;IAAe,IAAI,MAAM;IAAI,gBAAc,CAAC,CAAC,MAAM;IAAO,WAAU;cAClE,oBAAC,aAAD;KAA0B;eACvB,iBACC,oBAAC,eAAD;MAAe,OAAO,eAAe;MAAO,SAAS,eAAe;MAAW,IAC7E;KACQ;IACA,GAChB,oBAAC,eAAD,YACE,oBAAC,aAAD,YACG,MAAM,QAAQ,KAAK,QAClB,oBAAC,YAAD;IAA4B,OAAO,IAAI;cACrC,oBAAC,eAAD;KAAe,OAAO,IAAI;KAAO,SAAS,IAAI;KAAW;IAC9C,EAFI,IAAI,MAER,CACb,EACU,GACA,EACT;MACR,CAAC,cACA,oBAAC,QAAD;GAAM,WAAU;aAAsC;GAA2B,EAE/E"}
1
+ {"version":3,"file":"select-input.mjs","names":[],"sources":["../../../../src/components/defaults/adapters/select-input.tsx"],"sourcesContent":["import type { WidgetsByKind } from \"@pipe0/base\";\nimport { X } from \"lucide-react\";\nimport type { FieldHandle } from \"../../../types/field-handle.js\";\nimport { WidgetStrip } from \"../../../widgets/widget-strip.js\";\nimport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"../../ui/select.js\";\n\nfunction OptionContent({ label, widgets }: { label: string; widgets?: WidgetsByKind }) {\n return (\n <span className=\"pz:flex pz:items-center pz:gap-2\">\n <WidgetStrip widgets={widgets} />\n <span>{label}</span>\n </span>\n );\n}\n\nexport function SelectInputAdapter(field: FieldHandle<\"select_input\">) {\n const placeholder = field.meta.placeholder ?? \"\";\n const selectedOption = field.options.find((o) => o.value === field.selectedValue);\n const hasOptions = field.options.length > 0;\n // Optional fields need a way back to the empty state — the base `Select` has\n // no built-in clear. Show an inline reset only once a value is set.\n const isClearable = !field.meta.required && !!field.selectedValue;\n\n return (\n <div data-p0=\"input\" className=\"pz:flex pz:flex-col pz:gap-1\">\n <div className=\"pz:relative\">\n <Select\n // `null` (not `undefined`) keeps the Select controlled for its whole\n // lifetime — `undefined` makes it uncontrolled while empty and flips to\n // controlled on first selection, which warns.\n value={field.selectedValue || null}\n onValueChange={(v) => {\n if (v === null) return;\n field.onSelect(v);\n }}\n name={field.path}\n disabled={!hasOptions}\n >\n <SelectTrigger id={field.id} aria-invalid={!!field.error} className=\"pz:w-full\">\n {/* Pad the value (not the trigger) so a long label doesn't run under\n the clear button — padding the trigger would shift the chevron. */}\n <SelectValue placeholder={placeholder} className={isClearable ? \"pz:pr-6\" : undefined}>\n {selectedOption ? (\n <OptionContent label={selectedOption.label} widgets={selectedOption.widgets} />\n ) : undefined}\n </SelectValue>\n </SelectTrigger>\n <SelectContent>\n <SelectGroup>\n {field.options.map((opt) => (\n <SelectItem key={opt.value} value={opt.value}>\n <OptionContent label={opt.label} widgets={opt.widgets} />\n </SelectItem>\n ))}\n </SelectGroup>\n </SelectContent>\n </Select>\n {isClearable && (\n // A real <button>, rendered as a SIBLING of the trigger (not a child):\n // Base UI's `Select.Trigger` is a native <button> that opens on its own\n // listener, so a nested control's click is swallowed before React can\n // stop it. As an overlay sibling the event never reaches the trigger.\n // Positioned inline just left of the chevron (which keeps its default\n // far-right spot since the trigger padding is untouched).\n <button\n type=\"button\"\n aria-label=\"Clear selection\"\n onClick={() => field.onSelect(\"\")}\n style={{\n position: \"absolute\",\n top: \"50%\",\n right: \"1.75rem\",\n transform: \"translateY(-50%)\",\n zIndex: 1,\n }}\n className=\"pz:flex pz:size-4 pz:cursor-pointer pz:items-center pz:justify-center pz:rounded pz:text-muted-foreground pz:transition-colors pz:hover:text-foreground\"\n >\n <X className=\"pz:size-3.5\" />\n </button>\n )}\n </div>\n {!hasOptions && (\n <span className=\"pz:text-xs pz:text-muted-foreground\">No options available</span>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;AAaA,SAAS,cAAc,EAAE,OAAO,WAAuD;AACrF,QACE,qBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,oBAAC,aAAD,EAAsB,SAAW,GACjC,oBAAC,QAAD,YAAO,OAAa,EACf;;;AAIX,SAAgB,mBAAmB,OAAoC;CACrE,MAAM,cAAc,MAAM,KAAK,eAAe;CAC9C,MAAM,iBAAiB,MAAM,QAAQ,MAAM,MAAM,EAAE,UAAU,MAAM,cAAc;CACjF,MAAM,aAAa,MAAM,QAAQ,SAAS;CAG1C,MAAM,cAAc,CAAC,MAAM,KAAK,YAAY,CAAC,CAAC,MAAM;AAEpD,QACE,qBAAC,OAAD;EAAK,WAAQ;EAAQ,WAAU;YAA/B,CACE,qBAAC,OAAD;GAAK,WAAU;aAAf,CACE,qBAAC,QAAD;IAIE,OAAO,MAAM,iBAAiB;IAC9B,gBAAgB,MAAM;AACpB,SAAI,MAAM,KAAM;AAChB,WAAM,SAAS,EAAE;;IAEnB,MAAM,MAAM;IACZ,UAAU,CAAC;cAVb,CAYE,oBAAC,eAAD;KAAe,IAAI,MAAM;KAAI,gBAAc,CAAC,CAAC,MAAM;KAAO,WAAU;eAGlE,oBAAC,aAAD;MAA0B;MAAa,WAAW,cAAc,YAAY;gBACzE,iBACC,oBAAC,eAAD;OAAe,OAAO,eAAe;OAAO,SAAS,eAAe;OAAW,IAC7E;MACQ;KACA,GAChB,oBAAC,eAAD,YACE,oBAAC,aAAD,YACG,MAAM,QAAQ,KAAK,QAClB,oBAAC,YAAD;KAA4B,OAAO,IAAI;eACrC,oBAAC,eAAD;MAAe,OAAO,IAAI;MAAO,SAAS,IAAI;MAAW;KAC9C,EAFI,IAAI,MAER,CACb,EACU,GACA,EACT;OACR,eAOC,oBAAC,UAAD;IACE,MAAK;IACL,cAAW;IACX,eAAe,MAAM,SAAS,GAAG;IACjC,OAAO;KACL,UAAU;KACV,KAAK;KACL,OAAO;KACP,WAAW;KACX,QAAQ;KACT;IACD,WAAU;cAEV,oBAAC,GAAD,EAAG,WAAU,eAAgB;IACtB,EAEP;MACL,CAAC,cACA,oBAAC,QAAD;GAAM,WAAU;aAAsC;GAA2B,EAE/E"}
@@ -7,7 +7,7 @@ interface CatalogCreditBadgeProps {
7
7
  /** Override the auto-bound credit amount. */
8
8
  creditAmount?: number;
9
9
  /** Override the auto-bound cost mode. */
10
- costMode?: "per_result" | "per_search" | "per_page";
10
+ costMode?: "per_result" | "per_search" | "per_page" | "usage";
11
11
  freeLabel?: ReactNode;
12
12
  creditPrefix?: ReactNode;
13
13
  className?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"card-derived.d.mts","names":[],"sources":["../../../../src/components/defaults/catalog/card-derived.tsx"],"mappings":";;;;;UAgBiB,uBAAA;;EAEf,YAAA;EAFe;EAIf,QAAA;EACA,SAAA,GAAY,SAAA;EACZ,YAAA,GAAe,SAAA;EACf,SAAA;EACA,QAAA;EACA,OAAA,GAAU,qBAAA;AAAA;;;;;;iBAQI,kBAAA,CAAA;EACd,YAAA;EACA,QAAA;EACA,SAAA;EACA,YAAA;EACA,QAAA;EACA,SAAA;EACA;AAAA,GACC,uBAAA,GAAuB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,UA8BT,sBAAA;EACf,MAAA;EACA,MAAA;EACA,OAAA,GAAU,IAAA;EAjDqB;EAmD/B,QAAA,GAAW,SAAA;EACX,gBAAA,GAAmB,SAAA;AAAA;AAAA,UAGJ,sBAAA;EACf,SAAA;EA9CA;EAgDA,MAAA;EA9CA;EAgDA,aAAA,IAAiB,SAAA;EA9CjB;EAgDA,gBAAA,IAAoB,SAAA;EACpB,KAAA,GAAQ,SAAA;EACR,SAAA,IAAa,SAAA,aAAsB,SAAA;EACnC,SAAA;EACA,OAAA,GAAU,qBAAA;EAzDV;EA2DA,MAAA,IAAU,KAAA,EAAO,sBAAA,KAA2B,SAAA;AAAA;;;;;;;iBAS9B,iBAAA,CAAA;EACd,SAAA;EACA,MAAA;EACA,aAAA;EACA,gBAAA;EACA,KAAA;EACA,SAAA;EACA,SAAA;EACA,OAAA;EACA;AAAA,GACC,sBAAA,GAAsB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,UAwHR,qBAAA;EAjMf;EAmMA,OAAA;EAlMA;EAoMA,IAAA;EACA,KAAA,GAAQ,SAAA;EACR,SAAA;EACA,OAAA,GAAU,qBAAA;AAAA;AAAA,iBAGI,gBAAA,CAAA;EACd,OAAA;EACA,IAAA;EACA,KAAA;EACA,SAAA;EACA;AAAA,GACC,qBAAA,GAAqB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,UAyBP,kBAAA;;EAEf,KAAA;EACA,SAAA;AAAA;AAAA,iBAGc,aAAA,CAAA;EAAgB,KAAA;EAAO;AAAA,GAAa,kBAAA,GAAkB,oBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"card-derived.d.mts","names":[],"sources":["../../../../src/components/defaults/catalog/card-derived.tsx"],"mappings":";;;;;UAgBiB,uBAAA;;EAEf,YAAA;EAFe;EAIf,QAAA;EACA,SAAA,GAAY,SAAA;EACZ,YAAA,GAAe,SAAA;EACf,SAAA;EACA,QAAA;EACA,OAAA,GAAU,qBAAA;AAAA;;;;;;iBAQI,kBAAA,CAAA;EACd,YAAA;EACA,QAAA;EACA,SAAA;EACA,YAAA;EACA,QAAA;EACA,SAAA;EACA;AAAA,GACC,uBAAA,GAAuB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,UAqCT,sBAAA;EACf,MAAA;EACA,MAAA;EACA,OAAA,GAAU,IAAA;EAxDqB;EA0D/B,QAAA,GAAW,SAAA;EACX,gBAAA,GAAmB,SAAA;AAAA;AAAA,UAGJ,sBAAA;EACf,SAAA;EArDA;EAuDA,MAAA;EArDA;EAuDA,aAAA,IAAiB,SAAA;EArDjB;EAuDA,gBAAA,IAAoB,SAAA;EACpB,KAAA,GAAQ,SAAA;EACR,SAAA,IAAa,SAAA,aAAsB,SAAA;EACnC,SAAA;EACA,OAAA,GAAU,qBAAA;EAhEV;EAkEA,MAAA,IAAU,KAAA,EAAO,sBAAA,KAA2B,SAAA;AAAA;;;;;;;iBAS9B,iBAAA,CAAA;EACd,SAAA;EACA,MAAA;EACA,aAAA;EACA,gBAAA;EACA,KAAA;EACA,SAAA;EACA,SAAA;EACA,OAAA;EACA;AAAA,GACC,sBAAA,GAAsB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,UAwHR,qBAAA;EAxMf;EA0MA,OAAA;EAzMA;EA2MA,IAAA;EACA,KAAA,GAAQ,SAAA;EACR,SAAA;EACA,OAAA,GAAU,qBAAA;AAAA;AAAA,iBAGI,gBAAA,CAAA;EACd,OAAA;EACA,IAAA;EACA,KAAA;EACA,SAAA;EACA;AAAA,GACC,qBAAA,GAAqB,oBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,UAyBP,kBAAA;;EAEf,KAAA;EACA,SAAA;AAAA;AAAA,iBAGc,aAAA,CAAA;EAAgB,KAAA;EAAO;AAAA,GAAa,kBAAA,GAAkB,oBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -18,18 +18,22 @@ function CatalogCreditBadge({ creditAmount, costMode, freeLabel = "Free", credit
18
18
  const amount = creditAmount ?? (card && "startingCreditAmount" in card ? card.startingCreditAmount : 0);
19
19
  const mode = costMode ?? (card && "costMode" in card ? card.costMode : "per_result");
20
20
  const denom = mode === "per_result" ? "result" : mode === "per_search" ? "search" : "page";
21
+ const display = Math.round(amount * 1e3) / 1e3;
21
22
  return /* @__PURE__ */ jsx(CatalogCardBadge, {
22
23
  disabled,
23
24
  variant,
24
25
  className: cn("pz:whitespace-nowrap", className),
25
- children: amount > 0 ? /* @__PURE__ */ jsxs("span", {
26
+ children: mode === "usage" ? /* @__PURE__ */ jsxs("span", {
27
+ className: "pz:inline-flex pz:items-center pz:gap-1",
28
+ children: [/* @__PURE__ */ jsx(Coins, { className: "pz:size-3" }), /* @__PURE__ */ jsx("span", { children: "Usage" })]
29
+ }) : display > 0 ? /* @__PURE__ */ jsxs("span", {
26
30
  className: "pz:inline-flex pz:items-center pz:gap-0.5",
27
31
  children: [
28
32
  creditPrefix ? /* @__PURE__ */ jsx("span", {
29
33
  className: "pz:mr-1",
30
34
  children: creditPrefix
31
35
  }) : null,
32
- /* @__PURE__ */ jsx("span", { children: amount }),
36
+ /* @__PURE__ */ jsx("span", { children: display }),
33
37
  /* @__PURE__ */ jsx(Coins, { className: "pz:size-3" }),
34
38
  /* @__PURE__ */ jsxs("span", { children: ["/ ", denom] })
35
39
  ]