@tetrascience-npm/tetrascience-react-ui 1.0.0-beta.157.1 → 1.0.0-beta.158.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 (68) hide show
  1. package/README.md +113 -0
  2. package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.cjs +1 -1
  3. package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.cjs.map +1 -1
  4. package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.d.ts +5 -1
  5. package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.js +32 -30
  6. package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.js.map +1 -1
  7. package/dist/components/composed/PlateMapEditor/PlateMapEditor.cjs +1 -1
  8. package/dist/components/composed/PlateMapEditor/PlateMapEditor.cjs.map +1 -1
  9. package/dist/components/composed/PlateMapEditor/PlateMapEditor.d.ts +269 -13
  10. package/dist/components/composed/PlateMapEditor/PlateMapEditor.js +392 -310
  11. package/dist/components/composed/PlateMapEditor/PlateMapEditor.js.map +1 -1
  12. package/dist/components/composed/PlateMapEditor/PlateMapForm.cjs +1 -1
  13. package/dist/components/composed/PlateMapEditor/PlateMapForm.cjs.map +1 -1
  14. package/dist/components/composed/PlateMapEditor/PlateMapForm.d.ts +5 -1
  15. package/dist/components/composed/PlateMapEditor/PlateMapForm.js +19 -15
  16. package/dist/components/composed/PlateMapEditor/PlateMapForm.js.map +1 -1
  17. package/dist/components/composed/PlateMapEditor/PlateMapGrid.cjs +1 -1
  18. package/dist/components/composed/PlateMapEditor/PlateMapGrid.cjs.map +1 -1
  19. package/dist/components/composed/PlateMapEditor/PlateMapGrid.d.ts +12 -1
  20. package/dist/components/composed/PlateMapEditor/PlateMapGrid.js +69 -63
  21. package/dist/components/composed/PlateMapEditor/PlateMapGrid.js.map +1 -1
  22. package/dist/components/composed/PlateMapEditor/PlateMapManifest.cjs +1 -1
  23. package/dist/components/composed/PlateMapEditor/PlateMapManifest.cjs.map +1 -1
  24. package/dist/components/composed/PlateMapEditor/PlateMapManifest.d.ts +13 -4
  25. package/dist/components/composed/PlateMapEditor/PlateMapManifest.js +38 -29
  26. package/dist/components/composed/PlateMapEditor/PlateMapManifest.js.map +1 -1
  27. package/dist/components/composed/PlateMapEditor/PlatePaintGrid.cjs +1 -1
  28. package/dist/components/composed/PlateMapEditor/PlatePaintGrid.cjs.map +1 -1
  29. package/dist/components/composed/PlateMapEditor/PlatePaintGrid.d.ts +6 -1
  30. package/dist/components/composed/PlateMapEditor/PlatePaintGrid.js +490 -275
  31. package/dist/components/composed/PlateMapEditor/PlatePaintGrid.js.map +1 -1
  32. package/dist/components/composed/PlateMapEditor/WellLegend.cjs +1 -1
  33. package/dist/components/composed/PlateMapEditor/WellLegend.cjs.map +1 -1
  34. package/dist/components/composed/PlateMapEditor/WellLegend.d.ts +8 -2
  35. package/dist/components/composed/PlateMapEditor/WellLegend.js +8 -7
  36. package/dist/components/composed/PlateMapEditor/WellLegend.js.map +1 -1
  37. package/dist/components/composed/PlateMapEditor/WellManifestTable.cjs +1 -1
  38. package/dist/components/composed/PlateMapEditor/WellManifestTable.cjs.map +1 -1
  39. package/dist/components/composed/PlateMapEditor/WellManifestTable.d.ts +6 -2
  40. package/dist/components/composed/PlateMapEditor/WellManifestTable.js +288 -256
  41. package/dist/components/composed/PlateMapEditor/WellManifestTable.js.map +1 -1
  42. package/dist/components/composed/PlateMapEditor/WellMetadataForm.cjs +1 -1
  43. package/dist/components/composed/PlateMapEditor/WellMetadataForm.cjs.map +1 -1
  44. package/dist/components/composed/PlateMapEditor/WellMetadataForm.d.ts +5 -1
  45. package/dist/components/composed/PlateMapEditor/WellMetadataForm.js +61 -66
  46. package/dist/components/composed/PlateMapEditor/WellMetadataForm.js.map +1 -1
  47. package/dist/components/composed/PlateMapEditor/WellQuickPaint.cjs +2 -0
  48. package/dist/components/composed/PlateMapEditor/WellQuickPaint.cjs.map +1 -0
  49. package/dist/components/composed/PlateMapEditor/WellQuickPaint.d.ts +23 -0
  50. package/dist/components/composed/PlateMapEditor/WellQuickPaint.js +75 -0
  51. package/dist/components/composed/PlateMapEditor/WellQuickPaint.js.map +1 -0
  52. package/dist/components/composed/PlateMapEditor/index.d.ts +6 -2
  53. package/dist/components/composed/PlateMapEditor/types.d.ts +65 -0
  54. package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.cjs +2 -0
  55. package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.cjs.map +1 -0
  56. package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.d.ts +109 -0
  57. package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.js +246 -0
  58. package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.js.map +1 -0
  59. package/dist/composed/PlateMapEditor.cjs +1 -1
  60. package/dist/composed/PlateMapEditor.js +48 -42
  61. package/dist/composed/PlateMapEditor.js.map +1 -1
  62. package/dist/index.cjs +1 -1
  63. package/dist/index.css +1 -1
  64. package/dist/index.js +701 -695
  65. package/dist/index.js.map +1 -1
  66. package/dist/index.scoped.css +1 -1
  67. package/dist/index.tailwind.css +1 -1
  68. package/package.json +1 -1
package/README.md CHANGED
@@ -306,6 +306,119 @@ For AI-assisted consuming apps, add a short instruction like this to the app's `
306
306
  Use `ProcessFlow` from `@tetrascience-npm/tetrascience-react-ui` for multi-step workflow visualization. Do not build a custom stepper for upload, validation, review, approval, processing, or setup flows. Parent components own the workflow state and pass `steps: ProcessFlowStep[]`; each step status must be one of `PROCESS_FLOW_STEP_STATUSES`. Use `selectedStepId` only for the viewed/selected step. Keep completion/error side effects in the parent workflow code, not inside `ProcessFlow`.
