@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.
Files changed (71) hide show
  1. package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.cjs +2 -0
  2. package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.cjs.map +1 -0
  3. package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.js +140 -0
  4. package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.js.map +1 -0
  5. package/dist/components/composed/PlateMapEditor/PlateMapActionsMenu.cjs +2 -0
  6. package/dist/components/composed/PlateMapEditor/PlateMapActionsMenu.cjs.map +1 -0
  7. package/dist/components/composed/PlateMapEditor/PlateMapActionsMenu.js +126 -0
  8. package/dist/components/composed/PlateMapEditor/PlateMapActionsMenu.js.map +1 -0
  9. package/dist/components/composed/PlateMapEditor/PlateMapEditor.cjs +2 -0
  10. package/dist/components/composed/PlateMapEditor/PlateMapEditor.cjs.map +1 -0
  11. package/dist/components/composed/PlateMapEditor/PlateMapEditor.js +422 -0
  12. package/dist/components/composed/PlateMapEditor/PlateMapEditor.js.map +1 -0
  13. package/dist/components/composed/PlateMapEditor/PlateMapPlateSelector.cjs +2 -0
  14. package/dist/components/composed/PlateMapEditor/PlateMapPlateSelector.cjs.map +1 -0
  15. package/dist/components/composed/PlateMapEditor/PlateMapPlateSelector.js +136 -0
  16. package/dist/components/composed/PlateMapEditor/PlateMapPlateSelector.js.map +1 -0
  17. package/dist/components/composed/PlateMapEditor/PlatePaintGrid.cjs +2 -0
  18. package/dist/components/composed/PlateMapEditor/PlatePaintGrid.cjs.map +1 -0
  19. package/dist/components/composed/PlateMapEditor/PlatePaintGrid.js +389 -0
  20. package/dist/components/composed/PlateMapEditor/PlatePaintGrid.js.map +1 -0
  21. package/dist/components/composed/PlateMapEditor/PlateZoomControl.cjs +2 -0
  22. package/dist/components/composed/PlateMapEditor/PlateZoomControl.cjs.map +1 -0
  23. package/dist/components/composed/PlateMapEditor/PlateZoomControl.js +54 -0
  24. package/dist/components/composed/PlateMapEditor/PlateZoomControl.js.map +1 -0
  25. package/dist/components/composed/PlateMapEditor/TemplateIOPanel.cjs +2 -0
  26. package/dist/components/composed/PlateMapEditor/TemplateIOPanel.cjs.map +1 -0
  27. package/dist/components/composed/PlateMapEditor/TemplateIOPanel.js +96 -0
  28. package/dist/components/composed/PlateMapEditor/TemplateIOPanel.js.map +1 -0
  29. package/dist/components/composed/PlateMapEditor/WellLegend.cjs +2 -0
  30. package/dist/components/composed/PlateMapEditor/WellLegend.cjs.map +1 -0
  31. package/dist/components/composed/PlateMapEditor/WellLegend.js +58 -0
  32. package/dist/components/composed/PlateMapEditor/WellLegend.js.map +1 -0
  33. package/dist/components/composed/PlateMapEditor/WellManifestTable.cjs +2 -0
  34. package/dist/components/composed/PlateMapEditor/WellManifestTable.cjs.map +1 -0
  35. package/dist/components/composed/PlateMapEditor/WellManifestTable.js +421 -0
  36. package/dist/components/composed/PlateMapEditor/WellManifestTable.js.map +1 -0
  37. package/dist/components/composed/PlateMapEditor/WellMetadataForm.cjs +2 -0
  38. package/dist/components/composed/PlateMapEditor/WellMetadataForm.cjs.map +1 -0
  39. package/dist/components/composed/PlateMapEditor/WellMetadataForm.js +177 -0
  40. package/dist/components/composed/PlateMapEditor/WellMetadataForm.js.map +1 -0
  41. package/dist/components/composed/PlateMapEditor/autoFill.cjs +2 -0
  42. package/dist/components/composed/PlateMapEditor/autoFill.cjs.map +1 -0
  43. package/dist/components/composed/PlateMapEditor/autoFill.js +41 -0
  44. package/dist/components/composed/PlateMapEditor/autoFill.js.map +1 -0
  45. package/dist/components/composed/PlateMapEditor/csvPlateTriage.cjs +4 -0
  46. package/dist/components/composed/PlateMapEditor/csvPlateTriage.cjs.map +1 -0
  47. package/dist/components/composed/PlateMapEditor/csvPlateTriage.js +103 -0
  48. package/dist/components/composed/PlateMapEditor/csvPlateTriage.js.map +1 -0
  49. package/dist/components/composed/PlateMapEditor/helpers.cjs +2 -0
  50. package/dist/components/composed/PlateMapEditor/helpers.cjs.map +1 -0
  51. package/dist/components/composed/PlateMapEditor/helpers.js +11 -0
  52. package/dist/components/composed/PlateMapEditor/helpers.js.map +1 -0
  53. package/dist/components/composed/PlateMapEditor/wellGrid.cjs +2 -0
  54. package/dist/components/composed/PlateMapEditor/wellGrid.cjs.map +1 -0
  55. package/dist/components/composed/PlateMapEditor/wellGrid.js +56 -0
  56. package/dist/components/composed/PlateMapEditor/wellGrid.js.map +1 -0
  57. package/dist/components/ui/data-table/data-table.cjs +1 -1
  58. package/dist/components/ui/data-table/data-table.cjs.map +1 -1
  59. package/dist/components/ui/data-table/data-table.js +1 -0
  60. package/dist/components/ui/data-table/data-table.js.map +1 -1
  61. package/dist/components/ui/popover.cjs +2 -0
  62. package/dist/components/ui/popover.cjs.map +1 -0
  63. package/dist/components/ui/popover.js +45 -0
  64. package/dist/components/ui/popover.js.map +1 -0
  65. package/dist/index.cjs +1 -1
  66. package/dist/index.css +1 -1
  67. package/dist/index.d.ts +555 -0
  68. package/dist/index.js +637 -595
  69. package/dist/index.js.map +1 -1
  70. package/dist/index.tailwind.css +1 -1
  71. package/package.json +1 -1
