@tetrascience-npm/tetrascience-react-ui 0.5.0-beta.60.1 → 0.5.0-beta.61.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/dist/components/composed/PlateMapEditor/ManifestFilterPopover.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.js +140 -0
- package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlateMapActionsMenu.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/PlateMapActionsMenu.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlateMapActionsMenu.js +126 -0
- package/dist/components/composed/PlateMapEditor/PlateMapActionsMenu.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.js +422 -0
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlateMapPlateSelector.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/PlateMapPlateSelector.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlateMapPlateSelector.js +136 -0
- package/dist/components/composed/PlateMapEditor/PlateMapPlateSelector.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlatePaintGrid.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/PlatePaintGrid.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlatePaintGrid.js +389 -0
- package/dist/components/composed/PlateMapEditor/PlatePaintGrid.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlateZoomControl.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/PlateZoomControl.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlateZoomControl.js +54 -0
- package/dist/components/composed/PlateMapEditor/PlateZoomControl.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/TemplateIOPanel.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/TemplateIOPanel.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/TemplateIOPanel.js +96 -0
- package/dist/components/composed/PlateMapEditor/TemplateIOPanel.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/WellLegend.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/WellLegend.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/WellLegend.js +58 -0
- package/dist/components/composed/PlateMapEditor/WellLegend.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/WellManifestTable.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/WellManifestTable.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/WellManifestTable.js +421 -0
- package/dist/components/composed/PlateMapEditor/WellManifestTable.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/WellMetadataForm.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/WellMetadataForm.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/WellMetadataForm.js +177 -0
- package/dist/components/composed/PlateMapEditor/WellMetadataForm.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/autoFill.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/autoFill.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/autoFill.js +41 -0
- package/dist/components/composed/PlateMapEditor/autoFill.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/csvPlateTriage.cjs +4 -0
- package/dist/components/composed/PlateMapEditor/csvPlateTriage.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/csvPlateTriage.js +103 -0
- package/dist/components/composed/PlateMapEditor/csvPlateTriage.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/helpers.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/helpers.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/helpers.js +11 -0
- package/dist/components/composed/PlateMapEditor/helpers.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/wellGrid.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/wellGrid.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/wellGrid.js +56 -0
- package/dist/components/composed/PlateMapEditor/wellGrid.js.map +1 -0
- package/dist/components/ui/data-table/data-table.cjs +1 -1
- package/dist/components/ui/data-table/data-table.cjs.map +1 -1
- package/dist/components/ui/data-table/data-table.js +1 -0
- package/dist/components/ui/data-table/data-table.js.map +1 -1
- package/dist/components/ui/popover.cjs +2 -0
- package/dist/components/ui/popover.cjs.map +1 -0
- package/dist/components/ui/popover.js +45 -0
- package/dist/components/ui/popover.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +555 -0
- package/dist/index.js +637 -595
- package/dist/index.js.map +1 -1
- package/dist/index.tailwind.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +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"),n=require("../../ui/select.cjs"),z=require("../../../lib/utils.cjs"),P={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"},R=["is_empty","is_not_empty"],f=["contains","equals","not_equals","starts_with","ends_with","is_empty","is_not_empty"];function O(r,d){const l=d?.[0]??"contains";return{id:crypto.randomUUID(),columnId:r,operator:l,value:""}}function A({columns:r,columnLabel:d,filters:l,onFiltersChange:o,triggerLabel:m="Filter",className:g}){const _=t=>d?.(t)??t,v=r[0],y=v?.columnId??"",C=()=>o([...l,O(y,v?.operators)]),I=t=>o(l.filter(c=>c.id!==t)),p=(t,c)=>o(l.map(a=>a.id===t?{...a,...c}:a)),N=()=>o([]),i=l.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":i>0?`${m} (${i} active)`:m,children:[e.jsx(x.ListFilterIcon,{className:"size-3.5"}),m,i>0?e.jsx("span",{className:"flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground",children:i}):null]})}),e.jsx(h.PopoverContent,{align:"end",className:"min-w-80",children:e.jsxs("div",{className:"flex flex-col gap-2",children:[l.map(t=>{const a=r.find(s=>s.columnId===t.columnId)?.operators??f,S=R.includes(t.operator);return e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(n.Select,{value:t.columnId,onValueChange:s=>{const j=r.find(b=>b.columnId===s)?.operators??f,q=j.includes(t.operator)?t.operator:j[0]??"contains";p(t.id,{columnId:s,operator:q,value:""})},children:[e.jsx(n.SelectTrigger,{size:"sm",className:"w-36",children:e.jsx(n.SelectValue,{})}),e.jsx(n.SelectContent,{children:r.map(s=>e.jsx(n.SelectItem,{value:s.columnId,children:s.label??_(s.columnId)},s.columnId))})]}),e.jsxs(n.Select,{value:t.operator,onValueChange:s=>p(t.id,{operator:s,value:""}),children:[e.jsx(n.SelectTrigger,{size:"sm",className:"w-32",children:e.jsx(n.SelectValue,{})}),e.jsx(n.SelectContent,{children:a.map(s=>e.jsx(n.SelectItem,{value:s,children:P[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:r.length===0,children:[e.jsx(x.PlusIcon,{className:"size-3.5"}),"Add filter"]}),l.length>0?e.jsx(u.Button,{type:"button",variant:"ghost",size:"sm",className:"text-muted-foreground",onClick:N,children:"Clear all"}):null]})]})})]})}exports.ManifestFilterPopover=A;
|
|
2
|
+
//# sourceMappingURL=ManifestFilterPopover.cjs.map
|
|
@@ -0,0 +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\">\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 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 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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"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { jsxs as l, jsx as t } from "react/jsx-runtime";
|
|
2
|
+
import { ListFilterIcon as R, XIcon as S, PlusIcon as F } from "lucide-react";
|
|
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";
|
|
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 = {
|
|
9
|
+
contains: "contains",
|
|
10
|
+
equals: "equals",
|
|
11
|
+
not_equals: "not equals",
|
|
12
|
+
starts_with: "starts with",
|
|
13
|
+
ends_with: "ends with",
|
|
14
|
+
is_empty: "is empty",
|
|
15
|
+
is_not_empty: "is not empty"
|
|
16
|
+
}, L = ["is_empty", "is_not_empty"], C = [
|
|
17
|
+
"contains",
|
|
18
|
+
"equals",
|
|
19
|
+
"not_equals",
|
|
20
|
+
"starts_with",
|
|
21
|
+
"ends_with",
|
|
22
|
+
"is_empty",
|
|
23
|
+
"is_not_empty"
|
|
24
|
+
];
|
|
25
|
+
function B(n, u) {
|
|
26
|
+
const r = u?.[0] ?? "contains";
|
|
27
|
+
return { id: crypto.randomUUID(), columnId: n, operator: r, value: "" };
|
|
28
|
+
}
|
|
29
|
+
function Q({
|
|
30
|
+
columns: n,
|
|
31
|
+
columnLabel: u,
|
|
32
|
+
filters: r,
|
|
33
|
+
onFiltersChange: o,
|
|
34
|
+
triggerLabel: d = "Filter",
|
|
35
|
+
className: I
|
|
36
|
+
}) {
|
|
37
|
+
const N = (e) => u?.(e) ?? e, h = n[0], w = h?.columnId ?? "", z = () => o([...r, B(w, h?.operators)]), b = (e) => o(r.filter((c) => c.id !== e)), p = (e, c) => o(r.map((s) => s.id === e ? { ...s, ...c } : s)), A = () => o([]), i = r.length;
|
|
38
|
+
return /* @__PURE__ */ l(T, { children: [
|
|
39
|
+
/* @__PURE__ */ t(V, { asChild: !0, children: /* @__PURE__ */ l(
|
|
40
|
+
m,
|
|
41
|
+
{
|
|
42
|
+
type: "button",
|
|
43
|
+
variant: "outline",
|
|
44
|
+
size: "sm",
|
|
45
|
+
"data-slot": "manifest-filter",
|
|
46
|
+
className: U(I),
|
|
47
|
+
"aria-label": i > 0 ? `${d} (${i} active)` : d,
|
|
48
|
+
children: [
|
|
49
|
+
/* @__PURE__ */ t(R, { className: "size-3.5" }),
|
|
50
|
+
d,
|
|
51
|
+
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
|
+
]
|
|
53
|
+
}
|
|
54
|
+
) }),
|
|
55
|
+
/* @__PURE__ */ t(k, { align: "end", className: "min-w-80", children: /* @__PURE__ */ l("div", { className: "flex flex-col gap-2", children: [
|
|
56
|
+
r.map((e) => {
|
|
57
|
+
const s = n.find((a) => a.columnId === e.columnId)?.operators ?? C, E = L.includes(e.operator);
|
|
58
|
+
return /* @__PURE__ */ l("div", { className: "flex items-center gap-2", children: [
|
|
59
|
+
/* @__PURE__ */ l(
|
|
60
|
+
v,
|
|
61
|
+
{
|
|
62
|
+
value: e.columnId,
|
|
63
|
+
onValueChange: (a) => {
|
|
64
|
+
const f = n.find((P) => P.columnId === a)?.operators ?? C, O = f.includes(e.operator) ? e.operator : f[0] ?? "contains";
|
|
65
|
+
p(e.id, { columnId: a, operator: O, value: "" });
|
|
66
|
+
},
|
|
67
|
+
children: [
|
|
68
|
+
/* @__PURE__ */ t(g, { 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)) })
|
|
70
|
+
]
|
|
71
|
+
}
|
|
72
|
+
),
|
|
73
|
+
/* @__PURE__ */ l(
|
|
74
|
+
v,
|
|
75
|
+
{
|
|
76
|
+
value: e.operator,
|
|
77
|
+
onValueChange: (a) => p(e.id, { operator: a, value: "" }),
|
|
78
|
+
children: [
|
|
79
|
+
/* @__PURE__ */ t(g, { size: "sm", className: "w-32", children: /* @__PURE__ */ t(_, {}) }),
|
|
80
|
+
/* @__PURE__ */ t(x, { children: s.map((a) => /* @__PURE__ */ t(y, { value: a, children: j[a] }, a)) })
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
),
|
|
84
|
+
E ? /* @__PURE__ */ t("div", { className: "h-8 w-40", "aria-hidden": !0 }) : /* @__PURE__ */ t(
|
|
85
|
+
q,
|
|
86
|
+
{
|
|
87
|
+
className: "w-40",
|
|
88
|
+
placeholder: "Value…",
|
|
89
|
+
value: e.value,
|
|
90
|
+
onChange: (a) => p(e.id, { value: a.target.value })
|
|
91
|
+
}
|
|
92
|
+
),
|
|
93
|
+
/* @__PURE__ */ t(
|
|
94
|
+
m,
|
|
95
|
+
{
|
|
96
|
+
type: "button",
|
|
97
|
+
variant: "ghost",
|
|
98
|
+
size: "icon",
|
|
99
|
+
className: "size-8 shrink-0 text-muted-foreground hover:text-foreground",
|
|
100
|
+
onClick: () => b(e.id),
|
|
101
|
+
"aria-label": "Remove filter",
|
|
102
|
+
children: /* @__PURE__ */ t(S, { className: "size-3.5" })
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
] }, e.id);
|
|
106
|
+
}),
|
|
107
|
+
/* @__PURE__ */ l("div", { className: "flex items-center gap-2", children: [
|
|
108
|
+
/* @__PURE__ */ l(
|
|
109
|
+
m,
|
|
110
|
+
{
|
|
111
|
+
type: "button",
|
|
112
|
+
variant: "outline",
|
|
113
|
+
size: "sm",
|
|
114
|
+
onClick: z,
|
|
115
|
+
disabled: n.length === 0,
|
|
116
|
+
children: [
|
|
117
|
+
/* @__PURE__ */ t(F, { className: "size-3.5" }),
|
|
118
|
+
"Add filter"
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
),
|
|
122
|
+
r.length > 0 ? /* @__PURE__ */ t(
|
|
123
|
+
m,
|
|
124
|
+
{
|
|
125
|
+
type: "button",
|
|
126
|
+
variant: "ghost",
|
|
127
|
+
size: "sm",
|
|
128
|
+
className: "text-muted-foreground",
|
|
129
|
+
onClick: A,
|
|
130
|
+
children: "Clear all"
|
|
131
|
+
}
|
|
132
|
+
) : null
|
|
133
|
+
] })
|
|
134
|
+
] }) })
|
|
135
|
+
] });
|
|
136
|
+
}
|
|
137
|
+
export {
|
|
138
|
+
Q as ManifestFilterPopover
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=ManifestFilterPopover.js.map
|
|
@@ -0,0 +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\">\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 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 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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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),d=require("lucide-react"),C=require("react"),A=require("./csvPlateTriage.cjs"),F=require("./helpers.cjs"),I=require("../../ui/button.cjs"),n=require("../../ui/dropdown-menu.cjs"),z=require("../../../lib/utils.cjs");function _(t){const a=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const r in t)if(r!=="default"){const i=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(a,r,i.get?i:{enumerable:!0,get:()=>t[r]})}}return a.default=t,Object.freeze(a)}const x=_(C);function b({inputRef:t,accept:a,onPick:r,triageCsv:i}){return e.jsx("input",{ref:t,type:"file",accept:a,hidden:!0,onChange:o=>{const u=o.currentTarget,s=o.target.files?.[0];if(!s){u.value="";return}(async()=>{const c=i?await A.triagePlateMapCsvFile(s):void 0;await(c?r?.(s,c):r?.(s)),u.value=""})()}})}function G({templates:t,templateId:a,onTemplateChange:r,onClearTemplate:i,hasEntries:o,onImportCsv:u,onExportCsv:s,onImportTemplate:c,onExportTemplate:j,label:g="Actions",align:D="start",side:m="bottom",csvAccept:S=".csv,text/csv",templateAccept:k="application/json",importTemplateLabel:y="Import template (JSON)",exportTemplateLabel:R="Save template",importCsvLabel:v="Import plate map (CSV)",exportCsvLabel:q="Export plate map (CSV)",clearLabel:N="Clear template",className:O}){const f=x.useRef(null),M=x.useRef(null),h=x.useMemo(()=>F.groupTemplateOptions(t),[t]),w=o===!1;return h.length>0||!!c||!!j||!!u||!!s||!!i?e.jsxs(e.Fragment,{children:[e.jsxs(n.DropdownMenu,{children:[e.jsx(n.DropdownMenuTrigger,{asChild:!0,children:e.jsxs(I.Button,{type:"button",variant:"outline",size:"sm",className:z.cn("min-w-24 justify-between",O),children:[e.jsx("span",{children:g}),e.jsx(d.ChevronDown,{"aria-hidden":!0})]})}),e.jsxs(n.DropdownMenuContent,{align:D,side:m,className:"w-64",children:[h.map(([p,P])=>e.jsxs(n.DropdownMenuGroup,{children:[p?e.jsx(n.DropdownMenuLabel,{children:p}):null,P.map(l=>e.jsxs(n.DropdownMenuItem,{disabled:l.disabled,onClick:()=>r?.(l.id),children:[l.id===a?e.jsx(d.Check,{"aria-hidden":!0}):e.jsx("span",{className:"size-4","aria-hidden":!0}),e.jsxs("span",{className:"flex min-w-0 flex-col",children:[e.jsx("span",{className:"truncate",children:l.label}),l.description?e.jsx("span",{className:"truncate text-xs text-muted-foreground",children:l.description}):null]})]},l.id))]},p||"templates")),h.length>0&&(c||j||u||s||i)?e.jsx(n.DropdownMenuSeparator,{}):null,c?e.jsxs(n.DropdownMenuItem,{onClick:()=>f.current?.click(),children:[e.jsx(d.LayoutTemplate,{"aria-hidden":!0}),y]}):null,j?e.jsxs(n.DropdownMenuItem,{disabled:w,onClick:()=>j(),children:[e.jsx(d.SaveAll,{"aria-hidden":!0}),R]}):null,(c||j)&&(u||s)?e.jsx(n.DropdownMenuSeparator,{}):null,u?e.jsxs(n.DropdownMenuItem,{onClick:()=>M.current?.click(),children:[e.jsx(d.Upload,{"aria-hidden":!0}),v]}):null,s?e.jsxs(n.DropdownMenuItem,{disabled:w,onClick:()=>s(),children:[e.jsx(d.Download,{"aria-hidden":!0}),q]}):null,i?e.jsxs(e.Fragment,{children:[e.jsx(n.DropdownMenuSeparator,{}),e.jsxs(n.DropdownMenuItem,{variant:"destructive",disabled:!a&&!o,onClick:()=>i(),children:[e.jsx(d.Trash2,{"aria-hidden":!0}),N]})]}):null]})]}),e.jsx(b,{inputRef:f,accept:k,onPick:c}),e.jsx(b,{inputRef:M,accept:S,onPick:u,triageCsv:!0})]}):null}exports.PlateMapActionsMenu=G;
|
|
2
|
+
//# sourceMappingURL=PlateMapActionsMenu.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlateMapActionsMenu.cjs","sources":["../../../../src/components/composed/PlateMapEditor/PlateMapActionsMenu.tsx"],"sourcesContent":["import { Check, ChevronDown, Download, LayoutTemplate, SaveAll, Trash2, Upload } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { triagePlateMapCsvFile } from \"./csvPlateTriage\";\nimport { groupTemplateOptions } from \"./helpers\";\n\nimport type { ImportExportHandlers, PlateMapCsvTriage, TemplateOption } from \"./types\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface PlateMapActionsMenuProps extends ImportExportHandlers {\n templates?: TemplateOption[];\n templateId?: string;\n onTemplateChange?: (id: string) => void;\n onClearTemplate?: () => void;\n /** Disable export/save actions when there is nothing to export. */\n hasEntries?: boolean;\n label?: React.ReactNode;\n align?: \"start\" | \"center\" | \"end\";\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n csvAccept?: string;\n templateAccept?: string;\n importTemplateLabel?: string;\n exportTemplateLabel?: string;\n importCsvLabel?: string;\n exportCsvLabel?: string;\n clearLabel?: string;\n className?: string;\n}\n\nfunction HiddenFileInput({\n inputRef,\n accept,\n onPick,\n triageCsv,\n}: {\n inputRef: React.RefObject<HTMLInputElement | null>;\n accept: string;\n onPick?: (file: File, triage?: PlateMapCsvTriage) => void | Promise<void>;\n triageCsv?: boolean;\n}) {\n return (\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n hidden\n onChange={(event) => {\n const input = event.currentTarget;\n const file = event.target.files?.[0];\n if (!file) {\n input.value = \"\";\n return;\n }\n\n void (async () => {\n const triage = triageCsv ? await triagePlateMapCsvFile(file) : undefined;\n await (triage ? onPick?.(file, triage) : onPick?.(file));\n input.value = \"\";\n })();\n }}\n />\n );\n}\n\nexport function PlateMapActionsMenu({\n templates,\n templateId,\n onTemplateChange,\n onClearTemplate,\n hasEntries,\n onImportCsv,\n onExportCsv,\n onImportTemplate,\n onExportTemplate,\n label = \"Actions\",\n align = \"start\",\n side = \"bottom\",\n csvAccept = \".csv,text/csv\",\n templateAccept = \"application/json\",\n importTemplateLabel = \"Import template (JSON)\",\n exportTemplateLabel = \"Save template\",\n importCsvLabel = \"Import plate map (CSV)\",\n exportCsvLabel = \"Export plate map (CSV)\",\n clearLabel = \"Clear template\",\n className,\n}: PlateMapActionsMenuProps) {\n const templateInputRef = React.useRef<HTMLInputElement>(null);\n const csvInputRef = React.useRef<HTMLInputElement>(null);\n const templateGroups = React.useMemo(() => groupTemplateOptions(templates), [templates]);\n const disableEntryExport = hasEntries === false;\n\n const hasMenuItems =\n templateGroups.length > 0 ||\n !!onImportTemplate ||\n !!onExportTemplate ||\n !!onImportCsv ||\n !!onExportCsv ||\n !!onClearTemplate;\n\n if (!hasMenuItems) return null;\n\n return (\n <>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" className={cn(\"min-w-24 justify-between\", className)}>\n <span>{label}</span>\n <ChevronDown aria-hidden />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align={align} side={side} className=\"w-64\">\n {templateGroups.map(([group, options]) => (\n <DropdownMenuGroup key={group || \"templates\"}>\n {group ? <DropdownMenuLabel>{group}</DropdownMenuLabel> : null}\n {options.map((template) => (\n <DropdownMenuItem\n key={template.id}\n disabled={template.disabled}\n onClick={() => onTemplateChange?.(template.id)}\n >\n {template.id === templateId ? <Check aria-hidden /> : <span className=\"size-4\" aria-hidden />}\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate\">{template.label}</span>\n {template.description ? (\n <span className=\"truncate text-xs text-muted-foreground\">{template.description}</span>\n ) : null}\n </span>\n </DropdownMenuItem>\n ))}\n </DropdownMenuGroup>\n ))}\n\n {templateGroups.length > 0 &&\n (onImportTemplate || onExportTemplate || onImportCsv || onExportCsv || onClearTemplate) ? (\n <DropdownMenuSeparator />\n ) : null}\n\n {onImportTemplate ? (\n <DropdownMenuItem onClick={() => templateInputRef.current?.click()}>\n <LayoutTemplate aria-hidden />\n {importTemplateLabel}\n </DropdownMenuItem>\n ) : null}\n {onExportTemplate ? (\n <DropdownMenuItem disabled={disableEntryExport} onClick={() => onExportTemplate()}>\n <SaveAll aria-hidden />\n {exportTemplateLabel}\n </DropdownMenuItem>\n ) : null}\n\n {(onImportTemplate || onExportTemplate) && (onImportCsv || onExportCsv) ? <DropdownMenuSeparator /> : null}\n\n {onImportCsv ? (\n <DropdownMenuItem onClick={() => csvInputRef.current?.click()}>\n <Upload aria-hidden />\n {importCsvLabel}\n </DropdownMenuItem>\n ) : null}\n {onExportCsv ? (\n <DropdownMenuItem disabled={disableEntryExport} onClick={() => onExportCsv()}>\n <Download aria-hidden />\n {exportCsvLabel}\n </DropdownMenuItem>\n ) : null}\n\n {onClearTemplate ? (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n variant=\"destructive\"\n disabled={!templateId && !hasEntries}\n onClick={() => onClearTemplate()}\n >\n <Trash2 aria-hidden />\n {clearLabel}\n </DropdownMenuItem>\n </>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n <HiddenFileInput inputRef={templateInputRef} accept={templateAccept} onPick={onImportTemplate} />\n <HiddenFileInput inputRef={csvInputRef} accept={csvAccept} onPick={onImportCsv} triageCsv />\n </>\n );\n}\n"],"names":["HiddenFileInput","inputRef","accept","onPick","triageCsv","jsx","event","input","file","triage","triagePlateMapCsvFile","PlateMapActionsMenu","templates","templateId","onTemplateChange","onClearTemplate","hasEntries","onImportCsv","onExportCsv","onImportTemplate","onExportTemplate","label","align","side","csvAccept","templateAccept","importTemplateLabel","exportTemplateLabel","importCsvLabel","exportCsvLabel","clearLabel","className","templateInputRef","React","csvInputRef","templateGroups","groupTemplateOptions","disableEntryExport","jsxs","Fragment","DropdownMenu","DropdownMenuTrigger","Button","cn","ChevronDown","DropdownMenuContent","group","options","DropdownMenuGroup","DropdownMenuLabel","template","DropdownMenuItem","Check","DropdownMenuSeparator","LayoutTemplate","SaveAll","Upload","Download","Trash2"],"mappings":"smBAwCA,SAASA,EAAgB,CACvB,SAAAC,EACA,OAAAC,EACA,OAAAC,EACA,UAAAC,CACF,EAKG,CACD,OACEC,EAAAA,IAAC,QAAA,CACC,IAAKJ,EACL,KAAK,OACL,OAAAC,EACA,OAAM,GACN,SAAWI,GAAU,CACnB,MAAMC,EAAQD,EAAM,cACdE,EAAOF,EAAM,OAAO,QAAQ,CAAC,EACnC,GAAI,CAACE,EAAM,CACTD,EAAM,MAAQ,GACd,MACF,EAEM,SAAY,CAChB,MAAME,EAASL,EAAY,MAAMM,EAAAA,sBAAsBF,CAAI,EAAI,OAC/D,MAAOC,EAASN,IAASK,EAAMC,CAAM,EAAIN,IAASK,CAAI,GACtDD,EAAM,MAAQ,EAChB,GAAA,CACF,CAAA,CAAA,CAGN,CAEO,SAASI,EAAoB,CAClC,UAAAC,EACA,WAAAC,EACA,iBAAAC,EACA,gBAAAC,EACA,WAAAC,EACA,YAAAC,EACA,YAAAC,EACA,iBAAAC,EACA,iBAAAC,EACA,MAAAC,EAAQ,UACR,MAAAC,EAAQ,QACR,KAAAC,EAAO,SACP,UAAAC,EAAY,gBACZ,eAAAC,EAAiB,mBACjB,oBAAAC,EAAsB,yBACtB,oBAAAC,EAAsB,gBACtB,eAAAC,EAAiB,yBACjB,eAAAC,EAAiB,yBACjB,WAAAC,EAAa,iBACb,UAAAC,CACF,EAA6B,CAC3B,MAAMC,EAAmBC,EAAM,OAAyB,IAAI,EACtDC,EAAcD,EAAM,OAAyB,IAAI,EACjDE,EAAiBF,EAAM,QAAQ,IAAMG,EAAAA,qBAAqBxB,CAAS,EAAG,CAACA,CAAS,CAAC,EACjFyB,EAAqBrB,IAAe,GAU1C,OAPEmB,EAAe,OAAS,GACxB,CAAC,CAAChB,GACF,CAAC,CAACC,GACF,CAAC,CAACH,GACF,CAAC,CAACC,GACF,CAAC,CAACH,EAKFuB,EAAAA,KAAAC,WAAA,CACE,SAAA,CAAAD,OAACE,EAAAA,aAAA,CACC,SAAA,CAAAnC,MAACoC,EAAAA,oBAAA,CAAoB,QAAO,GAC1B,SAAAH,EAAAA,KAACI,UAAO,KAAK,SAAS,QAAQ,UAAU,KAAK,KAAK,UAAWC,EAAAA,GAAG,2BAA4BZ,CAAS,EACnG,SAAA,CAAA1B,EAAAA,IAAC,QAAM,SAAAgB,CAAA,CAAM,EACbhB,EAAAA,IAACuC,EAAAA,YAAA,CAAY,cAAW,EAAA,CAAC,CAAA,CAAA,CAC3B,CAAA,CACF,EACAN,EAAAA,KAACO,EAAAA,oBAAA,CAAoB,MAAAvB,EAAc,KAAAC,EAAY,UAAU,OACtD,SAAA,CAAAY,EAAe,IAAI,CAAC,CAACW,EAAOC,CAAO,WACjCC,oBAAA,CACE,SAAA,CAAAF,EAAQzC,EAAAA,IAAC4C,EAAAA,kBAAA,CAAmB,SAAAH,CAAA,CAAM,EAAuB,KACzDC,EAAQ,IAAKG,GACZZ,EAAAA,KAACa,EAAAA,iBAAA,CAEC,SAAUD,EAAS,SACnB,QAAS,IAAMpC,IAAmBoC,EAAS,EAAE,EAE5C,SAAA,CAAAA,EAAS,KAAOrC,EAAaR,EAAAA,IAAC+C,EAAAA,MAAA,CAAM,cAAW,EAAA,CAAC,EAAK/C,EAAAA,IAAC,OAAA,CAAK,UAAU,SAAS,cAAW,GAAC,EAC3FiC,EAAAA,KAAC,OAAA,CAAK,UAAU,wBACd,SAAA,CAAAjC,EAAAA,IAAC,OAAA,CAAK,UAAU,WAAY,SAAA6C,EAAS,MAAM,EAC1CA,EAAS,YACR7C,MAAC,OAAA,CAAK,UAAU,yCAA0C,SAAA6C,EAAS,YAAY,EAC7E,IAAA,CAAA,CACN,CAAA,CAAA,EAVKA,EAAS,EAAA,CAYjB,CAAA,GAhBqBJ,GAAS,WAiBjC,CACD,EAEAX,EAAe,OAAS,IACxBhB,GAAoBC,GAAoBH,GAAeC,GAAeH,GACrEV,EAAAA,IAACgD,wBAAA,CAAA,CAAsB,EACrB,KAEHlC,SACEgC,EAAAA,iBAAA,CAAiB,QAAS,IAAMnB,EAAiB,SAAS,QACzD,SAAA,CAAA3B,EAAAA,IAACiD,EAAAA,eAAA,CAAe,cAAW,EAAA,CAAC,EAC3B5B,CAAA,CAAA,CACH,EACE,KACHN,SACE+B,EAAAA,iBAAA,CAAiB,SAAUd,EAAoB,QAAS,IAAMjB,IAC7D,SAAA,CAAAf,EAAAA,IAACkD,EAAAA,QAAA,CAAQ,cAAW,EAAA,CAAC,EACpB5B,CAAA,CAAA,CACH,EACE,MAEFR,GAAoBC,KAAsBH,GAAeC,GAAeb,MAACgD,EAAAA,wBAAsB,EAAK,KAErGpC,SACEkC,EAAAA,iBAAA,CAAiB,QAAS,IAAMjB,EAAY,SAAS,QACpD,SAAA,CAAA7B,EAAAA,IAACmD,EAAAA,OAAA,CAAO,cAAW,EAAA,CAAC,EACnB5B,CAAA,CAAA,CACH,EACE,KACHV,SACEiC,EAAAA,iBAAA,CAAiB,SAAUd,EAAoB,QAAS,IAAMnB,IAC7D,SAAA,CAAAb,EAAAA,IAACoD,EAAAA,SAAA,CAAS,cAAW,EAAA,CAAC,EACrB5B,CAAA,CAAA,CACH,EACE,KAEHd,EACCuB,EAAAA,KAAAC,WAAA,CACE,SAAA,CAAAlC,EAAAA,IAACgD,EAAAA,sBAAA,EAAsB,EACvBf,EAAAA,KAACa,EAAAA,iBAAA,CACC,QAAQ,cACR,SAAU,CAACtC,GAAc,CAACG,EAC1B,QAAS,IAAMD,EAAA,EAEf,SAAA,CAAAV,EAAAA,IAACqD,EAAAA,OAAA,CAAO,cAAW,EAAA,CAAC,EACnB5B,CAAA,CAAA,CAAA,CACH,CAAA,CACF,EACE,IAAA,CAAA,CACN,CAAA,EACF,QACC9B,EAAA,CAAgB,SAAUgC,EAAkB,OAAQP,EAAgB,OAAQN,EAAkB,EAC/Fd,EAAAA,IAACL,GAAgB,SAAUkC,EAAa,OAAQV,EAAW,OAAQP,EAAa,UAAS,EAAA,CAAC,CAAA,EAC5F,EAnFwB,IAqF5B"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { jsxs as n, Fragment as k, jsx as e } from "react/jsx-runtime";
|
|
2
|
+
import { ChevronDown as G, Check as L, LayoutTemplate as O, SaveAll as V, Upload as B, Download as H, Trash2 as J } from "lucide-react";
|
|
3
|
+
import * as m from "react";
|
|
4
|
+
import { triagePlateMapCsvFile as U } from "./csvPlateTriage.js";
|
|
5
|
+
import { groupTemplateOptions as q } from "./helpers.js";
|
|
6
|
+
import { Button as K } from "../../ui/button.js";
|
|
7
|
+
import { DropdownMenu as Q, DropdownMenuTrigger as W, DropdownMenuContent as X, DropdownMenuGroup as Y, DropdownMenuLabel as Z, DropdownMenuItem as c, DropdownMenuSeparator as w } from "../../ui/dropdown-menu.js";
|
|
8
|
+
import { cn as _ } from "../../../lib/utils.js";
|
|
9
|
+
function D({
|
|
10
|
+
inputRef: s,
|
|
11
|
+
accept: o,
|
|
12
|
+
onPick: h,
|
|
13
|
+
triageCsv: l
|
|
14
|
+
}) {
|
|
15
|
+
return /* @__PURE__ */ e(
|
|
16
|
+
"input",
|
|
17
|
+
{
|
|
18
|
+
ref: s,
|
|
19
|
+
type: "file",
|
|
20
|
+
accept: o,
|
|
21
|
+
hidden: !0,
|
|
22
|
+
onChange: (d) => {
|
|
23
|
+
const r = d.currentTarget, i = d.target.files?.[0];
|
|
24
|
+
if (!i) {
|
|
25
|
+
r.value = "";
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
(async () => {
|
|
29
|
+
const t = l ? await U(i) : void 0;
|
|
30
|
+
await (t ? h?.(i, t) : h?.(i)), r.value = "";
|
|
31
|
+
})();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
function te({
|
|
37
|
+
templates: s,
|
|
38
|
+
templateId: o,
|
|
39
|
+
onTemplateChange: h,
|
|
40
|
+
onClearTemplate: l,
|
|
41
|
+
hasEntries: d,
|
|
42
|
+
onImportCsv: r,
|
|
43
|
+
onExportCsv: i,
|
|
44
|
+
onImportTemplate: t,
|
|
45
|
+
onExportTemplate: u,
|
|
46
|
+
label: N = "Actions",
|
|
47
|
+
align: R = "start",
|
|
48
|
+
side: v = "bottom",
|
|
49
|
+
csvAccept: y = ".csv,text/csv",
|
|
50
|
+
templateAccept: S = "application/json",
|
|
51
|
+
importTemplateLabel: C = "Import template (JSON)",
|
|
52
|
+
exportTemplateLabel: j = "Save template",
|
|
53
|
+
importCsvLabel: x = "Import plate map (CSV)",
|
|
54
|
+
exportCsvLabel: A = "Export plate map (CSV)",
|
|
55
|
+
clearLabel: F = "Clear template",
|
|
56
|
+
className: P
|
|
57
|
+
}) {
|
|
58
|
+
const M = m.useRef(null), b = m.useRef(null), f = m.useMemo(() => q(s), [s]), g = d === !1;
|
|
59
|
+
return f.length > 0 || !!t || !!u || !!r || !!i || !!l ? /* @__PURE__ */ n(k, { children: [
|
|
60
|
+
/* @__PURE__ */ n(Q, { children: [
|
|
61
|
+
/* @__PURE__ */ e(W, { asChild: !0, children: /* @__PURE__ */ n(K, { type: "button", variant: "outline", size: "sm", className: _("min-w-24 justify-between", P), children: [
|
|
62
|
+
/* @__PURE__ */ e("span", { children: N }),
|
|
63
|
+
/* @__PURE__ */ e(G, { "aria-hidden": !0 })
|
|
64
|
+
] }) }),
|
|
65
|
+
/* @__PURE__ */ n(X, { align: R, side: v, className: "w-64", children: [
|
|
66
|
+
f.map(([p, z]) => /* @__PURE__ */ n(Y, { children: [
|
|
67
|
+
p ? /* @__PURE__ */ e(Z, { children: p }) : null,
|
|
68
|
+
z.map((a) => /* @__PURE__ */ n(
|
|
69
|
+
c,
|
|
70
|
+
{
|
|
71
|
+
disabled: a.disabled,
|
|
72
|
+
onClick: () => h?.(a.id),
|
|
73
|
+
children: [
|
|
74
|
+
a.id === o ? /* @__PURE__ */ e(L, { "aria-hidden": !0 }) : /* @__PURE__ */ e("span", { className: "size-4", "aria-hidden": !0 }),
|
|
75
|
+
/* @__PURE__ */ n("span", { className: "flex min-w-0 flex-col", children: [
|
|
76
|
+
/* @__PURE__ */ e("span", { className: "truncate", children: a.label }),
|
|
77
|
+
a.description ? /* @__PURE__ */ e("span", { className: "truncate text-xs text-muted-foreground", children: a.description }) : null
|
|
78
|
+
] })
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
a.id
|
|
82
|
+
))
|
|
83
|
+
] }, p || "templates")),
|
|
84
|
+
f.length > 0 && (t || u || r || i || l) ? /* @__PURE__ */ e(w, {}) : null,
|
|
85
|
+
t ? /* @__PURE__ */ n(c, { onClick: () => M.current?.click(), children: [
|
|
86
|
+
/* @__PURE__ */ e(O, { "aria-hidden": !0 }),
|
|
87
|
+
C
|
|
88
|
+
] }) : null,
|
|
89
|
+
u ? /* @__PURE__ */ n(c, { disabled: g, onClick: () => u(), children: [
|
|
90
|
+
/* @__PURE__ */ e(V, { "aria-hidden": !0 }),
|
|
91
|
+
j
|
|
92
|
+
] }) : null,
|
|
93
|
+
(t || u) && (r || i) ? /* @__PURE__ */ e(w, {}) : null,
|
|
94
|
+
r ? /* @__PURE__ */ n(c, { onClick: () => b.current?.click(), children: [
|
|
95
|
+
/* @__PURE__ */ e(B, { "aria-hidden": !0 }),
|
|
96
|
+
x
|
|
97
|
+
] }) : null,
|
|
98
|
+
i ? /* @__PURE__ */ n(c, { disabled: g, onClick: () => i(), children: [
|
|
99
|
+
/* @__PURE__ */ e(H, { "aria-hidden": !0 }),
|
|
100
|
+
A
|
|
101
|
+
] }) : null,
|
|
102
|
+
l ? /* @__PURE__ */ n(k, { children: [
|
|
103
|
+
/* @__PURE__ */ e(w, {}),
|
|
104
|
+
/* @__PURE__ */ n(
|
|
105
|
+
c,
|
|
106
|
+
{
|
|
107
|
+
variant: "destructive",
|
|
108
|
+
disabled: !o && !d,
|
|
109
|
+
onClick: () => l(),
|
|
110
|
+
children: [
|
|
111
|
+
/* @__PURE__ */ e(J, { "aria-hidden": !0 }),
|
|
112
|
+
F
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
] }) : null
|
|
117
|
+
] })
|
|
118
|
+
] }),
|
|
119
|
+
/* @__PURE__ */ e(D, { inputRef: M, accept: S, onPick: t }),
|
|
120
|
+
/* @__PURE__ */ e(D, { inputRef: b, accept: y, onPick: r, triageCsv: !0 })
|
|
121
|
+
] }) : null;
|
|
122
|
+
}
|
|
123
|
+
export {
|
|
124
|
+
te as PlateMapActionsMenu
|
|
125
|
+
};
|
|
126
|
+
//# sourceMappingURL=PlateMapActionsMenu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlateMapActionsMenu.js","sources":["../../../../src/components/composed/PlateMapEditor/PlateMapActionsMenu.tsx"],"sourcesContent":["import { Check, ChevronDown, Download, LayoutTemplate, SaveAll, Trash2, Upload } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { triagePlateMapCsvFile } from \"./csvPlateTriage\";\nimport { groupTemplateOptions } from \"./helpers\";\n\nimport type { ImportExportHandlers, PlateMapCsvTriage, TemplateOption } from \"./types\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface PlateMapActionsMenuProps extends ImportExportHandlers {\n templates?: TemplateOption[];\n templateId?: string;\n onTemplateChange?: (id: string) => void;\n onClearTemplate?: () => void;\n /** Disable export/save actions when there is nothing to export. */\n hasEntries?: boolean;\n label?: React.ReactNode;\n align?: \"start\" | \"center\" | \"end\";\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n csvAccept?: string;\n templateAccept?: string;\n importTemplateLabel?: string;\n exportTemplateLabel?: string;\n importCsvLabel?: string;\n exportCsvLabel?: string;\n clearLabel?: string;\n className?: string;\n}\n\nfunction HiddenFileInput({\n inputRef,\n accept,\n onPick,\n triageCsv,\n}: {\n inputRef: React.RefObject<HTMLInputElement | null>;\n accept: string;\n onPick?: (file: File, triage?: PlateMapCsvTriage) => void | Promise<void>;\n triageCsv?: boolean;\n}) {\n return (\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n hidden\n onChange={(event) => {\n const input = event.currentTarget;\n const file = event.target.files?.[0];\n if (!file) {\n input.value = \"\";\n return;\n }\n\n void (async () => {\n const triage = triageCsv ? await triagePlateMapCsvFile(file) : undefined;\n await (triage ? onPick?.(file, triage) : onPick?.(file));\n input.value = \"\";\n })();\n }}\n />\n );\n}\n\nexport function PlateMapActionsMenu({\n templates,\n templateId,\n onTemplateChange,\n onClearTemplate,\n hasEntries,\n onImportCsv,\n onExportCsv,\n onImportTemplate,\n onExportTemplate,\n label = \"Actions\",\n align = \"start\",\n side = \"bottom\",\n csvAccept = \".csv,text/csv\",\n templateAccept = \"application/json\",\n importTemplateLabel = \"Import template (JSON)\",\n exportTemplateLabel = \"Save template\",\n importCsvLabel = \"Import plate map (CSV)\",\n exportCsvLabel = \"Export plate map (CSV)\",\n clearLabel = \"Clear template\",\n className,\n}: PlateMapActionsMenuProps) {\n const templateInputRef = React.useRef<HTMLInputElement>(null);\n const csvInputRef = React.useRef<HTMLInputElement>(null);\n const templateGroups = React.useMemo(() => groupTemplateOptions(templates), [templates]);\n const disableEntryExport = hasEntries === false;\n\n const hasMenuItems =\n templateGroups.length > 0 ||\n !!onImportTemplate ||\n !!onExportTemplate ||\n !!onImportCsv ||\n !!onExportCsv ||\n !!onClearTemplate;\n\n if (!hasMenuItems) return null;\n\n return (\n <>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" className={cn(\"min-w-24 justify-between\", className)}>\n <span>{label}</span>\n <ChevronDown aria-hidden />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align={align} side={side} className=\"w-64\">\n {templateGroups.map(([group, options]) => (\n <DropdownMenuGroup key={group || \"templates\"}>\n {group ? <DropdownMenuLabel>{group}</DropdownMenuLabel> : null}\n {options.map((template) => (\n <DropdownMenuItem\n key={template.id}\n disabled={template.disabled}\n onClick={() => onTemplateChange?.(template.id)}\n >\n {template.id === templateId ? <Check aria-hidden /> : <span className=\"size-4\" aria-hidden />}\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate\">{template.label}</span>\n {template.description ? (\n <span className=\"truncate text-xs text-muted-foreground\">{template.description}</span>\n ) : null}\n </span>\n </DropdownMenuItem>\n ))}\n </DropdownMenuGroup>\n ))}\n\n {templateGroups.length > 0 &&\n (onImportTemplate || onExportTemplate || onImportCsv || onExportCsv || onClearTemplate) ? (\n <DropdownMenuSeparator />\n ) : null}\n\n {onImportTemplate ? (\n <DropdownMenuItem onClick={() => templateInputRef.current?.click()}>\n <LayoutTemplate aria-hidden />\n {importTemplateLabel}\n </DropdownMenuItem>\n ) : null}\n {onExportTemplate ? (\n <DropdownMenuItem disabled={disableEntryExport} onClick={() => onExportTemplate()}>\n <SaveAll aria-hidden />\n {exportTemplateLabel}\n </DropdownMenuItem>\n ) : null}\n\n {(onImportTemplate || onExportTemplate) && (onImportCsv || onExportCsv) ? <DropdownMenuSeparator /> : null}\n\n {onImportCsv ? (\n <DropdownMenuItem onClick={() => csvInputRef.current?.click()}>\n <Upload aria-hidden />\n {importCsvLabel}\n </DropdownMenuItem>\n ) : null}\n {onExportCsv ? (\n <DropdownMenuItem disabled={disableEntryExport} onClick={() => onExportCsv()}>\n <Download aria-hidden />\n {exportCsvLabel}\n </DropdownMenuItem>\n ) : null}\n\n {onClearTemplate ? (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem\n variant=\"destructive\"\n disabled={!templateId && !hasEntries}\n onClick={() => onClearTemplate()}\n >\n <Trash2 aria-hidden />\n {clearLabel}\n </DropdownMenuItem>\n </>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n <HiddenFileInput inputRef={templateInputRef} accept={templateAccept} onPick={onImportTemplate} />\n <HiddenFileInput inputRef={csvInputRef} accept={csvAccept} onPick={onImportCsv} triageCsv />\n </>\n );\n}\n"],"names":["HiddenFileInput","inputRef","accept","onPick","triageCsv","jsx","event","input","file","triage","triagePlateMapCsvFile","PlateMapActionsMenu","templates","templateId","onTemplateChange","onClearTemplate","hasEntries","onImportCsv","onExportCsv","onImportTemplate","onExportTemplate","label","align","side","csvAccept","templateAccept","importTemplateLabel","exportTemplateLabel","importCsvLabel","exportCsvLabel","clearLabel","className","templateInputRef","React","csvInputRef","templateGroups","groupTemplateOptions","disableEntryExport","jsxs","Fragment","DropdownMenu","DropdownMenuTrigger","Button","cn","ChevronDown","DropdownMenuContent","group","options","DropdownMenuGroup","DropdownMenuLabel","template","DropdownMenuItem","Check","DropdownMenuSeparator","LayoutTemplate","SaveAll","Upload","Download","Trash2"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime"),bt=require("react"),Pt=require("./csvPlateTriage.cjs"),gt=require("./PlateMapActionsMenu.cjs"),St=require("./PlateMapPlateSelector.cjs"),yt=require("./PlatePaintGrid.cjs"),Z=require("./wellGrid.cjs"),Mt=require("./WellManifestTable.cjs"),Ct=require("./WellMetadataForm.cjs"),te=require("../../ui/badge.cjs"),f=require("../../ui/card.cjs"),ne=require("../../ui/separator.cjs"),ee=require("../../../lib/utils.cjs");function wt(n){const r=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const a in n)if(a!=="default"){const o=Object.getOwnPropertyDescriptor(n,a);Object.defineProperty(r,a,o.get?o:{enumerable:!0,get:()=>n[a]})}}return r.default=n,Object.freeze(r)}const i=wt(bt),se="::",Nt="plateBarcode",Et="Plate Barcode";function re(n,r){return`${n}${se}${r}`}function le(n,r){const a=`${n}${se}`;return r.startsWith(a)?r.slice(a.length):void 0}function At(n,r){return{...n??{},...r}}function kt(n,r){const a=new Map;return n.forEach(o=>a.set(o.id,o)),(r??[]).forEach(o=>a.set(o.id,o)),[...a.values()]}function Wt(n,r){const a=n.find(o=>o.id===r);return a||(r?void 0:n[0])}function Tt(n,r){if(!r)return n;const a=new Map;return n.forEach((o,m)=>{const d=le(r,m);d&&a.set(d,o)}),a}function qt({importedPlates:n,selectedPlateId:r,isControlled:a,providedPlateCount:o}){const m=n[0]?.id;return m?n.some(d=>d.id===r)?void 0:m:!a&&o===0?null:void 0}function Ft(n,r){return n>0||!!r}function zt(n,r){return!n||!!r}function _t({title:n,badges:r}){return!n&&!r?null:t.jsxs("div",{className:"flex flex-wrap items-center justify-between gap-2",children:[n?t.jsx("h2",{className:"text-lg font-semibold",children:n}):t.jsx("span",{}),r?t.jsx("div",{className:"flex flex-wrap gap-2",children:r}):null]})}function Bt({legend:n}){return n?t.jsxs(t.Fragment,{children:[t.jsx(ne.Separator,{}),n]}):null}function Ot({format:n,rows:r,columns:a,values:o,onChange:m,selection:d,onSelectionChange:b,fields:g,tableColumns:S,colorForWell:ae,emptyEntry:E,mergeOnApply:oe,isPopulated:ie,cycleFieldOnWellDoubleClick:T,title:ce,badges:de,banner:ue,legend:fe,formExtras:xe,formSlot:me,footer:B,plateTitle:pe="Plate",plateToolbar:he,plates:A,activePlateId:O,onPlateChange:y,onAddPlate:D,onRemovePlate:ve,addPlateLabel:je,removePlateLabel:be,plateSelectorLabel:Pe,plateSelectorVariant:ge,plateBarcodeField:Se,plateBarcodeColumnHeader:L=Et,hidePlateBarcodeColumn:R=!1,groups:q,activeGroupId:ye,onGroupClick:Me,renderHoverSummary:Ce,cellSize:we,emptyWellFillColor:Ne,wellShape:Ee,framedPlate:Ae,wrapWell:ke,highlightedWellIds:We,onHoveredWellChange:Te,manifestFilterable:qe,manifestGroupable:Fe,autoScaleGrid:ze,minCellSize:_e,maxCellSize:Be,className:Oe,templates:De,templateId:Le,onTemplateChange:Re,onClearTemplate:Ie,onImportCsv:F,onExportCsv:He,onImportTemplate:Ke,onExportTemplate:$e,csvAccept:Ge,templateAccept:Ue,label:Ve,align:Ye,side:Je,importTemplateLabel:Qe,exportTemplateLabel:Xe,importCsvLabel:Ze,exportCsvLabel:et,clearLabel:tt}){const nt=Z.resolveDimensions(n,r,a),[I,H]=i.useState({}),[M,K]=i.useState(null),[$,G]=i.useState(),[U,st]=i.useState([]),[rt,V]=i.useState(),lt=oe??At,k=i.useMemo(()=>kt(U,A),[U,A]),P=O!==void 0,W=O??rt,Y=i.useMemo(()=>Wt(k,W),[k,W]),h=Y?.barcode,u=h,p=!!u,v=Se??Nt,J=i.useRef(u),j=i.useMemo(()=>Tt(o,u),[u,o]),Q=i.useCallback(e=>!p||!u?e:re(u,e),[u,p]),at=i.useCallback(e=>{P||V(e),y?.(e)},[P,y]),ot=i.useCallback(async(e,s)=>{if(s){const l=Pt.plateOptionsFromCsvTriage(s);st(l);const c=qt({importedPlates:l,selectedPlateId:W,isControlled:P,providedPlateCount:A?.length??0});c!==void 0&&(P||V(c??void 0),c&&y?.(c))}await F?.(e,s)},[P,F,y,A,W]),C=i.useCallback(e=>!p||!h?e:{...e,[v]:h},[h,v,p]),w=i.useCallback(e=>{if(!p||!u){m(e);return}const s=new Map(o);[...s.keys()].forEach(l=>{le(u,l)&&s.delete(l)}),e.forEach((l,c)=>{s.set(Q(c),C(l))}),m(s)},[u,p,m,C,Q,o]);i.useEffect(()=>{J.current!==u&&(J.current=u,H({}),K(null),G(void 0),d.size>0&&b(new Set))},[u,b,d.size]);const x=i.useMemo(()=>{if(!T)return;const e=g.find(s=>s.key===T);if(!(e?.kind!=="select"||!e.options?.length))return e},[T,g]),it=()=>{if(d.size===0)return;const e=new Map(j);d.forEach(s=>{const c=e.get(s)??E(s),N=C(lt(c,I,s));e.set(s,N)}),w(e)},ct=()=>{if(d.size===0)return;const e=new Map(j);d.forEach(s=>e.delete(s)),w(e)},dt=i.useCallback(e=>{if(!x?.options?.length)return;const s=new Map(j),l=s.get(e),c=l?.[x.key],N=x.options.findIndex(_=>_.value===c),X=x.options[(N+1)%x.options.length];if(!X)return;const jt=l??E(e);s.set(e,C({...jt,[x.key]:X.value})),w(s),G(_=>({wellId:e,key:(_?.key??0)+1}))},[w,x,E,j,C]),ut=()=>{b(new Set(Z.allPositions(nt)))},ft=()=>b(new Set),z=M?j.get(M):void 0,xt=z?g.map(e=>{const s=z[e.key];if(s==null||s==="")return null;if(e.kind==="select")return(e.options??[]).find(c=>c.value===s)?.label??String(s);if(e.kind==="multiselect"){const l=Array.isArray(s)?s:[];return l.length===0?null:l.map(c=>(e.options??[]).find(N=>N.value===c)?.label??c).join(", ")}return e.kind==="boolean"?s?e.label:null:String(s)}).filter(Boolean):[],mt=M?Ce?.(z,M)??[M,...xt].join(" • "):" ",pt=i.useMemo(()=>!p||!h||R||S.some(l=>l.field===v||l.id===v)?S:[{id:v,header:L,minWidth:150,render:({row:l})=>{const c=l[v]??h;return t.jsx(te.Badge,{variant:"outline",children:String(c)})}},...S],[h,v,R,p,L,S]),ht=Ft(k.length,D),vt=zt(P,y);return t.jsxs("div",{"data-slot":"plate-map-editor",className:ee.cn("flex flex-col gap-4",Oe),children:[t.jsx(_t,{title:ce,badges:de}),ue,t.jsxs("div",{className:"flex flex-wrap gap-3 md:flex-nowrap",children:[t.jsx(f.Card,{className:"flex w-full max-w-[360px] min-w-[300px] basis-[360px] flex-col",size:"sm",children:t.jsxs(f.CardContent,{className:"flex h-full flex-1 flex-col gap-3",children:[me??t.jsx(Ct.WellMetadataForm,{fields:g,value:I,onChange:H,selectionSize:d.size,onApply:it,onClear:ct,extras:xe}),t.jsx(Bt,{legend:fe})]})}),t.jsxs(f.Card,{className:"flex min-w-[360px] flex-1 flex-col",size:"sm",children:[t.jsxs(f.CardHeader,{className:"border-b",children:[t.jsxs("div",{className:"flex min-w-0 flex-wrap items-center gap-2",children:[t.jsx(f.CardTitle,{className:"min-w-0",children:pe}),ht?t.jsx(St.PlateMapPlateSelector,{plates:k,activePlateId:Y?.id,onPlateChange:vt?at:void 0,onAddPlate:D,onRemovePlate:ve,addPlateLabel:je,removePlateLabel:be,label:Pe,variant:ge}):null]}),t.jsx(f.CardAction,{className:"flex flex-wrap items-center gap-2",children:t.jsx(gt.PlateMapActionsMenu,{templates:De,templateId:Le,onTemplateChange:Re,onClearTemplate:Ie,hasEntries:o.size>0,onImportCsv:F?ot:void 0,onExportCsv:He,onImportTemplate:Ke,onExportTemplate:$e,csvAccept:Ge,templateAccept:Ue,label:Ve,align:Ye,side:Je,importTemplateLabel:Qe,exportTemplateLabel:Xe,importCsvLabel:Ze,exportCsvLabel:et,clearLabel:tt})})]}),t.jsxs(f.CardContent,{className:"flex flex-col gap-1.5",children:[t.jsxs("div",{className:"flex flex-wrap items-center justify-start gap-3",children:[he,t.jsxs("div",{className:"flex items-center gap-2 text-xs",children:[t.jsx("button",{type:"button",className:"font-medium text-primary underline-offset-2 hover:text-primary/80 hover:underline",onClick:ut,children:"Select all"}),t.jsx("span",{className:"text-muted-foreground/60",children:"·"}),t.jsx("button",{type:"button",className:"font-medium text-primary underline-offset-2 hover:text-primary/80 hover:underline",onClick:ft,children:"Deselect all"})]})]}),t.jsx("div",{className:"h-5 text-xs text-muted-foreground",children:mt}),t.jsx(yt.PlatePaintGrid,{format:n,rows:r,columns:a,values:j,selection:d,onSelectionChange:b,colorForWell:ae,emptyWellFillColor:Ne,wellShape:Ee,framed:Ae,wrapWell:ke,highlightedWellIds:We,onWellHover:e=>{K(e),Te?.(e)},onWellDoubleClick:x?dt:void 0,selectionFillMode:x?"well":"selection",flashWellId:$?.wellId,flashWellKey:$?.key,cellSize:we,autoScale:ze,minCellSize:_e,maxCellSize:Be}),q&&q.length>0?t.jsxs(t.Fragment,{children:[t.jsx(ne.Separator,{className:"mt-2"}),t.jsx("div",{className:"flex max-h-28 flex-wrap gap-3 overflow-y-auto pt-1",children:q.map(e=>{const s=e.id===ye,l=e.count??e.wellIds?.length;return t.jsxs("button",{type:"button",disabled:e.disabled,onClick:()=>Me?.(e),className:ee.cn("flex w-16 flex-col items-center gap-1 rounded-md px-1 py-1 text-center text-xs transition-colors","text-muted-foreground hover:bg-muted/60",s&&"bg-muted text-foreground ring-1 ring-primary/40",e.disabled&&"pointer-events-none opacity-50"),title:e.label,children:[t.jsx("span",{className:"size-7 rounded-full border",style:{backgroundColor:e.color,borderColor:e.borderColor},"aria-hidden":!0}),t.jsx("span",{className:"w-full truncate text-foreground",children:e.label}),l===void 0?null:t.jsxs("span",{className:"text-[0.7rem] leading-none text-muted-foreground",children:[l," wells"]})]},e.id)})})]}):null]})]})]}),t.jsxs(f.Card,{size:"sm",children:[t.jsx(f.CardHeader,{className:"border-b",children:t.jsx(f.CardTitle,{children:"Sample manifest"})}),t.jsx(f.CardContent,{children:t.jsx(Mt.WellManifestTable,{values:j,onChange:w,columns:pt,fields:g,selection:d,onSelectionChange:b,emptyEntry:E,isPopulated:ie,filterable:qe,groupable:Fe})})]}),B?t.jsx("div",{className:"flex justify-end gap-2 pt-2",children:B}):null]})}exports.PlateBadge=te.Badge;exports.PlateMapEditor=Ot;exports.getPlateMapScopedWellId=re;
|
|
2
|
+
//# sourceMappingURL=PlateMapEditor.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlateMapEditor.cjs","sources":["../../../../src/components/composed/PlateMapEditor/PlateMapEditor.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport { plateOptionsFromCsvTriage } from \"./csvPlateTriage\";\nimport { PlateMapActionsMenu } from \"./PlateMapActionsMenu\";\nimport { PlateMapPlateSelector } from \"./PlateMapPlateSelector\";\nimport { PlatePaintGrid } from \"./PlatePaintGrid\";\nimport { resolveDimensions, allPositions } from \"./wellGrid\";\nimport { WellManifestTable } from \"./WellManifestTable\";\nimport { WellMetadataForm } from \"./WellMetadataForm\";\n\nimport type { PlateMapActionsMenuProps } from \"./PlateMapActionsMenu\";\nimport type { PlateMapPlateSelectorVariant } from \"./PlateMapPlateSelector\";\nimport type { WellShape } from \"./PlatePaintGrid\";\nimport type {\n PlateFormat,\n PlateMapCsvTriage,\n PlateMapGroupOption,\n PlateMapPlateOption,\n WellColumn,\n WellField,\n WellId,\n WellRecord,\n} from \"./types\";\n\nimport { Badge } from \"@/components/ui/badge\";\nimport { Card, CardAction, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { cn } from \"@/lib/utils\";\n\nconst PLATE_WELL_KEY_SEPARATOR = \"::\";\nconst DEFAULT_PLATE_BARCODE_FIELD = \"plateBarcode\";\nconst DEFAULT_PLATE_BARCODE_HEADER = \"Plate Barcode\";\n\nexport function getPlateMapScopedWellId(plateBarcode: string, wellId: WellId): WellId {\n return `${plateBarcode}${PLATE_WELL_KEY_SEPARATOR}${wellId}`;\n}\n\nfunction wellIdFromPlateWellKey(plateId: string, key: WellId): WellId | undefined {\n const prefix = `${plateId}${PLATE_WELL_KEY_SEPARATOR}`;\n return key.startsWith(prefix) ? key.slice(prefix.length) : undefined;\n}\n\nexport interface PlateMapEditorProps<T extends WellRecord = WellRecord> extends Omit<\n PlateMapActionsMenuProps,\n \"hasEntries\" | \"className\"\n> {\n format: PlateFormat;\n rows?: number;\n columns?: number;\n values: Map<WellId, T>;\n onChange: (next: Map<WellId, T>) => void;\n selection: Set<WellId>;\n onSelectionChange: (next: Set<WellId>) => void;\n\n fields: WellField<T>[];\n tableColumns: WellColumn<T>[];\n\n /** Resolves the SVG fill color for a well. */\n colorForWell: (well: T | undefined, wellId: WellId) => string;\n /** Builds an empty record when a well is freshly created. */\n emptyEntry: (wellId: WellId) => T;\n /**\n * Merges the staged form record onto an existing well record on Apply.\n * Defaults to a shallow merge (form keys overwrite existing keys when set).\n */\n mergeOnApply?: (existing: T | undefined, staged: Partial<T>, wellId: WellId) => T;\n /** Filter for the manifest's \"hide empty\" mode. */\n isPopulated?: (row: T) => boolean;\n /** Select field cycled when double-clicking a single well, e.g. role painting. */\n cycleFieldOnWellDoubleClick?: keyof T & string;\n\n /** Optional header title (e.g. plate set name). */\n title?: string;\n /** Optional badges shown in the header next to the title. */\n badges?: React.ReactNode;\n /** Optional banner (e.g. import/error alert) shown above the layout. */\n banner?: React.ReactNode;\n /** Legend block rendered under the form column. */\n legend?: React.ReactNode;\n /** Form helper slot rendered between fields and the action row. */\n formExtras?: React.ReactNode;\n /**\n * Fully replaces the left column. When set, the built-in `WellMetadataForm`\n * is omitted — use this for alternative workflows like a drag-and-drop\n * source palette. The legend slot still renders beneath the replacement.\n */\n formSlot?: React.ReactNode;\n /** Footer actions (e.g. Save, Back). */\n footer?: React.ReactNode;\n /** Title for the plate grid panel. */\n plateTitle?: React.ReactNode;\n /** Optional controls shown next to the built-in actions menu. */\n plateToolbar?: React.ReactNode;\n /** User-provided plates available for this editor. Barcodes are never generated by the editor. */\n plates?: PlateMapPlateOption[];\n activePlateId?: string;\n onPlateChange?: (plateId: string) => void;\n /** Opens the host app's manual barcode entry flow for creating a plate. */\n onAddPlate?: () => void;\n /** Removes a plate (typically when using `plateSelectorVariant=\"tabs\"`). */\n onRemovePlate?: (plateId: string) => void;\n addPlateLabel?: string;\n removePlateLabel?: string;\n plateSelectorLabel?: string;\n /** Layout of the plate selector. Defaults to `\"dropdown\"`. */\n plateSelectorVariant?: PlateMapPlateSelectorVariant;\n /** Row field used to stamp the active user-provided barcode onto edited wells. Defaults to `plateBarcode`. */\n plateBarcodeField?: keyof T & string;\n /** Header for the automatic manifest barcode column. */\n plateBarcodeColumnHeader?: string;\n /** Hide the automatic manifest barcode column when plate-scoped editing is active. */\n hidePlateBarcodeColumn?: boolean;\n /** Optional grouped well shortcuts rendered under the grid. */\n groups?: PlateMapGroupOption[];\n activeGroupId?: string;\n onGroupClick?: (group: PlateMapGroupOption) => void;\n /** Custom hover summary for the strip above the grid. */\n renderHoverSummary?: (well: T | undefined, wellId: WellId) => React.ReactNode;\n /** Fixed well size. When unset, the grid grows with the available width. */\n cellSize?: number;\n /** Fill color for empty wells. Pass `null` to delegate empty wells to `colorForWell`. */\n emptyWellFillColor?: string | null;\n /** Well shape forwarded to `PlatePaintGrid`. Defaults to `\"rect\"`. */\n wellShape?: WellShape;\n /** When true, wraps the grid in a card-like plate frame (rounded + border + soft shadow). */\n framedPlate?: boolean;\n /**\n * Forwarded to `PlatePaintGrid`. Render-prop that places a node inside each\n * absolute-positioned well cell — used to wire drop targets without binding\n * the kit to a specific DnD library.\n */\n wrapWell?: (wellId: WellId, cellSize: number) => React.ReactNode;\n /** Wells to highlight (e.g. when hovering a legend item externally). */\n highlightedWellIds?: ReadonlySet<WellId>;\n /** Fires whenever the currently hovered well changes (null on leave). */\n onHoveredWellChange?: (wellId: WellId | null) => void;\n /** Enables the filter popover on the manifest table. */\n manifestFilterable?: boolean;\n /** Enables the group-by selector on the manifest table. */\n manifestGroupable?: boolean;\n autoScaleGrid?: boolean;\n minCellSize?: number;\n maxCellSize?: number;\n\n className?: string;\n}\n\nfunction defaultMerge<T extends WellRecord>(existing: T | undefined, staged: Partial<T>): T {\n return { ...(existing ?? ({} as T)), ...staged };\n}\n\nfunction mergePlateOptions(\n importedPlates: PlateMapPlateOption[],\n providedPlates: PlateMapPlateOption[] | undefined,\n): PlateMapPlateOption[] {\n const merged = new Map<string, PlateMapPlateOption>();\n importedPlates.forEach((plate) => merged.set(plate.id, plate));\n (providedPlates ?? []).forEach((plate) => merged.set(plate.id, plate));\n return [...merged.values()];\n}\n\nfunction resolveActivePlate(\n plates: PlateMapPlateOption[],\n selectedPlateId: string | undefined,\n): PlateMapPlateOption | undefined {\n const matchingPlate = plates.find((plate) => plate.id === selectedPlateId);\n if (matchingPlate) return matchingPlate;\n return selectedPlateId ? undefined : plates[0];\n}\n\nfunction buildScopedValues<T extends WellRecord>(values: Map<WellId, T>, plateKey: string | undefined) {\n if (!plateKey) return values;\n\n const next = new Map<WellId, T>();\n values.forEach((row, key) => {\n const wellId = wellIdFromPlateWellKey(plateKey, key);\n if (wellId) next.set(wellId, row);\n });\n return next;\n}\n\nfunction resolveImportedPlateSelection({\n importedPlates,\n selectedPlateId,\n isControlled,\n providedPlateCount,\n}: {\n importedPlates: PlateMapPlateOption[];\n selectedPlateId?: string;\n isControlled: boolean;\n providedPlateCount: number;\n}): string | null | undefined {\n const firstImportedPlateId = importedPlates[0]?.id;\n if (!firstImportedPlateId) return !isControlled && providedPlateCount === 0 ? null : undefined;\n return importedPlates.some((plate) => plate.id === selectedPlateId) ? undefined : firstImportedPlateId;\n}\n\nfunction shouldShowPlateSelector(plateCount: number, onAddPlate: PlateMapEditorProps[\"onAddPlate\"]): boolean {\n return plateCount > 0 || !!onAddPlate;\n}\n\nfunction canUpdatePlateSelection(isControlled: boolean, onPlateChange: PlateMapEditorProps[\"onPlateChange\"]): boolean {\n return !isControlled || !!onPlateChange;\n}\n\nfunction PlateMapEditorTitleBar({ title, badges }: { title?: string; badges?: React.ReactNode }) {\n if (!title && !badges) return null;\n\n return (\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n {title ? <h2 className=\"text-lg font-semibold\">{title}</h2> : <span />}\n {badges ? <div className=\"flex flex-wrap gap-2\">{badges}</div> : null}\n </div>\n );\n}\n\nfunction PlateMapEditorLegend({ legend }: { legend?: React.ReactNode }) {\n if (!legend) return null;\n\n return (\n <>\n <Separator />\n {legend}\n </>\n );\n}\n\nexport function PlateMapEditor<T extends WellRecord = WellRecord>({\n format,\n rows,\n columns,\n values,\n onChange,\n selection,\n onSelectionChange,\n fields,\n tableColumns,\n colorForWell,\n emptyEntry,\n mergeOnApply,\n isPopulated,\n cycleFieldOnWellDoubleClick,\n title,\n badges,\n banner,\n legend,\n formExtras,\n formSlot,\n footer,\n plateTitle = \"Plate\",\n plateToolbar,\n plates,\n activePlateId,\n onPlateChange,\n onAddPlate,\n onRemovePlate,\n addPlateLabel,\n removePlateLabel,\n plateSelectorLabel,\n plateSelectorVariant,\n plateBarcodeField,\n plateBarcodeColumnHeader = DEFAULT_PLATE_BARCODE_HEADER,\n hidePlateBarcodeColumn = false,\n groups,\n activeGroupId,\n onGroupClick,\n renderHoverSummary,\n cellSize,\n emptyWellFillColor,\n wellShape,\n framedPlate,\n wrapWell,\n highlightedWellIds,\n onHoveredWellChange,\n manifestFilterable,\n manifestGroupable,\n autoScaleGrid,\n minCellSize,\n maxCellSize,\n className,\n templates,\n templateId,\n onTemplateChange,\n onClearTemplate,\n onImportCsv,\n onExportCsv,\n onImportTemplate,\n onExportTemplate,\n csvAccept,\n templateAccept,\n label,\n align,\n side,\n importTemplateLabel,\n exportTemplateLabel,\n importCsvLabel,\n exportCsvLabel,\n clearLabel,\n}: PlateMapEditorProps<T>) {\n const dims = resolveDimensions(format, rows, columns);\n const [staged, setStaged] = React.useState<Partial<T>>({});\n const [hoverPos, setHoverPos] = React.useState<WellId | null>(null);\n const [flashWell, setFlashWell] = React.useState<{ wellId: WellId; key: number }>();\n const [csvPlates, setCsvPlates] = React.useState<PlateMapPlateOption[]>([]);\n const [internalActivePlateId, setInternalActivePlateId] = React.useState<string>();\n\n const merge = mergeOnApply ?? defaultMerge<T>;\n const availablePlates = React.useMemo(() => mergePlateOptions(csvPlates, plates), [csvPlates, plates]);\n const isPlateSelectionControlled = activePlateId !== undefined;\n const selectedPlateId = activePlateId ?? internalActivePlateId;\n const activePlate = React.useMemo(\n () => resolveActivePlate(availablePlates, selectedPlateId),\n [availablePlates, selectedPlateId],\n );\n const activePlateBarcode = activePlate?.barcode;\n const activePlateKey = activePlateBarcode;\n const isPlateScoped = !!activePlateKey;\n const barcodeField = (plateBarcodeField ?? DEFAULT_PLATE_BARCODE_FIELD) as keyof T & string;\n const previousActivePlateKey = React.useRef<string | undefined>(activePlateKey);\n\n const scopedValues = React.useMemo(() => buildScopedValues(values, activePlateKey), [activePlateKey, values]);\n\n const toStoredWellKey = React.useCallback(\n (wellId: WellId): WellId => {\n if (!isPlateScoped || !activePlateKey) return wellId;\n return getPlateMapScopedWellId(activePlateKey, wellId);\n },\n [activePlateKey, isPlateScoped],\n );\n\n const handlePlateChange = React.useCallback(\n (plateId: string) => {\n if (!isPlateSelectionControlled) setInternalActivePlateId(plateId);\n onPlateChange?.(plateId);\n },\n [isPlateSelectionControlled, onPlateChange],\n );\n\n const handleImportCsv = React.useCallback(\n async (file: File, triage?: PlateMapCsvTriage) => {\n if (triage) {\n const nextCsvPlates = plateOptionsFromCsvTriage(triage);\n setCsvPlates(nextCsvPlates);\n const nextSelection = resolveImportedPlateSelection({\n importedPlates: nextCsvPlates,\n selectedPlateId,\n isControlled: isPlateSelectionControlled,\n providedPlateCount: plates?.length ?? 0,\n });\n\n if (nextSelection !== undefined) {\n if (!isPlateSelectionControlled) setInternalActivePlateId(nextSelection ?? undefined);\n if (nextSelection) onPlateChange?.(nextSelection);\n }\n }\n\n await onImportCsv?.(file, triage);\n },\n [isPlateSelectionControlled, onImportCsv, onPlateChange, plates, selectedPlateId],\n );\n\n const stampActivePlateBarcode = React.useCallback(\n (row: T): T => {\n if (!isPlateScoped || !activePlateBarcode) return row;\n return { ...(row as Record<string, unknown>), [barcodeField]: activePlateBarcode } as T;\n },\n [activePlateBarcode, barcodeField, isPlateScoped],\n );\n\n const commitScopedValues = React.useCallback(\n (nextScopedValues: Map<WellId, T>) => {\n if (!isPlateScoped || !activePlateKey) {\n onChange(nextScopedValues);\n return;\n }\n\n const next = new Map(values);\n [...next.keys()].forEach((key) => {\n if (wellIdFromPlateWellKey(activePlateKey, key)) {\n next.delete(key);\n }\n });\n nextScopedValues.forEach((row, wellId) => {\n next.set(toStoredWellKey(wellId), stampActivePlateBarcode(row));\n });\n onChange(next);\n },\n [activePlateKey, isPlateScoped, onChange, stampActivePlateBarcode, toStoredWellKey, values],\n );\n\n React.useEffect(() => {\n if (previousActivePlateKey.current === activePlateKey) return;\n previousActivePlateKey.current = activePlateKey;\n setStaged({});\n setHoverPos(null);\n setFlashWell(undefined);\n if (selection.size > 0) onSelectionChange(new Set());\n }, [activePlateKey, onSelectionChange, selection.size]);\n\n const doubleClickCycleField = React.useMemo(() => {\n if (!cycleFieldOnWellDoubleClick) return;\n const field = fields.find((f) => f.key === cycleFieldOnWellDoubleClick);\n if (field?.kind !== \"select\" || !field.options?.length) return;\n return field;\n }, [cycleFieldOnWellDoubleClick, fields]);\n\n const applyStagedToSelection = () => {\n if (selection.size === 0) return;\n const next = new Map(scopedValues);\n selection.forEach((wellId) => {\n const existing = next.get(wellId);\n const base = existing ?? emptyEntry(wellId);\n const merged = stampActivePlateBarcode(merge(base, staged, wellId));\n next.set(wellId, merged);\n });\n commitScopedValues(next);\n };\n\n const clearWells = () => {\n if (selection.size === 0) return;\n const next = new Map(scopedValues);\n selection.forEach((wellId) => next.delete(wellId));\n commitScopedValues(next);\n };\n\n const cycleWellField = React.useCallback(\n (wellId: WellId) => {\n if (!doubleClickCycleField?.options?.length) return;\n\n const next = new Map(scopedValues);\n const existing = next.get(wellId);\n const currentValue = existing?.[doubleClickCycleField.key];\n const currentIndex = doubleClickCycleField.options.findIndex((opt) => opt.value === currentValue);\n const nextOption = doubleClickCycleField.options[(currentIndex + 1) % doubleClickCycleField.options.length];\n if (!nextOption) return;\n\n const base = existing ?? emptyEntry(wellId);\n next.set(wellId, stampActivePlateBarcode({ ...base, [doubleClickCycleField.key]: nextOption.value } as T));\n commitScopedValues(next);\n setFlashWell((current) => ({ wellId, key: (current?.key ?? 0) + 1 }));\n },\n [commitScopedValues, doubleClickCycleField, emptyEntry, scopedValues, stampActivePlateBarcode],\n );\n\n const selectAll = () => {\n onSelectionChange(new Set(allPositions(dims)));\n };\n const deselectAll = () => onSelectionChange(new Set());\n\n const hoverEntry = hoverPos ? scopedValues.get(hoverPos) : undefined;\n\n const hoverFields = hoverEntry\n ? (fields\n .map((f) => {\n const v = hoverEntry[f.key];\n if (v === undefined || v === null || v === \"\") return null;\n if (f.kind === \"select\") {\n const opt = (f.options ?? []).find((o) => o.value === v);\n return opt?.label ?? String(v);\n }\n if (f.kind === \"multiselect\") {\n const arr = Array.isArray(v) ? (v as string[]) : [];\n if (arr.length === 0) return null;\n return arr\n .map((item) => (f.options ?? []).find((o) => o.value === item)?.label ?? item)\n .join(\", \");\n }\n if (f.kind === \"boolean\") {\n return v ? f.label : null;\n }\n return String(v);\n })\n .filter(Boolean) as string[])\n : [];\n\n const hoverSummary = hoverPos\n ? (renderHoverSummary?.(hoverEntry, hoverPos) ?? [hoverPos, ...hoverFields].join(\" • \"))\n : \" \";\n const manifestColumns = React.useMemo(() => {\n if (!isPlateScoped || !activePlateBarcode || hidePlateBarcodeColumn) return tableColumns;\n const alreadyHasBarcodeColumn = tableColumns.some(\n (column) => column.field === barcodeField || column.id === barcodeField,\n );\n if (alreadyHasBarcodeColumn) return tableColumns;\n\n const barcodeColumn: WellColumn<T> = {\n id: barcodeField,\n header: plateBarcodeColumnHeader,\n minWidth: 150,\n render: ({ row }) => {\n const barcode = row[barcodeField] ?? activePlateBarcode;\n return <Badge variant=\"outline\">{String(barcode)}</Badge>;\n },\n };\n return [barcodeColumn, ...tableColumns];\n }, [activePlateBarcode, barcodeField, hidePlateBarcodeColumn, isPlateScoped, plateBarcodeColumnHeader, tableColumns]);\n const showPlateSelector = shouldShowPlateSelector(availablePlates.length, onAddPlate);\n const canChangePlate = canUpdatePlateSelection(isPlateSelectionControlled, onPlateChange);\n\n return (\n <div data-slot=\"plate-map-editor\" className={cn(\"flex flex-col gap-4\", className)}>\n <PlateMapEditorTitleBar title={title} badges={badges} />\n\n {banner}\n\n <div className=\"flex flex-wrap gap-3 md:flex-nowrap\">\n {/* Form column */}\n <Card className=\"flex w-full max-w-[360px] min-w-[300px] basis-[360px] flex-col\" size=\"sm\">\n <CardContent className=\"flex h-full flex-1 flex-col gap-3\">\n {formSlot ?? (\n <WellMetadataForm\n fields={fields}\n value={staged}\n onChange={setStaged}\n selectionSize={selection.size}\n onApply={applyStagedToSelection}\n onClear={clearWells}\n extras={formExtras}\n />\n )}\n <PlateMapEditorLegend legend={legend} />\n </CardContent>\n </Card>\n\n {/* Plate column */}\n <Card className=\"flex min-w-[360px] flex-1 flex-col\" size=\"sm\">\n <CardHeader className=\"border-b\">\n <div className=\"flex min-w-0 flex-wrap items-center gap-2\">\n <CardTitle className=\"min-w-0\">{plateTitle}</CardTitle>\n {showPlateSelector ? (\n <PlateMapPlateSelector\n plates={availablePlates}\n activePlateId={activePlate?.id}\n onPlateChange={canChangePlate ? handlePlateChange : undefined}\n onAddPlate={onAddPlate}\n onRemovePlate={onRemovePlate}\n addPlateLabel={addPlateLabel}\n removePlateLabel={removePlateLabel}\n label={plateSelectorLabel}\n variant={plateSelectorVariant}\n />\n ) : null}\n </div>\n <CardAction className=\"flex flex-wrap items-center gap-2\">\n <PlateMapActionsMenu\n templates={templates}\n templateId={templateId}\n onTemplateChange={onTemplateChange}\n onClearTemplate={onClearTemplate}\n hasEntries={values.size > 0}\n onImportCsv={onImportCsv ? handleImportCsv : undefined}\n onExportCsv={onExportCsv}\n onImportTemplate={onImportTemplate}\n onExportTemplate={onExportTemplate}\n csvAccept={csvAccept}\n templateAccept={templateAccept}\n label={label}\n align={align}\n side={side}\n importTemplateLabel={importTemplateLabel}\n exportTemplateLabel={exportTemplateLabel}\n importCsvLabel={importCsvLabel}\n exportCsvLabel={exportCsvLabel}\n clearLabel={clearLabel}\n />\n </CardAction>\n </CardHeader>\n <CardContent className=\"flex flex-col gap-1.5\">\n <div className=\"flex flex-wrap items-center justify-start gap-3\">\n {plateToolbar}\n <div className=\"flex items-center gap-2 text-xs\">\n <button\n type=\"button\"\n className=\"font-medium text-primary underline-offset-2 hover:text-primary/80 hover:underline\"\n onClick={selectAll}\n >\n Select all\n </button>\n <span className=\"text-muted-foreground/60\">·</span>\n <button\n type=\"button\"\n className=\"font-medium text-primary underline-offset-2 hover:text-primary/80 hover:underline\"\n onClick={deselectAll}\n >\n Deselect all\n </button>\n </div>\n </div>\n <div className=\"h-5 text-xs text-muted-foreground\">{hoverSummary}</div>\n <PlatePaintGrid\n format={format}\n rows={rows}\n columns={columns}\n values={scopedValues}\n selection={selection}\n onSelectionChange={onSelectionChange}\n colorForWell={colorForWell}\n emptyWellFillColor={emptyWellFillColor}\n wellShape={wellShape}\n framed={framedPlate}\n wrapWell={wrapWell}\n highlightedWellIds={highlightedWellIds}\n onWellHover={(wellId) => {\n setHoverPos(wellId);\n onHoveredWellChange?.(wellId);\n }}\n onWellDoubleClick={doubleClickCycleField ? cycleWellField : undefined}\n selectionFillMode={doubleClickCycleField ? \"well\" : \"selection\"}\n flashWellId={flashWell?.wellId}\n flashWellKey={flashWell?.key}\n cellSize={cellSize}\n autoScale={autoScaleGrid}\n minCellSize={minCellSize}\n maxCellSize={maxCellSize}\n />\n {groups && groups.length > 0 ? (\n <>\n <Separator className=\"mt-2\" />\n <div className=\"flex max-h-28 flex-wrap gap-3 overflow-y-auto pt-1\">\n {groups.map((group) => {\n const isActive = group.id === activeGroupId;\n const count = group.count ?? group.wellIds?.length;\n return (\n <button\n key={group.id}\n type=\"button\"\n disabled={group.disabled}\n onClick={() => onGroupClick?.(group)}\n className={cn(\n \"flex w-16 flex-col items-center gap-1 rounded-md px-1 py-1 text-center text-xs transition-colors\",\n \"text-muted-foreground hover:bg-muted/60\",\n isActive && \"bg-muted text-foreground ring-1 ring-primary/40\",\n group.disabled && \"pointer-events-none opacity-50\",\n )}\n title={group.label}\n >\n <span\n className=\"size-7 rounded-full border\"\n style={{\n backgroundColor: group.color,\n borderColor: group.borderColor,\n }}\n aria-hidden\n />\n <span className=\"w-full truncate text-foreground\">{group.label}</span>\n {count === undefined ? null : (\n <span className=\"text-[0.7rem] leading-none text-muted-foreground\">{count} wells</span>\n )}\n </button>\n );\n })}\n </div>\n </>\n ) : null}\n </CardContent>\n </Card>\n </div>\n\n <Card size=\"sm\">\n <CardHeader className=\"border-b\">\n <CardTitle>Sample manifest</CardTitle>\n </CardHeader>\n <CardContent>\n <WellManifestTable\n values={scopedValues}\n onChange={commitScopedValues}\n columns={manifestColumns}\n fields={fields}\n selection={selection}\n onSelectionChange={onSelectionChange}\n emptyEntry={emptyEntry}\n isPopulated={isPopulated}\n filterable={manifestFilterable}\n groupable={manifestGroupable}\n />\n </CardContent>\n </Card>\n\n {footer ? <div className=\"flex justify-end gap-2 pt-2\">{footer}</div> : null}\n </div>\n );\n}\n\nexport { Badge as PlateBadge };\n"],"names":["PLATE_WELL_KEY_SEPARATOR","DEFAULT_PLATE_BARCODE_FIELD","DEFAULT_PLATE_BARCODE_HEADER","getPlateMapScopedWellId","plateBarcode","wellId","wellIdFromPlateWellKey","plateId","key","prefix","defaultMerge","existing","staged","mergePlateOptions","importedPlates","providedPlates","merged","plate","resolveActivePlate","plates","selectedPlateId","matchingPlate","buildScopedValues","values","plateKey","next","row","resolveImportedPlateSelection","isControlled","providedPlateCount","firstImportedPlateId","shouldShowPlateSelector","plateCount","onAddPlate","canUpdatePlateSelection","onPlateChange","PlateMapEditorTitleBar","title","badges","jsxs","jsx","PlateMapEditorLegend","legend","Fragment","Separator","PlateMapEditor","format","rows","columns","onChange","selection","onSelectionChange","fields","tableColumns","colorForWell","emptyEntry","mergeOnApply","isPopulated","cycleFieldOnWellDoubleClick","banner","formExtras","formSlot","footer","plateTitle","plateToolbar","activePlateId","onRemovePlate","addPlateLabel","removePlateLabel","plateSelectorLabel","plateSelectorVariant","plateBarcodeField","plateBarcodeColumnHeader","hidePlateBarcodeColumn","groups","activeGroupId","onGroupClick","renderHoverSummary","cellSize","emptyWellFillColor","wellShape","framedPlate","wrapWell","highlightedWellIds","onHoveredWellChange","manifestFilterable","manifestGroupable","autoScaleGrid","minCellSize","maxCellSize","className","templates","templateId","onTemplateChange","onClearTemplate","onImportCsv","onExportCsv","onImportTemplate","onExportTemplate","csvAccept","templateAccept","label","align","side","importTemplateLabel","exportTemplateLabel","importCsvLabel","exportCsvLabel","clearLabel","dims","resolveDimensions","setStaged","React","hoverPos","setHoverPos","flashWell","setFlashWell","csvPlates","setCsvPlates","internalActivePlateId","setInternalActivePlateId","merge","availablePlates","isPlateSelectionControlled","activePlate","activePlateBarcode","activePlateKey","isPlateScoped","barcodeField","previousActivePlateKey","scopedValues","toStoredWellKey","handlePlateChange","handleImportCsv","file","triage","nextCsvPlates","plateOptionsFromCsvTriage","nextSelection","stampActivePlateBarcode","commitScopedValues","nextScopedValues","doubleClickCycleField","field","f","applyStagedToSelection","base","clearWells","cycleWellField","currentValue","currentIndex","opt","nextOption","current","selectAll","allPositions","deselectAll","hoverEntry","hoverFields","v","o","arr","item","hoverSummary","manifestColumns","column","barcode","Badge","showPlateSelector","canChangePlate","cn","Card","CardContent","WellMetadataForm","CardHeader","CardTitle","PlateMapPlateSelector","CardAction","PlateMapActionsMenu","PlatePaintGrid","group","isActive","count","WellManifestTable"],"mappings":"+yBA6BMA,GAA2B,KAC3BC,GAA8B,eAC9BC,GAA+B,gBAE9B,SAASC,GAAwBC,EAAsBC,EAAwB,CACpF,MAAO,GAAGD,CAAY,GAAGJ,EAAwB,GAAGK,CAAM,EAC5D,CAEA,SAASC,GAAuBC,EAAiBC,EAAiC,CAChF,MAAMC,EAAS,GAAGF,CAAO,GAAGP,EAAwB,GACpD,OAAOQ,EAAI,WAAWC,CAAM,EAAID,EAAI,MAAMC,EAAO,MAAM,EAAI,MAC7D,CA2GA,SAASC,GAAmCC,EAAyBC,EAAuB,CAC1F,MAAO,CAAE,GAAID,GAAa,CAAA,EAAW,GAAGC,CAAA,CAC1C,CAEA,SAASC,GACPC,EACAC,EACuB,CACvB,MAAMC,MAAa,IACnB,OAAAF,EAAe,QAASG,GAAUD,EAAO,IAAIC,EAAM,GAAIA,CAAK,CAAC,GAC5DF,GAAkB,CAAA,GAAI,QAASE,GAAUD,EAAO,IAAIC,EAAM,GAAIA,CAAK,CAAC,EAC9D,CAAC,GAAGD,EAAO,QAAQ,CAC5B,CAEA,SAASE,GACPC,EACAC,EACiC,CACjC,MAAMC,EAAgBF,EAAO,KAAMF,GAAUA,EAAM,KAAOG,CAAe,EACzE,OAAIC,IACGD,EAAkB,OAAYD,EAAO,CAAC,EAC/C,CAEA,SAASG,GAAwCC,EAAwBC,EAA8B,CACrG,GAAI,CAACA,EAAU,OAAOD,EAEtB,MAAME,MAAW,IACjB,OAAAF,EAAO,QAAQ,CAACG,EAAKlB,IAAQ,CAC3B,MAAMH,EAASC,GAAuBkB,EAAUhB,CAAG,EAC/CH,GAAQoB,EAAK,IAAIpB,EAAQqB,CAAG,CAClC,CAAC,EACMD,CACT,CAEA,SAASE,GAA8B,CACrC,eAAAb,EACA,gBAAAM,EACA,aAAAQ,EACA,mBAAAC,CACF,EAK8B,CAC5B,MAAMC,EAAuBhB,EAAe,CAAC,GAAG,GAChD,OAAKgB,EACEhB,EAAe,KAAMG,GAAUA,EAAM,KAAOG,CAAe,EAAI,OAAYU,EADhD,CAACF,GAAgBC,IAAuB,EAAI,KAAO,MAEvF,CAEA,SAASE,GAAwBC,EAAoBC,EAAwD,CAC3G,OAAOD,EAAa,GAAK,CAAC,CAACC,CAC7B,CAEA,SAASC,GAAwBN,EAAuBO,EAA8D,CACpH,MAAO,CAACP,GAAgB,CAAC,CAACO,CAC5B,CAEA,SAASC,GAAuB,CAAE,MAAAC,EAAO,OAAAC,GAAwD,CAC/F,MAAI,CAACD,GAAS,CAACC,EAAe,KAG5BC,EAAAA,KAAC,MAAA,CAAI,UAAU,oDACZ,SAAA,CAAAF,QAAS,KAAA,CAAG,UAAU,wBAAyB,SAAAA,EAAM,QAAS,OAAA,CAAA,CAAK,EACnEC,EAASE,EAAAA,IAAC,MAAA,CAAI,UAAU,uBAAwB,WAAO,EAAS,IAAA,EACnE,CAEJ,CAEA,SAASC,GAAqB,CAAE,OAAAC,GAAwC,CACtE,OAAKA,EAGHH,EAAAA,KAAAI,WAAA,CACE,SAAA,CAAAH,EAAAA,IAACI,GAAAA,UAAA,EAAU,EACVF,CAAA,EACH,EANkB,IAQtB,CAEO,SAASG,GAAkD,CAChE,OAAAC,EACA,KAAAC,EACA,QAAAC,EACA,OAAAzB,EACA,SAAA0B,EACA,UAAAC,EACA,kBAAAC,EACA,OAAAC,EACA,aAAAC,EACA,aAAAC,GACA,WAAAC,EACA,aAAAC,GACA,YAAAC,GACA,4BAAAC,EACA,MAAArB,GACA,OAAAC,GACA,OAAAqB,GACA,OAAAjB,GACA,WAAAkB,GACA,SAAAC,GACA,OAAAC,EACA,WAAAC,GAAa,QACb,aAAAC,GACA,OAAA7C,EACA,cAAA8C,EACA,cAAA9B,EACA,WAAAF,EACA,cAAAiC,GACA,cAAAC,GACA,iBAAAC,GACA,mBAAAC,GACA,qBAAAC,GACA,kBAAAC,GACA,yBAAAC,EAA2BtE,GAC3B,uBAAAuE,EAAyB,GACzB,OAAAC,EACA,cAAAC,GACA,aAAAC,GACA,mBAAAC,GACA,SAAAC,GACA,mBAAAC,GACA,UAAAC,GACA,YAAAC,GACA,SAAAC,GACA,mBAAAC,GACA,oBAAAC,GACA,mBAAAC,GACA,kBAAAC,GACA,cAAAC,GACA,YAAAC,GACA,YAAAC,GACA,UAAAC,GACA,UAAAC,GACA,WAAAC,GACA,iBAAAC,GACA,gBAAAC,GACA,YAAAC,EACA,YAAAC,GACA,iBAAAC,GACA,iBAAAC,GACA,UAAAC,GACA,eAAAC,GACA,MAAAC,GACA,MAAAC,GACA,KAAAC,GACA,oBAAAC,GACA,oBAAAC,GACA,eAAAC,GACA,eAAAC,GACA,WAAAC,EACF,EAA2B,CACzB,MAAMC,GAAOC,EAAAA,kBAAkBhE,EAAQC,EAAMC,CAAO,EAC9C,CAACpC,EAAQmG,CAAS,EAAIC,EAAM,SAAqB,CAAA,CAAE,EACnD,CAACC,EAAUC,CAAW,EAAIF,EAAM,SAAwB,IAAI,EAC5D,CAACG,EAAWC,CAAY,EAAIJ,EAAM,SAAA,EAClC,CAACK,EAAWC,EAAY,EAAIN,EAAM,SAAgC,CAAA,CAAE,EACpE,CAACO,GAAuBC,CAAwB,EAAIR,EAAM,SAAA,EAE1DS,GAAQjE,IAAgB9C,GACxBgH,EAAkBV,EAAM,QAAQ,IAAMnG,GAAkBwG,EAAWlG,CAAM,EAAG,CAACkG,EAAWlG,CAAM,CAAC,EAC/FwG,EAA6B1D,IAAkB,OAC/C7C,EAAkB6C,GAAiBsD,GACnCK,EAAcZ,EAAM,QACxB,IAAM9F,GAAmBwG,EAAiBtG,CAAe,EACzD,CAACsG,EAAiBtG,CAAe,CAAA,EAE7ByG,EAAqBD,GAAa,QAClCE,EAAiBD,EACjBE,EAAgB,CAAC,CAACD,EAClBE,EAAgBzD,IAAqBtE,GACrCgI,EAAyBjB,EAAM,OAA2Bc,CAAc,EAExEI,EAAelB,EAAM,QAAQ,IAAM1F,GAAkBC,EAAQuG,CAAc,EAAG,CAACA,EAAgBvG,CAAM,CAAC,EAEtG4G,EAAkBnB,EAAM,YAC3B3G,GACK,CAAC0H,GAAiB,CAACD,EAAuBzH,EACvCF,GAAwB2H,EAAgBzH,CAAM,EAEvD,CAACyH,EAAgBC,CAAa,CAAA,EAG1BK,GAAoBpB,EAAM,YAC7BzG,GAAoB,CACdoH,GAA4BH,EAAyBjH,CAAO,EACjE4B,IAAgB5B,CAAO,CACzB,EACA,CAACoH,EAA4BxF,CAAa,CAAA,EAGtCkG,GAAkBrB,EAAM,YAC5B,MAAOsB,EAAYC,IAA+B,CAChD,GAAIA,EAAQ,CACV,MAAMC,EAAgBC,GAAAA,0BAA0BF,CAAM,EACtDjB,GAAakB,CAAa,EAC1B,MAAME,EAAgB/G,GAA8B,CAClD,eAAgB6G,EAChB,gBAAApH,EACA,aAAcuG,EACd,mBAAoBxG,GAAQ,QAAU,CAAA,CACvC,EAEGuH,IAAkB,SACff,GAA4BH,EAAyBkB,GAAiB,MAAS,EAChFA,OAA+BA,CAAa,EAEpD,CAEA,MAAM3C,IAAcuC,EAAMC,CAAM,CAClC,EACA,CAACZ,EAA4B5B,EAAa5D,EAAehB,EAAQC,CAAe,CAAA,EAG5EuH,EAA0B3B,EAAM,YACnCtF,GACK,CAACqG,GAAiB,CAACF,EAA2BnG,EAC3C,CAAE,GAAIA,EAAiC,CAACsG,CAAY,EAAGH,CAAA,EAEhE,CAACA,EAAoBG,EAAcD,CAAa,CAAA,EAG5Ca,EAAqB5B,EAAM,YAC9B6B,GAAqC,CACpC,GAAI,CAACd,GAAiB,CAACD,EAAgB,CACrC7E,EAAS4F,CAAgB,EACzB,MACF,CAEA,MAAMpH,EAAO,IAAI,IAAIF,CAAM,EAC3B,CAAC,GAAGE,EAAK,KAAA,CAAM,EAAE,QAASjB,GAAQ,CAC5BF,GAAuBwH,EAAgBtH,CAAG,GAC5CiB,EAAK,OAAOjB,CAAG,CAEnB,CAAC,EACDqI,EAAiB,QAAQ,CAACnH,EAAKrB,IAAW,CACxCoB,EAAK,IAAI0G,EAAgB9H,CAAM,EAAGsI,EAAwBjH,CAAG,CAAC,CAChE,CAAC,EACDuB,EAASxB,CAAI,CACf,EACA,CAACqG,EAAgBC,EAAe9E,EAAU0F,EAAyBR,EAAiB5G,CAAM,CAAA,EAG5FyF,EAAM,UAAU,IAAM,CAChBiB,EAAuB,UAAYH,IACvCG,EAAuB,QAAUH,EACjCf,EAAU,CAAA,CAAE,EACZG,EAAY,IAAI,EAChBE,EAAa,MAAS,EAClBlE,EAAU,KAAO,GAAGC,EAAkB,IAAI,GAAK,EACrD,EAAG,CAAC2E,EAAgB3E,EAAmBD,EAAU,IAAI,CAAC,EAEtD,MAAM4F,EAAwB9B,EAAM,QAAQ,IAAM,CAChD,GAAI,CAACtD,EAA6B,OAClC,MAAMqF,EAAQ3F,EAAO,KAAM4F,GAAMA,EAAE,MAAQtF,CAA2B,EACtE,GAAI,EAAAqF,GAAO,OAAS,UAAY,CAACA,EAAM,SAAS,QAChD,OAAOA,CACT,EAAG,CAACrF,EAA6BN,CAAM,CAAC,EAElC6F,GAAyB,IAAM,CACnC,GAAI/F,EAAU,OAAS,EAAG,OAC1B,MAAMzB,EAAO,IAAI,IAAIyG,CAAY,EACjChF,EAAU,QAAS7C,GAAW,CAE5B,MAAM6I,EADWzH,EAAK,IAAIpB,CAAM,GACPkD,EAAWlD,CAAM,EACpCW,EAAS2H,EAAwBlB,GAAMyB,EAAMtI,EAAQP,CAAM,CAAC,EAClEoB,EAAK,IAAIpB,EAAQW,CAAM,CACzB,CAAC,EACD4H,EAAmBnH,CAAI,CACzB,EAEM0H,GAAa,IAAM,CACvB,GAAIjG,EAAU,OAAS,EAAG,OAC1B,MAAMzB,EAAO,IAAI,IAAIyG,CAAY,EACjChF,EAAU,QAAS7C,GAAWoB,EAAK,OAAOpB,CAAM,CAAC,EACjDuI,EAAmBnH,CAAI,CACzB,EAEM2H,GAAiBpC,EAAM,YAC1B3G,GAAmB,CAClB,GAAI,CAACyI,GAAuB,SAAS,OAAQ,OAE7C,MAAMrH,EAAO,IAAI,IAAIyG,CAAY,EAC3BvH,EAAWc,EAAK,IAAIpB,CAAM,EAC1BgJ,EAAe1I,IAAWmI,EAAsB,GAAG,EACnDQ,EAAeR,EAAsB,QAAQ,UAAWS,GAAQA,EAAI,QAAUF,CAAY,EAC1FG,EAAaV,EAAsB,SAASQ,EAAe,GAAKR,EAAsB,QAAQ,MAAM,EAC1G,GAAI,CAACU,EAAY,OAEjB,MAAMN,GAAOvI,GAAY4C,EAAWlD,CAAM,EAC1CoB,EAAK,IAAIpB,EAAQsI,EAAwB,CAAE,GAAGO,GAAM,CAACJ,EAAsB,GAAG,EAAGU,EAAW,KAAA,CAAY,CAAC,EACzGZ,EAAmBnH,CAAI,EACvB2F,EAAcqC,IAAa,CAAE,OAAApJ,EAAQ,KAAMoJ,GAAS,KAAO,GAAK,CAAA,EAAI,CACtE,EACA,CAACb,EAAoBE,EAAuBvF,EAAY2E,EAAcS,CAAuB,CAAA,EAGzFe,GAAY,IAAM,CACtBvG,EAAkB,IAAI,IAAIwG,eAAa9C,EAAI,CAAC,CAAC,CAC/C,EACM+C,GAAc,IAAMzG,EAAkB,IAAI,GAAK,EAE/C0G,EAAa5C,EAAWiB,EAAa,IAAIjB,CAAQ,EAAI,OAErD6C,GAAcD,EACfzG,EACE,IAAK4F,GAAM,CACV,MAAMe,EAAIF,EAAWb,EAAE,GAAG,EAC1B,GAAuBe,GAAM,MAAQA,IAAM,GAAI,OAAO,KACtD,GAAIf,EAAE,OAAS,SAEb,OADaA,EAAE,SAAW,CAAA,GAAI,KAAMgB,GAAMA,EAAE,QAAUD,CAAC,GAC3C,OAAS,OAAOA,CAAC,EAE/B,GAAIf,EAAE,OAAS,cAAe,CAC5B,MAAMiB,EAAM,MAAM,QAAQF,CAAC,EAAKA,EAAiB,CAAA,EACjD,OAAIE,EAAI,SAAW,EAAU,KACtBA,EACJ,IAAKC,IAAUlB,EAAE,SAAW,CAAA,GAAI,KAAMgB,GAAMA,EAAE,QAAUE,CAAI,GAAG,OAASA,CAAI,EAC5E,KAAK,IAAI,CACd,CACA,OAAIlB,EAAE,OAAS,UACNe,EAAIf,EAAE,MAAQ,KAEhB,OAAOe,CAAC,CACjB,CAAC,EACA,OAAO,OAAO,EACjB,CAAA,EAEEI,GAAelD,EAChBpC,KAAqBgF,EAAY5C,CAAQ,GAAK,CAACA,EAAU,GAAG6C,EAAW,EAAE,KAAK,KAAK,EACpF,IACEM,GAAkBpD,EAAM,QAAQ,IAChC,CAACe,GAAiB,CAACF,GAAsBpD,GACbpB,EAAa,KAC1CgH,GAAWA,EAAO,QAAUrC,GAAgBqC,EAAO,KAAOrC,CAAA,EAEzB3E,EAW7B,CAT8B,CACnC,GAAI2E,EACJ,OAAQxD,EACR,SAAU,IACV,OAAQ,CAAC,CAAE,IAAA9C,KAAU,CACnB,MAAM4I,EAAU5I,EAAIsG,CAAY,GAAKH,EACrC,aAAQ0C,GAAAA,MAAA,CAAM,QAAQ,UAAW,SAAA,OAAOD,CAAO,EAAE,CACnD,CAAA,EAEqB,GAAGjH,CAAY,EACrC,CAACwE,EAAoBG,EAAcvD,EAAwBsD,EAAevD,EAA0BnB,CAAY,CAAC,EAC9GmH,GAAoBzI,GAAwB2F,EAAgB,OAAQzF,CAAU,EAC9EwI,GAAiBvI,GAAwByF,EAA4BxF,CAAa,EAExF,OACEI,OAAC,OAAI,YAAU,mBAAmB,UAAWmI,GAAAA,GAAG,sBAAuBhF,EAAS,EAC9E,SAAA,CAAAlD,EAAAA,IAACJ,GAAA,CAAuB,MAAAC,GAAc,OAAAC,EAAA,CAAgB,EAErDqB,GAEDpB,EAAAA,KAAC,MAAA,CAAI,UAAU,sCAEb,SAAA,CAAAC,EAAAA,IAACmI,EAAAA,KAAA,CAAK,UAAU,iEAAiE,KAAK,KACpF,SAAApI,EAAAA,KAACqI,EAAAA,YAAA,CAAY,UAAU,oCACpB,SAAA,CAAA/G,IACCrB,EAAAA,IAACqI,GAAAA,iBAAA,CACC,OAAAzH,EACA,MAAOxC,EACP,SAAUmG,EACV,cAAe7D,EAAU,KACzB,QAAS+F,GACT,QAASE,GACT,OAAQvF,EAAA,CAAA,EAGZpB,MAACC,IAAqB,OAAAC,EAAA,CAAgB,CAAA,CAAA,CACxC,CAAA,CACF,EAGAH,EAAAA,KAACoI,EAAAA,KAAA,CAAK,UAAU,qCAAqC,KAAK,KACxD,SAAA,CAAApI,EAAAA,KAACuI,EAAAA,WAAA,CAAW,UAAU,WACpB,SAAA,CAAAvI,EAAAA,KAAC,MAAA,CAAI,UAAU,4CACb,SAAA,CAAAC,EAAAA,IAACuI,EAAAA,UAAA,CAAU,UAAU,UAAW,SAAAhH,GAAW,EAC1CyG,GACChI,EAAAA,IAACwI,GAAAA,sBAAA,CACC,OAAQtD,EACR,cAAeE,GAAa,GAC5B,cAAe6C,GAAiBrC,GAAoB,OACpD,WAAAnG,EACA,cAAAiC,GACA,cAAAC,GACA,iBAAAC,GACA,MAAOC,GACP,QAASC,EAAA,CAAA,EAET,IAAA,EACN,EACA9B,EAAAA,IAACyI,EAAAA,WAAA,CAAW,UAAU,oCACpB,SAAAzI,EAAAA,IAAC0I,GAAAA,oBAAA,CACC,UAAAvF,GACA,WAAAC,GACA,iBAAAC,GACA,gBAAAC,GACA,WAAYvE,EAAO,KAAO,EAC1B,YAAawE,EAAcsC,GAAkB,OAC7C,YAAArC,GACA,iBAAAC,GACA,iBAAAC,GACA,UAAAC,GACA,eAAAC,GACA,MAAAC,GACA,MAAAC,GACA,KAAAC,GACA,oBAAAC,GACA,oBAAAC,GACA,eAAAC,GACA,eAAAC,GACA,WAAAC,EAAA,CAAA,CACF,CACF,CAAA,EACF,EACArE,EAAAA,KAACqI,EAAAA,YAAA,CAAY,UAAU,wBACrB,SAAA,CAAArI,EAAAA,KAAC,MAAA,CAAI,UAAU,kDACZ,SAAA,CAAAyB,GACDzB,EAAAA,KAAC,MAAA,CAAI,UAAU,kCACb,SAAA,CAAAC,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,UAAU,oFACV,QAASkH,GACV,SAAA,YAAA,CAAA,EAGDlH,EAAAA,IAAC,OAAA,CAAK,UAAU,2BAA2B,SAAA,IAAC,EAC5CA,EAAAA,IAAC,SAAA,CACC,KAAK,SACL,UAAU,oFACV,QAASoH,GACV,SAAA,cAAA,CAAA,CAED,CAAA,CACF,CAAA,EACF,EACApH,EAAAA,IAAC,MAAA,CAAI,UAAU,oCAAqC,SAAA2H,GAAa,EACjE3H,EAAAA,IAAC2I,GAAAA,eAAA,CACC,OAAArI,EACA,KAAAC,EACA,QAAAC,EACA,OAAQkF,EACR,UAAAhF,EACA,kBAAAC,EACA,aAAAG,GACA,mBAAAyB,GACA,UAAAC,GACA,OAAQC,GACR,SAAAC,GACA,mBAAAC,GACA,YAAc9E,GAAW,CACvB6G,EAAY7G,CAAM,EAClB+E,KAAsB/E,CAAM,CAC9B,EACA,kBAAmByI,EAAwBM,GAAiB,OAC5D,kBAAmBN,EAAwB,OAAS,YACpD,YAAa3B,GAAW,OACxB,aAAcA,GAAW,IACzB,SAAArC,GACA,UAAWS,GACX,YAAAC,GACA,YAAAC,EAAA,CAAA,EAEDf,GAAUA,EAAO,OAAS,EACzBnC,EAAAA,KAAAI,EAAAA,SAAA,CACE,SAAA,CAAAH,EAAAA,IAACI,GAAAA,UAAA,CAAU,UAAU,MAAA,CAAO,QAC3B,MAAA,CAAI,UAAU,qDACZ,SAAA8B,EAAO,IAAK0G,GAAU,CACrB,MAAMC,EAAWD,EAAM,KAAOzG,GACxB2G,EAAQF,EAAM,OAASA,EAAM,SAAS,OAC5C,OACE7I,EAAAA,KAAC,SAAA,CAEC,KAAK,SACL,SAAU6I,EAAM,SAChB,QAAS,IAAMxG,KAAewG,CAAK,EACnC,UAAWV,GAAAA,GACT,mGACA,0CACAW,GAAY,kDACZD,EAAM,UAAY,gCAAA,EAEpB,MAAOA,EAAM,MAEb,SAAA,CAAA5I,EAAAA,IAAC,OAAA,CACC,UAAU,6BACV,MAAO,CACL,gBAAiB4I,EAAM,MACvB,YAAaA,EAAM,WAAA,EAErB,cAAW,EAAA,CAAA,EAEb5I,EAAAA,IAAC,OAAA,CAAK,UAAU,kCAAmC,WAAM,MAAM,EAC9D8I,IAAU,OAAY,KACrB/I,EAAAA,KAAC,OAAA,CAAK,UAAU,mDAAoD,SAAA,CAAA+I,EAAM,QAAA,CAAA,CAAM,CAAA,CAAA,EAtB7EF,EAAM,EAAA,CA0BjB,CAAC,CAAA,CACH,CAAA,CAAA,CACF,EACE,IAAA,CAAA,CACN,CAAA,CAAA,CACF,CAAA,EACF,EAEA7I,EAAAA,KAACoI,EAAAA,KAAA,CAAK,KAAK,KACT,SAAA,CAAAnI,EAAAA,IAACsI,EAAAA,YAAW,UAAU,WACpB,SAAAtI,MAACuI,EAAAA,UAAA,CAAU,2BAAe,CAAA,CAC5B,QACCH,EAAAA,YAAA,CACC,SAAApI,EAAAA,IAAC+I,GAAAA,kBAAA,CACC,OAAQrD,EACR,SAAUU,EACV,QAASwB,GACT,OAAAhH,EACA,UAAAF,EACA,kBAAAC,EACA,WAAAI,EACA,YAAAE,GACA,WAAY4B,GACZ,UAAWC,EAAA,CAAA,CACb,CACF,CAAA,EACF,EAECxB,EAAStB,EAAAA,IAAC,MAAA,CAAI,UAAU,8BAA+B,WAAO,EAAS,IAAA,EAC1E,CAEJ"}
|