307
307
  ```
308
308
 
309
+ #### PlateMapEditor
310
+
311
+ `PlateMapEditor` is the standard plate-map editing surface — a metadata form, an
312
+ interactive plate grid, and a sample manifest, wired together by a staged-edit
313
+ controller. Use it as-is for the default layout; tune it with props; and drop to
314
+ `usePlateMapEditorState` only when you need a layout the props can't express.
315
+
316
+ ```tsx
317
+ import { PlateMapEditor } from "@tetrascience-npm/tetrascience-react-ui";
318
+
319
+ function PlateScreen() {
320
+ const [values, setValues] = React.useState(new Map());
321
+ const [selection, setSelection] = React.useState(new Set());
322
+
323
+ return (
324
+ <PlateMapEditor
325
+ format="96"
326
+ values={values}
327
+ onChange={setValues}
328
+ selection={selection}
329
+ onSelectionChange={setSelection}
330
+ fields={FIELDS}
331
+ tableColumns={COLUMNS}
332
+ />
333
+ );
334
+ }
335
+ ```
336
+
337
+ ##### Choosing a level
338
+
339
+ | You need | Use |
340
+ | --- | --- |
341
+ | The standard surface, tuned by props | `PlateMapEditor` |
342
+ | The form somewhere the editor can't reach | `PlateMapEditor` + `hideForm` + the imperative handle |
343
+ | A layout no prop combination expresses | `usePlateMapEditorState` + `PlateMapForm` / `PlateMapGrid` / `PlateMapManifest` |
344
+
345
+ Dropping to the hook keeps the apply/clear semantics, plate scoping, and barcode
346
+ stamping — you only take over layout. Do not re-implement staged edits by hand.
347
+
348
+ ##### Customization
349
+
350
+ - **Layout** — `formPlacement` (`start` / `end` / `top` / `bottom`), `stackAt`,
351
+ `formWidth`, `hideForm`, `hideManifest`.
352
+ - **Slots** — `banner` (whole editor), `plateBanner` (plate card only),
353
+ `plateToolbar` (above grid), `plateFooter` (the plate card's footer),
354
+ `footer` (editor-wide action row), `legend` + `legendPlacement`,
355
+ `formExtras`, `formSlot`, `manifestSlot`.
356
+ - **Edit state** — `staged` / `onStagedChange` for a controlled staged record,
357
+ `mergeOnApply` for custom merge semantics, `applyScope="all-plates"` to write
358
+ across every plate at once.
359
+ - **Manifest** — one `manifest` object: `{ filterable, filterColumns, groupable,
360
+ defaultGroupBy, pageSize, pageSizeOptions, enableFillDown }`. Structural bits
361
+ stay top-level: `hideManifest`, `manifestTitle`, `manifestSlot`.
362
+ - **Labels** — one `labels` object covers every string the editor and its
363
+ manifest render, typed as the exported `PlateMapEditorLabels`, so an app's
364
+ translation table is a type error away from going stale:
365
+ `const fr: PlateMapEditorLabels = { … }`. `plateTitle` / `manifestTitle` and
366
+ the import/export menu labels are separate props (they take `ReactNode`, not
367
+ plain text).
368
+ - **Styling** — `className` / `style` on the root, plus one `classNames` map for
369
+ the regions (`PlateMapEditorClassNames`): `{ layout, formCard, plateCard,
370
+ manifestCard, form, grid, manifest }`. Each card also carries
371
+ `data-plate-map-region="form|plate|manifest"`, so plain CSS can target the
372
+ same regions without threading props.
373
+
374
+ ##### Rendering the form outside the editor
375
+
376
+ Hide the built-in form and drive the staged state through the imperative handle.
377
+ The editor keeps owning selection and apply; your form just feeds it.
378
+
379
+ ```tsx
380
+ const editor = React.useRef<PlateMapEditorHandle<MyWell>>(null);
381
+
382
+ <>
383
+ <MySidebarForm
384
+ onChange={(next) => editor.current?.setStaged(next)}
385
+ onApply={() => editor.current?.apply()}
386
+ onClear={() => editor.current?.clear()}
387
+ />
388
+ <PlateMapEditor ref={editor} hideForm {...rest} />
389
+ </>;
390
+ ```
391
+
392
+ ##### Responsiveness
393
+
394
+ The editor responds to **its container's** width, not the viewport's, so it lays
395
+ out correctly inside a narrow panel, split pane, or drawer on a wide screen.
396
+ `stackAt` names a container width — `sm` 640 / `md` 768 (default) / `lg` 1024 /
397
+ `xl` 1280 / `never` — below which the form and grid stack full-width. A plate too
398
+ dense to fit scrolls inside its own container rather than widening the page.
399
+
400
+ ##### Migration notes
401
+
402
+ Everything below is additive; existing code keeps working unchanged.
403
+
404
+ - **Layout now tracks container width, not viewport width.** This is the one
405
+ behavioural change. If your editor is full-width the result is effectively the
406
+ same; if it sits in a narrow panel on a wide screen it will now correctly
407
+ stack instead of rendering a cramped two-column layout. Tune with `stackAt`.
408
+ - `colorForWell` and `emptyEntry` are now **optional** — delete them for
409
+ read-only or single-category views and sensible defaults apply.
410
+ - `className` on the root always worked despite reports otherwise; `style` is
411
+ new.
412
+ - If you previously forked or re-assembled the primitives to change layout,
413
+ replace that with `formPlacement` / `stackAt` / `formWidth` and the slots, or
414
+ with `usePlateMapEditorState` if you still need custom structure. Hand-rolled
415
+ staged/apply logic should be deleted in favour of the hook.
416
+ - If you worked around the hardcoded 360px form column with a width utility,
417
+ switch to `formWidth` — it is the supported path and applies only at and above
418
+ `stackAt`.
419
+ - `manifestFilterable` and `manifestGroupable` still work but are deprecated in
420
+ favour of `manifest={{ filterable, groupable }}`.
421
+
309
422
  ### Charts (`charts/`)
310
423
 
311
424
  Plotly.js-based data visualisations:
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),x=require("lucide-react"),u=require("../../../ui/button.cjs"),w=require("../../../ui/input.cjs"),h=require("../../../ui/popover.cjs"),l=require("../../../ui/select.cjs"),z=require("../../../lib/utils.cjs"),F={contains:"contains",equals:"equals",not_equals:"not equals",starts_with:"starts with",ends_with:"ends with",is_empty:"is empty",is_not_empty:"is not empty"},P=["is_empty","is_not_empty"],f=["contains","equals","not_equals","starts_with","ends_with","is_empty","is_not_empty"];function R(n,d){const a=d?.[0]??"contains";return{id:crypto.randomUUID(),columnId:n,operator:a,value:""}}function O({columns:n,columnLabel:d,filters:a,onFiltersChange:i,triggerLabel:m="Filter",className:g}){const _=t=>d?.(t)??t,v=n[0],y=v?.columnId??"",C=()=>i([...a,R(y,v?.operators)]),I=t=>i(a.filter(c=>c.id!==t)),p=(t,c)=>i(a.map(r=>r.id===t?{...r,...c}:r)),N=()=>i([]),o=a.length;return e.jsxs(h.Popover,{children:[e.jsx(h.PopoverTrigger,{asChild:!0,children:e.jsxs(u.Button,{type:"button",variant:"outline",size:"sm","data-slot":"manifest-filter",className:z.cn(g),"aria-label":o>0?`${m} (${o} active)`:m,children:[e.jsx(x.ListFilterIcon,{className:"size-3.5"}),m,o>0?e.jsx("span",{className:"flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground",children:o}):null]})}),e.jsx(h.PopoverContent,{align:"end",className:"min-w-80","aria-label":"Filter conditions",children:e.jsxs("div",{className:"flex flex-col gap-2",children:[a.map(t=>{const r=n.find(s=>s.columnId===t.columnId)?.operators??f,S=P.includes(t.operator);return e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(l.Select,{value:t.columnId,onValueChange:s=>{const j=n.find(q=>q.columnId===s)?.operators??f,b=j.includes(t.operator)?t.operator:j[0]??"contains";p(t.id,{columnId:s,operator:b,value:""})},children:[e.jsx(l.SelectTrigger,{"aria-label":"Filter column",size:"sm",className:"w-36",children:e.jsx(l.SelectValue,{})}),e.jsx(l.SelectContent,{children:n.map(s=>e.jsx(l.SelectItem,{value:s.columnId,children:s.label??_(s.columnId)},s.columnId))})]}),e.jsxs(l.Select,{value:t.operator,onValueChange:s=>p(t.id,{operator:s,value:""}),children:[e.jsx(l.SelectTrigger,{"aria-label":"Filter operator",size:"sm",className:"w-32",children:e.jsx(l.SelectValue,{})}),e.jsx(l.SelectContent,{children:r.map(s=>e.jsx(l.SelectItem,{value:s,children:F[s]},s))})]}),S?e.jsx("div",{className:"h-8 w-40","aria-hidden":!0}):e.jsx(w.Input,{className:"w-40",placeholder:"Value…",value:t.value,onChange:s=>p(t.id,{value:s.target.value})}),e.jsx(u.Button,{type:"button",variant:"ghost",size:"icon",className:"size-8 shrink-0 text-muted-foreground hover:text-foreground",onClick:()=>I(t.id),"aria-label":"Remove filter",children:e.jsx(x.XIcon,{className:"size-3.5"})})]},t.id)}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(u.Button,{type:"button",variant:"outline",size:"sm",onClick:C,disabled:n.length===0,children:[e.jsx(x.PlusIcon,{className:"size-3.5"}),"Add filter"]}),a.length>0?e.jsx(u.Button,{type:"button",variant:"ghost",size:"sm",className:"text-muted-foreground",onClick:N,children:"Clear all"}):null]})]})})]})}exports.ManifestFilterPopover=O;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),x=require("lucide-react"),u=require("../../../ui/button.cjs"),F=require("../../../ui/input.cjs"),h=require("../../../ui/popover.cjs"),l=require("../../../ui/select.cjs"),P=require("../../../lib/utils.cjs"),R={contains:"contains",equals:"equals",not_equals:"not equals",starts_with:"starts with",ends_with:"ends with",is_empty:"is empty",is_not_empty:"is not empty"},O=["is_empty","is_not_empty"],f=["contains","equals","not_equals","starts_with","ends_with","is_empty","is_not_empty"];function A(n,d){const a=d?.[0]??"contains";return{id:crypto.randomUUID(),columnId:n,operator:a,value:""}}function E({columns:n,columnLabel:d,filters:a,onFiltersChange:i,triggerLabel:m="Filter",valuePlaceholder:g="Value…",clearAllLabel:_="Clear all",className:y}){const C=t=>d?.(t)??t,v=n[0],I=v?.columnId??"",N=()=>i([...a,A(I,v?.operators)]),S=t=>i(a.filter(c=>c.id!==t)),p=(t,c)=>i(a.map(r=>r.id===t?{...r,...c}:r)),b=()=>i([]),o=a.length;return e.jsxs(h.Popover,{children:[e.jsx(h.PopoverTrigger,{asChild:!0,children:e.jsxs(u.Button,{type:"button",variant:"outline",size:"sm","data-slot":"manifest-filter",className:P.cn(y),"aria-label":o>0?`${m} (${o} active)`:m,children:[e.jsx(x.ListFilterIcon,{className:"size-3.5"}),m,o>0?e.jsx("span",{className:"flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground",children:o}):null]})}),e.jsx(h.PopoverContent,{align:"end",className:"min-w-80","aria-label":"Filter conditions",children:e.jsxs("div",{className:"flex flex-col gap-2",children:[a.map(t=>{const r=n.find(s=>s.columnId===t.columnId)?.operators??f,q=O.includes(t.operator);return e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(l.Select,{value:t.columnId,onValueChange:s=>{const j=n.find(z=>z.columnId===s)?.operators??f,w=j.includes(t.operator)?t.operator:j[0]??"contains";p(t.id,{columnId:s,operator:w,value:""})},children:[e.jsx(l.SelectTrigger,{"aria-label":"Filter column",size:"sm",className:"w-36",children:e.jsx(l.SelectValue,{})}),e.jsx(l.SelectContent,{children:n.map(s=>e.jsx(l.SelectItem,{value:s.columnId,children:s.label??C(s.columnId)},s.columnId))})]}),e.jsxs(l.Select,{value:t.operator,onValueChange:s=>p(t.id,{operator:s,value:""}),children:[e.jsx(l.SelectTrigger,{"aria-label":"Filter operator",size:"sm",className:"w-32",children:e.jsx(l.SelectValue,{})}),e.jsx(l.SelectContent,{children:r.map(s=>e.jsx(l.SelectItem,{value:s,children:R[s]},s))})]}),q?e.jsx("div",{className:"h-8 w-40","aria-hidden":!0}):e.jsx(F.Input,{className:"w-40",placeholder:g,value:t.value,onChange:s=>p(t.id,{value:s.target.value})}),e.jsx(u.Button,{type:"button",variant:"ghost",size:"icon",className:"size-8 shrink-0 text-muted-foreground hover:text-foreground",onClick:()=>S(t.id),"aria-label":"Remove filter",children:e.jsx(x.XIcon,{className:"size-3.5"})})]},t.id)}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(u.Button,{type:"button",variant:"outline",size:"sm",onClick:N,disabled:n.length===0,children:[e.jsx(x.PlusIcon,{className:"size-3.5"}),"Add filter"]}),a.length>0?e.jsx(u.Button,{type:"button",variant:"ghost",size:"sm",className:"text-muted-foreground",onClick:b,children:_}):null]})]})})]})}exports.ManifestFilterPopover=E;
2
2
  //# sourceMappingURL=ManifestFilterPopover.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ManifestFilterPopover.cjs","sources":["../../../../src/components/composed/PlateMapEditor/ManifestFilterPopover.tsx"],"sourcesContent":["import { ListFilterIcon, PlusIcon, XIcon } from \"lucide-react\";\n\nimport type { FilterColumnConfig, FilterCondition, FilterOperator } from \"@/components/ui/data-table/data-table\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/ui/popover\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"@/components/ui/select\";\nimport { cn } from \"@/lib/utils\";\n\nconst OPERATOR_LABELS: Record<FilterOperator, string> = {\n contains: \"contains\",\n equals: \"equals\",\n not_equals: \"not equals\",\n starts_with: \"starts with\",\n ends_with: \"ends with\",\n is_empty: \"is empty\",\n is_not_empty: \"is not empty\",\n};\n\nconst VALUE_FREE_OPERATORS: FilterOperator[] = [\"is_empty\", \"is_not_empty\"];\n\nconst DEFAULT_OPERATORS: FilterOperator[] = [\n \"contains\",\n \"equals\",\n \"not_equals\",\n \"starts_with\",\n \"ends_with\",\n \"is_empty\",\n \"is_not_empty\",\n];\n\nfunction makeCondition(columnId: string, allowedOperators?: FilterOperator[]): FilterCondition {\n const operator = allowedOperators?.[0] ?? \"contains\";\n return { id: crypto.randomUUID(), columnId, operator, value: \"\" };\n}\n\nexport interface ManifestFilterPopoverProps {\n /** Filterable columns. Operator subsets can be specified per column. */\n columns: FilterColumnConfig[];\n /** Column id → display label resolver. */\n columnLabel?: (columnId: string) => string;\n filters: FilterCondition[];\n onFiltersChange: (next: FilterCondition[]) => void;\n triggerLabel?: string;\n className?: string;\n}\n\nexport function ManifestFilterPopover({\n columns,\n columnLabel,\n filters,\n onFiltersChange,\n triggerLabel = \"Filter\",\n className,\n}: ManifestFilterPopoverProps) {\n const labelFor = (columnId: string): string => columnLabel?.(columnId) ?? columnId;\n const firstColumn = columns[0];\n const firstColumnId = firstColumn?.columnId ?? \"\";\n\n const addFilter = () => onFiltersChange([...filters, makeCondition(firstColumnId, firstColumn?.operators)]);\n const removeFilter = (id: string) => onFiltersChange(filters.filter((cond) => cond.id !== id));\n const updateFilter = (id: string, patch: Partial<FilterCondition>) =>\n onFiltersChange(filters.map((cond) => (cond.id === id ? { ...cond, ...patch } : cond)));\n const clearAll = () => onFiltersChange([]);\n\n const activeCount = filters.length;\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n data-slot=\"manifest-filter\"\n className={cn(className)}\n aria-label={activeCount > 0 ? `${triggerLabel} (${activeCount} active)` : triggerLabel}\n >\n <ListFilterIcon className=\"size-3.5\" />\n {triggerLabel}\n {activeCount > 0 ? (\n <span className=\"flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground\">\n {activeCount}\n </span>\n ) : null}\n </Button>\n </PopoverTrigger>\n\n <PopoverContent align=\"end\" className=\"min-w-80\" aria-label=\"Filter conditions\">\n <div className=\"flex flex-col gap-2\">\n {filters.map((condition) => {\n const colConfig = columns.find((c) => c.columnId === condition.columnId);\n const operators = colConfig?.operators ?? DEFAULT_OPERATORS;\n const isValueFree = VALUE_FREE_OPERATORS.includes(condition.operator);\n return (\n <div key={condition.id} className=\"flex items-center gap-2\">\n <Select\n value={condition.columnId}\n onValueChange={(value) => {\n const nextConfig = columns.find((c) => c.columnId === value);\n const nextOperators = nextConfig?.operators ?? DEFAULT_OPERATORS;\n const nextOperator = nextOperators.includes(condition.operator)\n ? condition.operator\n : (nextOperators[0] ?? \"contains\");\n updateFilter(condition.id, { columnId: value, operator: nextOperator, value: \"\" });\n }}\n >\n <SelectTrigger aria-label=\"Filter column\" size=\"sm\" className=\"w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {columns.map((c) => (\n <SelectItem key={c.columnId} value={c.columnId}>\n {c.label ?? labelFor(c.columnId)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <Select\n value={condition.operator}\n onValueChange={(value) =>\n updateFilter(condition.id, { operator: value as FilterOperator, value: \"\" })\n }\n >\n <SelectTrigger aria-label=\"Filter operator\" size=\"sm\" className=\"w-32\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {OPERATOR_LABELS[op]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {isValueFree ? (\n <div className=\"h-8 w-40\" aria-hidden />\n ) : (\n <Input\n className=\"w-40\"\n placeholder=\"Value…\"\n value={condition.value}\n onChange={(event) => updateFilter(condition.id, { value: event.target.value })}\n />\n )}\n\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-8 shrink-0 text-muted-foreground hover:text-foreground\"\n onClick={() => removeFilter(condition.id)}\n aria-label=\"Remove filter\"\n >\n <XIcon className=\"size-3.5\" />\n </Button>\n </div>\n );\n })}\n\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={addFilter}\n disabled={columns.length === 0}\n >\n <PlusIcon className=\"size-3.5\" />\n Add filter\n </Button>\n {filters.length > 0 ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-muted-foreground\"\n onClick={clearAll}\n >\n Clear all\n </Button>\n ) : null}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n"],"names":["OPERATOR_LABELS","VALUE_FREE_OPERATORS","DEFAULT_OPERATORS","makeCondition","columnId","allowedOperators","operator","ManifestFilterPopover","columns","columnLabel","filters","onFiltersChange","triggerLabel","className","labelFor","firstColumn","firstColumnId","addFilter","removeFilter","id","cond","updateFilter","patch","clearAll","activeCount","Popover","jsx","PopoverTrigger","jsxs","Button","cn","ListFilterIcon","PopoverContent","condition","operators","c","isValueFree","Select","value","nextOperators","nextOperator","SelectTrigger","SelectValue","SelectContent","SelectItem","op","Input","event","XIcon","PlusIcon"],"mappings":"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"}
1
+ {"version":3,"file":"ManifestFilterPopover.cjs","sources":["../../../../src/components/composed/PlateMapEditor/ManifestFilterPopover.tsx"],"sourcesContent":["import { ListFilterIcon, PlusIcon, XIcon } from \"lucide-react\";\n\nimport type { FilterColumnConfig, FilterCondition, FilterOperator } from \"@/components/ui/data-table/data-table\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/ui/popover\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"@/components/ui/select\";\nimport { cn } from \"@/lib/utils\";\n\nconst OPERATOR_LABELS: Record<FilterOperator, string> = {\n contains: \"contains\",\n equals: \"equals\",\n not_equals: \"not equals\",\n starts_with: \"starts with\",\n ends_with: \"ends with\",\n is_empty: \"is empty\",\n is_not_empty: \"is not empty\",\n};\n\nconst VALUE_FREE_OPERATORS: FilterOperator[] = [\"is_empty\", \"is_not_empty\"];\n\nconst DEFAULT_OPERATORS: FilterOperator[] = [\n \"contains\",\n \"equals\",\n \"not_equals\",\n \"starts_with\",\n \"ends_with\",\n \"is_empty\",\n \"is_not_empty\",\n];\n\nfunction makeCondition(columnId: string, allowedOperators?: FilterOperator[]): FilterCondition {\n const operator = allowedOperators?.[0] ?? \"contains\";\n return { id: crypto.randomUUID(), columnId, operator, value: \"\" };\n}\n\nexport interface ManifestFilterPopoverProps {\n /** Filterable columns. Operator subsets can be specified per column. */\n columns: FilterColumnConfig[];\n /** Column id → display label resolver. */\n columnLabel?: (columnId: string) => string;\n filters: FilterCondition[];\n onFiltersChange: (next: FilterCondition[]) => void;\n triggerLabel?: string;\n /** Placeholder for a condition's value input. Defaults to `\"Value…\"`. */\n valuePlaceholder?: string;\n /** Label for the clear-every-filter button. Defaults to `\"Clear all\"`. */\n clearAllLabel?: string;\n className?: string;\n}\n\nexport function ManifestFilterPopover({\n columns,\n columnLabel,\n filters,\n onFiltersChange,\n triggerLabel = \"Filter\",\n valuePlaceholder = \"Value…\",\n clearAllLabel = \"Clear all\",\n className,\n}: ManifestFilterPopoverProps) {\n const labelFor = (columnId: string): string => columnLabel?.(columnId) ?? columnId;\n const firstColumn = columns[0];\n const firstColumnId = firstColumn?.columnId ?? \"\";\n\n const addFilter = () => onFiltersChange([...filters, makeCondition(firstColumnId, firstColumn?.operators)]);\n const removeFilter = (id: string) => onFiltersChange(filters.filter((cond) => cond.id !== id));\n const updateFilter = (id: string, patch: Partial<FilterCondition>) =>\n onFiltersChange(filters.map((cond) => (cond.id === id ? { ...cond, ...patch } : cond)));\n const clearAll = () => onFiltersChange([]);\n\n const activeCount = filters.length;\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n data-slot=\"manifest-filter\"\n className={cn(className)}\n aria-label={activeCount > 0 ? `${triggerLabel} (${activeCount} active)` : triggerLabel}\n >\n <ListFilterIcon className=\"size-3.5\" />\n {triggerLabel}\n {activeCount > 0 ? (\n <span className=\"flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground\">\n {activeCount}\n </span>\n ) : null}\n </Button>\n </PopoverTrigger>\n\n <PopoverContent align=\"end\" className=\"min-w-80\" aria-label=\"Filter conditions\">\n <div className=\"flex flex-col gap-2\">\n {filters.map((condition) => {\n const colConfig = columns.find((c) => c.columnId === condition.columnId);\n const operators = colConfig?.operators ?? DEFAULT_OPERATORS;\n const isValueFree = VALUE_FREE_OPERATORS.includes(condition.operator);\n return (\n <div key={condition.id} className=\"flex items-center gap-2\">\n <Select\n value={condition.columnId}\n onValueChange={(value) => {\n const nextConfig = columns.find((c) => c.columnId === value);\n const nextOperators = nextConfig?.operators ?? DEFAULT_OPERATORS;\n const nextOperator = nextOperators.includes(condition.operator)\n ? condition.operator\n : (nextOperators[0] ?? \"contains\");\n updateFilter(condition.id, { columnId: value, operator: nextOperator, value: \"\" });\n }}\n >\n <SelectTrigger aria-label=\"Filter column\" size=\"sm\" className=\"w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {columns.map((c) => (\n <SelectItem key={c.columnId} value={c.columnId}>\n {c.label ?? labelFor(c.columnId)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <Select\n value={condition.operator}\n onValueChange={(value) =>\n updateFilter(condition.id, { operator: value as FilterOperator, value: \"\" })\n }\n >\n <SelectTrigger aria-label=\"Filter operator\" size=\"sm\" className=\"w-32\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {OPERATOR_LABELS[op]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {isValueFree ? (\n <div className=\"h-8 w-40\" aria-hidden />\n ) : (\n <Input\n className=\"w-40\"\n placeholder={valuePlaceholder}\n value={condition.value}\n onChange={(event) => updateFilter(condition.id, { value: event.target.value })}\n />\n )}\n\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-8 shrink-0 text-muted-foreground hover:text-foreground\"\n onClick={() => removeFilter(condition.id)}\n aria-label=\"Remove filter\"\n >\n <XIcon className=\"size-3.5\" />\n </Button>\n </div>\n );\n })}\n\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={addFilter}\n disabled={columns.length === 0}\n >\n <PlusIcon className=\"size-3.5\" />\n Add filter\n </Button>\n {filters.length > 0 ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-muted-foreground\"\n onClick={clearAll}\n >\n {clearAllLabel}\n </Button>\n ) : null}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n"],"names":["OPERATOR_LABELS","VALUE_FREE_OPERATORS","DEFAULT_OPERATORS","makeCondition","columnId","allowedOperators","operator","ManifestFilterPopover","columns","columnLabel","filters","onFiltersChange","triggerLabel","valuePlaceholder","clearAllLabel","className","labelFor","firstColumn","firstColumnId","addFilter","removeFilter","id","cond","updateFilter","patch","clearAll","activeCount","Popover","jsx","PopoverTrigger","jsxs","Button","cn","ListFilterIcon","PopoverContent","condition","operators","c","isValueFree","Select","value","nextOperators","nextOperator","SelectTrigger","SelectValue","SelectContent","SelectItem","op","Input","event","XIcon","PlusIcon"],"mappings":"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"}
@@ -7,6 +7,10 @@ export interface ManifestFilterPopoverProps {
7
7
  filters: FilterCondition[];
8
8
  onFiltersChange: (next: FilterCondition[]) => void;
9
9
  triggerLabel?: string;
10
+ /** Placeholder for a condition's value input. Defaults to `"Value…"`. */
11
+ valuePlaceholder?: string;
12
+ /** Label for the clear-every-filter button. Defaults to `"Clear all"`. */
13
+ clearAllLabel?: string;
10
14
  className?: string;
11
15
  }