@@ -0,0 +1,177 @@
1
+ import { jsxs as o, jsx as n } from "react/jsx-runtime";
2
+ import * as I from "react";
3
+ import { Button as x } from "../../ui/button.js";
4
+ import { Checkbox as S } from "../../ui/checkbox.js";
5
+ import { useComboboxAnchor as $, Combobox as F, ComboboxChips as V, ComboboxValue as A, ComboboxChip as M, ComboboxChipsInput as B, ComboboxContent as T, ComboboxEmpty as _, ComboboxList as j, ComboboxItem as E } from "../../ui/combobox.js";
6
+ import { Input as L } from "../../ui/input.js";
7
+ import { Label as P } from "../../ui/label.js";
8
+ import { Select as R, SelectTrigger as Y, SelectValue as D, SelectContent as K, SelectItem as U } from "../../ui/select.js";
9
+ import { Switch as W } from "../../ui/switch.js";
10
+ import { cn as q } from "../../../lib/utils.js";
11
+ function G({
12
+ id: i,
13
+ value: c,
14
+ options: a,
15
+ placeholder: m,
16
+ onChange: k
17
+ }) {
18
+ const p = $(), b = I.useMemo(() => {
19
+ const l = /* @__PURE__ */ new Map();
20
+ return a.forEach((s) => l.set(s.value, s.label)), l;
21
+ }, [a]);
22
+ return /* @__PURE__ */ o(F, { multiple: !0, items: a.map((l) => l.value), value: c, onValueChange: k, children: [
23
+ /* @__PURE__ */ o(V, { ref: p, className: "min-h-9 w-full", children: [
24
+ /* @__PURE__ */ n(A, { children: (l) => l.map((s) => /* @__PURE__ */ n(M, { children: b.get(s) ?? s }, s)) }),
25
+ /* @__PURE__ */ n(B, { id: i, placeholder: m ?? "Select…" })
26
+ ] }),
27
+ /* @__PURE__ */ o(T, { anchor: p, children: [
28
+ /* @__PURE__ */ n(_, { children: "No options." }),
29
+ /* @__PURE__ */ n(j, { children: (l) => /* @__PURE__ */ n(E, { value: l, children: /* @__PURE__ */ o("span", { className: "inline-flex items-center gap-2", children: [
30
+ (() => {
31
+ const s = a.find((g) => g.value === l);
32
+ return s?.swatch ? /* @__PURE__ */ n(
33
+ "span",
34
+ {
35
+ className: "inline-block h-2.5 w-2.5 rounded-sm border border-foreground/20",
36
+ style: { backgroundColor: s.swatch },
37
+ "aria-hidden": !0
38
+ }
39
+ ) : null;
40
+ })(),
41
+ b.get(l) ?? l
42
+ ] }) }, l) })
43
+ ] })
44
+ ] });
45
+ }
46
+ const H = {
47
+ number: "number",
48
+ integer: "number",
49
+ date: "date",
50
+ datetime: "datetime-local",
51
+ time: "time"
52
+ };
53
+ function J(i) {
54
+ return H[i] ?? "text";
55
+ }
56
+ function O(i, c) {
57
+ if (c !== "") {
58
+ if (i === "number") {
59
+ const a = parseFloat(c);
60
+ return Number.isFinite(a) ? a : c;
61
+ }
62
+ if (i === "integer") {
63
+ const a = parseInt(c, 10);
64
+ return Number.isFinite(a) ? a : c;
65
+ }
66
+ return c;
67
+ }
68
+ }
69
+ function le({
70
+ fields: i,
71
+ value: c,
72
+ onChange: a,
73
+ selectionSize: m,
74
+ onApply: k,
75
+ onClear: p,
76
+ applyLabel: b = "Apply",
77
+ clearLabel: l = "Clear wells",
78
+ extras: s,
79
+ className: g
80
+ }) {
81
+ const u = (e, r) => {
82
+ a({ ...c, [e]: r });
83
+ }, d = (e) => /* @__PURE__ */ o(P, { htmlFor: `field-${e.key}`, className: "gap-1.5", children: [
84
+ e.icon ? /* @__PURE__ */ n("span", { className: "inline-flex text-muted-foreground [&_svg]:size-3.5", children: e.icon }) : null,
85
+ e.label
86
+ ] }), C = (e, r) => /* @__PURE__ */ o("div", { className: "flex flex-col gap-1.5", children: [
87
+ d(e),
88
+ e.render({
89
+ value: r,
90
+ onChange: (t) => u(e.key, t),
91
+ selectionSize: m
92
+ })
93
+ ] }, e.key), y = (e, r) => /* @__PURE__ */ o("div", { className: "flex flex-col gap-1.5", children: [
94
+ d(e),
95
+ /* @__PURE__ */ o(R, { value: r ?? "", onValueChange: (t) => u(e.key, t), children: [
96
+ /* @__PURE__ */ n(Y, { id: `field-${e.key}`, size: "sm", className: "w-full", children: /* @__PURE__ */ n(D, { placeholder: e.placeholder ?? "Select…" }) }),
97
+ /* @__PURE__ */ n(K, { children: (e.options ?? []).map((t) => /* @__PURE__ */ n(U, { value: t.value, children: /* @__PURE__ */ o("span", { className: "inline-flex items-center gap-2", children: [
98
+ t.swatch ? /* @__PURE__ */ n(
99
+ "span",
100
+ {
101
+ className: "inline-block h-2.5 w-2.5 rounded-sm border border-foreground/20",
102
+ style: { backgroundColor: t.swatch },
103
+ "aria-hidden": !0
104
+ }
105
+ ) : null,
106
+ t.label
107
+ ] }) }, t.value)) })
108
+ ] })
109
+ ] }, e.key), v = (e, r) => {
110
+ const t = Array.isArray(r) ? r : [];
111
+ return /* @__PURE__ */ o("div", { className: "flex flex-col gap-1.5", children: [
112
+ d(e),
113
+ /* @__PURE__ */ n(
114
+ G,
115
+ {
116
+ id: `field-${e.key}`,
117
+ value: t,
118
+ options: e.options ?? [],
119
+ placeholder: e.placeholder,
120
+ onChange: (h) => u(e.key, h.length === 0 ? void 0 : h)
121
+ }
122
+ )
123
+ ] }, e.key);
124
+ }, N = (e, r) => {
125
+ const t = !!r;
126
+ return e.boolStyle === "switch" ? /* @__PURE__ */ o("div", { className: "flex items-center justify-between gap-2", children: [
127
+ d(e),
128
+ /* @__PURE__ */ n(
129
+ W,
130
+ {
131
+ id: `field-${e.key}`,
132
+ checked: t,
133
+ onCheckedChange: (h) => u(e.key, h)
134
+ }
135
+ )
136
+ ] }, e.key) : /* @__PURE__ */ o("div", { className: "flex items-center gap-2", children: [
137
+ /* @__PURE__ */ n(
138
+ S,
139
+ {
140
+ id: `field-${e.key}`,
141
+ checked: t,
142
+ onCheckedChange: (h) => u(e.key, h === !0)
143
+ }
144
+ ),
145
+ d(e)
146
+ ] }, e.key);
147
+ }, f = (e, r) => /* @__PURE__ */ o("div", { className: "flex flex-col gap-1.5", children: [
148
+ d(e),
149
+ /* @__PURE__ */ n(
150
+ L,
151
+ {
152
+ id: `field-${e.key}`,
153
+ type: J(e.kind),
154
+ step: e.kind === "integer" ? 1 : void 0,
155
+ placeholder: e.placeholder,
156
+ value: r ?? "",
157
+ onChange: (t) => u(e.key, O(e.kind, t.target.value))
158
+ }
159
+ )
160
+ ] }, e.key), w = (e) => {
161
+ const r = c[e.key];
162
+ return e.render ? C(e, r) : e.kind === "select" ? y(e, r) : e.kind === "multiselect" ? v(e, r) : e.kind === "boolean" ? N(e, r) : f(e, r);
163
+ };
164
+ return /* @__PURE__ */ o("div", { "data-slot": "well-metadata-form", className: q("flex flex-col gap-3", g), children: [
165
+ /* @__PURE__ */ n("div", { className: "text-sm font-medium", children: m > 0 ? `Apply to ${m} well${m === 1 ? "" : "s"}` : "Select wells to edit" }),
166
+ i.map(w),
167
+ s,
168
+ /* @__PURE__ */ o("div", { className: "mt-auto flex gap-2 pt-1", children: [
169
+ /* @__PURE__ */ n(x, { variant: "default", size: "sm", disabled: m === 0, onClick: k, children: b }),
170
+ /* @__PURE__ */ n(x, { variant: "outline", size: "sm", disabled: m === 0, onClick: p, children: l })
171
+ ] })
172
+ ] });
173
+ }
174
+ export {
175
+ le as WellMetadataForm
176
+ };
177
+ //# sourceMappingURL=WellMetadataForm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WellMetadataForm.js","sources":["../../../../src/components/composed/PlateMapEditor/WellMetadataForm.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport type { WellField, WellRecord, WellSelectOption } from \"./types\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Checkbox } from \"@/components/ui/checkbox\";\nimport {\n Combobox,\n ComboboxChip,\n ComboboxChips,\n ComboboxChipsInput,\n ComboboxContent,\n ComboboxEmpty,\n ComboboxItem,\n ComboboxList,\n ComboboxValue,\n useComboboxAnchor,\n} from \"@/components/ui/combobox\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"@/components/ui/select\";\nimport { Switch } from \"@/components/ui/switch\";\nimport { cn } from \"@/lib/utils\";\n\nfunction MultiSelectControl({\n id,\n value,\n options,\n placeholder,\n onChange,\n}: {\n id: string;\n value: string[];\n options: WellSelectOption[];\n placeholder?: string;\n onChange: (next: string[]) => void;\n}) {\n const anchorRef = useComboboxAnchor();\n const labelByValue = React.useMemo(() => {\n const m = new Map<string, string>();\n options.forEach((o) => m.set(o.value, o.label));\n return m;\n }, [options]);\n\n return (\n <Combobox multiple items={options.map((o) => o.value)} value={value} onValueChange={onChange}>\n <ComboboxChips ref={anchorRef} className=\"min-h-9 w-full\">\n <ComboboxValue>\n {(items: string[]) =>\n items.map((item) => <ComboboxChip key={item}>{labelByValue.get(item) ?? item}</ComboboxChip>)\n }\n </ComboboxValue>\n <ComboboxChipsInput id={id} placeholder={placeholder ?? \"Select…\"} />\n </ComboboxChips>\n <ComboboxContent anchor={anchorRef}>\n <ComboboxEmpty>No options.</ComboboxEmpty>\n <ComboboxList>\n {(item: string) => (\n <ComboboxItem key={item} value={item}>\n <span className=\"inline-flex items-center gap-2\">\n {(() => {\n const opt = options.find((o) => o.value === item);\n return opt?.swatch ? (\n <span\n className=\"inline-block h-2.5 w-2.5 rounded-sm border border-foreground/20\"\n style={{ backgroundColor: opt.swatch }}\n aria-hidden\n />\n ) : null;\n })()}\n {labelByValue.get(item) ?? item}\n </span>\n </ComboboxItem>\n )}\n </ComboboxList>\n </ComboboxContent>\n </Combobox>\n );\n}\n\nexport interface WellMetadataFormProps<T extends WellRecord = WellRecord> {\n fields: WellField<T>[];\n /** Staged values used to apply across the selection. */\n value: Partial<T>;\n onChange: (next: Partial<T>) => void;\n selectionSize: number;\n onApply: () => void;\n onClear: () => void;\n applyLabel?: string;\n clearLabel?: string;\n /** Optional extra slot rendered between fields and action row. */\n extras?: React.ReactNode;\n className?: string;\n}\n\nconst INPUT_TYPE_BY_KIND: Record<string, string> = {\n number: \"number\",\n integer: \"number\",\n date: \"date\",\n datetime: \"datetime-local\",\n time: \"time\",\n};\n\nfunction resolveInputType(kind: string): string {\n return INPUT_TYPE_BY_KIND[kind] ?? \"text\";\n}\n\nfunction parseInputValue(kind: string, raw: string): unknown {\n if (raw === \"\") return undefined;\n if (kind === \"number\") {\n const num = parseFloat(raw);\n return Number.isFinite(num) ? num : raw;\n }\n if (kind === \"integer\") {\n const num = parseInt(raw, 10);\n return Number.isFinite(num) ? num : raw;\n }\n return raw;\n}\n\nexport function WellMetadataForm<T extends WellRecord = WellRecord>({\n fields,\n value,\n onChange,\n selectionSize,\n onApply,\n onClear,\n applyLabel = \"Apply\",\n clearLabel = \"Clear wells\",\n extras,\n className,\n}: WellMetadataFormProps<T>) {\n const setField = (key: keyof T & string, next: unknown) => {\n onChange({ ...value, [key]: next } as Partial<T>);\n };\n\n const renderLabel = (field: WellField<T>) => (\n <Label htmlFor={`field-${field.key}`} className=\"gap-1.5\">\n {field.icon ? <span className=\"inline-flex text-muted-foreground [&_svg]:size-3.5\">{field.icon}</span> : null}\n {field.label}\n </Label>\n );\n\n const renderCustom = (field: WellField<T>, fieldValue: unknown) => (\n <div key={field.key} className=\"flex flex-col gap-1.5\">\n {renderLabel(field)}\n {field.render!({\n value: fieldValue,\n onChange: (next) => setField(field.key, next),\n selectionSize,\n })}\n </div>\n );\n\n const renderSelect = (field: WellField<T>, fieldValue: unknown) => (\n <div key={field.key} className=\"flex flex-col gap-1.5\">\n {renderLabel(field)}\n <Select value={(fieldValue as string | undefined) ?? \"\"} onValueChange={(v) => setField(field.key, v)}>\n <SelectTrigger id={`field-${field.key}`} size=\"sm\" className=\"w-full\">\n <SelectValue placeholder={field.placeholder ?? \"Select…\"} />\n </SelectTrigger>\n <SelectContent>\n {(field.options ?? []).map((opt) => (\n <SelectItem key={opt.value} value={opt.value}>\n <span className=\"inline-flex items-center gap-2\">\n {opt.swatch ? (\n <span\n className=\"inline-block h-2.5 w-2.5 rounded-sm border border-foreground/20\"\n style={{ backgroundColor: opt.swatch }}\n aria-hidden\n />\n ) : null}\n {opt.label}\n </span>\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n );\n\n const renderMultiSelect = (field: WellField<T>, fieldValue: unknown) => {\n const current = Array.isArray(fieldValue) ? (fieldValue as string[]) : [];\n return (\n <div key={field.key} className=\"flex flex-col gap-1.5\">\n {renderLabel(field)}\n <MultiSelectControl\n id={`field-${field.key}`}\n value={current}\n options={field.options ?? []}\n placeholder={field.placeholder}\n onChange={(next) => setField(field.key, next.length === 0 ? undefined : next)}\n />\n </div>\n );\n };\n\n const renderBoolean = (field: WellField<T>, fieldValue: unknown) => {\n const checked = !!fieldValue;\n if (field.boolStyle === \"switch\") {\n return (\n <div key={field.key} className=\"flex items-center justify-between gap-2\">\n {renderLabel(field)}\n <Switch\n id={`field-${field.key}`}\n checked={checked}\n onCheckedChange={(c) => setField(field.key, c)}\n />\n </div>\n );\n }\n return (\n <div key={field.key} className=\"flex items-center gap-2\">\n <Checkbox\n id={`field-${field.key}`}\n checked={checked}\n onCheckedChange={(c) => setField(field.key, c === true)}\n />\n {renderLabel(field)}\n </div>\n );\n };\n\n const renderInput = (field: WellField<T>, fieldValue: unknown) => (\n <div key={field.key} className=\"flex flex-col gap-1.5\">\n {renderLabel(field)}\n <Input\n id={`field-${field.key}`}\n type={resolveInputType(field.kind)}\n step={field.kind === \"integer\" ? 1 : undefined}\n placeholder={field.placeholder}\n value={(fieldValue as string | number | undefined) ?? \"\"}\n onChange={(e) => setField(field.key, parseInputValue(field.kind, e.target.value))}\n />\n </div>\n );\n\n const renderField = (field: WellField<T>) => {\n const fieldValue = value[field.key];\n if (field.render) return renderCustom(field, fieldValue);\n if (field.kind === \"select\") return renderSelect(field, fieldValue);\n if (field.kind === \"multiselect\") return renderMultiSelect(field, fieldValue);\n if (field.kind === \"boolean\") return renderBoolean(field, fieldValue);\n return renderInput(field, fieldValue);\n };\n\n return (\n <div data-slot=\"well-metadata-form\" className={cn(\"flex flex-col gap-3\", className)}>\n <div className=\"text-sm font-medium\">\n {selectionSize > 0 ? `Apply to ${selectionSize} well${selectionSize === 1 ? \"\" : \"s\"}` : \"Select wells to edit\"}\n </div>\n\n {fields.map(renderField)}\n\n {extras}\n\n <div className=\"mt-auto flex gap-2 pt-1\">\n <Button variant=\"default\" size=\"sm\" disabled={selectionSize === 0} onClick={onApply}>\n {applyLabel}\n </Button>\n <Button variant=\"outline\" size=\"sm\" disabled={selectionSize === 0} onClick={onClear}>\n {clearLabel}\n </Button>\n </div>\n </div>\n );\n}\n"],"names":["MultiSelectControl","id","value","options","placeholder","onChange","anchorRef","useComboboxAnchor","labelByValue","React","m","o","jsxs","Combobox","ComboboxChips","jsx","ComboboxValue","items","item","ComboboxChip","ComboboxChipsInput","ComboboxContent","ComboboxEmpty","ComboboxList","ComboboxItem","opt","INPUT_TYPE_BY_KIND","resolveInputType","kind","parseInputValue","raw","num","WellMetadataForm","fields","selectionSize","onApply","onClear","applyLabel","clearLabel","extras","className","setField","key","next","renderLabel","field","Label","renderCustom","fieldValue","renderSelect","Select","v","SelectTrigger","SelectValue","SelectContent","SelectItem","renderMultiSelect","current","renderBoolean","checked","Switch","c","Checkbox","renderInput","Input","e","renderField","cn","Button"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("./wellGrid.cjs");function p(s,n,o){const{rows:c,columns:t}=s;if(n==="row-major")return f.pos(Math.floor(o/t),o%t,t);if(n==="column-major")return f.pos(o%c,Math.floor(o/c),t);if(n==="row-snake"){const i=Math.floor(o/t),e=o%t,l=i%2===0?e:t-1-e;return f.pos(i,l,t)}const r=Math.floor(o/c),u=o%c,a=r%2===0?u:c-1-u;return f.pos(a,r,t)}function m(s,n,o){if(!o||!f.parsePos(o,s))return 0;const t=s.rows*s.columns;for(let r=0;r<t;r++)if(p(s,n,r)===o)return r;return 0}function h(s){const{dims:n,count:o,startWellId:c,strategy:t="row-major",replicates:r=1}=s;if(o<=0||r<=0)return[];const u=n.rows*n.columns,a=m(n,t,c),i=Math.min(u,a+o*r),e=[];for(let l=a;l<i;l++)e.push(p(n,t,l));return e}function M(s,n,o){const c=h({...o,count:s.length}),t=o.replicates??1,r=new Map;return c.forEach((u,a)=>{const i=Math.floor(a/t),e=s[i];e!==void 0&&r.set(u,n(e,i,u))}),r}exports.autoFillPositions=h;exports.autoFillRecords=M;
2
+ //# sourceMappingURL=autoFill.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoFill.cjs","sources":["../../../../src/components/composed/PlateMapEditor/autoFill.ts"],"sourcesContent":["import { parsePos, pos } from \"./wellGrid\";\n\nimport type { PlateDimensions, WellId } from \"./types\";\n\nexport type FillStrategy = \"row-major\" | \"column-major\" | \"row-snake\" | \"column-snake\";\n\nexport interface AutoFillOptions {\n dims: PlateDimensions;\n /** Number of distinct items to place. Each is repeated `replicates` times. */\n count: number;\n /** First well in fill order. Defaults to position (0,0). */\n startWellId?: WellId;\n /** Traversal strategy. Defaults to `\"row-major\"`. */\n strategy?: FillStrategy;\n /** Consecutive cells assigned per item. Defaults to 1. */\n replicates?: number;\n}\n\nfunction positionAtIndex(dims: PlateDimensions, strategy: FillStrategy, index: number): WellId {\n const { rows, columns } = dims;\n if (strategy === \"row-major\") {\n return pos(Math.floor(index / columns), index % columns, columns);\n }\n if (strategy === \"column-major\") {\n return pos(index % rows, Math.floor(index / rows), columns);\n }\n if (strategy === \"row-snake\") {\n const r = Math.floor(index / columns);\n const i = index % columns;\n const c = r % 2 === 0 ? i : columns - 1 - i;\n return pos(r, c, columns);\n }\n const c = Math.floor(index / rows);\n const i = index % rows;\n const r = c % 2 === 0 ? i : rows - 1 - i;\n return pos(r, c, columns);\n}\n\nfunction findStartIndex(dims: PlateDimensions, strategy: FillStrategy, startWellId: WellId | undefined): number {\n if (!startWellId) return 0;\n const parsed = parsePos(startWellId, dims);\n if (!parsed) return 0;\n const total = dims.rows * dims.columns;\n for (let i = 0; i < total; i++) {\n if (positionAtIndex(dims, strategy, i) === startWellId) return i;\n }\n return 0;\n}\n\n/**\n * Compute the ordered list of well ids to assign to `count` items (each\n * repeated `replicates` times) starting from `startWellId`. Returns at most\n * `dims.rows * dims.columns` ids — overflow is silently truncated so callers\n * can paginate onto multiple plates themselves.\n */\nexport function autoFillPositions(options: AutoFillOptions): WellId[] {\n const { dims, count, startWellId, strategy = \"row-major\", replicates = 1 } = options;\n if (count <= 0 || replicates <= 0) return [];\n\n const total = dims.rows * dims.columns;\n const startIdx = findStartIndex(dims, strategy, startWellId);\n const end = Math.min(total, startIdx + count * replicates);\n const out: WellId[] = [];\n for (let i = startIdx; i < end; i++) {\n out.push(positionAtIndex(dims, strategy, i));\n }\n return out;\n}\n\n/**\n * Convenience wrapper: assigns each input item to `replicates` consecutive\n * wells, returning a `Map<WellId, T>`. The mapper builds the record stored at\n * each well (e.g. stamp item-specific fields onto a base record).\n */\nexport function autoFillRecords<T>(\n items: T[],\n buildRecord: (item: T, index: number, wellId: WellId) => T,\n options: Omit<AutoFillOptions, \"count\">,\n): Map<WellId, T> {\n const positions = autoFillPositions({ ...options, count: items.length });\n const replicates = options.replicates ?? 1;\n const next = new Map<WellId, T>();\n positions.forEach((wellId, idx) => {\n const itemIndex = Math.floor(idx / replicates);\n const item = items[itemIndex];\n if (item === undefined) return;\n next.set(wellId, buildRecord(item, itemIndex, wellId));\n });\n return next;\n}\n"],"names":["positionAtIndex","dims","strategy","index","rows","columns","pos","r","i","c","findStartIndex","startWellId","parsePos","total","autoFillPositions","options","count","replicates","startIdx","end","out","autoFillRecords","items","buildRecord","positions","next","wellId","idx","itemIndex","item"],"mappings":"kHAkBA,SAASA,EAAgBC,EAAuBC,EAAwBC,EAAuB,CAC7F,KAAM,CAAE,KAAAC,EAAM,QAAAC,CAAA,EAAYJ,EAC1B,GAAIC,IAAa,YACf,OAAOI,EAAAA,IAAI,KAAK,MAAMH,EAAQE,CAAO,EAAGF,EAAQE,EAASA,CAAO,EAElE,GAAIH,IAAa,eACf,OAAOI,EAAAA,IAAIH,EAAQC,EAAM,KAAK,MAAMD,EAAQC,CAAI,EAAGC,CAAO,EAE5D,GAAIH,IAAa,YAAa,CAC5B,MAAMK,EAAI,KAAK,MAAMJ,EAAQE,CAAO,EAC9BG,EAAIL,EAAQE,EACZI,EAAIF,EAAI,IAAM,EAAIC,EAAIH,EAAU,EAAIG,EAC1C,OAAOF,MAAIC,EAAGE,EAAGJ,CAAO,CAC1B,CACA,MAAMI,EAAI,KAAK,MAAMN,EAAQC,CAAI,EAC3BI,EAAIL,EAAQC,EACZG,EAAIE,EAAI,IAAM,EAAID,EAAIJ,EAAO,EAAII,EACvC,OAAOF,MAAIC,EAAGE,EAAGJ,CAAO,CAC1B,CAEA,SAASK,EAAeT,EAAuBC,EAAwBS,EAAyC,CAG9G,GAFI,CAACA,GAED,CADWC,EAAAA,SAASD,EAAaV,CAAI,EAC5B,MAAO,GACpB,MAAMY,EAAQZ,EAAK,KAAOA,EAAK,QAC/B,QAASO,EAAI,EAAGA,EAAIK,EAAOL,IACzB,GAAIR,EAAgBC,EAAMC,EAAUM,CAAC,IAAMG,EAAa,OAAOH,EAEjE,MAAO,EACT,CAQO,SAASM,EAAkBC,EAAoC,CACpE,KAAM,CAAE,KAAAd,EAAM,MAAAe,EAAO,YAAAL,EAAa,SAAAT,EAAW,YAAa,WAAAe,EAAa,GAAMF,EAC7E,GAAIC,GAAS,GAAKC,GAAc,QAAU,CAAA,EAE1C,MAAMJ,EAAQZ,EAAK,KAAOA,EAAK,QACzBiB,EAAWR,EAAeT,EAAMC,EAAUS,CAAW,EACrDQ,EAAM,KAAK,IAAIN,EAAOK,EAAWF,EAAQC,CAAU,EACnDG,EAAgB,CAAA,EACtB,QAASZ,EAAIU,EAAUV,EAAIW,EAAKX,IAC9BY,EAAI,KAAKpB,EAAgBC,EAAMC,EAAUM,CAAC,CAAC,EAE7C,OAAOY,CACT,CAOO,SAASC,EACdC,EACAC,EACAR,EACgB,CAChB,MAAMS,EAAYV,EAAkB,CAAE,GAAGC,EAAS,MAAOO,EAAM,OAAQ,EACjEL,EAAaF,EAAQ,YAAc,EACnCU,MAAW,IACjB,OAAAD,EAAU,QAAQ,CAACE,EAAQC,IAAQ,CACjC,MAAMC,EAAY,KAAK,MAAMD,EAAMV,CAAU,EACvCY,EAAOP,EAAMM,CAAS,EACxBC,IAAS,QACbJ,EAAK,IAAIC,EAAQH,EAAYM,EAAMD,EAAWF,CAAM,CAAC,CACvD,CAAC,EACMD,CACT"}
@@ -0,0 +1,41 @@
1
+ import { parsePos as h, pos as l } from "./wellGrid.js";
2
+ function p(s, n, o) {
3
+ const { rows: c, columns: t } = s;
4
+ if (n === "row-major")
5
+ return l(Math.floor(o / t), o % t, t);
6
+ if (n === "column-major")
7
+ return l(o % c, Math.floor(o / c), t);
8
+ if (n === "row-snake") {
9
+ const a = Math.floor(o / t), u = o % t, i = a % 2 === 0 ? u : t - 1 - u;
10
+ return l(a, i, t);
11
+ }
12
+ const r = Math.floor(o / c), e = o % c, f = r % 2 === 0 ? e : c - 1 - e;
13
+ return l(f, r, t);
14
+ }
15
+ function m(s, n, o) {
16
+ if (!o || !h(o, s)) return 0;
17
+ const t = s.rows * s.columns;
18
+ for (let r = 0; r < t; r++)
19
+ if (p(s, n, r) === o) return r;
20
+ return 0;
21
+ }
22
+ function M(s) {
23
+ const { dims: n, count: o, startWellId: c, strategy: t = "row-major", replicates: r = 1 } = s;
24
+ if (o <= 0 || r <= 0) return [];
25
+ const e = n.rows * n.columns, f = m(n, t, c), a = Math.min(e, f + o * r), u = [];
26
+ for (let i = f; i < a; i++)
27
+ u.push(p(n, t, i));
28
+ return u;
29
+ }
30
+ function j(s, n, o) {
31
+ const c = M({ ...o, count: s.length }), t = o.replicates ?? 1, r = /* @__PURE__ */ new Map();
32
+ return c.forEach((e, f) => {
33
+ const a = Math.floor(f / t), u = s[a];
34
+ u !== void 0 && r.set(e, n(u, a, e));
35
+ }), r;
36
+ }
37
+ export {
38
+ M as autoFillPositions,
39
+ j as autoFillRecords
40
+ };
41
+ //# sourceMappingURL=autoFill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoFill.js","sources":["../../../../src/components/composed/PlateMapEditor/autoFill.ts"],"sourcesContent":["import { parsePos, pos } from \"./wellGrid\";\n\nimport type { PlateDimensions, WellId } from \"./types\";\n\nexport type FillStrategy = \"row-major\" | \"column-major\" | \"row-snake\" | \"column-snake\";\n\nexport interface AutoFillOptions {\n dims: PlateDimensions;\n /** Number of distinct items to place. Each is repeated `replicates` times. */\n count: number;\n /** First well in fill order. Defaults to position (0,0). */\n startWellId?: WellId;\n /** Traversal strategy. Defaults to `\"row-major\"`. */\n strategy?: FillStrategy;\n /** Consecutive cells assigned per item. Defaults to 1. */\n replicates?: number;\n}\n\nfunction positionAtIndex(dims: PlateDimensions, strategy: FillStrategy, index: number): WellId {\n const { rows, columns } = dims;\n if (strategy === \"row-major\") {\n return pos(Math.floor(index / columns), index % columns, columns);\n }\n if (strategy === \"column-major\") {\n return pos(index % rows, Math.floor(index / rows), columns);\n }\n if (strategy === \"row-snake\") {\n const r = Math.floor(index / columns);\n const i = index % columns;\n const c = r % 2 === 0 ? i : columns - 1 - i;\n return pos(r, c, columns);\n }\n const c = Math.floor(index / rows);\n const i = index % rows;\n const r = c % 2 === 0 ? i : rows - 1 - i;\n return pos(r, c, columns);\n}\n\nfunction findStartIndex(dims: PlateDimensions, strategy: FillStrategy, startWellId: WellId | undefined): number {\n if (!startWellId) return 0;\n const parsed = parsePos(startWellId, dims);\n if (!parsed) return 0;\n const total = dims.rows * dims.columns;\n for (let i = 0; i < total; i++) {\n if (positionAtIndex(dims, strategy, i) === startWellId) return i;\n }\n return 0;\n}\n\n/**\n * Compute the ordered list of well ids to assign to `count` items (each\n * repeated `replicates` times) starting from `startWellId`. Returns at most\n * `dims.rows * dims.columns` ids — overflow is silently truncated so callers\n * can paginate onto multiple plates themselves.\n */\nexport function autoFillPositions(options: AutoFillOptions): WellId[] {\n const { dims, count, startWellId, strategy = \"row-major\", replicates = 1 } = options;\n if (count <= 0 || replicates <= 0) return [];\n\n const total = dims.rows * dims.columns;\n const startIdx = findStartIndex(dims, strategy, startWellId);\n const end = Math.min(total, startIdx + count * replicates);\n const out: WellId[] = [];\n for (let i = startIdx; i < end; i++) {\n out.push(positionAtIndex(dims, strategy, i));\n }\n return out;\n}\n\n/**\n * Convenience wrapper: assigns each input item to `replicates` consecutive\n * wells, returning a `Map<WellId, T>`. The mapper builds the record stored at\n * each well (e.g. stamp item-specific fields onto a base record).\n */\nexport function autoFillRecords<T>(\n items: T[],\n buildRecord: (item: T, index: number, wellId: WellId) => T,\n options: Omit<AutoFillOptions, \"count\">,\n): Map<WellId, T> {\n const positions = autoFillPositions({ ...options, count: items.length });\n const replicates = options.replicates ?? 1;\n const next = new Map<WellId, T>();\n positions.forEach((wellId, idx) => {\n const itemIndex = Math.floor(idx / replicates);\n const item = items[itemIndex];\n if (item === undefined) return;\n next.set(wellId, buildRecord(item, itemIndex, wellId));\n });\n return next;\n}\n"],"names":["positionAtIndex","dims","strategy","index","rows","columns","pos","r","i","c","findStartIndex","startWellId","parsePos","total","autoFillPositions","options","count","replicates","startIdx","end","out","autoFillRecords","items","buildRecord","positions","next","wellId","idx","itemIndex","item"],"mappings":";AAkBA,SAASA,EAAgBC,GAAuBC,GAAwBC,GAAuB;AAC7F,QAAM,EAAE,MAAAC,GAAM,SAAAC,EAAA,IAAYJ;AAC1B,MAAIC,MAAa;AACf,WAAOI,EAAI,KAAK,MAAMH,IAAQE,CAAO,GAAGF,IAAQE,GAASA,CAAO;AAElE,MAAIH,MAAa;AACf,WAAOI,EAAIH,IAAQC,GAAM,KAAK,MAAMD,IAAQC,CAAI,GAAGC,CAAO;AAE5D,MAAIH,MAAa,aAAa;AAC5B,UAAMK,IAAI,KAAK,MAAMJ,IAAQE,CAAO,GAC9BG,IAAIL,IAAQE,GACZI,IAAIF,IAAI,MAAM,IAAIC,IAAIH,IAAU,IAAIG;AAC1C,WAAOF,EAAIC,GAAGE,GAAGJ,CAAO;AAAA,EAC1B;AACA,QAAMI,IAAI,KAAK,MAAMN,IAAQC,CAAI,GAC3BI,IAAIL,IAAQC,GACZG,IAAIE,IAAI,MAAM,IAAID,IAAIJ,IAAO,IAAII;AACvC,SAAOF,EAAIC,GAAGE,GAAGJ,CAAO;AAC1B;AAEA,SAASK,EAAeT,GAAuBC,GAAwBS,GAAyC;AAG9G,MAFI,CAACA,KAED,CADWC,EAASD,GAAaV,CAAI,EAC5B,QAAO;AACpB,QAAMY,IAAQZ,EAAK,OAAOA,EAAK;AAC/B,WAASO,IAAI,GAAGA,IAAIK,GAAOL;AACzB,QAAIR,EAAgBC,GAAMC,GAAUM,CAAC,MAAMG,EAAa,QAAOH;AAEjE,SAAO;AACT;AAQO,SAASM,EAAkBC,GAAoC;AACpE,QAAM,EAAE,MAAAd,GAAM,OAAAe,GAAO,aAAAL,GAAa,UAAAT,IAAW,aAAa,YAAAe,IAAa,MAAMF;AAC7E,MAAIC,KAAS,KAAKC,KAAc,UAAU,CAAA;AAE1C,QAAMJ,IAAQZ,EAAK,OAAOA,EAAK,SACzBiB,IAAWR,EAAeT,GAAMC,GAAUS,CAAW,GACrDQ,IAAM,KAAK,IAAIN,GAAOK,IAAWF,IAAQC,CAAU,GACnDG,IAAgB,CAAA;AACtB,WAAS,IAAIF,GAAU,IAAIC,GAAK;AAC9B,IAAAC,EAAI,KAAKpB,EAAgBC,GAAMC,GAAU,CAAC,CAAC;AAE7C,SAAOkB;AACT;AAOO,SAASC,EACdC,GACAC,GACAR,GACgB;AAChB,QAAMS,IAAYV,EAAkB,EAAE,GAAGC,GAAS,OAAOO,EAAM,QAAQ,GACjEL,IAAaF,EAAQ,cAAc,GACnCU,wBAAW,IAAA;AACjB,SAAAD,EAAU,QAAQ,CAACE,GAAQC,MAAQ;AACjC,UAAMC,IAAY,KAAK,MAAMD,IAAMV,CAAU,GACvCY,IAAOP,EAAMM,CAAS;AAC5B,IAAIC,MAAS,UACbJ,EAAK,IAAIC,GAAQH,EAAYM,GAAMD,GAAWF,CAAM,CAAC;AAAA,EACvD,CAAC,GACMD;AACT;"}
@@ -0,0 +1,4 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const C=["plate barcode","plate_barcode","plate-barcode","plateBarcode","plate id","plate_id","plateId"],v=new Set(["text/csv","application/vnd.ms-excel"]);function u(r){return r.trim().toLowerCase().replace(/[^a-z0-9]/g,"")}function p(r,e){return[...r,e]}function d(r,e,o){const t=p(e,o);return t.some(s=>s.trim()!=="")?{records:[...r,t],record:[]}:{records:r,record:[]}}function g(r,e,o){const t=r[e];return t!=='"'?{field:`${o}${t}`,quoted:!0,index:e}:r[e+1]==='"'?{field:`${o}"`,quoted:!0,index:e+1}:{field:o,quoted:!1,index:e}}function h(r){let e=[],o=[],t="",s=!1;for(let n=0;n<r.length;n+=1){const a=r[n];if(s){const c=g(r,n,t);t=c.field,s=c.quoted,n=c.index}else if(a==='"'&&t==="")s=!0;else if(a===",")o=p(o,t),t="";else if(a===`
2
+ `||a==="\r"){const c=d(e,o,t);e=c.records,o=c.record,t="",a==="\r"&&r[n+1]===`
3
+ `&&(n+=1)}else t=`${t}${a}`}return(t!==""||o.length>0)&&(e=d(e,o,t).records),e}function w(r,e){const o=[e?.plateBarcodeColumn,...e?.plateBarcodeColumnAliases??[],...C].filter(t=>!!t).map(u);return r.find(t=>o.includes(u(t)))}function B(r,e,o){const t=r.reduce((s,n,a)=>(s[n]=e[a]??"",s),{});return{line:o+2,values:t}}function M(r,e){const o=new Map,t=[];return r.forEach(s=>{const n=e?s.values[e]?.trim():"";if(!n){t.push(s);return}const a=o.get(n)??[];o.set(n,[...a,s])}),{plateRows:o,missingBarcodeRows:t}}function f(r,e){const o=h(r),t=o[0]?.map(i=>i.trim())??[],s=w(t,e),n=o.slice(1).map((i,l)=>B(t,i,l)),{plateRows:a,missingBarcodeRows:c}=M(n,s),m=[...a.entries()].map(([i,l])=>({id:i,barcode:i,rows:l,rowCount:l.length}));return{headers:t,plateBarcodeColumn:s,rows:n,plates:m,missingBarcodeRows:c}}function R(r){return r.plates.map(e=>({id:e.id,barcode:e.barcode,count:e.rowCount}))}async function P(r,e){if(r.name.toLowerCase().endsWith(".csv")||v.has(r.type))return f(await r.text(),e)}exports.plateOptionsFromCsvTriage=R;exports.triagePlateMapCsvByBarcode=f;exports.triagePlateMapCsvFile=P;
4
+ //# sourceMappingURL=csvPlateTriage.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csvPlateTriage.cjs","sources":["../../../../src/components/composed/PlateMapEditor/csvPlateTriage.ts"],"sourcesContent":["import type {\n PlateMapCsvPlate,\n PlateMapCsvRow,\n PlateMapCsvTriage,\n PlateMapCsvTriageOptions,\n PlateMapPlateOption,\n} from \"./types\";\n\nconst DEFAULT_PLATE_BARCODE_COLUMNS = [\n \"plate barcode\",\n \"plate_barcode\",\n \"plate-barcode\",\n \"plateBarcode\",\n \"plate id\",\n \"plate_id\",\n \"plateId\",\n];\n\nconst CSV_MIME_TYPES = new Set([\"text/csv\", \"application/vnd.ms-excel\"]);\n\nfunction normalizeColumnName(value: string): string {\n return value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]/g, \"\");\n}\n\nfunction pushCsvField(record: string[], field: string): string[] {\n return [...record, field];\n}\n\nfunction pushCsvRecord(\n records: string[][],\n record: string[],\n field: string,\n): { records: string[][]; record: string[] } {\n const nextRecord = pushCsvField(record, field);\n if (nextRecord.some((cell) => cell.trim() !== \"\")) {\n return { records: [...records, nextRecord], record: [] };\n }\n return { records, record: [] };\n}\n\nfunction readQuotedCsvCharacter(text: string, index: number, field: string) {\n const char = text[index];\n if (char !== '\"') return { field: `${field}${char}`, quoted: true, index };\n if (text[index + 1] === '\"') return { field: `${field}\"`, quoted: true, index: index + 1 };\n return { field, quoted: false, index };\n}\n\nfunction parseCsvRecords(text: string): string[][] {\n let records: string[][] = [];\n let record: string[] = [];\n let field = \"\";\n let quoted = false;\n\n for (let index = 0; index < text.length; index += 1) {\n const char = text[index];\n\n if (quoted) {\n const next = readQuotedCsvCharacter(text, index, field);\n field = next.field;\n quoted = next.quoted;\n index = next.index;\n } else if (char === '\"' && field === \"\") {\n quoted = true;\n } else if (char === \",\") {\n record = pushCsvField(record, field);\n field = \"\";\n } else if (char === \"\\n\" || char === \"\\r\") {\n const next = pushCsvRecord(records, record, field);\n records = next.records;\n record = next.record;\n field = \"\";\n if (char === \"\\r\" && text[index + 1] === \"\\n\") index += 1;\n } else {\n field = `${field}${char}`;\n }\n }\n\n if (field !== \"\" || record.length > 0) {\n records = pushCsvRecord(records, record, field).records;\n }\n\n return records;\n}\n\nfunction resolvePlateBarcodeColumn(headers: string[], options?: PlateMapCsvTriageOptions): string | undefined {\n const candidates = [\n options?.plateBarcodeColumn,\n ...(options?.plateBarcodeColumnAliases ?? []),\n ...DEFAULT_PLATE_BARCODE_COLUMNS,\n ]\n .filter((candidate): candidate is string => !!candidate)\n .map(normalizeColumnName);\n\n return headers.find((header) => candidates.includes(normalizeColumnName(header)));\n}\n\nfunction recordToRow(headers: string[], record: string[], recordIndex: number): PlateMapCsvRow {\n const values = headers.reduce<Record<string, string>>((acc, header, headerIndex) => {\n acc[header] = record[headerIndex] ?? \"\";\n return acc;\n }, {});\n\n return {\n line: recordIndex + 2,\n values,\n };\n}\n\nfunction groupRowsByBarcode(rows: PlateMapCsvRow[], plateBarcodeColumn: string | undefined) {\n const plateRows = new Map<string, PlateMapCsvRow[]>();\n const missingBarcodeRows: PlateMapCsvRow[] = [];\n\n rows.forEach((row) => {\n const barcode = plateBarcodeColumn ? row.values[plateBarcodeColumn]?.trim() : \"\";\n if (!barcode) {\n missingBarcodeRows.push(row);\n return;\n }\n\n const rowsForBarcode = plateRows.get(barcode) ?? [];\n plateRows.set(barcode, [...rowsForBarcode, row]);\n });\n\n return { plateRows, missingBarcodeRows };\n}\n\nexport function triagePlateMapCsvByBarcode(text: string, options?: PlateMapCsvTriageOptions): PlateMapCsvTriage {\n const records = parseCsvRecords(text);\n const headers = records[0]?.map((header) => header.trim()) ?? [];\n const plateBarcodeColumn = resolvePlateBarcodeColumn(headers, options);\n const rows = records.slice(1).map((record, recordIndex) => recordToRow(headers, record, recordIndex));\n const { plateRows, missingBarcodeRows } = groupRowsByBarcode(rows, plateBarcodeColumn);\n const plates: PlateMapCsvPlate[] = [...plateRows.entries()].map(([barcode, plateRowsForBarcode]) => ({\n id: barcode,\n barcode,\n rows: plateRowsForBarcode,\n rowCount: plateRowsForBarcode.length,\n }));\n\n return {\n headers,\n plateBarcodeColumn,\n rows,\n plates,\n missingBarcodeRows,\n };\n}\n\nexport function plateOptionsFromCsvTriage(triage: PlateMapCsvTriage): PlateMapPlateOption[] {\n return triage.plates.map((plate) => ({\n id: plate.id,\n barcode: plate.barcode,\n count: plate.rowCount,\n }));\n}\n\nexport async function triagePlateMapCsvFile(\n file: File,\n options?: PlateMapCsvTriageOptions,\n): Promise<PlateMapCsvTriage | undefined> {\n const fileName = file.name.toLowerCase();\n const isCsv = fileName.endsWith(\".csv\") || CSV_MIME_TYPES.has(file.type);\n if (!isCsv) return undefined;\n\n return triagePlateMapCsvByBarcode(await file.text(), options);\n}\n"],"names":["DEFAULT_PLATE_BARCODE_COLUMNS","CSV_MIME_TYPES","normalizeColumnName","value","pushCsvField","record","field","pushCsvRecord","records","nextRecord","cell","readQuotedCsvCharacter","text","index","char","parseCsvRecords","quoted","next","resolvePlateBarcodeColumn","headers","options","candidates","candidate","header","recordToRow","recordIndex","values","acc","headerIndex","groupRowsByBarcode","rows","plateBarcodeColumn","plateRows","missingBarcodeRows","row","barcode","rowsForBarcode","triagePlateMapCsvByBarcode","plates","plateRowsForBarcode","plateOptionsFromCsvTriage","triage","plate","triagePlateMapCsvFile","file"],"mappings":"gFAQA,MAAMA,EAAgC,CACpC,gBACA,gBACA,gBACA,eACA,WACA,WACA,SACF,EAEMC,EAAiB,IAAI,IAAI,CAAC,WAAY,0BAA0B,CAAC,EAEvE,SAASC,EAAoBC,EAAuB,CAClD,OAAOA,EACJ,OACA,cACA,QAAQ,aAAc,EAAE,CAC7B,CAEA,SAASC,EAAaC,EAAkBC,EAAyB,CAC/D,MAAO,CAAC,GAAGD,EAAQC,CAAK,CAC1B,CAEA,SAASC,EACPC,EACAH,EACAC,EAC2C,CAC3C,MAAMG,EAAaL,EAAaC,EAAQC,CAAK,EAC7C,OAAIG,EAAW,KAAMC,GAASA,EAAK,KAAA,IAAW,EAAE,EACvC,CAAE,QAAS,CAAC,GAAGF,EAASC,CAAU,EAAG,OAAQ,EAAC,EAEhD,CAAE,QAAAD,EAAS,OAAQ,EAAC,CAC7B,CAEA,SAASG,EAAuBC,EAAcC,EAAeP,EAAe,CAC1E,MAAMQ,EAAOF,EAAKC,CAAK,EACvB,OAAIC,IAAS,IAAY,CAAE,MAAO,GAAGR,CAAK,GAAGQ,CAAI,GAAI,OAAQ,GAAM,MAAAD,CAAA,EAC/DD,EAAKC,EAAQ,CAAC,IAAM,IAAY,CAAE,MAAO,GAAGP,CAAK,IAAK,OAAQ,GAAM,MAAOO,EAAQ,CAAA,EAChF,CAAE,MAAAP,EAAO,OAAQ,GAAO,MAAAO,CAAA,CACjC,CAEA,SAASE,EAAgBH,EAA0B,CACjD,IAAIJ,EAAsB,CAAA,EACtBH,EAAmB,CAAA,EACnBC,EAAQ,GACRU,EAAS,GAEb,QAASH,EAAQ,EAAGA,EAAQD,EAAK,OAAQC,GAAS,EAAG,CACnD,MAAMC,EAAOF,EAAKC,CAAK,EAEvB,GAAIG,EAAQ,CACV,MAAMC,EAAON,EAAuBC,EAAMC,EAAOP,CAAK,EACtDA,EAAQW,EAAK,MACbD,EAASC,EAAK,OACdJ,EAAQI,EAAK,KACf,SAAWH,IAAS,KAAOR,IAAU,GACnCU,EAAS,WACAF,IAAS,IAClBT,EAASD,EAAaC,EAAQC,CAAK,EACnCA,EAAQ,WACCQ,IAAS;AAAA,GAAQA,IAAS,KAAM,CACzC,MAAMG,EAAOV,EAAcC,EAASH,EAAQC,CAAK,EACjDE,EAAUS,EAAK,QACfZ,EAASY,EAAK,OACdX,EAAQ,GACJQ,IAAS,MAAQF,EAAKC,EAAQ,CAAC,IAAM;AAAA,IAAMA,GAAS,EAC1D,MACEP,EAAQ,GAAGA,CAAK,GAAGQ,CAAI,EAE3B,CAEA,OAAIR,IAAU,IAAMD,EAAO,OAAS,KAClCG,EAAUD,EAAcC,EAASH,EAAQC,CAAK,EAAE,SAG3CE,CACT,CAEA,SAASU,EAA0BC,EAAmBC,EAAwD,CAC5G,MAAMC,EAAa,CACjBD,GAAS,mBACT,GAAIA,GAAS,2BAA6B,CAAA,EAC1C,GAAGpB,CAAA,EAEF,OAAQsB,GAAmC,CAAC,CAACA,CAAS,EACtD,IAAIpB,CAAmB,EAE1B,OAAOiB,EAAQ,KAAMI,GAAWF,EAAW,SAASnB,EAAoBqB,CAAM,CAAC,CAAC,CAClF,CAEA,SAASC,EAAYL,EAAmBd,EAAkBoB,EAAqC,CAC7F,MAAMC,EAASP,EAAQ,OAA+B,CAACQ,EAAKJ,EAAQK,KAClED,EAAIJ,CAAM,EAAIlB,EAAOuB,CAAW,GAAK,GAC9BD,GACN,CAAA,CAAE,EAEL,MAAO,CACL,KAAMF,EAAc,EACpB,OAAAC,CAAA,CAEJ,CAEA,SAASG,EAAmBC,EAAwBC,EAAwC,CAC1F,MAAMC,MAAgB,IAChBC,EAAuC,CAAA,EAE7C,OAAAH,EAAK,QAASI,GAAQ,CACpB,MAAMC,EAAUJ,EAAqBG,EAAI,OAAOH,CAAkB,GAAG,OAAS,GAC9E,GAAI,CAACI,EAAS,CACZF,EAAmB,KAAKC,CAAG,EAC3B,MACF,CAEA,MAAME,EAAiBJ,EAAU,IAAIG,CAAO,GAAK,CAAA,EACjDH,EAAU,IAAIG,EAAS,CAAC,GAAGC,EAAgBF,CAAG,CAAC,CACjD,CAAC,EAEM,CAAE,UAAAF,EAAW,mBAAAC,CAAA,CACtB,CAEO,SAASI,EAA2BzB,EAAcQ,EAAuD,CAC9G,MAAMZ,EAAUO,EAAgBH,CAAI,EAC9BO,EAAUX,EAAQ,CAAC,GAAG,IAAKe,GAAWA,EAAO,KAAA,CAAM,GAAK,CAAA,EACxDQ,EAAqBb,EAA0BC,EAASC,CAAO,EAC/DU,EAAOtB,EAAQ,MAAM,CAAC,EAAE,IAAI,CAACH,EAAQoB,IAAgBD,EAAYL,EAASd,EAAQoB,CAAW,CAAC,EAC9F,CAAE,UAAAO,EAAW,mBAAAC,CAAA,EAAuBJ,EAAmBC,EAAMC,CAAkB,EAC/EO,EAA6B,CAAC,GAAGN,EAAU,QAAA,CAAS,EAAE,IAAI,CAAC,CAACG,EAASI,CAAmB,KAAO,CACnG,GAAIJ,EACJ,QAAAA,EACA,KAAMI,EACN,SAAUA,EAAoB,MAAA,EAC9B,EAEF,MAAO,CACL,QAAApB,EACA,mBAAAY,EACA,KAAAD,EACA,OAAAQ,EACA,mBAAAL,CAAA,CAEJ,CAEO,SAASO,EAA0BC,EAAkD,CAC1F,OAAOA,EAAO,OAAO,IAAKC,IAAW,CACnC,GAAIA,EAAM,GACV,QAASA,EAAM,QACf,MAAOA,EAAM,QAAA,EACb,CACJ,CAEA,eAAsBC,EACpBC,EACAxB,EACwC,CAGxC,GAFiBwB,EAAK,KAAK,YAAA,EACJ,SAAS,MAAM,GAAK3C,EAAe,IAAI2C,EAAK,IAAI,EAGvE,OAAOP,EAA2B,MAAMO,EAAK,KAAA,EAAQxB,CAAO,CAC9D"}
@@ -0,0 +1,103 @@
1
+ const m = [
2
+ "plate barcode",
3
+ "plate_barcode",
4
+ "plate-barcode",
5
+ "plateBarcode",
6
+ "plate id",
7
+ "plate_id",
8
+ "plateId"
9
+ ], C = /* @__PURE__ */ new Set(["text/csv", "application/vnd.ms-excel"]);
10
+ function d(t) {
11
+ return t.trim().toLowerCase().replace(/[^a-z0-9]/g, "");
12
+ }
13
+ function f(t, e) {
14
+ return [...t, e];
15
+ }
16
+ function l(t, e, o) {
17
+ const r = f(e, o);
18
+ return r.some((s) => s.trim() !== "") ? { records: [...t, r], record: [] } : { records: t, record: [] };
19
+ }
20
+ function v(t, e, o) {
21
+ const r = t[e];
22
+ return r !== '"' ? { field: `${o}${r}`, quoted: !0, index: e } : t[e + 1] === '"' ? { field: `${o}"`, quoted: !0, index: e + 1 } : { field: o, quoted: !1, index: e };
23
+ }
24
+ function h(t) {
25
+ let e = [], o = [], r = "", s = !1;
26
+ for (let n = 0; n < t.length; n += 1) {
27
+ const c = t[n];
28
+ if (s) {
29
+ const a = v(t, n, r);
30
+ r = a.field, s = a.quoted, n = a.index;
31
+ } else if (c === '"' && r === "")
32
+ s = !0;
33
+ else if (c === ",")
34
+ o = f(o, r), r = "";
35
+ else if (c === `
36
+ ` || c === "\r") {
37
+ const a = l(e, o, r);
38
+ e = a.records, o = a.record, r = "", c === "\r" && t[n + 1] === `
39
+ ` && (n += 1);
40
+ } else
41
+ r = `${r}${c}`;
42
+ }
43
+ return (r !== "" || o.length > 0) && (e = l(e, o, r).records), e;
44
+ }
45
+ function w(t, e) {
46
+ const o = [
47
+ e?.plateBarcodeColumn,
48
+ ...e?.plateBarcodeColumnAliases ?? [],
49
+ ...m
50
+ ].filter((r) => !!r).map(d);
51
+ return t.find((r) => o.includes(d(r)));
52
+ }
53
+ function g(t, e, o) {
54
+ const r = t.reduce((s, n, c) => (s[n] = e[c] ?? "", s), {});
55
+ return {
56
+ line: o + 2,
57
+ values: r
58
+ };
59
+ }
60
+ function B(t, e) {
61
+ const o = /* @__PURE__ */ new Map(), r = [];
62
+ return t.forEach((s) => {
63
+ const n = e ? s.values[e]?.trim() : "";
64
+ if (!n) {
65
+ r.push(s);
66
+ return;
67
+ }
68
+ const c = o.get(n) ?? [];
69
+ o.set(n, [...c, s]);
70
+ }), { plateRows: o, missingBarcodeRows: r };
71
+ }
72
+ function R(t, e) {
73
+ const o = h(t), r = o[0]?.map((i) => i.trim()) ?? [], s = w(r, e), n = o.slice(1).map((i, u) => g(r, i, u)), { plateRows: c, missingBarcodeRows: a } = B(n, s), p = [...c.entries()].map(([i, u]) => ({
74
+ id: i,
75
+ barcode: i,
76
+ rows: u,
77
+ rowCount: u.length
78
+ }));
79
+ return {
80
+ headers: r,
81
+ plateBarcodeColumn: s,
82
+ rows: n,
83
+ plates: p,
84
+ missingBarcodeRows: a
85
+ };
86
+ }
87
+ function _(t) {
88
+ return t.plates.map((e) => ({
89
+ id: e.id,
90
+ barcode: e.barcode,
91
+ count: e.rowCount
92
+ }));
93
+ }
94
+ async function E(t, e) {
95
+ if (t.name.toLowerCase().endsWith(".csv") || C.has(t.type))
96
+ return R(await t.text(), e);
97
+ }
98
+ export {
99
+ _ as plateOptionsFromCsvTriage,
100
+ R as triagePlateMapCsvByBarcode,
101
+ E as triagePlateMapCsvFile
102
+ };
103
+ //# sourceMappingURL=csvPlateTriage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"csvPlateTriage.js","sources":["../../../../src/components/composed/PlateMapEditor/csvPlateTriage.ts"],"sourcesContent":["import type {\n PlateMapCsvPlate,\n PlateMapCsvRow,\n PlateMapCsvTriage,\n PlateMapCsvTriageOptions,\n PlateMapPlateOption,\n} from \"./types\";\n\nconst DEFAULT_PLATE_BARCODE_COLUMNS = [\n \"plate barcode\",\n \"plate_barcode\",\n \"plate-barcode\",\n \"plateBarcode\",\n \"plate id\",\n \"plate_id\",\n \"plateId\",\n];\n\nconst CSV_MIME_TYPES = new Set([\"text/csv\", \"application/vnd.ms-excel\"]);\n\nfunction normalizeColumnName(value: string): string {\n return value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]/g, \"\");\n}\n\nfunction pushCsvField(record: string[], field: string): string[] {\n return [...record, field];\n}\n\nfunction pushCsvRecord(\n records: string[][],\n record: string[],\n field: string,\n): { records: string[][]; record: string[] } {\n const nextRecord = pushCsvField(record, field);\n if (nextRecord.some((cell) => cell.trim() !== \"\")) {\n return { records: [...records, nextRecord], record: [] };\n }\n return { records, record: [] };\n}\n\nfunction readQuotedCsvCharacter(text: string, index: number, field: string) {\n const char = text[index];\n if (char !== '\"') return { field: `${field}${char}`, quoted: true, index };\n if (text[index + 1] === '\"') return { field: `${field}\"`, quoted: true, index: index + 1 };\n return { field, quoted: false, index };\n}\n\nfunction parseCsvRecords(text: string): string[][] {\n let records: string[][] = [];\n let record: string[] = [];\n let field = \"\";\n let quoted = false;\n\n for (let index = 0; index < text.length; index += 1) {\n const char = text[index];\n\n if (quoted) {\n const next = readQuotedCsvCharacter(text, index, field);\n field = next.field;\n quoted = next.quoted;\n index = next.index;\n } else if (char === '\"' && field === \"\") {\n quoted = true;\n } else if (char === \",\") {\n record = pushCsvField(record, field);\n field = \"\";\n } else if (char === \"\\n\" || char === \"\\r\") {\n const next = pushCsvRecord(records, record, field);\n records = next.records;\n record = next.record;\n field = \"\";\n if (char === \"\\r\" && text[index + 1] === \"\\n\") index += 1;\n } else {\n field = `${field}${char}`;\n }\n }\n\n if (field !== \"\" || record.length > 0) {\n records = pushCsvRecord(records, record, field).records;\n }\n\n return records;\n}\n\nfunction resolvePlateBarcodeColumn(headers: string[], options?: PlateMapCsvTriageOptions): string | undefined {\n const candidates = [\n options?.plateBarcodeColumn,\n ...(options?.plateBarcodeColumnAliases ?? []),\n ...DEFAULT_PLATE_BARCODE_COLUMNS,\n ]\n .filter((candidate): candidate is string => !!candidate)\n .map(normalizeColumnName);\n\n return headers.find((header) => candidates.includes(normalizeColumnName(header)));\n}\n\nfunction recordToRow(headers: string[], record: string[], recordIndex: number): PlateMapCsvRow {\n const values = headers.reduce<Record<string, string>>((acc, header, headerIndex) => {\n acc[header] = record[headerIndex] ?? \"\";\n return acc;\n }, {});\n\n return {\n line: recordIndex + 2,\n values,\n };\n}\n\nfunction groupRowsByBarcode(rows: PlateMapCsvRow[], plateBarcodeColumn: string | undefined) {\n const plateRows = new Map<string, PlateMapCsvRow[]>();\n const missingBarcodeRows: PlateMapCsvRow[] = [];\n\n rows.forEach((row) => {\n const barcode = plateBarcodeColumn ? row.values[plateBarcodeColumn]?.trim() : \"\";\n if (!barcode) {\n missingBarcodeRows.push(row);\n return;\n }\n\n const rowsForBarcode = plateRows.get(barcode) ?? [];\n plateRows.set(barcode, [...rowsForBarcode, row]);\n });\n\n return { plateRows, missingBarcodeRows };\n}\n\nexport function triagePlateMapCsvByBarcode(text: string, options?: PlateMapCsvTriageOptions): PlateMapCsvTriage {\n const records = parseCsvRecords(text);\n const headers = records[0]?.map((header) => header.trim()) ?? [];\n const plateBarcodeColumn = resolvePlateBarcodeColumn(headers, options);\n const rows = records.slice(1).map((record, recordIndex) => recordToRow(headers, record, recordIndex));\n const { plateRows, missingBarcodeRows } = groupRowsByBarcode(rows, plateBarcodeColumn);\n const plates: PlateMapCsvPlate[] = [...plateRows.entries()].map(([barcode, plateRowsForBarcode]) => ({\n id: barcode,\n barcode,\n rows: plateRowsForBarcode,\n rowCount: plateRowsForBarcode.length,\n }));\n\n return {\n headers,\n plateBarcodeColumn,\n rows,\n plates,\n missingBarcodeRows,\n };\n}\n\nexport function plateOptionsFromCsvTriage(triage: PlateMapCsvTriage): PlateMapPlateOption[] {\n return triage.plates.map((plate) => ({\n id: plate.id,\n barcode: plate.barcode,\n count: plate.rowCount,\n }));\n}\n\nexport async function triagePlateMapCsvFile(\n file: File,\n options?: PlateMapCsvTriageOptions,\n): Promise<PlateMapCsvTriage | undefined> {\n const fileName = file.name.toLowerCase();\n const isCsv = fileName.endsWith(\".csv\") || CSV_MIME_TYPES.has(file.type);\n if (!isCsv) return undefined;\n\n return triagePlateMapCsvByBarcode(await file.text(), options);\n}\n"],"names":["DEFAULT_PLATE_BARCODE_COLUMNS","CSV_MIME_TYPES","normalizeColumnName","value","pushCsvField","record","field","pushCsvRecord","records","nextRecord","cell","readQuotedCsvCharacter","text","index","char","parseCsvRecords","quoted","next","resolvePlateBarcodeColumn","headers","options","candidates","candidate","header","recordToRow","recordIndex","values","acc","headerIndex","groupRowsByBarcode","rows","plateBarcodeColumn","plateRows","missingBarcodeRows","row","barcode","rowsForBarcode","triagePlateMapCsvByBarcode","plates","plateRowsForBarcode","plateOptionsFromCsvTriage","triage","plate","triagePlateMapCsvFile","file"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function s(r){const t=new Map;return(r??[]).forEach(e=>{const o=e.group??"",n=t.get(o)??[];n.push(e),t.set(o,n)}),[...t.entries()]}exports.groupTemplateOptions=s;
2
+ //# sourceMappingURL=helpers.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.cjs","sources":["../../../../src/components/composed/PlateMapEditor/helpers.ts"],"sourcesContent":["import type { TemplateOption } from \"./types\";\n\nexport function groupTemplateOptions(templates: TemplateOption[] | undefined): [string, TemplateOption[]][] {\n const groups = new Map<string, TemplateOption[]>();\n (templates ?? []).forEach((template) => {\n const key = template.group ?? \"\";\n const list = groups.get(key) ?? [];\n list.push(template);\n groups.set(key, list);\n });\n return [...groups.entries()];\n}\n"],"names":["groupTemplateOptions","templates","groups","template","key","list"],"mappings":"gFAEO,SAASA,EAAqBC,EAAuE,CAC1G,MAAMC,MAAa,IACnB,OAACD,GAAa,CAAA,GAAI,QAASE,GAAa,CACtC,MAAMC,EAAMD,EAAS,OAAS,GACxBE,EAAOH,EAAO,IAAIE,CAAG,GAAK,CAAA,EAChCC,EAAK,KAAKF,CAAQ,EAClBD,EAAO,IAAIE,EAAKC,CAAI,CACtB,CAAC,EACM,CAAC,GAAGH,EAAO,SAAS,CAC7B"}
@@ -0,0 +1,11 @@
1
+ function r(e) {
2
+ const o = /* @__PURE__ */ new Map();
3
+ return (e ?? []).forEach((t) => {
4
+ const n = t.group ?? "", s = o.get(n) ?? [];
5
+ s.push(t), o.set(n, s);
6
+ }), [...o.entries()];
7
+ }
8
+ export {
9
+ r as groupTemplateOptions
10
+ };
11
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sources":["../../../../src/components/composed/PlateMapEditor/helpers.ts"],"sourcesContent":["import type { TemplateOption } from \"./types\";\n\nexport function groupTemplateOptions(templates: TemplateOption[] | undefined): [string, TemplateOption[]][] {\n const groups = new Map<string, TemplateOption[]>();\n (templates ?? []).forEach((template) => {\n const key = template.group ?? \"\";\n const list = groups.get(key) ?? [];\n list.push(template);\n groups.set(key, list);\n });\n return [...groups.entries()];\n}\n"],"names":["groupTemplateOptions","templates","groups","template","key","list"],"mappings":"AAEO,SAASA,EAAqBC,GAAuE;AAC1G,QAAMC,wBAAa,IAAA;AACnB,UAACD,KAAa,CAAA,GAAI,QAAQ,CAACE,MAAa;AACtC,UAAMC,IAAMD,EAAS,SAAS,IACxBE,IAAOH,EAAO,IAAIE,CAAG,KAAK,CAAA;AAChC,IAAAC,EAAK,KAAKF,CAAQ,GAClBD,EAAO,IAAIE,GAAKC,CAAI;AAAA,EACtB,CAAC,GACM,CAAC,GAAGH,EAAO,SAAS;AAC7B;"}
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const I={96:{rows:8,columns:12},384:{rows:16,columns:24},1536:{rows:32,columns:48}},c=26,e=65,p=100,A=2,C=3;function D(o,t,n){return o==="custom"?{rows:t??8,columns:n??12}:I[o]}function m(o){if(o<c)return String.fromCharCode(e+o);const t=Math.floor(o/c)-1,n=o%c;return String.fromCharCode(e+t)+String.fromCharCode(e+n)}function P(o){return o.length===1?o.charCodeAt(0)-e:(o.charCodeAt(0)-e+1)*c+(o.charCodeAt(1)-e)}function S(o){return o>=p?C:A}function l(o,t,n){return`${m(o)}${String(t+1).padStart(S(n),"0")}`}function _(o,t){if(!o)return null;const n=/^([A-Z]{1,2})(\d+)$/.exec(o);if(!n)return null;const r=P(n[1]),s=parseInt(n[2],10)-1;return Number.isNaN(s)||r<0||r>=t.rows||s<0||s>=t.columns?null:{row:r,col:s}}function w(o,t,n,r,s){const a=[Math.min(o,n),Math.max(o,n)],f=[Math.min(t,r),Math.max(t,r)],h=[];for(let u=a[0];u<=a[1];u++)for(let i=f[0];i<=f[1];i++)h.push(l(u,i,s));return h}function L(o){const t=[];for(let n=0;n<o.rows;n++)for(let r=0;r<o.columns;r++)t.push(l(n,r,o.columns));return t}exports.allPositions=L;exports.parsePos=_;exports.parseRowLabel=P;exports.pos=l;exports.rectPositions=w;exports.resolveDimensions=D;exports.rowLabel=m;
2
+ //# sourceMappingURL=wellGrid.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wellGrid.cjs","sources":["../../../../src/components/composed/PlateMapEditor/wellGrid.ts"],"sourcesContent":["import type { PlateDimensions, PlateFormat, WellId } from \"./types\";\n\nconst FORMAT_DIMENSIONS: Record<Exclude<PlateFormat, \"custom\">, PlateDimensions> = {\n \"96\": { rows: 8, columns: 12 },\n \"384\": { rows: 16, columns: 24 },\n \"1536\": { rows: 32, columns: 48 },\n};\n\nconst ALPHABET_SIZE = 26;\nconst CHAR_CODE_A = 65;\nconst TRIPLE_DIGIT_THRESHOLD = 100;\nconst DOUBLE_DIGIT_PAD = 2;\nconst TRIPLE_DIGIT_PAD = 3;\n\nexport function resolveDimensions(\n format: PlateFormat,\n rows?: number,\n columns?: number,\n): PlateDimensions {\n if (format === \"custom\") {\n return {\n rows: rows ?? 8,\n columns: columns ?? 12,\n };\n }\n return FORMAT_DIMENSIONS[format];\n}\n\n/** Letter for a row index. Supports A-Z then AA-ZZ for 1536-format plates. */\nexport function rowLabel(row: number): string {\n if (row < ALPHABET_SIZE) return String.fromCharCode(CHAR_CODE_A + row);\n const high = Math.floor(row / ALPHABET_SIZE) - 1;\n const low = row % ALPHABET_SIZE;\n return (\n String.fromCharCode(CHAR_CODE_A + high) +\n String.fromCharCode(CHAR_CODE_A + low)\n );\n}\n\nexport function parseRowLabel(label: string): number {\n if (label.length === 1) return label.charCodeAt(0) - CHAR_CODE_A;\n return (\n (label.charCodeAt(0) - CHAR_CODE_A + 1) * ALPHABET_SIZE +\n (label.charCodeAt(1) - CHAR_CODE_A)\n );\n}\n\n/** Pad column number to a width that matches the plate's column count. */\nfunction colPadWidth(columns: number): number {\n return columns >= TRIPLE_DIGIT_THRESHOLD ? TRIPLE_DIGIT_PAD : DOUBLE_DIGIT_PAD;\n}\n\nexport function pos(row: number, col: number, columns: number): WellId {\n return `${rowLabel(row)}${String(col + 1).padStart(colPadWidth(columns), \"0\")}`;\n}\n\nexport function parsePos(\n id: WellId,\n dims: PlateDimensions,\n): { row: number; col: number } | null {\n if (!id) return null;\n const m = /^([A-Z]{1,2})(\\d+)$/.exec(id);\n if (!m) return null;\n const row = parseRowLabel(m[1]);\n const col = parseInt(m[2], 10) - 1;\n if (\n Number.isNaN(col) ||\n row < 0 ||\n row >= dims.rows ||\n col < 0 ||\n col >= dims.columns\n ) {\n return null;\n }\n return { row, col };\n}\n\nexport function rectPositions(\n r0: number,\n c0: number,\n r1: number,\n c1: number,\n columns: number,\n): WellId[] {\n const rs = [Math.min(r0, r1), Math.max(r0, r1)];\n const cs = [Math.min(c0, c1), Math.max(c0, c1)];\n const out: WellId[] = [];\n for (let r = rs[0]; r <= rs[1]; r++) {\n for (let c = cs[0]; c <= cs[1]; c++) {\n out.push(pos(r, c, columns));\n }\n }\n return out;\n}\n\nexport function allPositions(dims: PlateDimensions): WellId[] {\n const out: WellId[] = [];\n for (let r = 0; r < dims.rows; r++) {\n for (let c = 0; c < dims.columns; c++) {\n out.push(pos(r, c, dims.columns));\n }\n }\n return out;\n}\n"],"names":["FORMAT_DIMENSIONS","ALPHABET_SIZE","CHAR_CODE_A","TRIPLE_DIGIT_THRESHOLD","DOUBLE_DIGIT_PAD","TRIPLE_DIGIT_PAD","resolveDimensions","format","rows","columns","rowLabel","row","high","low","parseRowLabel","label","colPadWidth","pos","col","parsePos","id","dims","m","rectPositions","r0","c0","r1","c1","rs","cs","out","r","c","allPositions"],"mappings":"gFAEA,MAAMA,EAA6E,CACjF,GAAM,CAAE,KAAM,EAAG,QAAS,EAAA,EAC1B,IAAO,CAAE,KAAM,GAAI,QAAS,EAAA,EAC5B,KAAQ,CAAE,KAAM,GAAI,QAAS,EAAA,CAC/B,EAEMC,EAAgB,GAChBC,EAAc,GACdC,EAAyB,IACzBC,EAAmB,EACnBC,EAAmB,EAElB,SAASC,EACdC,EACAC,EACAC,EACiB,CACjB,OAAIF,IAAW,SACN,CACL,KAAMC,GAAQ,EACd,QAASC,GAAW,EAAA,EAGjBT,EAAkBO,CAAM,CACjC,CAGO,SAASG,EAASC,EAAqB,CAC5C,GAAIA,EAAMV,EAAe,OAAO,OAAO,aAAaC,EAAcS,CAAG,EACrE,MAAMC,EAAO,KAAK,MAAMD,EAAMV,CAAa,EAAI,EACzCY,EAAMF,EAAMV,EAClB,OACE,OAAO,aAAaC,EAAcU,CAAI,EACtC,OAAO,aAAaV,EAAcW,CAAG,CAEzC,CAEO,SAASC,EAAcC,EAAuB,CACnD,OAAIA,EAAM,SAAW,EAAUA,EAAM,WAAW,CAAC,EAAIb,GAElDa,EAAM,WAAW,CAAC,EAAIb,EAAc,GAAKD,GACzCc,EAAM,WAAW,CAAC,EAAIb,EAE3B,CAGA,SAASc,EAAYP,EAAyB,CAC5C,OAAOA,GAAWN,EAAyBE,EAAmBD,CAChE,CAEO,SAASa,EAAIN,EAAaO,EAAaT,EAAyB,CACrE,MAAO,GAAGC,EAASC,CAAG,CAAC,GAAG,OAAOO,EAAM,CAAC,EAAE,SAASF,EAAYP,CAAO,EAAG,GAAG,CAAC,EAC/E,CAEO,SAASU,EACdC,EACAC,EACqC,CACrC,GAAI,CAACD,EAAI,OAAO,KAChB,MAAME,EAAI,sBAAsB,KAAKF,CAAE,EACvC,GAAI,CAACE,EAAG,OAAO,KACf,MAAMX,EAAMG,EAAcQ,EAAE,CAAC,CAAC,EACxBJ,EAAM,SAASI,EAAE,CAAC,EAAG,EAAE,EAAI,EACjC,OACE,OAAO,MAAMJ,CAAG,GAChBP,EAAM,GACNA,GAAOU,EAAK,MACZH,EAAM,GACNA,GAAOG,EAAK,QAEL,KAEF,CAAE,IAAAV,EAAK,IAAAO,CAAA,CAChB,CAEO,SAASK,EACdC,EACAC,EACAC,EACAC,EACAlB,EACU,CACV,MAAMmB,EAAK,CAAC,KAAK,IAAIJ,EAAIE,CAAE,EAAG,KAAK,IAAIF,EAAIE,CAAE,CAAC,EACxCG,EAAK,CAAC,KAAK,IAAIJ,EAAIE,CAAE,EAAG,KAAK,IAAIF,EAAIE,CAAE,CAAC,EACxCG,EAAgB,CAAA,EACtB,QAASC,EAAIH,EAAG,CAAC,EAAGG,GAAKH,EAAG,CAAC,EAAGG,IAC9B,QAASC,EAAIH,EAAG,CAAC,EAAGG,GAAKH,EAAG,CAAC,EAAGG,IAC9BF,EAAI,KAAKb,EAAIc,EAAGC,EAAGvB,CAAO,CAAC,EAG/B,OAAOqB,CACT,CAEO,SAASG,EAAaZ,EAAiC,CAC5D,MAAMS,EAAgB,CAAA,EACtB,QAASC,EAAI,EAAGA,EAAIV,EAAK,KAAMU,IAC7B,QAASC,EAAI,EAAGA,EAAIX,EAAK,QAASW,IAChCF,EAAI,KAAKb,EAAIc,EAAGC,EAAGX,EAAK,OAAO,CAAC,EAGpC,OAAOS,CACT"}
@@ -0,0 +1,56 @@
1
+ const m = {
2
+ 96: { rows: 8, columns: 12 },
3
+ 384: { rows: 16, columns: 24 },
4
+ 1536: { rows: 32, columns: 48 }
5
+ }, u = 26, c = 65, I = 100, A = 2, C = 3;
6
+ function S(n, t, o) {
7
+ return n === "custom" ? {
8
+ rows: t ?? 8,
9
+ columns: o ?? 12
10
+ } : m[n];
11
+ }
12
+ function D(n) {
13
+ if (n < u) return String.fromCharCode(c + n);
14
+ const t = Math.floor(n / u) - 1, o = n % u;
15
+ return String.fromCharCode(c + t) + String.fromCharCode(c + o);
16
+ }
17
+ function _(n) {
18
+ return n.length === 1 ? n.charCodeAt(0) - c : (n.charCodeAt(0) - c + 1) * u + (n.charCodeAt(1) - c);
19
+ }
20
+ function P(n) {
21
+ return n >= I ? C : A;
22
+ }
23
+ function h(n, t, o) {
24
+ return `${D(n)}${String(t + 1).padStart(P(o), "0")}`;
25
+ }
26
+ function p(n, t) {
27
+ if (!n) return null;
28
+ const o = /^([A-Z]{1,2})(\d+)$/.exec(n);
29
+ if (!o) return null;
30
+ const r = _(o[1]), s = parseInt(o[2], 10) - 1;
31
+ return Number.isNaN(s) || r < 0 || r >= t.rows || s < 0 || s >= t.columns ? null : { row: r, col: s };
32
+ }
33
+ function E(n, t, o, r, s) {
34
+ const f = [Math.min(n, o), Math.max(n, o)], l = [Math.min(t, r), Math.max(t, r)], a = [];
35
+ for (let e = f[0]; e <= f[1]; e++)
36
+ for (let i = l[0]; i <= l[1]; i++)
37
+ a.push(h(e, i, s));
38
+ return a;
39
+ }
40
+ function T(n) {
41
+ const t = [];
42
+ for (let o = 0; o < n.rows; o++)
43
+ for (let r = 0; r < n.columns; r++)
44
+ t.push(h(o, r, n.columns));
45
+ return t;
46
+ }
47
+ export {
48
+ T as allPositions,
49
+ p as parsePos,
50
+ _ as parseRowLabel,
51
+ h as pos,
52
+ E as rectPositions,
53
+ S as resolveDimensions,
54
+ D as rowLabel
55
+ };
56
+ //# sourceMappingURL=wellGrid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wellGrid.js","sources":["../../../../src/components/composed/PlateMapEditor/wellGrid.ts"],"sourcesContent":["import type { PlateDimensions, PlateFormat, WellId } from \"./types\";\n\nconst FORMAT_DIMENSIONS: Record<Exclude<PlateFormat, \"custom\">, PlateDimensions> = {\n \"96\": { rows: 8, columns: 12 },\n \"384\": { rows: 16, columns: 24 },\n \"1536\": { rows: 32, columns: 48 },\n};\n\nconst ALPHABET_SIZE = 26;\nconst CHAR_CODE_A = 65;\nconst TRIPLE_DIGIT_THRESHOLD = 100;\nconst DOUBLE_DIGIT_PAD = 2;\nconst TRIPLE_DIGIT_PAD = 3;\n\nexport function resolveDimensions(\n format: PlateFormat,\n rows?: number,\n columns?: number,\n): PlateDimensions {\n if (format === \"custom\") {\n return {\n rows: rows ?? 8,\n columns: columns ?? 12,\n };\n }\n return FORMAT_DIMENSIONS[format];\n}\n\n/** Letter for a row index. Supports A-Z then AA-ZZ for 1536-format plates. */\nexport function rowLabel(row: number): string {\n if (row < ALPHABET_SIZE) return String.fromCharCode(CHAR_CODE_A + row);\n const high = Math.floor(row / ALPHABET_SIZE) - 1;\n const low = row % ALPHABET_SIZE;\n return (\n String.fromCharCode(CHAR_CODE_A + high) +\n String.fromCharCode(CHAR_CODE_A + low)\n );\n}\n\nexport function parseRowLabel(label: string): number {\n if (label.length === 1) return label.charCodeAt(0) - CHAR_CODE_A;\n return (\n (label.charCodeAt(0) - CHAR_CODE_A + 1) * ALPHABET_SIZE +\n (label.charCodeAt(1) - CHAR_CODE_A)\n );\n}\n\n/** Pad column number to a width that matches the plate's column count. */\nfunction colPadWidth(columns: number): number {\n return columns >= TRIPLE_DIGIT_THRESHOLD ? TRIPLE_DIGIT_PAD : DOUBLE_DIGIT_PAD;\n}\n\nexport function pos(row: number, col: number, columns: number): WellId {\n return `${rowLabel(row)}${String(col + 1).padStart(colPadWidth(columns), \"0\")}`;\n}\n\nexport function parsePos(\n id: WellId,\n dims: PlateDimensions,\n): { row: number; col: number } | null {\n if (!id) return null;\n const m = /^([A-Z]{1,2})(\\d+)$/.exec(id);\n if (!m) return null;\n const row = parseRowLabel(m[1]);\n const col = parseInt(m[2], 10) - 1;\n if (\n Number.isNaN(col) ||\n row < 0 ||\n row >= dims.rows ||\n col < 0 ||\n col >= dims.columns\n ) {\n return null;\n }\n return { row, col };\n}\n\nexport function rectPositions(\n r0: number,\n c0: number,\n r1: number,\n c1: number,\n columns: number,\n): WellId[] {\n const rs = [Math.min(r0, r1), Math.max(r0, r1)];\n const cs = [Math.min(c0, c1), Math.max(c0, c1)];\n const out: WellId[] = [];\n for (let r = rs[0]; r <= rs[1]; r++) {\n for (let c = cs[0]; c <= cs[1]; c++) {\n out.push(pos(r, c, columns));\n }\n }\n return out;\n}\n\nexport function allPositions(dims: PlateDimensions): WellId[] {\n const out: WellId[] = [];\n for (let r = 0; r < dims.rows; r++) {\n for (let c = 0; c < dims.columns; c++) {\n out.push(pos(r, c, dims.columns));\n }\n }\n return out;\n}\n"],"names":["FORMAT_DIMENSIONS","ALPHABET_SIZE","CHAR_CODE_A","TRIPLE_DIGIT_THRESHOLD","DOUBLE_DIGIT_PAD","TRIPLE_DIGIT_PAD","resolveDimensions","format","rows","columns","rowLabel","row","high","low","parseRowLabel","label","colPadWidth","pos","col","parsePos","id","dims","m","rectPositions","r0","c0","r1","c1","rs","cs","out","r","c","allPositions"],"mappings":"AAEA,MAAMA,IAA6E;AAAA,EACjF,IAAM,EAAE,MAAM,GAAG,SAAS,GAAA;AAAA,EAC1B,KAAO,EAAE,MAAM,IAAI,SAAS,GAAA;AAAA,EAC5B,MAAQ,EAAE,MAAM,IAAI,SAAS,GAAA;AAC/B,GAEMC,IAAgB,IAChBC,IAAc,IACdC,IAAyB,KACzBC,IAAmB,GACnBC,IAAmB;AAElB,SAASC,EACdC,GACAC,GACAC,GACiB;AACjB,SAAIF,MAAW,WACN;AAAA,IACL,MAAMC,KAAQ;AAAA,IACd,SAASC,KAAW;AAAA,EAAA,IAGjBT,EAAkBO,CAAM;AACjC;AAGO,SAASG,EAASC,GAAqB;AAC5C,MAAIA,IAAMV,EAAe,QAAO,OAAO,aAAaC,IAAcS,CAAG;AACrE,QAAMC,IAAO,KAAK,MAAMD,IAAMV,CAAa,IAAI,GACzCY,IAAMF,IAAMV;AAClB,SACE,OAAO,aAAaC,IAAcU,CAAI,IACtC,OAAO,aAAaV,IAAcW,CAAG;AAEzC;AAEO,SAASC,EAAcC,GAAuB;AACnD,SAAIA,EAAM,WAAW,IAAUA,EAAM,WAAW,CAAC,IAAIb,KAElDa,EAAM,WAAW,CAAC,IAAIb,IAAc,KAAKD,KACzCc,EAAM,WAAW,CAAC,IAAIb;AAE3B;AAGA,SAASc,EAAYP,GAAyB;AAC5C,SAAOA,KAAWN,IAAyBE,IAAmBD;AAChE;AAEO,SAASa,EAAIN,GAAaO,GAAaT,GAAyB;AACrE,SAAO,GAAGC,EAASC,CAAG,CAAC,GAAG,OAAOO,IAAM,CAAC,EAAE,SAASF,EAAYP,CAAO,GAAG,GAAG,CAAC;AAC/E;AAEO,SAASU,EACdC,GACAC,GACqC;AACrC,MAAI,CAACD,EAAI,QAAO;AAChB,QAAME,IAAI,sBAAsB,KAAKF,CAAE;AACvC,MAAI,CAACE,EAAG,QAAO;AACf,QAAMX,IAAMG,EAAcQ,EAAE,CAAC,CAAC,GACxBJ,IAAM,SAASI,EAAE,CAAC,GAAG,EAAE,IAAI;AACjC,SACE,OAAO,MAAMJ,CAAG,KAChBP,IAAM,KACNA,KAAOU,EAAK,QACZH,IAAM,KACNA,KAAOG,EAAK,UAEL,OAEF,EAAE,KAAAV,GAAK,KAAAO,EAAA;AAChB;AAEO,SAASK,EACdC,GACAC,GACAC,GACAC,GACAlB,GACU;AACV,QAAMmB,IAAK,CAAC,KAAK,IAAIJ,GAAIE,CAAE,GAAG,KAAK,IAAIF,GAAIE,CAAE,CAAC,GACxCG,IAAK,CAAC,KAAK,IAAIJ,GAAIE,CAAE,GAAG,KAAK,IAAIF,GAAIE,CAAE,CAAC,GACxCG,IAAgB,CAAA;AACtB,WAASC,IAAIH,EAAG,CAAC,GAAGG,KAAKH,EAAG,CAAC,GAAGG;AAC9B,aAASC,IAAIH,EAAG,CAAC,GAAGG,KAAKH,EAAG,CAAC,GAAGG;AAC9B,MAAAF,EAAI,KAAKb,EAAIc,GAAGC,GAAGvB,CAAO,CAAC;AAG/B,SAAOqB;AACT;AAEO,SAASG,EAAaZ,GAAiC;AAC5D,QAAMS,IAAgB,CAAA;AACtB,WAASC,IAAI,GAAGA,IAAIV,EAAK,MAAMU;AAC7B,aAASC,IAAI,GAAGA,IAAIX,EAAK,SAASW;AAChC,MAAAF,EAAI,KAAKb,EAAIc,GAAGC,GAAGX,EAAK,OAAO,CAAC;AAGpC,SAAOS;AACT;"}