@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.
- package/README.md +113 -0
- package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.d.ts +5 -1
- package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.js +32 -30
- package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.d.ts +269 -13
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.js +392 -310
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapForm.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapForm.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapForm.d.ts +5 -1
- package/dist/components/composed/PlateMapEditor/PlateMapForm.js +19 -15
- package/dist/components/composed/PlateMapEditor/PlateMapForm.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.d.ts +12 -1
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.js +69 -63
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapManifest.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapManifest.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapManifest.d.ts +13 -4
- package/dist/components/composed/PlateMapEditor/PlateMapManifest.js +38 -29
- package/dist/components/composed/PlateMapEditor/PlateMapManifest.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlatePaintGrid.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/PlatePaintGrid.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlatePaintGrid.d.ts +6 -1
- package/dist/components/composed/PlateMapEditor/PlatePaintGrid.js +490 -275
- package/dist/components/composed/PlateMapEditor/PlatePaintGrid.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellLegend.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/WellLegend.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellLegend.d.ts +8 -2
- package/dist/components/composed/PlateMapEditor/WellLegend.js +8 -7
- package/dist/components/composed/PlateMapEditor/WellLegend.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellManifestTable.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/WellManifestTable.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellManifestTable.d.ts +6 -2
- package/dist/components/composed/PlateMapEditor/WellManifestTable.js +288 -256
- package/dist/components/composed/PlateMapEditor/WellManifestTable.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellMetadataForm.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/WellMetadataForm.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellMetadataForm.d.ts +5 -1
- package/dist/components/composed/PlateMapEditor/WellMetadataForm.js +61 -66
- package/dist/components/composed/PlateMapEditor/WellMetadataForm.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellQuickPaint.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/WellQuickPaint.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/WellQuickPaint.d.ts +23 -0
- package/dist/components/composed/PlateMapEditor/WellQuickPaint.js +75 -0
- package/dist/components/composed/PlateMapEditor/WellQuickPaint.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/index.d.ts +6 -2
- package/dist/components/composed/PlateMapEditor/types.d.ts +65 -0
- package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.d.ts +109 -0
- package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.js +246 -0
- package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.js.map +1 -0
- package/dist/composed/PlateMapEditor.cjs +1 -1
- package/dist/composed/PlateMapEditor.js +48 -42
- package/dist/composed/PlateMapEditor.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +701 -695
- package/dist/index.js.map +1 -1
- package/dist/index.scoped.css +1 -1
- package/dist/index.tailwind.css +1 -1
- 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"),
|
|
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":"mUAUMA,EAAkD,CACtD,SAAU,WACV,OAAQ,SACR,WAAY,aACZ,YAAa,cACb,UAAW,YACX,SAAU,WACV,aAAc,cAChB,EAEMC,EAAyC,CAAC,WAAY,cAAc,EAEpEC,EAAsC,CAC1C,WACA,SACA,aACA,cACA,YACA,WACA,cACF,EAEA,SAASC,EAAcC,EAAkBC,EAAsD,CAC7F,MAAMC,EAAWD,IAAmB,CAAC,GAAK,WAC1C,MAAO,CAAE,GAAI,OAAO,WAAA,EAAc,SAAAD,EAAU,SAAAE,EAAU,MAAO,EAAA,CAC/D,CAaO,SAASC,EAAsB,CACpC,QAAAC,EACA,YAAAC,EACA,QAAAC,EACA,gBAAAC,EACA,aAAAC,EAAe,SACf,UAAAC,CACF,EAA+B,CAC7B,MAAMC,EAAYV,GAA6BK,IAAcL,CAAQ,GAAKA,EACpEW,EAAcP,EAAQ,CAAC,EACvBQ,EAAgBD,GAAa,UAAY,GAEzCE,EAAY,IAAMN,EAAgB,CAAC,GAAGD,EAASP,EAAca,EAAeD,GAAa,SAAS,CAAC,CAAC,EACpGG,EAAgBC,GAAeR,EAAgBD,EAAQ,OAAQU,GAASA,EAAK,KAAOD,CAAE,CAAC,EACvFE,EAAe,CAACF,EAAYG,IAChCX,EAAgBD,EAAQ,IAAKU,GAAUA,EAAK,KAAOD,EAAK,CAAE,GAAGC,EAAM,GAAGE,CAAA,EAAUF,CAAK,CAAC,EAClFG,EAAW,IAAMZ,EAAgB,EAAE,EAEnCa,EAAcd,EAAQ,OAE5B,cACGe,UAAA,CACC,SAAA,CAAAC,EAAAA,IAACC,EAAAA,eAAA,CAAe,QAAO,GACrB,SAAAC,EAAAA,KAACC,EAAAA,OAAA,CACC,KAAK,SACL,QAAQ,UACR,KAAK,KACL,YAAU,kBACV,UAAWC,EAAAA,GAAGjB,CAAS,EACvB,aAAYW,EAAc,EAAI,GAAGZ,CAAY,KAAKY,CAAW,WAAaZ,EAE1E,SAAA,CAAAc,EAAAA,IAACK,EAAAA,eAAA,CAAe,UAAU,UAAA,CAAW,EACpCnB,EACAY,EAAc,EACbE,EAAAA,IAAC,QAAK,UAAU,kHACb,WACH,EACE,IAAA,CAAA,CAAA,EAER,EAEAA,EAAAA,IAACM,EAAAA,eAAA,CAAe,MAAM,MAAM,UAAU,WAAW,aAAW,oBACxD,SAAAJ,EAAAA,KAAC,MAAA,CAAI,UAAU,sBACZ,SAAA,CAAAlB,EAAQ,IAAKuB,GAAc,CAE1B,MAAMC,EADY1B,EAAQ,KAAM2B,GAAMA,EAAE,WAAaF,EAAU,QAAQ,GAC1C,WAAa/B,EACpCkC,EAAcnC,EAAqB,SAASgC,EAAU,QAAQ,EACpE,OACEL,EAAAA,KAAC,MAAA,CAAuB,UAAU,0BAChC,SAAA,CAAAA,EAAAA,KAACS,EAAAA,OAAA,CACC,MAAOJ,EAAU,SACjB,cAAgBK,GAAU,CAExB,MAAMC,EADa/B,EAAQ,KAAM2B,GAAMA,EAAE,WAAaG,CAAK,GACzB,WAAapC,EACzCsC,EAAeD,EAAc,SAASN,EAAU,QAAQ,EAC1DA,EAAU,SACTM,EAAc,CAAC,GAAK,WACzBlB,EAAaY,EAAU,GAAI,CAAE,SAAUK,EAAO,SAAUE,EAAc,MAAO,GAAI,CACnF,EAEA,SAAA,CAAAd,EAAAA,IAACe,EAAAA,cAAA,CAAc,aAAW,gBAAgB,KAAK,KAAK,UAAU,OAC5D,SAAAf,EAAAA,IAACgB,EAAAA,YAAA,CAAA,CAAY,CAAA,CACf,EACAhB,EAAAA,IAACiB,EAAAA,eACE,SAAAnC,EAAQ,IAAK2B,GACZT,EAAAA,IAACkB,cAA4B,MAAOT,EAAE,SACnC,SAAAA,EAAE,OAASrB,EAASqB,EAAE,QAAQ,GADhBA,EAAE,QAEnB,CACD,CAAA,CACH,CAAA,CAAA,CAAA,EAGFP,EAAAA,KAACS,EAAAA,OAAA,CACC,MAAOJ,EAAU,SACjB,cAAgBK,GACdjB,EAAaY,EAAU,GAAI,CAAE,SAAUK,EAAyB,MAAO,GAAI,EAG7E,SAAA,CAAAZ,EAAAA,IAACe,EAAAA,cAAA,CAAc,aAAW,kBAAkB,KAAK,KAAK,UAAU,OAC9D,SAAAf,EAAAA,IAACgB,EAAAA,YAAA,CAAA,CAAY,CAAA,CACf,EACAhB,EAAAA,IAACiB,EAAAA,cAAA,CACE,SAAAT,EAAU,IAAKW,GACdnB,MAACkB,EAAAA,WAAA,CAAoB,MAAOC,EACzB,SAAA7C,EAAgB6C,CAAE,CAAA,EADJA,CAEjB,CACD,CAAA,CACH,CAAA,CAAA,CAAA,EAGDT,EACCV,EAAAA,IAAC,MAAA,CAAI,UAAU,WAAW,cAAW,GAAC,EAEtCA,EAAAA,IAACoB,EAAAA,MAAA,CACC,UAAU,OACV,YAAY,SACZ,MAAOb,EAAU,MACjB,SAAWc,GAAU1B,EAAaY,EAAU,GAAI,CAAE,MAAOc,EAAM,OAAO,KAAA,CAAO,CAAA,CAAA,EAIjFrB,EAAAA,IAACG,EAAAA,OAAA,CACC,KAAK,SACL,QAAQ,QACR,KAAK,OACL,UAAU,8DACV,QAAS,IAAMX,EAAae,EAAU,EAAE,EACxC,aAAW,gBAEX,SAAAP,EAAAA,IAACsB,EAAAA,MAAA,CAAM,UAAU,UAAA,CAAW,CAAA,CAAA,CAC9B,CAAA,EA9DQf,EAAU,EA+DpB,CAEJ,CAAC,EAEDL,EAAAA,KAAC,MAAA,CAAI,UAAU,0BACb,SAAA,CAAAA,EAAAA,KAACC,EAAAA,OAAA,CACC,KAAK,SACL,QAAQ,UACR,KAAK,KACL,QAASZ,EACT,SAAUT,EAAQ,SAAW,EAE7B,SAAA,CAAAkB,EAAAA,IAACuB,EAAAA,SAAA,CAAS,UAAU,UAAA,CAAW,EAAE,YAAA,CAAA,CAAA,EAGlCvC,EAAQ,OAAS,EAChBgB,EAAAA,IAACG,EAAAA,OAAA,CACC,KAAK,SACL,QAAQ,QACR,KAAK,KACL,UAAU,wBACV,QAASN,EACV,SAAA,WAAA,CAAA,EAGC,IAAA,CAAA,CACN,CAAA,CAAA,CACF,CAAA,CACJ,CAAA,EACF,CAEJ"}
|
|
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
|
|
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
|
|
5
|
-
import { Popover as
|
|
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
|
|
8
|
-
const
|
|
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
|
-
},
|
|
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
|
|
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
|
|
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
|
-
|
|
35
|
+
valuePlaceholder: I = "Value…",
|
|
36
|
+
clearAllLabel: N = "Clear all",
|
|
37
|
+
className: b
|
|
36
38
|
}) {
|
|
37
|
-
const
|
|
38
|
-
return /* @__PURE__ */ l(
|
|
39
|
-
/* @__PURE__ */ t(
|
|
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:
|
|
48
|
+
className: L(b),
|
|
47
49
|
"aria-label": i > 0 ? `${d} (${i} active)` : d,
|
|
48
50
|
children: [
|
|
49
|
-
/* @__PURE__ */ t(
|
|
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(
|
|
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,
|
|
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((
|
|
65
|
-
p(e.id, { columnId: a, operator:
|
|
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 ??
|
|
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:
|
|
82
|
+
/* @__PURE__ */ t(x, { children: o.map((a) => /* @__PURE__ */ t(y, { value: a, children: B[a] }, a)) })
|
|
81
83
|
]
|
|
82
84
|
}
|
|
83
85
|
),
|
|
84
|
-
|
|
85
|
-
|
|
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:
|
|
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: () =>
|
|
102
|
+
onClick: () => A(e.id),
|
|
101
103
|
"aria-label": "Remove filter",
|
|
102
|
-
children: /* @__PURE__ */ t(
|
|
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:
|
|
116
|
+
onClick: F,
|
|
115
117
|
disabled: n.length === 0,
|
|
116
118
|
children: [
|
|
117
|
-
/* @__PURE__ */ t(
|
|
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:
|
|
130
|
-
children:
|
|
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
|
-
|
|
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
|
|
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
|