12
- export declare function ManifestFilterPopover({ columns, columnLabel, filters, onFiltersChange, triggerLabel, className, }: ManifestFilterPopoverProps): import("react/jsx-runtime").JSX.Element;
16
+ export declare function ManifestFilterPopover({ columns, columnLabel, filters, onFiltersChange, triggerLabel, valuePlaceholder, clearAllLabel, className, }: ManifestFilterPopoverProps): import("react/jsx-runtime").JSX.Element;
@@ -1,11 +1,11 @@
1
1
  import { jsxs as l, jsx as t } from "react/jsx-runtime";
2
- import { ListFilterIcon as P, XIcon as R, PlusIcon as S } from "lucide-react";
2
+ import { ListFilterIcon as S, XIcon as q, PlusIcon as T } from "lucide-react";
3
3
  import { Button as m } from "../../../ui/button.js";
4
- import { Input as q } from "../../../ui/input.js";
5
- import { Popover as T, PopoverTrigger as V, PopoverContent as k } from "../../../ui/popover.js";
4
+ import { Input as V } from "../../../ui/input.js";
5
+ import { Popover as k, PopoverTrigger as U, PopoverContent as j } from "../../../ui/popover.js";
6
6
  import { Select as v, SelectTrigger as g, SelectValue as _, SelectContent as x, SelectItem as y } from "../../../ui/select.js";
7
- import { cn as U } from "../../../lib/utils.js";
8
- const j = {
7
+ import { cn as L } from "../../../lib/utils.js";
8
+ const B = {
9
9
  contains: "contains",
10
10
  equals: "equals",
11
11
  not_equals: "not equals",
@@ -13,7 +13,7 @@ const j = {
13
13
  ends_with: "ends with",
14
14
  is_empty: "is empty",
15
15
  is_not_empty: "is not empty"
16
- }, L = ["is_empty", "is_not_empty"], C = [
16
+ }, D = ["is_empty", "is_not_empty"], C = [
17
17
  "contains",
18
18
  "equals",
19
19
  "not_equals",
@@ -22,51 +22,53 @@ const j = {
22
22
  "is_empty",
23
23
  "is_not_empty"
24
24
  ];
25
- function B(n, u) {
25
+ function $(n, u) {
26
26
  const r = u?.[0] ?? "contains";
27
27
  return { id: crypto.randomUUID(), columnId: n, operator: r, value: "" };
28
28
  }
29
- function Q({
29
+ function Y({
30
30
  columns: n,
31
31
  columnLabel: u,
32
32
  filters: r,
33
33
  onFiltersChange: s,
34
34
  triggerLabel: d = "Filter",
35
- className: I
35
+ valuePlaceholder: I = "Value…",
36
+ clearAllLabel: N = "Clear all",
37
+ className: b
36
38
  }) {
37
- const N = (e) => u?.(e) ?? e, h = n[0], b = h?.columnId ?? "", w = () => s([...r, B(b, h?.operators)]), z = (e) => s(r.filter((c) => c.id !== e)), p = (e, c) => s(r.map((o) => o.id === e ? { ...o, ...c } : o)), F = () => s([]), i = r.length;
38
- return /* @__PURE__ */ l(T, { children: [
39
- /* @__PURE__ */ t(V, { asChild: !0, children: /* @__PURE__ */ l(
39
+ const w = (e) => u?.(e) ?? e, h = n[0], z = h?.columnId ?? "", F = () => s([...r, $(z, h?.operators)]), A = (e) => s(r.filter((c) => c.id !== e)), p = (e, c) => s(r.map((o) => o.id === e ? { ...o, ...c } : o)), E = () => s([]), i = r.length;
40
+ return /* @__PURE__ */ l(k, { children: [
41
+ /* @__PURE__ */ t(U, { asChild: !0, children: /* @__PURE__ */ l(
40
42
  m,
41
43
  {
42
44
  type: "button",
43
45
  variant: "outline",
44
46
  size: "sm",
45
47
  "data-slot": "manifest-filter",
46
- className: U(I),
48
+ className: L(b),
47
49
  "aria-label": i > 0 ? `${d} (${i} active)` : d,
48
50
  children: [
49
- /* @__PURE__ */ t(P, { className: "size-3.5" }),
51
+ /* @__PURE__ */ t(S, { className: "size-3.5" }),
50
52
  d,
51
53
  i > 0 ? /* @__PURE__ */ t("span", { className: "flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground", children: i }) : null
52
54
  ]
53
55
  }
54
56
  ) }),
55
- /* @__PURE__ */ t(k, { align: "end", className: "min-w-80", "aria-label": "Filter conditions", children: /* @__PURE__ */ l("div", { className: "flex flex-col gap-2", children: [
57
+ /* @__PURE__ */ t(j, { align: "end", className: "min-w-80", "aria-label": "Filter conditions", children: /* @__PURE__ */ l("div", { className: "flex flex-col gap-2", children: [
56
58
  r.map((e) => {
57
- const o = n.find((a) => a.columnId === e.columnId)?.operators ?? C, A = L.includes(e.operator);
59
+ const o = n.find((a) => a.columnId === e.columnId)?.operators ?? C, O = D.includes(e.operator);
58
60
  return /* @__PURE__ */ l("div", { className: "flex items-center gap-2", children: [
59
61
  /* @__PURE__ */ l(
60
62
  v,
61
63
  {
62
64
  value: e.columnId,
63
65
  onValueChange: (a) => {
64
- const f = n.find((O) => O.columnId === a)?.operators ?? C, E = f.includes(e.operator) ? e.operator : f[0] ?? "contains";
65
- p(e.id, { columnId: a, operator: E, value: "" });
66
+ const f = n.find((R) => R.columnId === a)?.operators ?? C, P = f.includes(e.operator) ? e.operator : f[0] ?? "contains";
67
+ p(e.id, { columnId: a, operator: P, value: "" });
66
68
  },
67
69
  children: [
68
70
  /* @__PURE__ */ t(g, { "aria-label": "Filter column", size: "sm", className: "w-36", children: /* @__PURE__ */ t(_, {}) }),
69
- /* @__PURE__ */ t(x, { children: n.map((a) => /* @__PURE__ */ t(y, { value: a.columnId, children: a.label ?? N(a.columnId) }, a.columnId)) })
71
+ /* @__PURE__ */ t(x, { children: n.map((a) => /* @__PURE__ */ t(y, { value: a.columnId, children: a.label ?? w(a.columnId) }, a.columnId)) })
70
72
  ]
71
73
  }
72
74
  ),
@@ -77,15 +79,15 @@ function Q({
77
79
  onValueChange: (a) => p(e.id, { operator: a, value: "" }),
78
80
  children: [
79
81
  /* @__PURE__ */ t(g, { "aria-label": "Filter operator", size: "sm", className: "w-32", children: /* @__PURE__ */ t(_, {}) }),
80
- /* @__PURE__ */ t(x, { children: o.map((a) => /* @__PURE__ */ t(y, { value: a, children: j[a] }, a)) })
82
+ /* @__PURE__ */ t(x, { children: o.map((a) => /* @__PURE__ */ t(y, { value: a, children: B[a] }, a)) })
81
83
  ]
82
84
  }
83
85
  ),
84
- A ? /* @__PURE__ */ t("div", { className: "h-8 w-40", "aria-hidden": !0 }) : /* @__PURE__ */ t(
85
- q,
86
+ O ? /* @__PURE__ */ t("div", { className: "h-8 w-40", "aria-hidden": !0 }) : /* @__PURE__ */ t(
87
+ V,
86
88
  {
87
89
  className: "w-40",
88
- placeholder: "Value…",
90
+ placeholder: I,
89
91
  value: e.value,
90
92
  onChange: (a) => p(e.id, { value: a.target.value })
91
93
  }
@@ -97,9 +99,9 @@ function Q({
97
99
  variant: "ghost",
98
100
  size: "icon",
99
101
  className: "size-8 shrink-0 text-muted-foreground hover:text-foreground",
100
- onClick: () => z(e.id),
102
+ onClick: () => A(e.id),
101
103
  "aria-label": "Remove filter",
102
- children: /* @__PURE__ */ t(R, { className: "size-3.5" })
104
+ children: /* @__PURE__ */ t(q, { className: "size-3.5" })
103
105
  }
104
106
  )
105
107
  ] }, e.id);
@@ -111,10 +113,10 @@ function Q({
111
113
  type: "button",
112
114
  variant: "outline",
113
115
  size: "sm",
114
- onClick: w,
116
+ onClick: F,
115
117
  disabled: n.length === 0,
116
118
  children: [
117
- /* @__PURE__ */ t(S, { className: "size-3.5" }),
119
+ /* @__PURE__ */ t(T, { className: "size-3.5" }),
118
120
  "Add filter"
119
121
  ]
120
122
  }
@@ -126,8 +128,8 @@ function Q({
126
128
  variant: "ghost",
127
129
  size: "sm",
128
130
  className: "text-muted-foreground",
129
- onClick: F,
130
- children: "Clear all"
131
+ onClick: E,
132
+ children: N
131
133
  }
132
134
  ) : null
133
135
  ] })
@@ -135,6 +137,6 @@ function Q({
135
137
  ] });
136
138
  }
137
139
  export {
138
- Q as ManifestFilterPopover
140
+ Y as ManifestFilterPopover
139
141
  };
140
142
  //# sourceMappingURL=ManifestFilterPopover.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ManifestFilterPopover.js","sources":["../../../../src/components/composed/PlateMapEditor/ManifestFilterPopover.tsx"],"sourcesContent":["import { ListFilterIcon, PlusIcon, XIcon } from \"lucide-react\";\n\nimport type { FilterColumnConfig, FilterCondition, FilterOperator } from \"@/components/ui/data-table/data-table\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/ui/popover\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"@/components/ui/select\";\nimport { cn } from \"@/lib/utils\";\n\nconst OPERATOR_LABELS: Record<FilterOperator, string> = {\n contains: \"contains\",\n equals: \"equals\",\n not_equals: \"not equals\",\n starts_with: \"starts with\",\n ends_with: \"ends with\",\n is_empty: \"is empty\",\n is_not_empty: \"is not empty\",\n};\n\nconst VALUE_FREE_OPERATORS: FilterOperator[] = [\"is_empty\", \"is_not_empty\"];\n\nconst DEFAULT_OPERATORS: FilterOperator[] = [\n \"contains\",\n \"equals\",\n \"not_equals\",\n \"starts_with\",\n \"ends_with\",\n \"is_empty\",\n \"is_not_empty\",\n];\n\nfunction makeCondition(columnId: string, allowedOperators?: FilterOperator[]): FilterCondition {\n const operator = allowedOperators?.[0] ?? \"contains\";\n return { id: crypto.randomUUID(), columnId, operator, value: \"\" };\n}\n\nexport interface ManifestFilterPopoverProps {\n /** Filterable columns. Operator subsets can be specified per column. */\n columns: FilterColumnConfig[];\n /** Column id → display label resolver. */\n columnLabel?: (columnId: string) => string;\n filters: FilterCondition[];\n onFiltersChange: (next: FilterCondition[]) => void;\n triggerLabel?: string;\n className?: string;\n}\n\nexport function ManifestFilterPopover({\n columns,\n columnLabel,\n filters,\n onFiltersChange,\n triggerLabel = \"Filter\",\n className,\n}: ManifestFilterPopoverProps) {\n const labelFor = (columnId: string): string => columnLabel?.(columnId) ?? columnId;\n const firstColumn = columns[0];\n const firstColumnId = firstColumn?.columnId ?? \"\";\n\n const addFilter = () => onFiltersChange([...filters, makeCondition(firstColumnId, firstColumn?.operators)]);\n const removeFilter = (id: string) => onFiltersChange(filters.filter((cond) => cond.id !== id));\n const updateFilter = (id: string, patch: Partial<FilterCondition>) =>\n onFiltersChange(filters.map((cond) => (cond.id === id ? { ...cond, ...patch } : cond)));\n const clearAll = () => onFiltersChange([]);\n\n const activeCount = filters.length;\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n data-slot=\"manifest-filter\"\n className={cn(className)}\n aria-label={activeCount > 0 ? `${triggerLabel} (${activeCount} active)` : triggerLabel}\n >\n <ListFilterIcon className=\"size-3.5\" />\n {triggerLabel}\n {activeCount > 0 ? (\n <span className=\"flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground\">\n {activeCount}\n </span>\n ) : null}\n </Button>\n </PopoverTrigger>\n\n <PopoverContent align=\"end\" className=\"min-w-80\" aria-label=\"Filter conditions\">\n <div className=\"flex flex-col gap-2\">\n {filters.map((condition) => {\n const colConfig = columns.find((c) => c.columnId === condition.columnId);\n const operators = colConfig?.operators ?? DEFAULT_OPERATORS;\n const isValueFree = VALUE_FREE_OPERATORS.includes(condition.operator);\n return (\n <div key={condition.id} className=\"flex items-center gap-2\">\n <Select\n value={condition.columnId}\n onValueChange={(value) => {\n const nextConfig = columns.find((c) => c.columnId === value);\n const nextOperators = nextConfig?.operators ?? DEFAULT_OPERATORS;\n const nextOperator = nextOperators.includes(condition.operator)\n ? condition.operator\n : (nextOperators[0] ?? \"contains\");\n updateFilter(condition.id, { columnId: value, operator: nextOperator, value: \"\" });\n }}\n >\n <SelectTrigger aria-label=\"Filter column\" size=\"sm\" className=\"w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {columns.map((c) => (\n <SelectItem key={c.columnId} value={c.columnId}>\n {c.label ?? labelFor(c.columnId)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <Select\n value={condition.operator}\n onValueChange={(value) =>\n updateFilter(condition.id, { operator: value as FilterOperator, value: \"\" })\n }\n >\n <SelectTrigger aria-label=\"Filter operator\" size=\"sm\" className=\"w-32\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {OPERATOR_LABELS[op]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {isValueFree ? (\n <div className=\"h-8 w-40\" aria-hidden />\n ) : (\n <Input\n className=\"w-40\"\n placeholder=\"Value…\"\n value={condition.value}\n onChange={(event) => updateFilter(condition.id, { value: event.target.value })}\n />\n )}\n\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-8 shrink-0 text-muted-foreground hover:text-foreground\"\n onClick={() => removeFilter(condition.id)}\n aria-label=\"Remove filter\"\n >\n <XIcon className=\"size-3.5\" />\n </Button>\n </div>\n );\n })}\n\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={addFilter}\n disabled={columns.length === 0}\n >\n <PlusIcon className=\"size-3.5\" />\n Add filter\n </Button>\n {filters.length > 0 ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-muted-foreground\"\n onClick={clearAll}\n >\n Clear all\n </Button>\n ) : null}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n"],"names":["OPERATOR_LABELS","VALUE_FREE_OPERATORS","DEFAULT_OPERATORS","makeCondition","columnId","allowedOperators","operator","ManifestFilterPopover","columns","columnLabel","filters","onFiltersChange","triggerLabel","className","labelFor","firstColumn","firstColumnId","addFilter","removeFilter","id","cond","updateFilter","patch","clearAll","activeCount","Popover","jsx","PopoverTrigger","jsxs","Button","cn","ListFilterIcon","PopoverContent","condition","operators","c","isValueFree","Select","value","nextOperators","nextOperator","SelectTrigger","SelectValue","SelectContent","SelectItem","op","Input","event","XIcon","PlusIcon"],"mappings":"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"}
1
+ {"version":3,"file":"ManifestFilterPopover.js","sources":["../../../../src/components/composed/PlateMapEditor/ManifestFilterPopover.tsx"],"sourcesContent":["import { ListFilterIcon, PlusIcon, XIcon } from \"lucide-react\";\n\nimport type { FilterColumnConfig, FilterCondition, FilterOperator } from \"@/components/ui/data-table/data-table\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/ui/popover\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"@/components/ui/select\";\nimport { cn } from \"@/lib/utils\";\n\nconst OPERATOR_LABELS: Record<FilterOperator, string> = {\n contains: \"contains\",\n equals: \"equals\",\n not_equals: \"not equals\",\n starts_with: \"starts with\",\n ends_with: \"ends with\",\n is_empty: \"is empty\",\n is_not_empty: \"is not empty\",\n};\n\nconst VALUE_FREE_OPERATORS: FilterOperator[] = [\"is_empty\", \"is_not_empty\"];\n\nconst DEFAULT_OPERATORS: FilterOperator[] = [\n \"contains\",\n \"equals\",\n \"not_equals\",\n \"starts_with\",\n \"ends_with\",\n \"is_empty\",\n \"is_not_empty\",\n];\n\nfunction makeCondition(columnId: string, allowedOperators?: FilterOperator[]): FilterCondition {\n const operator = allowedOperators?.[0] ?? \"contains\";\n return { id: crypto.randomUUID(), columnId, operator, value: \"\" };\n}\n\nexport interface ManifestFilterPopoverProps {\n /** Filterable columns. Operator subsets can be specified per column. */\n columns: FilterColumnConfig[];\n /** Column id → display label resolver. */\n columnLabel?: (columnId: string) => string;\n filters: FilterCondition[];\n onFiltersChange: (next: FilterCondition[]) => void;\n triggerLabel?: string;\n /** Placeholder for a condition's value input. Defaults to `\"Value…\"`. */\n valuePlaceholder?: string;\n /** Label for the clear-every-filter button. Defaults to `\"Clear all\"`. */\n clearAllLabel?: string;\n className?: string;\n}\n\nexport function ManifestFilterPopover({\n columns,\n columnLabel,\n filters,\n onFiltersChange,\n triggerLabel = \"Filter\",\n valuePlaceholder = \"Value…\",\n clearAllLabel = \"Clear all\",\n className,\n}: ManifestFilterPopoverProps) {\n const labelFor = (columnId: string): string => columnLabel?.(columnId) ?? columnId;\n const firstColumn = columns[0];\n const firstColumnId = firstColumn?.columnId ?? \"\";\n\n const addFilter = () => onFiltersChange([...filters, makeCondition(firstColumnId, firstColumn?.operators)]);\n const removeFilter = (id: string) => onFiltersChange(filters.filter((cond) => cond.id !== id));\n const updateFilter = (id: string, patch: Partial<FilterCondition>) =>\n onFiltersChange(filters.map((cond) => (cond.id === id ? { ...cond, ...patch } : cond)));\n const clearAll = () => onFiltersChange([]);\n\n const activeCount = filters.length;\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n data-slot=\"manifest-filter\"\n className={cn(className)}\n aria-label={activeCount > 0 ? `${triggerLabel} (${activeCount} active)` : triggerLabel}\n >\n <ListFilterIcon className=\"size-3.5\" />\n {triggerLabel}\n {activeCount > 0 ? (\n <span className=\"flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground\">\n {activeCount}\n </span>\n ) : null}\n </Button>\n </PopoverTrigger>\n\n <PopoverContent align=\"end\" className=\"min-w-80\" aria-label=\"Filter conditions\">\n <div className=\"flex flex-col gap-2\">\n {filters.map((condition) => {\n const colConfig = columns.find((c) => c.columnId === condition.columnId);\n const operators = colConfig?.operators ?? DEFAULT_OPERATORS;\n const isValueFree = VALUE_FREE_OPERATORS.includes(condition.operator);\n return (\n <div key={condition.id} className=\"flex items-center gap-2\">\n <Select\n value={condition.columnId}\n onValueChange={(value) => {\n const nextConfig = columns.find((c) => c.columnId === value);\n const nextOperators = nextConfig?.operators ?? DEFAULT_OPERATORS;\n const nextOperator = nextOperators.includes(condition.operator)\n ? condition.operator\n : (nextOperators[0] ?? \"contains\");\n updateFilter(condition.id, { columnId: value, operator: nextOperator, value: \"\" });\n }}\n >\n <SelectTrigger aria-label=\"Filter column\" size=\"sm\" className=\"w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {columns.map((c) => (\n <SelectItem key={c.columnId} value={c.columnId}>\n {c.label ?? labelFor(c.columnId)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <Select\n value={condition.operator}\n onValueChange={(value) =>\n updateFilter(condition.id, { operator: value as FilterOperator, value: \"\" })\n }\n >\n <SelectTrigger aria-label=\"Filter operator\" size=\"sm\" className=\"w-32\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {OPERATOR_LABELS[op]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {isValueFree ? (\n <div className=\"h-8 w-40\" aria-hidden />\n ) : (\n <Input\n className=\"w-40\"\n placeholder={valuePlaceholder}\n value={condition.value}\n onChange={(event) => updateFilter(condition.id, { value: event.target.value })}\n />\n )}\n\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-8 shrink-0 text-muted-foreground hover:text-foreground\"\n onClick={() => removeFilter(condition.id)}\n aria-label=\"Remove filter\"\n >\n <XIcon className=\"size-3.5\" />\n </Button>\n </div>\n );\n })}\n\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={addFilter}\n disabled={columns.length === 0}\n >\n <PlusIcon className=\"size-3.5\" />\n Add filter\n </Button>\n {filters.length > 0 ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-muted-foreground\"\n onClick={clearAll}\n >\n {clearAllLabel}\n </Button>\n ) : null}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n"],"names":["OPERATOR_LABELS","VALUE_FREE_OPERATORS","DEFAULT_OPERATORS","makeCondition","columnId","allowedOperators","operator","ManifestFilterPopover","columns","columnLabel","filters","onFiltersChange","triggerLabel","valuePlaceholder","clearAllLabel","className","labelFor","firstColumn","firstColumnId","addFilter","removeFilter","id","cond","updateFilter","patch","clearAll","activeCount","Popover","jsx","PopoverTrigger","jsxs","Button","cn","ListFilterIcon","PopoverContent","condition","operators","c","isValueFree","Select","value","nextOperators","nextOperator","SelectTrigger","SelectValue","SelectContent","SelectItem","op","Input","event","XIcon","PlusIcon"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=require("react/jsx-runtime"),dt=require("react"),ut=require("./csvPlateTriage.cjs"),ft=require("./PlateMapActionsMenu.cjs"),xt=require("./PlateMapForm.cjs"),pt=require("./PlateMapGrid.cjs"),mt=require("./PlateMapManifest.cjs"),Pt=require("./PlateMapPlateSelector.cjs"),J=require("../../../ui/badge.cjs"),f=require("../../../ui/card.cjs"),ht=require("../../../lib/utils.cjs");function vt(t){const n=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const r in t)if(r!=="default"){const l=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(n,r,l.get?l:{enumerable:!0,get:()=>t[r]})}}return n.default=t,Object.freeze(n)}const o=vt(dt),Q="::",gt="plateBarcode",jt="Plate Barcode";function X(t,n){return`${t}${Q}${n}`}function Z(t,n){const r=`${t}${Q}`;return n.startsWith(r)?n.slice(r.length):void 0}function Mt(t,n){return{...t??{},...n}}function bt(t,n){const r=new Map;return t.forEach(l=>r.set(l.id,l)),(n??[]).forEach(l=>r.set(l.id,l)),[...r.values()]}function St(t,n){const r=t.find(l=>l.id===n);return r||(n?void 0:t[0])}function Ct(t,n){if(!n)return t;const r=new Map;return t.forEach((l,p)=>{const i=Z(n,p);i&&r.set(i,l)}),r}function Et({importedPlates:t,selectedPlateId:n,isControlled:r,providedPlateCount:l}){const p=t[0]?.id;return p?t.some(i=>i.id===n)?void 0:p:!r&&l===0?null:void 0}function wt(t,n){return t>0||!!n}function At(t,n){return!t||!!n}function yt({title:t,badges:n}){return!t&&!n?null:s.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-2",children:[t?s.jsx("h2",{className:"text-lg font-semibold",children:t}):s.jsx("span",{}),n?s.jsx("div",{className:"flex flex-wrap gap-2",children:n}):null]})}function Nt({format:t,rows:n,columns:r,values:l,onChange:p,selection:i,onSelectionChange:E,fields:j,tableColumns:M,colorForWell:ee,emptyEntry:w,mergeOnApply:te,isPopulated:ne,cycleFieldOnWellDoubleClick:T,title:se,badges:ae,banner:re,legend:le,formExtras:oe,formSlot:ce,footer:q,plateTitle:ie="Plate",plateToolbar:de,plates:A,activePlateId:z,onPlateChange:b,onAddPlate:B,onRemovePlate:ue,addPlateLabel:fe,removePlateLabel:xe,plateSelectorLabel:pe,plateSelectorVariant:me,plateBarcodeField:Pe,plateBarcodeColumnHeader:F=jt,hidePlateBarcodeColumn:O=!1,groups:he,activeGroupId:ve,onGroupClick:ge,renderHoverSummary:je,cellSize:Me,emptyWellFillColor:be,wellShape:Se,framedPlate:Ce,wrapWell:Ee,highlightedWellIds:we,onHoveredWellChange:Ae,manifestFilterable:ye,manifestGroupable:Ne,autoScaleGrid:Te,minCellSize:We,maxCellSize:_e,className:ke,templates:qe,templateId:ze,onTemplateChange:Be,onClearTemplate:Fe,onImportCsv:W,onExportCsv:Oe,onImportTemplate:Re,onExportTemplate:Le,csvAccept:De,templateAccept:Ie,label:He,align:Ke,side:$e,importTemplateLabel:Ue,exportTemplateLabel:Ve,importCsvLabel:Ge,exportCsvLabel:Ye,clearLabel:Je}){const[R,L]=o.useState({}),[Qe,D]=o.useState(null),[I,H]=o.useState(),[K,Xe]=o.useState([]),[Ze,$]=o.useState(),et=te??Mt,y=o.useMemo(()=>bt(K,A),[K,A]),v=z!==void 0,N=z??Ze,U=o.useMemo(()=>St(y,N),[y,N]),P=U?.barcode,d=P,m=!!d,h=Pe??gt,V=o.useRef(d),g=o.useMemo(()=>Ct(l,d),[d,l]),G=o.useCallback(e=>!m||!d?e:X(d,e),[d,m]),tt=o.useCallback(e=>{v||$(e),b?.(e)},[v,b]),nt=o.useCallback(async(e,a)=>{if(a){const c=ut.plateOptionsFromCsvTriage(a);Xe(c);const u=Et({importedPlates:c,selectedPlateId:N,isControlled:v,providedPlateCount:A?.length??0});u!==void 0&&(v||$(u??void 0),u&&b?.(u))}await W?.(e,a)},[v,W,b,A,N]),S=o.useCallback(e=>!m||!P?e:{...e,[h]:P},[P,h,m]),C=o.useCallback(e=>{if(!m||!d){p(e);return}const a=new Map(l);[...a.keys()].forEach(c=>{Z(d,c)&&a.delete(c)}),e.forEach((c,u)=>{a.set(G(u),S(c))}),p(a)},[d,m,p,S,G,l]);o.useEffect(()=>{V.current!==d&&(V.current=d,L({}),D(null),H(void 0),i.size>0&&E(new Set))},[d,E,i.size]);const x=o.useMemo(()=>{if(!T)return;const e=j.find(a=>a.key===T);if(!(e?.kind!=="select"||!e.options?.length))return e},[T,j]),st=()=>{if(i.size===0)return;const e=new Map(g);i.forEach(a=>{const u=e.get(a)??w(a),_=S(et(u,R,a));e.set(a,_)}),C(e)},at=()=>{if(i.size===0)return;const e=new Map(g);i.forEach(a=>e.delete(a)),C(e)},rt=o.useCallback(e=>{if(!x?.options?.length)return;const a=new Map(g),c=a.get(e),u=c?.[x.key],_=x.options.findIndex(k=>k.value===u),Y=x.options[(_+1)%x.options.length];if(!Y)return;const it=c??w(e);a.set(e,S({...it,[x.key]:Y.value})),C(a),H(k=>({wellId:e,key:(k?.key??0)+1}))},[C,x,w,g,S]),lt=o.useMemo(()=>!m||!P||O||M.some(c=>c.field===h||c.id===h)?M:[{id:h,header:F,minWidth:150,render:({row:c})=>{const u=c[h]??P;return s.jsx(J.Badge,{variant:"outline",children:String(u)})}},...M],[P,h,O,m,F,M]),ot=wt(y.length,B),ct=At(v,b);return s.jsxs("div",{"data-slot":"plate-map-editor",className:ht.cn("flex flex-col gap-4",ke),children:[s.jsx(yt,{title:se,badges:ae}),re,s.jsxs("div",{className:"flex flex-wrap gap-3 md:flex-nowrap",children:[s.jsx(f.Card,{className:"flex w-full max-w-[360px] min-w-[300px] basis-[360px] flex-col",size:"sm",children:s.jsx(f.CardContent,{className:"flex h-full flex-1 flex-col gap-3",children:s.jsx(xt.PlateMapForm,{fields:j,value:R,onChange:L,selectionSize:i.size,onApply:st,onClear:at,extras:oe,legend:le,formSlot:ce})})}),s.jsxs(f.Card,{className:"flex min-w-[360px] flex-1 flex-col",size:"sm",children:[s.jsxs(f.CardHeader,{className:"border-b",children:[s.jsxs("div",{className:"flex min-w-0 flex-wrap items-center gap-2",children:[s.jsx(f.CardTitle,{className:"min-w-0",children:ie}),ot?s.jsx(Pt.PlateMapPlateSelector,{plates:y,activePlateId:U?.id,onPlateChange:ct?tt:void 0,onAddPlate:B,onRemovePlate:ue,addPlateLabel:fe,removePlateLabel:xe,label:pe,variant:me}):null]}),s.jsx(f.CardAction,{className:"flex flex-wrap items-center gap-2",children:s.jsx(ft.PlateMapActionsMenu,{templates:qe,templateId:ze,onTemplateChange:Be,onClearTemplate:Fe,hasEntries:l.size>0,onImportCsv:W?nt:void 0,onExportCsv:Oe,onImportTemplate:Re,onExportTemplate:Le,csvAccept:De,templateAccept:Ie,label:He,align:Ke,side:$e,importTemplateLabel:Ue,exportTemplateLabel:Ve,importCsvLabel:Ge,exportCsvLabel:Ye,clearLabel:Je})})]}),s.jsx(f.CardContent,{children:s.jsx(pt.PlateMapGrid,{format:t,rows:n,columns:r,values:g,selection:i,onSelectionChange:E,colorForWell:ee,fields:j,renderHoverSummary:je,hoveredWellId:Qe,onHoveredWellChange:e=>{D(e),Ae?.(e)},toolbar:de,emptyWellFillColor:be,wellShape:Se,framed:Ce,wrapWell:Ee,highlightedWellIds:we,onWellDoubleClick:x?rt:void 0,selectionFillMode:x?"well":"selection",flashWellId:I?.wellId,flashWellKey:I?.key,cellSize:Me,autoScale:Te,minCellSize:We,maxCellSize:_e,groups:he,activeGroupId:ve,onGroupClick:ge})})]})]}),s.jsxs(f.Card,{size:"sm",children:[s.jsx(f.CardHeader,{className:"border-b",children:s.jsx(f.CardTitle,{children:"Sample manifest"})}),s.jsx(f.CardContent,{children:s.jsx(mt.PlateMapManifest,{values:g,onChange:C,columns:lt,fields:j,selection:i,onSelectionChange:E,emptyEntry:w,isPopulated:ne,filterable:ye,groupable:Ne})})]}),q?s.jsx("div",{className:"flex justify-end gap-2 pt-2",children:q}):null]})}exports.PlateBadge=J.Badge;exports.PlateMapEditor=Nt;exports.getPlateMapScopedWellId=X;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),pt=require("react"),ut=require("./PlateMapActionsMenu.cjs"),mt=require("./PlateMapForm.cjs"),ft=require("./PlateMapGrid.cjs"),xt=require("./PlateMapManifest.cjs"),gt=require("./PlateMapPlateSelector.cjs"),D=require("./usePlateMapEditorState.cjs"),ht=require("./WellQuickPaint.cjs"),U=require("../../../ui/badge.cjs"),n=require("../../../ui/card.cjs"),j=require("../../../lib/utils.cjs");function wt(a){const l=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(a){for(const i in a)if(i!=="default"){const c=Object.getOwnPropertyDescriptor(a,i);Object.defineProperty(l,i,c.get?c:{enumerable:!0,get:()=>a[i]})}}return l.default=a,Object.freeze(l)}const h=wt(pt),vt="plateBarcode",jt="Plate Barcode",St="Sample manifest",Pt="360px",bt={sm:"@min-[640px]:flex-row @min-[640px]:items-start",md:"@min-[768px]:flex-row @min-[768px]:items-start",lg:"@min-[1024px]:flex-row @min-[1024px]:items-start",xl:"@min-[1280px]:flex-row @min-[1280px]:items-start",never:""},Ct={sm:"@min-[640px]:w-[var(--plate-map-form-width)] @min-[640px]:shrink-0",md:"@min-[768px]:w-[var(--plate-map-form-width)] @min-[768px]:shrink-0",lg:"@min-[1024px]:w-[var(--plate-map-form-width)] @min-[1024px]:shrink-0",xl:"@min-[1280px]:w-[var(--plate-map-form-width)] @min-[1280px]:shrink-0",never:""};function Mt(a,l){return a>0||!!l}function Et({title:a,badges:l}){return!a&&!l?null:e.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-2",children:[a?e.jsx("h2",{className:"text-lg font-semibold",children:a}):e.jsx("span",{}),l?e.jsx("div",{className:"flex flex-wrap gap-2",children:l}):null]})}function yt({visible:a,...l}){return a?e.jsx(gt.PlateMapPlateSelector,{...l}):null}function Tt({hidden:a,title:l,cardClassName:i,slot:c,manifest:P}){return a?null:e.jsxs(n.Card,{size:"sm","data-plate-map-region":"manifest",className:j.cn("w-full min-w-0",i),children:[l?e.jsx(n.CardHeader,{className:"border-b",children:e.jsx(n.CardTitle,{children:l})}):null,e.jsx(n.CardContent,{className:"min-w-0",children:c??P})]})}function Dt({format:a,rows:l,columns:i,values:c,onChange:P,selection:o,onSelectionChange:b,fields:g,tableColumns:w,colorForWell:Q=D.defaultColorForWell,emptyEntry:N,mergeOnApply:K,isPopulated:Y,cycleFieldOnWellDoubleClick:$,quickPaintField:d,staged:J,onStagedChange:X,title:Z,badges:ee,banner:te,legend:C,legendPlacement:W="form",formExtras:ae,formSlot:le,hideForm:re=!1,formPlacement:S="start",stackAt:_="md",formWidth:M=Pt,footer:A,plateTitle:ne="Plate",plateToolbar:se,plateBanner:oe,plateFooter:I,plates:ie,activePlateId:ce,onPlateChange:de,onAddPlate:L,onRemovePlate:pe,addPlateLabel:ue,removePlateLabel:me,plateSelectorLabel:fe,plateSelectorVariant:xe,plateBarcodeField:z,applyScope:ge,plateBarcodeColumnHeader:O=jt,hidePlateBarcodeColumn:F=!1,groups:he,activeGroupId:we,onGroupClick:ve,renderHoverSummary:je,cellSize:Se,emptyWellFillColor:Pe,wellShape:be,selectionFillMode:Ce,framedPlate:Me,wrapWell:Ee,highlightedWellIds:ye,onHoveredWellChange:Te,hideSelectionControls:De,labels:p,hideManifest:Ne=!1,manifestSlot:We,manifestTitle:_e=St,manifest:E,manifestFilterable:Ae,manifestGroupable:Ie,autoScaleGrid:Le,minCellSize:ze,maxCellSize:Oe,className:Fe,style:ke,classNames:u,ref:Be,templates:qe,templateId:He,onTemplateChange:Re,onClearTemplate:Ge,onImportCsv:k,onExportCsv:Ve,onImportTemplate:Ue,onExportTemplate:Qe,csvAccept:Ke,templateAccept:Ye,label:$e,align:Je,side:Xe,importTemplateLabel:Ze,exportTemplateLabel:et,importCsvLabel:tt,exportCsvLabel:at,clearLabel:lt}){const t=D.usePlateMapEditorState({values:c,onChange:P,selection:o,onSelectionChange:b,emptyEntry:N,mergeOnApply:K,staged:J,onStagedChange:X,fields:g,cycleFieldOnWellDoubleClick:$,plates:ie,activePlateId:ce,onPlateChange:de,plateBarcodeField:z,applyScope:ge,onImportCsv:k});h.useImperativeHandle(Be,()=>({apply:r=>t.applyStagedToSelection(r),clear:r=>t.clearWells(r),setStaged:r=>t.setStaged(r),getStaged:()=>t.staged}),[t]);const m={...E,filterable:Ae??E?.filterable,groupable:Ie??E?.groupable},v=z??vt,y=t.activePlate?.barcode,rt=h.useMemo(()=>!t.isPlateScoped||!y||F||w.some(s=>s.field===v||s.id===v)?w:[{id:v,header:O,minWidth:150,render:({row:s})=>{const x=s[v]??y;return e.jsx(U.Badge,{variant:"outline",children:String(x)})}},...w],[y,v,F,t.isPlateScoped,O,w]),[B,T]=h.useState(!1);h.useEffect(()=>T(!1),[o]),h.useEffect(()=>{if(!d)return;const r=f=>{const s=f.target;s?.closest?.("[data-well]")||s?.closest?.('[data-slot="well-quick-paint"]')||T(!0)};return document.addEventListener("pointerdown",r,!0),()=>document.removeEventListener("pointerdown",r,!0)},[d]);const nt=h.useMemo(()=>{if(!d||o.size===0||B)return;const r=g.find(x=>x.key===d);if(r?.kind!=="select"||!r.options?.length)return;let f,s=!0;for(const x of o){const G=t.scopedValues.get(x)?.[d],V=typeof G=="string"?G:void 0;if(s)f=V,s=!1;else if(f!==V){f=void 0;break}}return e.jsx(ht.WellQuickPaint,{options:r.options,activeValue:f,selectionSize:o.size,onPick:x=>{t.applyToSelection({[d]:x}),T(!0)}})},[g,B,d,o,t]),st=Mt(t.availablePlates.length,L),ot=W==="plate"&&C?e.jsx("div",{className:"mt-3 border-t pt-3",children:C}):void 0,q=S==="top"||S==="bottom",it=S==="start"||S==="top",ct=typeof M=="number"?`${M}px`:M,H=re?null:e.jsx(n.Card,{"data-plate-map-region":"form",className:j.cn("flex w-full min-w-0 flex-col",!q&&Ct[_],u?.formCard),size:"sm",children:e.jsx(n.CardContent,{className:"flex h-full flex-1 flex-col gap-3",children:e.jsx(mt.PlateMapForm,{fields:g,value:t.staged,onChange:t.setStaged,selectionSize:o.size,onApply:t.applyStagedToSelection,onClear:t.clearWells,applyLabel:p?.apply,clearLabel:p?.clearWells,selectionEmptyLabel:p?.selectionEmpty,selectionCountLabel:p?.selectionCount,extras:ae,legend:W==="form"?C:void 0,formSlot:le,className:u?.form})})},"form"),R=e.jsxs(n.Card,{"data-plate-map-region":"plate",className:j.cn("flex w-full min-w-0 flex-1 flex-col",u?.plateCard),size:"sm",children:[e.jsxs(n.CardHeader,{className:"border-b",children:[e.jsxs("div",{className:"flex min-w-0 flex-wrap items-center gap-2",children:[e.jsx(n.CardTitle,{className:"min-w-0",children:ne}),e.jsx(yt,{visible:st,plates:t.availablePlates,activePlateId:t.activePlate?.id,onPlateChange:t.canChangePlate?t.handlePlateChange:void 0,onAddPlate:L,onRemovePlate:pe,addPlateLabel:ue,removePlateLabel:me,label:fe,variant:xe})]}),e.jsx(n.CardAction,{className:"flex flex-wrap items-center gap-2",children:e.jsx(ut.PlateMapActionsMenu,{templates:qe,templateId:He,onTemplateChange:Re,onClearTemplate:Ge,hasEntries:c.size>0,onImportCsv:k?t.handleImportCsv:void 0,onExportCsv:Ve,onImportTemplate:Ue,onExportTemplate:Qe,csvAccept:Ke,templateAccept:Ye,label:$e,align:Je,side:Xe,importTemplateLabel:Ze,exportTemplateLabel:et,importCsvLabel:tt,exportCsvLabel:at,clearLabel:lt})})]}),e.jsx(n.CardContent,{className:"min-w-0",children:e.jsx(ft.PlateMapGrid,{format:a,rows:l,columns:i,values:t.scopedValues,selection:o,onSelectionChange:b,colorForWell:Q,fields:g,renderHoverSummary:je,hoveredWellId:t.hoveredWellId,onHoveredWellChange:r=>{t.setHoveredWellId(r),Te?.(r)},banner:oe,toolbar:se,footer:ot,hideSelectionControls:De,selectAllLabel:p?.selectAll,deselectAllLabel:p?.deselectAll,emptyWellFillColor:Pe,wellShape:be,framed:Me,wrapWell:Ee,quickPaint:nt,highlightedWellIds:ye,onWellDoubleClick:t.cycleWellField,selectionFillMode:Ce,flashWellId:t.flashWell?.wellId,flashWellKey:t.flashWell?.key,cellSize:Se,autoScale:Le,minCellSize:ze,maxCellSize:Oe,groups:he,activeGroupId:we,onGroupClick:ve,className:u?.grid})}),I?e.jsx(n.CardFooter,{className:"flex flex-wrap items-center gap-2 border-t",children:I}):null]},"plate"),dt=it?[H,R]:[R,H];return e.jsxs("div",{"data-slot":"plate-map-editor",className:j.cn("flex w-full min-w-0 flex-col gap-4",Fe),style:ke,children:[e.jsx(Et,{title:Z,badges:ee}),te,e.jsx("div",{"data-slot":"plate-map-editor-container",className:"@container w-full min-w-0",style:{"--plate-map-form-width":ct},children:e.jsx("div",{"data-slot":"plate-map-editor-layout",className:j.cn("flex w-full min-w-0 flex-col gap-3",!q&&bt[_],u?.layout),children:dt})}),e.jsx(Tt,{hidden:Ne,title:_e,cardClassName:u?.manifestCard,slot:We,manifest:e.jsx(xt.PlateMapManifest,{values:t.scopedValues,onChange:t.commitScopedValues,columns:rt,fields:g,selection:o,onSelectionChange:b,emptyEntry:N,isPopulated:Y,filterable:m.filterable,filterColumns:m.filterColumns,groupable:m.groupable,defaultGroupBy:m.defaultGroupBy,pageSize:m.pageSize,pageSizeOptions:m.pageSizeOptions,enableFillDown:m.enableFillDown,labels:p,className:u?.manifest})}),A?e.jsx("div",{className:"flex flex-wrap justify-end gap-2 pt-2",children:A}):null]})}exports.getPlateMapScopedWellId=D.getPlateMapScopedWellId;exports.PlateBadge=U.Badge;exports.PlateMapEditor=Dt;
2
2
  //# sourceMappingURL=PlateMapEditor.cjs.map