@tetrascience-npm/tetrascience-react-ui 0.5.0 → 0.6.0-beta.79.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -37
- 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 +341 -0
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlateMapForm.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/PlateMapForm.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlateMapForm.js +43 -0
- package/dist/components/composed/PlateMapEditor/PlateMapForm.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.js +154 -0
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlateMapManifest.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/PlateMapManifest.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/PlateMapManifest.js +44 -0
- package/dist/components/composed/PlateMapEditor/PlateMapManifest.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/composed/ProcessFlow/ProcessFlow.cjs +2 -0
- package/dist/components/composed/ProcessFlow/ProcessFlow.cjs.map +1 -0
- package/dist/components/composed/ProcessFlow/ProcessFlow.js +543 -0
- package/dist/components/composed/ProcessFlow/ProcessFlow.js.map +1 -0
- package/dist/components/composed/ProcessFlow/ProcessFlow.utils.cjs +2 -0
- package/dist/components/composed/ProcessFlow/ProcessFlow.utils.cjs.map +1 -0
- package/dist/components/composed/ProcessFlow/ProcessFlow.utils.js +84 -0
- package/dist/components/composed/ProcessFlow/ProcessFlow.utils.js.map +1 -0
- package/dist/components/ui/accordion.cjs +1 -1
- package/dist/components/ui/accordion.cjs.map +1 -1
- package/dist/components/ui/accordion.js +1 -1
- package/dist/components/ui/accordion.js.map +1 -1
- package/dist/components/ui/badge.cjs +1 -1
- package/dist/components/ui/badge.cjs.map +1 -1
- package/dist/components/ui/badge.js +18 -18
- package/dist/components/ui/badge.js.map +1 -1
- package/dist/components/ui/button.cjs +1 -1
- package/dist/components/ui/button.cjs.map +1 -1
- package/dist/components/ui/button.js +16 -16
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/calendar.cjs +1 -1
- package/dist/components/ui/calendar.cjs.map +1 -1
- package/dist/components/ui/calendar.js +5 -5
- package/dist/components/ui/calendar.js.map +1 -1
- package/dist/components/ui/card.cjs +1 -1
- package/dist/components/ui/card.cjs.map +1 -1
- package/dist/components/ui/card.js +1 -1
- package/dist/components/ui/card.js.map +1 -1
- package/dist/components/ui/checkbox.cjs +1 -1
- package/dist/components/ui/checkbox.cjs.map +1 -1
- package/dist/components/ui/checkbox.js +9 -9
- package/dist/components/ui/checkbox.js.map +1 -1
- package/dist/components/ui/combobox.cjs +1 -1
- package/dist/components/ui/combobox.cjs.map +1 -1
- package/dist/components/ui/combobox.js +5 -5
- package/dist/components/ui/combobox.js.map +1 -1
- package/dist/components/ui/data-table/data-table-group.cjs +2 -0
- package/dist/components/ui/data-table/data-table-group.cjs.map +1 -0
- package/dist/components/ui/data-table/data-table-group.js +118 -0
- package/dist/components/ui/data-table/data-table-group.js.map +1 -0
- package/dist/components/ui/data-table/data-table-pagination.cjs +1 -1
- package/dist/components/ui/data-table/data-table-pagination.cjs.map +1 -1
- package/dist/components/ui/data-table/data-table-pagination.js +22 -22
- package/dist/components/ui/data-table/data-table-pagination.js.map +1 -1
- 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 +567 -316
- package/dist/components/ui/data-table/data-table.js.map +1 -1
- package/dist/components/ui/dialog.cjs +1 -1
- package/dist/components/ui/dialog.cjs.map +1 -1
- package/dist/components/ui/dialog.js +13 -13
- package/dist/components/ui/dialog.js.map +1 -1
- package/dist/components/ui/input-group.cjs +1 -1
- package/dist/components/ui/input-group.cjs.map +1 -1
- package/dist/components/ui/input-group.js +29 -29
- package/dist/components/ui/input-group.js.map +1 -1
- package/dist/components/ui/input-otp.cjs +1 -1
- package/dist/components/ui/input-otp.cjs.map +1 -1
- package/dist/components/ui/input-otp.js +10 -10
- package/dist/components/ui/input-otp.js.map +1 -1
- package/dist/components/ui/input.cjs +1 -1
- package/dist/components/ui/input.cjs.map +1 -1
- package/dist/components/ui/input.js +7 -7
- package/dist/components/ui/input.js.map +1 -1
- package/dist/components/ui/item.cjs +1 -1
- package/dist/components/ui/item.cjs.map +1 -1
- package/dist/components/ui/item.js +17 -17
- package/dist/components/ui/item.js.map +1 -1
- package/dist/components/ui/navigation-menu.cjs +1 -1
- package/dist/components/ui/navigation-menu.cjs.map +1 -1
- package/dist/components/ui/navigation-menu.js +24 -24
- package/dist/components/ui/navigation-menu.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/components/ui/radio-group.cjs +1 -1
- package/dist/components/ui/radio-group.cjs.map +1 -1
- package/dist/components/ui/radio-group.js +16 -16
- package/dist/components/ui/radio-group.js.map +1 -1
- package/dist/components/ui/scroll-area.cjs +1 -1
- package/dist/components/ui/scroll-area.cjs.map +1 -1
- package/dist/components/ui/scroll-area.js +6 -6
- package/dist/components/ui/scroll-area.js.map +1 -1
- package/dist/components/ui/select.cjs +1 -1
- package/dist/components/ui/select.cjs.map +1 -1
- package/dist/components/ui/select.js +48 -48
- package/dist/components/ui/select.js.map +1 -1
- package/dist/components/ui/slider.cjs +1 -1
- package/dist/components/ui/slider.cjs.map +1 -1
- package/dist/components/ui/slider.js +22 -22
- package/dist/components/ui/slider.js.map +1 -1
- package/dist/components/ui/switch.cjs +1 -1
- package/dist/components/ui/switch.cjs.map +1 -1
- package/dist/components/ui/switch.js +14 -14
- package/dist/components/ui/switch.js.map +1 -1
- package/dist/components/ui/table.cjs +1 -1
- package/dist/components/ui/table.cjs.map +1 -1
- package/dist/components/ui/table.js +2 -2
- package/dist/components/ui/table.js.map +1 -1
- package/dist/components/ui/tabs.cjs +1 -1
- package/dist/components/ui/tabs.cjs.map +1 -1
- package/dist/components/ui/tabs.js +9 -9
- package/dist/components/ui/tabs.js.map +1 -1
- package/dist/components/ui/textarea.cjs +1 -1
- package/dist/components/ui/textarea.cjs.map +1 -1
- package/dist/components/ui/textarea.js +6 -6
- package/dist/components/ui/textarea.js.map +1 -1
- package/dist/components/ui/toggle.cjs +1 -1
- package/dist/components/ui/toggle.cjs.map +1 -1
- package/dist/components/ui/toggle.js +13 -13
- package/dist/components/ui/toggle.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +830 -3
- package/dist/index.js +649 -593
- package/dist/index.js.map +1 -1
- package/dist/index.tailwind.css +1 -1
- package/dist/utils/colors.cjs +1 -1
- package/dist/utils/colors.cjs.map +1 -1
- package/dist/utils/colors.js +43 -21
- package/dist/utils/colors.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlateMapGrid.cjs","sources":["../../../../src/components/composed/PlateMapEditor/PlateMapGrid.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport { PlatePaintGrid } from \"./PlatePaintGrid\";\nimport { allPositions, resolveDimensions } from \"./wellGrid\";\n\nimport type { WellShape } from \"./PlatePaintGrid\";\nimport type { PlateFormat, PlateMapGroupOption, WellField, WellId, WellRecord } from \"./types\";\n\nimport { Separator } from \"@/components/ui/separator\";\nimport { cn } from \"@/lib/utils\";\n\nfunction formatHoverFields<T extends WellRecord>(entry: T, fields: WellField<T>[]): string[] {\n return fields\n .map((f) => {\n const v = entry[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.map((item) => (f.options ?? []).find((o) => o.value === item)?.label ?? item).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\nexport interface PlateMapGridProps<T extends WellRecord = WellRecord> {\n format: PlateFormat;\n rows?: number;\n columns?: number;\n values: Map<WellId, T>;\n selection: Set<WellId>;\n onSelectionChange: (next: Set<WellId>) => void;\n /** Resolves the SVG fill color for a well. */\n colorForWell: (well: T | undefined, wellId: WellId) => string;\n /** Field schema used to build the default hover summary string. */\n fields?: WellField<T>[];\n /** Custom hover summary for the strip above the grid. */\n renderHoverSummary?: (well: T | undefined, wellId: WellId) => React.ReactNode;\n /** Fires whenever the currently hovered well changes (null on leave). */\n onHoveredWellChange?: (wellId: WellId | null) => void;\n /**\n * Controlled hovered well id. When provided, the panel reflects this value\n * instead of tracking hover internally — useful for syncing the hover\n * summary with external state (e.g. resetting it on a plate switch).\n */\n hoveredWellId?: WellId | null;\n /** Controls rendered to the left of the built-in select/deselect links. */\n toolbar?: React.ReactNode;\n /** Hide the built-in \"Select all\" / \"Deselect all\" links. */\n hideSelectionControls?: boolean;\n selectAllLabel?: string;\n deselectAllLabel?: string;\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 framed?: boolean;\n /** Render-prop that places a node inside each absolute-positioned well cell. */\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 onWellDoubleClick?: (wellId: WellId) => void;\n selectionFillMode?: \"selection\" | \"well\";\n flashWellId?: WellId;\n flashWellKey?: number;\n /** Fixed well size. When unset, the grid grows with the available width. */\n cellSize?: number;\n autoScale?: boolean;\n minCellSize?: number;\n maxCellSize?: number;\n /** Optional grouped well shortcuts rendered under the grid. */\n groups?: PlateMapGroupOption[];\n activeGroupId?: string;\n onGroupClick?: (group: PlateMapGroupOption) => void;\n className?: string;\n}\n\n/**\n * Card-agnostic plate grid panel. Renders the selection toolbar, hover\n * summary, the interactive `PlatePaintGrid`, and optional group shortcuts —\n * and owns the ephemeral hover state internally. The plate title, plate\n * selector, and actions menu are intentionally NOT part of this panel; compose\n * `PlateMapPlateSelector` and `PlateMapActionsMenu` around it for full layout\n * freedom.\n */\nexport function PlateMapGrid<T extends WellRecord = WellRecord>({\n format,\n rows,\n columns,\n values,\n selection,\n onSelectionChange,\n colorForWell,\n fields,\n renderHoverSummary,\n onHoveredWellChange,\n hoveredWellId,\n toolbar,\n hideSelectionControls = false,\n selectAllLabel = \"Select all\",\n deselectAllLabel = \"Deselect all\",\n emptyWellFillColor,\n wellShape,\n framed,\n wrapWell,\n highlightedWellIds,\n onWellDoubleClick,\n selectionFillMode,\n flashWellId,\n flashWellKey,\n cellSize,\n autoScale,\n minCellSize,\n maxCellSize,\n groups,\n activeGroupId,\n onGroupClick,\n className,\n}: PlateMapGridProps<T>) {\n const dims = resolveDimensions(format, rows, columns);\n const [internalHoverPos, setInternalHoverPos] = React.useState<WellId | null>(null);\n const isHoverControlled = hoveredWellId !== undefined;\n const hoverPos = isHoverControlled ? hoveredWellId : internalHoverPos;\n\n const selectAll = () => onSelectionChange(new Set(allPositions(dims)));\n const deselectAll = () => onSelectionChange(new Set());\n\n const hoverEntry = hoverPos ? values.get(hoverPos) : undefined;\n const hoverFields = hoverEntry && fields ? formatHoverFields(hoverEntry, fields) : [];\n const hoverSummary = hoverPos\n ? (renderHoverSummary?.(hoverEntry, hoverPos) ?? [hoverPos, ...hoverFields].join(\" • \"))\n : \" \";\n\n const showSelectionControls = !hideSelectionControls;\n const showToolbarRow = !!toolbar || showSelectionControls;\n\n return (\n <div data-slot=\"plate-map-grid\" className={cn(\"flex flex-col gap-1.5\", className)}>\n {showToolbarRow ? (\n <div className=\"flex flex-wrap items-center justify-start gap-3\">\n {toolbar}\n {showSelectionControls ? (\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 {selectAllLabel}\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 {deselectAllLabel}\n </button>\n </div>\n ) : null}\n </div>\n ) : null}\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={values}\n selection={selection}\n onSelectionChange={onSelectionChange}\n colorForWell={colorForWell}\n emptyWellFillColor={emptyWellFillColor}\n wellShape={wellShape}\n framed={framed}\n wrapWell={wrapWell}\n highlightedWellIds={highlightedWellIds}\n onWellHover={(wellId) => {\n if (!isHoverControlled) setInternalHoverPos(wellId);\n onHoveredWellChange?.(wellId);\n }}\n onWellDoubleClick={onWellDoubleClick}\n selectionFillMode={selectionFillMode}\n flashWellId={flashWellId}\n flashWellKey={flashWellKey}\n cellSize={cellSize}\n autoScale={autoScale}\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 </div>\n );\n}\n"],"names":["formatHoverFields","entry","fields","f","v","arr","item","o","PlateMapGrid","format","rows","columns","values","selection","onSelectionChange","colorForWell","renderHoverSummary","onHoveredWellChange","hoveredWellId","toolbar","hideSelectionControls","selectAllLabel","deselectAllLabel","emptyWellFillColor","wellShape","framed","wrapWell","highlightedWellIds","onWellDoubleClick","selectionFillMode","flashWellId","flashWellKey","cellSize","autoScale","minCellSize","maxCellSize","groups","activeGroupId","onGroupClick","className","dims","resolveDimensions","internalHoverPos","setInternalHoverPos","React","isHoverControlled","hoverPos","selectAll","allPositions","deselectAll","hoverEntry","hoverFields","hoverSummary","showSelectionControls","showToolbarRow","jsxs","cn","jsx","PlatePaintGrid","wellId","Fragment","Separator","group","isActive","count"],"mappings":"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"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { jsxs as o, jsx as l, Fragment as Y } from "react/jsx-runtime";
|
|
2
|
+
import * as Z from "react";
|
|
3
|
+
import { PlatePaintGrid as _ } from "./PlatePaintGrid.js";
|
|
4
|
+
import { allPositions as $, resolveDimensions as ee } from "./wellGrid.js";
|
|
5
|
+
import { Separator as te } from "../../ui/separator.js";
|
|
6
|
+
import { cn as y } from "../../../lib/utils.js";
|
|
7
|
+
function ne(a, d) {
|
|
8
|
+
return d.map((n) => {
|
|
9
|
+
const t = a[n.key];
|
|
10
|
+
if (t == null || t === "") return null;
|
|
11
|
+
if (n.kind === "select")
|
|
12
|
+
return (n.options ?? []).find((r) => r.value === t)?.label ?? String(t);
|
|
13
|
+
if (n.kind === "multiselect") {
|
|
14
|
+
const i = Array.isArray(t) ? t : [];
|
|
15
|
+
return i.length === 0 ? null : i.map((r) => (n.options ?? []).find((c) => c.value === r)?.label ?? r).join(", ");
|
|
16
|
+
}
|
|
17
|
+
return n.kind === "boolean" ? t ? n.label : null : String(t);
|
|
18
|
+
}).filter(Boolean);
|
|
19
|
+
}
|
|
20
|
+
function ae({
|
|
21
|
+
format: a,
|
|
22
|
+
rows: d,
|
|
23
|
+
columns: n,
|
|
24
|
+
values: t,
|
|
25
|
+
selection: i,
|
|
26
|
+
onSelectionChange: r,
|
|
27
|
+
colorForWell: c,
|
|
28
|
+
fields: f,
|
|
29
|
+
renderHoverSummary: N,
|
|
30
|
+
onHoveredWellChange: g,
|
|
31
|
+
hoveredWellId: x,
|
|
32
|
+
toolbar: p,
|
|
33
|
+
hideSelectionControls: w = !1,
|
|
34
|
+
selectAllLabel: k = "Select all",
|
|
35
|
+
deselectAllLabel: P = "Deselect all",
|
|
36
|
+
emptyWellFillColor: S,
|
|
37
|
+
wellShape: C,
|
|
38
|
+
framed: j,
|
|
39
|
+
wrapWell: A,
|
|
40
|
+
highlightedWellIds: H,
|
|
41
|
+
onWellDoubleClick: F,
|
|
42
|
+
selectionFillMode: D,
|
|
43
|
+
flashWellId: G,
|
|
44
|
+
flashWellKey: R,
|
|
45
|
+
cellSize: z,
|
|
46
|
+
autoScale: B,
|
|
47
|
+
minCellSize: E,
|
|
48
|
+
maxCellSize: M,
|
|
49
|
+
groups: m,
|
|
50
|
+
activeGroupId: T,
|
|
51
|
+
onGroupClick: q,
|
|
52
|
+
className: I
|
|
53
|
+
}) {
|
|
54
|
+
const J = ee(a, d, n), [K, L] = Z.useState(null), v = x !== void 0, s = v ? x : K, O = () => r(new Set($(J))), Q = () => r(/* @__PURE__ */ new Set()), u = s ? t.get(s) : void 0, U = u && f ? ne(u, f) : [], V = s ? N?.(u, s) ?? [s, ...U].join(" • ") : " ", h = !w, W = !!p || h;
|
|
55
|
+
return /* @__PURE__ */ o("div", { "data-slot": "plate-map-grid", className: y("flex flex-col gap-1.5", I), children: [
|
|
56
|
+
W ? /* @__PURE__ */ o("div", { className: "flex flex-wrap items-center justify-start gap-3", children: [
|
|
57
|
+
p,
|
|
58
|
+
h ? /* @__PURE__ */ o("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
59
|
+
/* @__PURE__ */ l(
|
|
60
|
+
"button",
|
|
61
|
+
{
|
|
62
|
+
type: "button",
|
|
63
|
+
className: "font-medium text-primary underline-offset-2 hover:text-primary/80 hover:underline",
|
|
64
|
+
onClick: O,
|
|
65
|
+
children: k
|
|
66
|
+
}
|
|
67
|
+
),
|
|
68
|
+
/* @__PURE__ */ l("span", { className: "text-muted-foreground/60", children: "·" }),
|
|
69
|
+
/* @__PURE__ */ l(
|
|
70
|
+
"button",
|
|
71
|
+
{
|
|
72
|
+
type: "button",
|
|
73
|
+
className: "font-medium text-primary underline-offset-2 hover:text-primary/80 hover:underline",
|
|
74
|
+
onClick: Q,
|
|
75
|
+
children: P
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
] }) : null
|
|
79
|
+
] }) : null,
|
|
80
|
+
/* @__PURE__ */ l("div", { className: "h-5 text-xs text-muted-foreground", children: V }),
|
|
81
|
+
/* @__PURE__ */ l(
|
|
82
|
+
_,
|
|
83
|
+
{
|
|
84
|
+
format: a,
|
|
85
|
+
rows: d,
|
|
86
|
+
columns: n,
|
|
87
|
+
values: t,
|
|
88
|
+
selection: i,
|
|
89
|
+
onSelectionChange: r,
|
|
90
|
+
colorForWell: c,
|
|
91
|
+
emptyWellFillColor: S,
|
|
92
|
+
wellShape: C,
|
|
93
|
+
framed: j,
|
|
94
|
+
wrapWell: A,
|
|
95
|
+
highlightedWellIds: H,
|
|
96
|
+
onWellHover: (e) => {
|
|
97
|
+
v || L(e), g?.(e);
|
|
98
|
+
},
|
|
99
|
+
onWellDoubleClick: F,
|
|
100
|
+
selectionFillMode: D,
|
|
101
|
+
flashWellId: G,
|
|
102
|
+
flashWellKey: R,
|
|
103
|
+
cellSize: z,
|
|
104
|
+
autoScale: B,
|
|
105
|
+
minCellSize: E,
|
|
106
|
+
maxCellSize: M
|
|
107
|
+
}
|
|
108
|
+
),
|
|
109
|
+
m && m.length > 0 ? /* @__PURE__ */ o(Y, { children: [
|
|
110
|
+
/* @__PURE__ */ l(te, { className: "mt-2" }),
|
|
111
|
+
/* @__PURE__ */ l("div", { className: "flex max-h-28 flex-wrap gap-3 overflow-y-auto pt-1", children: m.map((e) => {
|
|
112
|
+
const X = e.id === T, b = e.count ?? e.wellIds?.length;
|
|
113
|
+
return /* @__PURE__ */ o(
|
|
114
|
+
"button",
|
|
115
|
+
{
|
|
116
|
+
type: "button",
|
|
117
|
+
disabled: e.disabled,
|
|
118
|
+
onClick: () => q?.(e),
|
|
119
|
+
className: y(
|
|
120
|
+
"flex w-16 flex-col items-center gap-1 rounded-md px-1 py-1 text-center text-xs transition-colors",
|
|
121
|
+
"text-muted-foreground hover:bg-muted/60",
|
|
122
|
+
X && "bg-muted text-foreground ring-1 ring-primary/40",
|
|
123
|
+
e.disabled && "pointer-events-none opacity-50"
|
|
124
|
+
),
|
|
125
|
+
title: e.label,
|
|
126
|
+
children: [
|
|
127
|
+
/* @__PURE__ */ l(
|
|
128
|
+
"span",
|
|
129
|
+
{
|
|
130
|
+
className: "size-7 rounded-full border",
|
|
131
|
+
style: {
|
|
132
|
+
backgroundColor: e.color,
|
|
133
|
+
borderColor: e.borderColor
|
|
134
|
+
},
|
|
135
|
+
"aria-hidden": !0
|
|
136
|
+
}
|
|
137
|
+
),
|
|
138
|
+
/* @__PURE__ */ l("span", { className: "w-full truncate text-foreground", children: e.label }),
|
|
139
|
+
b === void 0 ? null : /* @__PURE__ */ o("span", { className: "text-[0.7rem] leading-none text-muted-foreground", children: [
|
|
140
|
+
b,
|
|
141
|
+
" wells"
|
|
142
|
+
] })
|
|
143
|
+
]
|
|
144
|
+
},
|
|
145
|
+
e.id
|
|
146
|
+
);
|
|
147
|
+
}) })
|
|
148
|
+
] }) : null
|
|
149
|
+
] });
|
|
150
|
+
}
|
|
151
|
+
export {
|
|
152
|
+
ae as PlateMapGrid
|
|
153
|
+
};
|
|
154
|
+
//# sourceMappingURL=PlateMapGrid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlateMapGrid.js","sources":["../../../../src/components/composed/PlateMapEditor/PlateMapGrid.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport { PlatePaintGrid } from \"./PlatePaintGrid\";\nimport { allPositions, resolveDimensions } from \"./wellGrid\";\n\nimport type { WellShape } from \"./PlatePaintGrid\";\nimport type { PlateFormat, PlateMapGroupOption, WellField, WellId, WellRecord } from \"./types\";\n\nimport { Separator } from \"@/components/ui/separator\";\nimport { cn } from \"@/lib/utils\";\n\nfunction formatHoverFields<T extends WellRecord>(entry: T, fields: WellField<T>[]): string[] {\n return fields\n .map((f) => {\n const v = entry[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.map((item) => (f.options ?? []).find((o) => o.value === item)?.label ?? item).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\nexport interface PlateMapGridProps<T extends WellRecord = WellRecord> {\n format: PlateFormat;\n rows?: number;\n columns?: number;\n values: Map<WellId, T>;\n selection: Set<WellId>;\n onSelectionChange: (next: Set<WellId>) => void;\n /** Resolves the SVG fill color for a well. */\n colorForWell: (well: T | undefined, wellId: WellId) => string;\n /** Field schema used to build the default hover summary string. */\n fields?: WellField<T>[];\n /** Custom hover summary for the strip above the grid. */\n renderHoverSummary?: (well: T | undefined, wellId: WellId) => React.ReactNode;\n /** Fires whenever the currently hovered well changes (null on leave). */\n onHoveredWellChange?: (wellId: WellId | null) => void;\n /**\n * Controlled hovered well id. When provided, the panel reflects this value\n * instead of tracking hover internally — useful for syncing the hover\n * summary with external state (e.g. resetting it on a plate switch).\n */\n hoveredWellId?: WellId | null;\n /** Controls rendered to the left of the built-in select/deselect links. */\n toolbar?: React.ReactNode;\n /** Hide the built-in \"Select all\" / \"Deselect all\" links. */\n hideSelectionControls?: boolean;\n selectAllLabel?: string;\n deselectAllLabel?: string;\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 framed?: boolean;\n /** Render-prop that places a node inside each absolute-positioned well cell. */\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 onWellDoubleClick?: (wellId: WellId) => void;\n selectionFillMode?: \"selection\" | \"well\";\n flashWellId?: WellId;\n flashWellKey?: number;\n /** Fixed well size. When unset, the grid grows with the available width. */\n cellSize?: number;\n autoScale?: boolean;\n minCellSize?: number;\n maxCellSize?: number;\n /** Optional grouped well shortcuts rendered under the grid. */\n groups?: PlateMapGroupOption[];\n activeGroupId?: string;\n onGroupClick?: (group: PlateMapGroupOption) => void;\n className?: string;\n}\n\n/**\n * Card-agnostic plate grid panel. Renders the selection toolbar, hover\n * summary, the interactive `PlatePaintGrid`, and optional group shortcuts —\n * and owns the ephemeral hover state internally. The plate title, plate\n * selector, and actions menu are intentionally NOT part of this panel; compose\n * `PlateMapPlateSelector` and `PlateMapActionsMenu` around it for full layout\n * freedom.\n */\nexport function PlateMapGrid<T extends WellRecord = WellRecord>({\n format,\n rows,\n columns,\n values,\n selection,\n onSelectionChange,\n colorForWell,\n fields,\n renderHoverSummary,\n onHoveredWellChange,\n hoveredWellId,\n toolbar,\n hideSelectionControls = false,\n selectAllLabel = \"Select all\",\n deselectAllLabel = \"Deselect all\",\n emptyWellFillColor,\n wellShape,\n framed,\n wrapWell,\n highlightedWellIds,\n onWellDoubleClick,\n selectionFillMode,\n flashWellId,\n flashWellKey,\n cellSize,\n autoScale,\n minCellSize,\n maxCellSize,\n groups,\n activeGroupId,\n onGroupClick,\n className,\n}: PlateMapGridProps<T>) {\n const dims = resolveDimensions(format, rows, columns);\n const [internalHoverPos, setInternalHoverPos] = React.useState<WellId | null>(null);\n const isHoverControlled = hoveredWellId !== undefined;\n const hoverPos = isHoverControlled ? hoveredWellId : internalHoverPos;\n\n const selectAll = () => onSelectionChange(new Set(allPositions(dims)));\n const deselectAll = () => onSelectionChange(new Set());\n\n const hoverEntry = hoverPos ? values.get(hoverPos) : undefined;\n const hoverFields = hoverEntry && fields ? formatHoverFields(hoverEntry, fields) : [];\n const hoverSummary = hoverPos\n ? (renderHoverSummary?.(hoverEntry, hoverPos) ?? [hoverPos, ...hoverFields].join(\" • \"))\n : \" \";\n\n const showSelectionControls = !hideSelectionControls;\n const showToolbarRow = !!toolbar || showSelectionControls;\n\n return (\n <div data-slot=\"plate-map-grid\" className={cn(\"flex flex-col gap-1.5\", className)}>\n {showToolbarRow ? (\n <div className=\"flex flex-wrap items-center justify-start gap-3\">\n {toolbar}\n {showSelectionControls ? (\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 {selectAllLabel}\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 {deselectAllLabel}\n </button>\n </div>\n ) : null}\n </div>\n ) : null}\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={values}\n selection={selection}\n onSelectionChange={onSelectionChange}\n colorForWell={colorForWell}\n emptyWellFillColor={emptyWellFillColor}\n wellShape={wellShape}\n framed={framed}\n wrapWell={wrapWell}\n highlightedWellIds={highlightedWellIds}\n onWellHover={(wellId) => {\n if (!isHoverControlled) setInternalHoverPos(wellId);\n onHoveredWellChange?.(wellId);\n }}\n onWellDoubleClick={onWellDoubleClick}\n selectionFillMode={selectionFillMode}\n flashWellId={flashWellId}\n flashWellKey={flashWellKey}\n cellSize={cellSize}\n autoScale={autoScale}\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 </div>\n );\n}\n"],"names":["formatHoverFields","entry","fields","f","v","o","arr","item","PlateMapGrid","format","rows","columns","values","selection","onSelectionChange","colorForWell","renderHoverSummary","onHoveredWellChange","hoveredWellId","toolbar","hideSelectionControls","selectAllLabel","deselectAllLabel","emptyWellFillColor","wellShape","framed","wrapWell","highlightedWellIds","onWellDoubleClick","selectionFillMode","flashWellId","flashWellKey","cellSize","autoScale","minCellSize","maxCellSize","groups","activeGroupId","onGroupClick","className","dims","resolveDimensions","internalHoverPos","setInternalHoverPos","React","isHoverControlled","hoverPos","selectAll","allPositions","deselectAll","hoverEntry","hoverFields","hoverSummary","showSelectionControls","showToolbarRow","jsxs","cn","jsx","PlatePaintGrid","wellId","Fragment","Separator","group","isActive","count"],"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"),M=require("./WellManifestTable.cjs"),p=require("../../../lib/utils.cjs");function b({values:t,onChange:l,columns:s,fields:n,selection:i,onSelectionChange:r,emptyEntry:u,isPopulated:c,filterable:o,groupable:f,pageSize:d,pageSizeOptions:m,title:a,className:x}){return e.jsxs("div",{"data-slot":"plate-map-manifest",className:p.cn("flex flex-col gap-3",x),children:[a?e.jsx("div",{className:"text-base leading-snug font-medium",children:a}):null,e.jsx(M.WellManifestTable,{values:t,onChange:l,columns:s,fields:n,selection:i,onSelectionChange:r,emptyEntry:u,isPopulated:c,filterable:o,groupable:f,pageSize:d,pageSizeOptions:m})]})}exports.PlateMapManifest=b;
|
|
2
|
+
//# sourceMappingURL=PlateMapManifest.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlateMapManifest.cjs","sources":["../../../../src/components/composed/PlateMapEditor/PlateMapManifest.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport { WellManifestTable } from \"./WellManifestTable\";\n\nimport type { WellColumn, WellField, WellId, WellRecord } from \"./types\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface PlateMapManifestProps<T extends WellRecord = WellRecord> {\n values: Map<WellId, T>;\n onChange: (next: Map<WellId, T>) => void;\n columns: WellColumn<T>[];\n fields?: WellField<T>[];\n selection?: Set<WellId>;\n onSelectionChange?: (next: Set<WellId>) => void;\n /** Builds an empty record when a manifest row is freshly created. */\n emptyEntry: (id: WellId) => T;\n /** Filter for the manifest's \"hide empty\" mode. */\n isPopulated?: (row: T) => boolean;\n /** Enables the filter popover on the manifest table. */\n filterable?: boolean;\n /** Enables the group-by selector on the manifest table. */\n groupable?: boolean;\n pageSize?: number;\n pageSizeOptions?: number[];\n /** Optional heading rendered above the table. Omit for a bare table. */\n title?: React.ReactNode;\n className?: string;\n}\n\n/**\n * Card-agnostic sample-manifest panel. A thin wrapper over `WellManifestTable`\n * with an optional heading and no width constraint of its own, so it can span\n * the full screen width or sit inside any container the caller chooses.\n */\nexport function PlateMapManifest<T extends WellRecord = WellRecord>({\n values,\n onChange,\n columns,\n fields,\n selection,\n onSelectionChange,\n emptyEntry,\n isPopulated,\n filterable,\n groupable,\n pageSize,\n pageSizeOptions,\n title,\n className,\n}: PlateMapManifestProps<T>) {\n return (\n <div data-slot=\"plate-map-manifest\" className={cn(\"flex flex-col gap-3\", className)}>\n {title ? <div className=\"text-base leading-snug font-medium\">{title}</div> : null}\n <WellManifestTable<T>\n values={values}\n onChange={onChange}\n columns={columns}\n fields={fields}\n selection={selection}\n onSelectionChange={onSelectionChange}\n emptyEntry={emptyEntry}\n isPopulated={isPopulated}\n filterable={filterable}\n groupable={groupable}\n pageSize={pageSize}\n pageSizeOptions={pageSizeOptions}\n />\n </div>\n );\n}\n"],"names":["PlateMapManifest","values","onChange","columns","fields","selection","onSelectionChange","emptyEntry","isPopulated","filterable","groupable","pageSize","pageSizeOptions","title","className","jsxs","cn","jsx","WellManifestTable"],"mappings":"8LAmCO,SAASA,EAAoD,CAClE,OAAAC,EACA,SAAAC,EACA,QAAAC,EACA,OAAAC,EACA,UAAAC,EACA,kBAAAC,EACA,WAAAC,EACA,YAAAC,EACA,WAAAC,EACA,UAAAC,EACA,SAAAC,EACA,gBAAAC,EACA,MAAAC,EACA,UAAAC,CACF,EAA6B,CAC3B,OACEC,OAAC,OAAI,YAAU,qBAAqB,UAAWC,EAAAA,GAAG,sBAAuBF,CAAS,EAC/E,SAAA,CAAAD,EAAQI,EAAAA,IAAC,MAAA,CAAI,UAAU,qCAAsC,WAAM,EAAS,KAC7EA,EAAAA,IAACC,EAAAA,kBAAA,CACC,OAAAjB,EACA,SAAAC,EACA,QAAAC,EACA,OAAAC,EACA,UAAAC,EACA,kBAAAC,EACA,WAAAC,EACA,YAAAC,EACA,WAAAC,EACA,UAAAC,EACA,SAAAC,EACA,gBAAAC,CAAA,CAAA,CACF,EACF,CAEJ"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsxs as u, jsx as a } from "react/jsx-runtime";
|
|
2
|
+
import { WellManifestTable as g } from "./WellManifestTable.js";
|
|
3
|
+
import { cn as M } from "../../../lib/utils.js";
|
|
4
|
+
function v({
|
|
5
|
+
values: l,
|
|
6
|
+
onChange: t,
|
|
7
|
+
columns: n,
|
|
8
|
+
fields: s,
|
|
9
|
+
selection: i,
|
|
10
|
+
onSelectionChange: m,
|
|
11
|
+
emptyEntry: o,
|
|
12
|
+
isPopulated: r,
|
|
13
|
+
filterable: f,
|
|
14
|
+
groupable: p,
|
|
15
|
+
pageSize: c,
|
|
16
|
+
pageSizeOptions: d,
|
|
17
|
+
title: e,
|
|
18
|
+
className: x
|
|
19
|
+
}) {
|
|
20
|
+
return /* @__PURE__ */ u("div", { "data-slot": "plate-map-manifest", className: M("flex flex-col gap-3", x), children: [
|
|
21
|
+
e ? /* @__PURE__ */ a("div", { className: "text-base leading-snug font-medium", children: e }) : null,
|
|
22
|
+
/* @__PURE__ */ a(
|
|
23
|
+
g,
|
|
24
|
+
{
|
|
25
|
+
values: l,
|
|
26
|
+
onChange: t,
|
|
27
|
+
columns: n,
|
|
28
|
+
fields: s,
|
|
29
|
+
selection: i,
|
|
30
|
+
onSelectionChange: m,
|
|
31
|
+
emptyEntry: o,
|
|
32
|
+
isPopulated: r,
|
|
33
|
+
filterable: f,
|
|
34
|
+
groupable: p,
|
|
35
|
+
pageSize: c,
|
|
36
|
+
pageSizeOptions: d
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
] });
|
|
40
|
+
}
|
|
41
|
+
export {
|
|
42
|
+
v as PlateMapManifest
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=PlateMapManifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlateMapManifest.js","sources":["../../../../src/components/composed/PlateMapEditor/PlateMapManifest.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport { WellManifestTable } from \"./WellManifestTable\";\n\nimport type { WellColumn, WellField, WellId, WellRecord } from \"./types\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport interface PlateMapManifestProps<T extends WellRecord = WellRecord> {\n values: Map<WellId, T>;\n onChange: (next: Map<WellId, T>) => void;\n columns: WellColumn<T>[];\n fields?: WellField<T>[];\n selection?: Set<WellId>;\n onSelectionChange?: (next: Set<WellId>) => void;\n /** Builds an empty record when a manifest row is freshly created. */\n emptyEntry: (id: WellId) => T;\n /** Filter for the manifest's \"hide empty\" mode. */\n isPopulated?: (row: T) => boolean;\n /** Enables the filter popover on the manifest table. */\n filterable?: boolean;\n /** Enables the group-by selector on the manifest table. */\n groupable?: boolean;\n pageSize?: number;\n pageSizeOptions?: number[];\n /** Optional heading rendered above the table. Omit for a bare table. */\n title?: React.ReactNode;\n className?: string;\n}\n\n/**\n * Card-agnostic sample-manifest panel. A thin wrapper over `WellManifestTable`\n * with an optional heading and no width constraint of its own, so it can span\n * the full screen width or sit inside any container the caller chooses.\n */\nexport function PlateMapManifest<T extends WellRecord = WellRecord>({\n values,\n onChange,\n columns,\n fields,\n selection,\n onSelectionChange,\n emptyEntry,\n isPopulated,\n filterable,\n groupable,\n pageSize,\n pageSizeOptions,\n title,\n className,\n}: PlateMapManifestProps<T>) {\n return (\n <div data-slot=\"plate-map-manifest\" className={cn(\"flex flex-col gap-3\", className)}>\n {title ? <div className=\"text-base leading-snug font-medium\">{title}</div> : null}\n <WellManifestTable<T>\n values={values}\n onChange={onChange}\n columns={columns}\n fields={fields}\n selection={selection}\n onSelectionChange={onSelectionChange}\n emptyEntry={emptyEntry}\n isPopulated={isPopulated}\n filterable={filterable}\n groupable={groupable}\n pageSize={pageSize}\n pageSizeOptions={pageSizeOptions}\n />\n </div>\n );\n}\n"],"names":["PlateMapManifest","values","onChange","columns","fields","selection","onSelectionChange","emptyEntry","isPopulated","filterable","groupable","pageSize","pageSizeOptions","title","className","jsxs","cn","jsx","WellManifestTable"],"mappings":";;;AAmCO,SAASA,EAAoD;AAAA,EAClE,QAAAC;AAAA,EACA,UAAAC;AAAA,EACA,SAAAC;AAAA,EACA,QAAAC;AAAA,EACA,WAAAC;AAAA,EACA,mBAAAC;AAAA,EACA,YAAAC;AAAA,EACA,aAAAC;AAAA,EACA,YAAAC;AAAA,EACA,WAAAC;AAAA,EACA,UAAAC;AAAA,EACA,iBAAAC;AAAA,EACA,OAAAC;AAAA,EACA,WAAAC;AACF,GAA6B;AAC3B,SACE,gBAAAC,EAAC,SAAI,aAAU,sBAAqB,WAAWC,EAAG,uBAAuBF,CAAS,GAC/E,UAAA;AAAA,IAAAD,IAAQ,gBAAAI,EAAC,OAAA,EAAI,WAAU,sCAAsC,aAAM,IAAS;AAAA,IAC7E,gBAAAA;AAAA,MAACC;AAAA,MAAA;AAAA,QACC,QAAAjB;AAAA,QACA,UAAAC;AAAA,QACA,SAAAC;AAAA,QACA,QAAAC;AAAA,QACA,WAAAC;AAAA,QACA,mBAAAC;AAAA,QACA,YAAAC;AAAA,QACA,aAAAC;AAAA,QACA,YAAAC;AAAA,QACA,WAAAC;AAAA,QACA,UAAAC;AAAA,QACA,iBAAAC;AAAA,MAAA;AAAA,IAAA;AAAA,EACF,GACF;AAEJ;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),t=require("lucide-react"),f=require("../../ui/badge.cjs"),u=require("../../ui/button.cjs"),s=require("../../ui/dropdown-menu.cjs"),g=require("../../ui/toggle-group.cjs"),x=require("../../../lib/utils.cjs");function w({plate:i,selected:l}){return e.jsx(f.Badge,{variant:l?"info":"outline",className:"max-w-44",children:e.jsx("span",{className:"truncate",children:i.label??i.barcode})})}function M({plates:i=[],activePlateId:l,onPlateChange:a,onAddPlate:r,onRemovePlate:j,addPlateLabel:o="Add Plate",removePlateLabel:v="Remove plate",label:h="Plate",variant:p="dropdown",align:N="start",side:y="bottom",className:m}){const c=i.find(n=>n.id===l)??(l?void 0:i[0]);return p==="tabs"?e.jsxs("div",{className:x.cn("inline-flex flex-wrap items-center gap-1",m),"data-slot":"plate-tabs",children:[e.jsx(g.ToggleGroup,{type:"single",variant:"outline",size:"sm",value:c?.id??"","aria-label":h,onValueChange:n=>{n&&a?.(n)},children:i.map(n=>{const d=n.id===c?.id,b=!!j&&i.length>1;return e.jsxs("div",{className:"inline-flex items-stretch",children:[e.jsxs(g.ToggleGroupItem,{value:n.id,disabled:n.disabled||!a,"aria-label":n.label??n.barcode,className:x.cn(b&&"rounded-r-none border-r-0"),children:[e.jsx("span",{className:"truncate",children:n.label??n.barcode}),n.count===void 0?null:e.jsx(f.Badge,{variant:d?"secondary":"outline",className:"ml-1 h-4 px-1 text-[0.65rem]",children:n.count})]}),b?e.jsx(u.Button,{type:"button",variant:"outline",size:"icon-sm","aria-label":`${v} ${n.label??n.barcode}`,className:"rounded-l-none",onClick:()=>j?.(n.id),children:e.jsx(t.X,{"aria-hidden":!0})}):null]},n.id)})}),r?e.jsx(u.Button,{type:"button",variant:"ghost",size:"icon-sm","aria-label":o,onClick:()=>r(),children:e.jsx(t.Plus,{"aria-hidden":!0})}):null]}):i.length===0?e.jsxs(u.Button,{type:"button",variant:"outline",size:"sm",disabled:!r,"aria-label":o,className:x.cn("min-w-28 justify-start",m),onClick:()=>r?.(),children:[e.jsx(t.Plus,{"aria-hidden":!0}),o]}):e.jsxs(s.DropdownMenu,{children:[e.jsx(s.DropdownMenuTrigger,{asChild:!0,children:e.jsxs(u.Button,{type:"button",variant:"outline",size:"sm","aria-label":h,className:x.cn("min-w-36 justify-between",m),children:[e.jsx("span",{className:"flex min-w-0 items-center gap-1.5",children:c?e.jsx(w,{plate:c,selected:!0}):e.jsx("span",{children:o})}),e.jsx(t.ChevronDown,{"aria-hidden":!0})]})}),e.jsxs(s.DropdownMenuContent,{align:N,side:y,className:"w-64",children:[e.jsx(s.DropdownMenuGroup,{children:i.map(n=>{const d=n.id===c?.id;return e.jsxs(s.DropdownMenuItem,{disabled:n.disabled||!a,onClick:()=>a?.(n.id),children:[d?e.jsx(t.Check,{"aria-hidden":!0}):e.jsx("span",{className:"size-4","aria-hidden":!0}),e.jsxs("span",{className:"flex min-w-0 flex-1 items-center justify-between gap-2",children:[e.jsx(w,{plate:n,selected:d}),n.count===void 0?null:e.jsxs("span",{className:"shrink-0 text-xs text-muted-foreground",children:[n.count," wells"]})]})]},n.id)})}),r?e.jsxs(e.Fragment,{children:[e.jsx(s.DropdownMenuSeparator,{}),e.jsxs(s.DropdownMenuItem,{onClick:()=>r(),children:[e.jsx(t.Plus,{"aria-hidden":!0}),o]})]}):null]})]})}exports.PlateMapPlateSelector=M;
|
|
2
|
+
//# sourceMappingURL=PlateMapPlateSelector.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlateMapPlateSelector.cjs","sources":["../../../../src/components/composed/PlateMapEditor/PlateMapPlateSelector.tsx"],"sourcesContent":["import { Check, ChevronDown, Plus, X } from \"lucide-react\";\n\nimport type { PlateMapPlateOption } from \"./types\";\n\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport { ToggleGroup, ToggleGroupItem } from \"@/components/ui/toggle-group\";\nimport { cn } from \"@/lib/utils\";\n\nexport type PlateMapPlateSelectorVariant = \"dropdown\" | \"tabs\";\n\nexport interface PlateMapPlateSelectorProps {\n plates?: PlateMapPlateOption[];\n activePlateId?: string;\n onPlateChange?: (plateId: string) => void;\n onAddPlate?: () => void;\n /** When supplied, the tabs variant renders a delete affordance per plate. */\n onRemovePlate?: (plateId: string) => void;\n addPlateLabel?: string;\n removePlateLabel?: string;\n label?: string;\n /** Layout. `\"dropdown\"` is the default single-trigger menu, `\"tabs\"` is a horizontal tab strip. */\n variant?: PlateMapPlateSelectorVariant;\n align?: \"start\" | \"center\" | \"end\";\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n className?: string;\n}\n\nfunction PlatePill({ plate, selected }: { plate: PlateMapPlateOption; selected?: boolean }) {\n return (\n <Badge variant={selected ? \"info\" : \"outline\"} className=\"max-w-44\">\n <span className=\"truncate\">{plate.label ?? plate.barcode}</span>\n </Badge>\n );\n}\n\nexport function PlateMapPlateSelector({\n plates = [],\n activePlateId,\n onPlateChange,\n onAddPlate,\n onRemovePlate,\n addPlateLabel = \"Add Plate\",\n removePlateLabel = \"Remove plate\",\n label = \"Plate\",\n variant = \"dropdown\",\n align = \"start\",\n side = \"bottom\",\n className,\n}: PlateMapPlateSelectorProps) {\n const activePlate = plates.find((plate) => plate.id === activePlateId) ?? (activePlateId ? undefined : plates[0]);\n\n if (variant === \"tabs\") {\n return (\n <div className={cn(\"inline-flex flex-wrap items-center gap-1\", className)} data-slot=\"plate-tabs\">\n <ToggleGroup\n type=\"single\"\n variant=\"outline\"\n size=\"sm\"\n value={activePlate?.id ?? \"\"}\n aria-label={label}\n onValueChange={(value) => {\n if (value) onPlateChange?.(value);\n }}\n >\n {plates.map((plate) => {\n const selected = plate.id === activePlate?.id;\n const canRemove = !!onRemovePlate && plates.length > 1;\n return (\n <div key={plate.id} className=\"inline-flex items-stretch\">\n <ToggleGroupItem\n value={plate.id}\n disabled={plate.disabled || !onPlateChange}\n aria-label={plate.label ?? plate.barcode}\n className={cn(canRemove && \"rounded-r-none border-r-0\")}\n >\n <span className=\"truncate\">{plate.label ?? plate.barcode}</span>\n {plate.count === undefined ? null : (\n <Badge variant={selected ? \"secondary\" : \"outline\"} className=\"ml-1 h-4 px-1 text-[0.65rem]\">\n {plate.count}\n </Badge>\n )}\n </ToggleGroupItem>\n {canRemove ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon-sm\"\n aria-label={`${removePlateLabel} ${plate.label ?? plate.barcode}`}\n className=\"rounded-l-none\"\n onClick={() => onRemovePlate?.(plate.id)}\n >\n <X aria-hidden />\n </Button>\n ) : null}\n </div>\n );\n })}\n </ToggleGroup>\n {onAddPlate ? (\n <Button type=\"button\" variant=\"ghost\" size=\"icon-sm\" aria-label={addPlateLabel} onClick={() => onAddPlate()}>\n <Plus aria-hidden />\n </Button>\n ) : null}\n </div>\n );\n }\n\n if (plates.length === 0) {\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={!onAddPlate}\n aria-label={addPlateLabel}\n className={cn(\"min-w-28 justify-start\", className)}\n onClick={() => onAddPlate?.()}\n >\n <Plus aria-hidden />\n {addPlateLabel}\n </Button>\n );\n }\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n aria-label={label}\n className={cn(\"min-w-36 justify-between\", className)}\n >\n <span className=\"flex min-w-0 items-center gap-1.5\">\n {activePlate ? <PlatePill plate={activePlate} selected /> : <span>{addPlateLabel}</span>}\n </span>\n <ChevronDown aria-hidden />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align={align} side={side} className=\"w-64\">\n <DropdownMenuGroup>\n {plates.map((plate) => {\n const selected = plate.id === activePlate?.id;\n return (\n <DropdownMenuItem\n key={plate.id}\n disabled={plate.disabled || !onPlateChange}\n onClick={() => onPlateChange?.(plate.id)}\n >\n {selected ? <Check aria-hidden /> : <span className=\"size-4\" aria-hidden />}\n <span className=\"flex min-w-0 flex-1 items-center justify-between gap-2\">\n <PlatePill plate={plate} selected={selected} />\n {plate.count === undefined ? null : (\n <span className=\"shrink-0 text-xs text-muted-foreground\">{plate.count} wells</span>\n )}\n </span>\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuGroup>\n {onAddPlate ? (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem onClick={() => onAddPlate()}>\n <Plus aria-hidden />\n {addPlateLabel}\n </DropdownMenuItem>\n </>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n"],"names":["PlatePill","plate","selected","Badge","jsx","PlateMapPlateSelector","plates","activePlateId","onPlateChange","onAddPlate","onRemovePlate","addPlateLabel","removePlateLabel","label","variant","align","side","className","activePlate","jsxs","cn","ToggleGroup","value","canRemove","ToggleGroupItem","Button","X","Plus","DropdownMenu","DropdownMenuTrigger","ChevronDown","DropdownMenuContent","DropdownMenuGroup","DropdownMenuItem","Check","Fragment","DropdownMenuSeparator"],"mappings":"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"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { jsxs as n, jsx as i, Fragment as C } from "react/jsx-runtime";
|
|
2
|
+
import { X as D, Plus as h, ChevronDown as M, Check as z } from "lucide-react";
|
|
3
|
+
import { Badge as x } from "../../ui/badge.js";
|
|
4
|
+
import { Button as c } from "../../ui/button.js";
|
|
5
|
+
import { DropdownMenu as j, DropdownMenuTrigger as G, DropdownMenuContent as T, DropdownMenuGroup as B, DropdownMenuItem as w, DropdownMenuSeparator as S } from "../../ui/dropdown-menu.js";
|
|
6
|
+
import { ToggleGroup as $, ToggleGroupItem as F } from "../../ui/toggle-group.js";
|
|
7
|
+
import { cn as u } from "../../../lib/utils.js";
|
|
8
|
+
function v({ plate: r, selected: l }) {
|
|
9
|
+
return /* @__PURE__ */ i(x, { variant: l ? "info" : "outline", className: "max-w-44", children: /* @__PURE__ */ i("span", { className: "truncate", children: r.label ?? r.barcode }) });
|
|
10
|
+
}
|
|
11
|
+
function J({
|
|
12
|
+
plates: r = [],
|
|
13
|
+
activePlateId: l,
|
|
14
|
+
onPlateChange: s,
|
|
15
|
+
onAddPlate: o,
|
|
16
|
+
onRemovePlate: b,
|
|
17
|
+
addPlateLabel: t = "Add Plate",
|
|
18
|
+
removePlateLabel: g = "Remove plate",
|
|
19
|
+
label: f = "Plate",
|
|
20
|
+
variant: N = "dropdown",
|
|
21
|
+
align: y = "start",
|
|
22
|
+
side: k = "bottom",
|
|
23
|
+
className: m
|
|
24
|
+
}) {
|
|
25
|
+
const a = r.find((e) => e.id === l) ?? (l ? void 0 : r[0]);
|
|
26
|
+
return N === "tabs" ? /* @__PURE__ */ n("div", { className: u("inline-flex flex-wrap items-center gap-1", m), "data-slot": "plate-tabs", children: [
|
|
27
|
+
/* @__PURE__ */ i(
|
|
28
|
+
$,
|
|
29
|
+
{
|
|
30
|
+
type: "single",
|
|
31
|
+
variant: "outline",
|
|
32
|
+
size: "sm",
|
|
33
|
+
value: a?.id ?? "",
|
|
34
|
+
"aria-label": f,
|
|
35
|
+
onValueChange: (e) => {
|
|
36
|
+
e && s?.(e);
|
|
37
|
+
},
|
|
38
|
+
children: r.map((e) => {
|
|
39
|
+
const d = e.id === a?.id, p = !!b && r.length > 1;
|
|
40
|
+
return /* @__PURE__ */ n("div", { className: "inline-flex items-stretch", children: [
|
|
41
|
+
/* @__PURE__ */ n(
|
|
42
|
+
F,
|
|
43
|
+
{
|
|
44
|
+
value: e.id,
|
|
45
|
+
disabled: e.disabled || !s,
|
|
46
|
+
"aria-label": e.label ?? e.barcode,
|
|
47
|
+
className: u(p && "rounded-r-none border-r-0"),
|
|
48
|
+
children: [
|
|
49
|
+
/* @__PURE__ */ i("span", { className: "truncate", children: e.label ?? e.barcode }),
|
|
50
|
+
e.count === void 0 ? null : /* @__PURE__ */ i(x, { variant: d ? "secondary" : "outline", className: "ml-1 h-4 px-1 text-[0.65rem]", children: e.count })
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
),
|
|
54
|
+
p ? /* @__PURE__ */ i(
|
|
55
|
+
c,
|
|
56
|
+
{
|
|
57
|
+
type: "button",
|
|
58
|
+
variant: "outline",
|
|
59
|
+
size: "icon-sm",
|
|
60
|
+
"aria-label": `${g} ${e.label ?? e.barcode}`,
|
|
61
|
+
className: "rounded-l-none",
|
|
62
|
+
onClick: () => b?.(e.id),
|
|
63
|
+
children: /* @__PURE__ */ i(D, { "aria-hidden": !0 })
|
|
64
|
+
}
|
|
65
|
+
) : null
|
|
66
|
+
] }, e.id);
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
),
|
|
70
|
+
o ? /* @__PURE__ */ i(c, { type: "button", variant: "ghost", size: "icon-sm", "aria-label": t, onClick: () => o(), children: /* @__PURE__ */ i(h, { "aria-hidden": !0 }) }) : null
|
|
71
|
+
] }) : r.length === 0 ? /* @__PURE__ */ n(
|
|
72
|
+
c,
|
|
73
|
+
{
|
|
74
|
+
type: "button",
|
|
75
|
+
variant: "outline",
|
|
76
|
+
size: "sm",
|
|
77
|
+
disabled: !o,
|
|
78
|
+
"aria-label": t,
|
|
79
|
+
className: u("min-w-28 justify-start", m),
|
|
80
|
+
onClick: () => o?.(),
|
|
81
|
+
children: [
|
|
82
|
+
/* @__PURE__ */ i(h, { "aria-hidden": !0 }),
|
|
83
|
+
t
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
) : /* @__PURE__ */ n(j, { children: [
|
|
87
|
+
/* @__PURE__ */ i(G, { asChild: !0, children: /* @__PURE__ */ n(
|
|
88
|
+
c,
|
|
89
|
+
{
|
|
90
|
+
type: "button",
|
|
91
|
+
variant: "outline",
|
|
92
|
+
size: "sm",
|
|
93
|
+
"aria-label": f,
|
|
94
|
+
className: u("min-w-36 justify-between", m),
|
|
95
|
+
children: [
|
|
96
|
+
/* @__PURE__ */ i("span", { className: "flex min-w-0 items-center gap-1.5", children: a ? /* @__PURE__ */ i(v, { plate: a, selected: !0 }) : /* @__PURE__ */ i("span", { children: t }) }),
|
|
97
|
+
/* @__PURE__ */ i(M, { "aria-hidden": !0 })
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
) }),
|
|
101
|
+
/* @__PURE__ */ n(T, { align: y, side: k, className: "w-64", children: [
|
|
102
|
+
/* @__PURE__ */ i(B, { children: r.map((e) => {
|
|
103
|
+
const d = e.id === a?.id;
|
|
104
|
+
return /* @__PURE__ */ n(
|
|
105
|
+
w,
|
|
106
|
+
{
|
|
107
|
+
disabled: e.disabled || !s,
|
|
108
|
+
onClick: () => s?.(e.id),
|
|
109
|
+
children: [
|
|
110
|
+
d ? /* @__PURE__ */ i(z, { "aria-hidden": !0 }) : /* @__PURE__ */ i("span", { className: "size-4", "aria-hidden": !0 }),
|
|
111
|
+
/* @__PURE__ */ n("span", { className: "flex min-w-0 flex-1 items-center justify-between gap-2", children: [
|
|
112
|
+
/* @__PURE__ */ i(v, { plate: e, selected: d }),
|
|
113
|
+
e.count === void 0 ? null : /* @__PURE__ */ n("span", { className: "shrink-0 text-xs text-muted-foreground", children: [
|
|
114
|
+
e.count,
|
|
115
|
+
" wells"
|
|
116
|
+
] })
|
|
117
|
+
] })
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
e.id
|
|
121
|
+
);
|
|
122
|
+
}) }),
|
|
123
|
+
o ? /* @__PURE__ */ n(C, { children: [
|
|
124
|
+
/* @__PURE__ */ i(S, {}),
|
|
125
|
+
/* @__PURE__ */ n(w, { onClick: () => o(), children: [
|
|
126
|
+
/* @__PURE__ */ i(h, { "aria-hidden": !0 }),
|
|
127
|
+
t
|
|
128
|
+
] })
|
|
129
|
+
] }) : null
|
|
130
|
+
] })
|
|
131
|
+
] });
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
J as PlateMapPlateSelector
|
|
135
|
+
};
|
|
136
|
+
//# sourceMappingURL=PlateMapPlateSelector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlateMapPlateSelector.js","sources":["../../../../src/components/composed/PlateMapEditor/PlateMapPlateSelector.tsx"],"sourcesContent":["import { Check, ChevronDown, Plus, X } from \"lucide-react\";\n\nimport type { PlateMapPlateOption } from \"./types\";\n\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport { ToggleGroup, ToggleGroupItem } from \"@/components/ui/toggle-group\";\nimport { cn } from \"@/lib/utils\";\n\nexport type PlateMapPlateSelectorVariant = \"dropdown\" | \"tabs\";\n\nexport interface PlateMapPlateSelectorProps {\n plates?: PlateMapPlateOption[];\n activePlateId?: string;\n onPlateChange?: (plateId: string) => void;\n onAddPlate?: () => void;\n /** When supplied, the tabs variant renders a delete affordance per plate. */\n onRemovePlate?: (plateId: string) => void;\n addPlateLabel?: string;\n removePlateLabel?: string;\n label?: string;\n /** Layout. `\"dropdown\"` is the default single-trigger menu, `\"tabs\"` is a horizontal tab strip. */\n variant?: PlateMapPlateSelectorVariant;\n align?: \"start\" | \"center\" | \"end\";\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n className?: string;\n}\n\nfunction PlatePill({ plate, selected }: { plate: PlateMapPlateOption; selected?: boolean }) {\n return (\n <Badge variant={selected ? \"info\" : \"outline\"} className=\"max-w-44\">\n <span className=\"truncate\">{plate.label ?? plate.barcode}</span>\n </Badge>\n );\n}\n\nexport function PlateMapPlateSelector({\n plates = [],\n activePlateId,\n onPlateChange,\n onAddPlate,\n onRemovePlate,\n addPlateLabel = \"Add Plate\",\n removePlateLabel = \"Remove plate\",\n label = \"Plate\",\n variant = \"dropdown\",\n align = \"start\",\n side = \"bottom\",\n className,\n}: PlateMapPlateSelectorProps) {\n const activePlate = plates.find((plate) => plate.id === activePlateId) ?? (activePlateId ? undefined : plates[0]);\n\n if (variant === \"tabs\") {\n return (\n <div className={cn(\"inline-flex flex-wrap items-center gap-1\", className)} data-slot=\"plate-tabs\">\n <ToggleGroup\n type=\"single\"\n variant=\"outline\"\n size=\"sm\"\n value={activePlate?.id ?? \"\"}\n aria-label={label}\n onValueChange={(value) => {\n if (value) onPlateChange?.(value);\n }}\n >\n {plates.map((plate) => {\n const selected = plate.id === activePlate?.id;\n const canRemove = !!onRemovePlate && plates.length > 1;\n return (\n <div key={plate.id} className=\"inline-flex items-stretch\">\n <ToggleGroupItem\n value={plate.id}\n disabled={plate.disabled || !onPlateChange}\n aria-label={plate.label ?? plate.barcode}\n className={cn(canRemove && \"rounded-r-none border-r-0\")}\n >\n <span className=\"truncate\">{plate.label ?? plate.barcode}</span>\n {plate.count === undefined ? null : (\n <Badge variant={selected ? \"secondary\" : \"outline\"} className=\"ml-1 h-4 px-1 text-[0.65rem]\">\n {plate.count}\n </Badge>\n )}\n </ToggleGroupItem>\n {canRemove ? (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon-sm\"\n aria-label={`${removePlateLabel} ${plate.label ?? plate.barcode}`}\n className=\"rounded-l-none\"\n onClick={() => onRemovePlate?.(plate.id)}\n >\n <X aria-hidden />\n </Button>\n ) : null}\n </div>\n );\n })}\n </ToggleGroup>\n {onAddPlate ? (\n <Button type=\"button\" variant=\"ghost\" size=\"icon-sm\" aria-label={addPlateLabel} onClick={() => onAddPlate()}>\n <Plus aria-hidden />\n </Button>\n ) : null}\n </div>\n );\n }\n\n if (plates.length === 0) {\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={!onAddPlate}\n aria-label={addPlateLabel}\n className={cn(\"min-w-28 justify-start\", className)}\n onClick={() => onAddPlate?.()}\n >\n <Plus aria-hidden />\n {addPlateLabel}\n </Button>\n );\n }\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n aria-label={label}\n className={cn(\"min-w-36 justify-between\", className)}\n >\n <span className=\"flex min-w-0 items-center gap-1.5\">\n {activePlate ? <PlatePill plate={activePlate} selected /> : <span>{addPlateLabel}</span>}\n </span>\n <ChevronDown aria-hidden />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align={align} side={side} className=\"w-64\">\n <DropdownMenuGroup>\n {plates.map((plate) => {\n const selected = plate.id === activePlate?.id;\n return (\n <DropdownMenuItem\n key={plate.id}\n disabled={plate.disabled || !onPlateChange}\n onClick={() => onPlateChange?.(plate.id)}\n >\n {selected ? <Check aria-hidden /> : <span className=\"size-4\" aria-hidden />}\n <span className=\"flex min-w-0 flex-1 items-center justify-between gap-2\">\n <PlatePill plate={plate} selected={selected} />\n {plate.count === undefined ? null : (\n <span className=\"shrink-0 text-xs text-muted-foreground\">{plate.count} wells</span>\n )}\n </span>\n </DropdownMenuItem>\n );\n })}\n </DropdownMenuGroup>\n {onAddPlate ? (\n <>\n <DropdownMenuSeparator />\n <DropdownMenuItem onClick={() => onAddPlate()}>\n <Plus aria-hidden />\n {addPlateLabel}\n </DropdownMenuItem>\n </>\n ) : null}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n"],"names":["PlatePill","plate","selected","Badge","jsx","PlateMapPlateSelector","plates","activePlateId","onPlateChange","onAddPlate","onRemovePlate","addPlateLabel","removePlateLabel","label","variant","align","side","className","activePlate","jsxs","cn","ToggleGroup","value","canRemove","ToggleGroupItem","Button","X","Plus","DropdownMenu","DropdownMenuTrigger","ChevronDown","DropdownMenuContent","DropdownMenuGroup","DropdownMenuItem","Check","Fragment","DropdownMenuSeparator"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("react/jsx-runtime"),_e=require("react"),v=require("./wellGrid.cjs"),ee=require("../../../lib/utils.cjs");function ye(n){const l=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const o in n)if(o!=="default"){const s=Object.getOwnPropertyDescriptor(n,o);Object.defineProperty(l,o,s.get?s:{enumerable:!0,get:()=>n[o]})}}return l.default=n,Object.freeze(l)}const L=ye(_e),we=34,be=24,xe=72,Ae=36,c=26,X=12,Me=1,oe=16,Pe=9,Te=5,N=1,z=4,te=4,Oe=3,se=5,q=650,ne="var(--surface-container)",le="var(--border)";function Re(n,l){const o=[];for(let s=0;s<n;s++)o.push(u.jsx("text",{x:c+s*l+l/2,y:c/2,textAnchor:"middle",fontSize:oe,className:"fill-muted-foreground",children:s+1},`c${s}`));return o}function je(n,l){const o=[];for(let s=0;s<n;s++)o.push(u.jsx("text",{x:c-Pe,y:c+s*l+l/2+Te,textAnchor:"end",fontSize:oe,className:"fill-muted-foreground",children:v.rowLabel(s)},`r${s}`));return o}function ke(n,l,o){const{dims:s,cellSize:e,values:E,selection:P,dragPositions:_,colorForWell:d,emptyWellFillColor:h,selectedFillColor:y,selectedFillOpacity:b,selectionFillMode:O,wellShape:m}=n,p=v.pos(l,o,s.columns),x=E.get(p),w=P.has(p)||_.has(p),k=x===void 0&&h!==null?h:d(x,p),T=w&&!(w&&O==="well"&&x!==void 0),R=T?y:k,j=T?b:void 0;if(m==="circle"){const F=c+o*e+e/2,A=c+l*e+e/2,S=e/2-N;return u.jsx("circle",{cx:F,cy:A,r:S,fill:R,fillOpacity:j,stroke:"none","data-well":p,"data-selected":w?"true":void 0},p)}return u.jsx("rect",{x:c+o*e,y:c+l*e,width:e,height:e,fill:R,fillOpacity:j,stroke:"none","data-well":p,"data-selected":w?"true":void 0},p)}function Fe({dims:n,...l}){const o=[];for(let s=0;s<n.rows;s++)for(let e=0;e<n.columns;e++)o.push(ke({dims:n,...l},s,e));return o}function Se(n,l,o){const s=[],e=c,E=c,P=c+n.columns*l,_=c+n.rows*l;for(let d=0;d<=n.columns;d++){const h=c+d*l;s.push(u.jsx("line",{x1:h,y1:E,x2:h,y2:_,stroke:o,strokeWidth:z,vectorEffect:"non-scaling-stroke",shapeRendering:"crispEdges",pointerEvents:"none","data-plate-grid-line":`column-${d}`},`grid-col-${d}`))}for(let d=0;d<=n.rows;d++){const h=c+d*l;s.push(u.jsx("line",{x1:e,y1:h,x2:P,y2:h,stroke:o,strokeWidth:z,vectorEffect:"non-scaling-stroke",shapeRendering:"crispEdges",pointerEvents:"none","data-plate-grid-line":`row-${d}`},`grid-row-${d}`))}return s}function De(n,l,o){const{dims:s,cellSize:e,selection:E,dragPositions:P,selectedBorderColor:_,flashWellId:d,flashWellKey:h,wellShape:y,highlightedWellIds:b,highlightBorderColor:O}=n,m=v.pos(l,o,s.columns),p=E.has(m)||P.has(m),x=d===m,w=!p&&b.has(m);if(!p&&!x&&!w)return null;const k=c+o*e+N,I=c+l*e+N,T=e-N*2,R=c+o*e+e/2,j=c+l*e+e/2,F=e/2-N,A=y==="circle",S=A?{cx:R,cy:j,r:F}:{x:k,y:I,width:T,height:T},M=(r,D,C)=>{const a={...S,...r,pointerEvents:"none"};return A?u.jsx("circle",{...a,children:D},C):u.jsx("rect",{...a,children:D},C)},$=u.jsxs(u.Fragment,{children:[u.jsx("animate",{attributeName:"fill-opacity",values:"0.24;0.1;0",dur:`${q}ms`,fill:"freeze"}),u.jsx("animate",{attributeName:"stroke-opacity",values:"0.92;0.42;0",dur:`${q}ms`,fill:"freeze"}),u.jsx("animate",{attributeName:"stroke-width",values:`${se};${te}`,dur:`${q}ms`,fill:"freeze"})]});return u.jsxs("g",{children:[p?M({fill:"none",stroke:_,strokeWidth:te,"data-well-selection":m}):null,w?M({fill:"none",stroke:O,strokeWidth:Oe,"data-well-highlight":m}):null,x?M({fill:_,fillOpacity:.24,stroke:_,strokeOpacity:.92,strokeWidth:se,"data-well-flash":m},$,`${m}-${h}`):null]},`overlay-${m}`)}function Ce({dims:n,...l}){const o=[];for(let s=0;s<n.rows;s++)for(let e=0;e<n.columns;e++){const E=De({dims:n,...l},s,e);E&&o.push(E)}return o}function We({format:n,rows:l,columns:o,values:s,selection:e,onSelectionChange:E,colorForWell:P,emptyWellFillColor:_=ne,wellShape:d="rect",framed:h=!1,cellSize:y,autoScale:b=!0,minCellSize:O=be,maxCellSize:m,borderColor:p=le,selectedBorderColor:x="var(--color-primary)",selectedFillColor:w="var(--color-primary)",selectedFillOpacity:k=.18,selectionFillMode:I="selection",flashWellId:T,flashWellKey:R,highlightedWellIds:j,highlightBorderColor:F="var(--color-primary)",onWellHover:A,onWellDoubleClick:S,wrapWell:M,className:$}){const r=v.resolveDimensions(n,l,o),D=L.useRef(null),C=L.useRef(null),[a,B]=L.useState(null),[U,re]=L.useState();L.useLayoutEffect(()=>{const i=D.current;if(!i||!b||y!==void 0)return;const t=()=>re(i.clientWidth);if(t(),typeof ResizeObserver>"u")return;const f=new ResizeObserver(t);return f.observe(i),()=>f.disconnect()},[b,y]);const g=L.useMemo(()=>{if(!b||y!==void 0||!U)return y??we;const i=m??(r.columns>12?Ae:xe),t=h?(X+Me)*2:0,f=Math.floor((U-c-t)/r.columns);return Math.max(O,Math.min(i,f))},[b,y,U,r.columns,h,m,O]),G=L.useCallback(i=>{const t=C.current;if(!t)return null;const f=t.getBoundingClientRect(),W=i.clientX-f.left-c,ve=i.clientY-f.top-c,H=Math.floor(W/g),K=Math.floor(ve/g);return K<0||K>=r.rows||H<0||H>=r.columns?null:{r:K,c:H}},[g,r.rows,r.columns]),ce=i=>{const t=G(i);if(!t)return;const f=i.shiftKey?"add":i.altKey?"remove":"replace";B({start:t,cur:t,mode:f})},ie=i=>{const t=G(i);t&&A&&A(v.pos(t.r,t.c,r.columns)),!(!a||!t)&&B({...a,cur:t})},Y=L.useCallback(()=>{if(!a)return;const i=v.rectPositions(a.start.r,a.start.c,a.cur.r,a.cur.c,r.columns);let t;a.mode==="replace"?t=new Set(i):a.mode==="add"?(t=new Set(e),i.forEach(f=>t.add(f))):(t=new Set(e),i.forEach(f=>t.delete(f))),E(t),B(null)},[a,r.columns,E,e]),ae=()=>Y(),ue=()=>{Y(),A?.(null)},de=i=>{const t=G(i);t&&S?.(v.pos(t.r,t.c,r.columns))},fe=L.useMemo(()=>a?new Set(v.rectPositions(a.start.r,a.start.c,a.cur.r,a.cur.c,r.columns)):new Set,[a,r.columns]),Z=z,J=r.columns*g+c+Z,Q=r.rows*g+c+Z,he=Re(r.columns,g),pe=je(r.rows,g),V={dims:r,cellSize:g,values:s,selection:e,dragPositions:fe,colorForWell:P,emptyWellFillColor:_,borderColor:p,selectedBorderColor:x,selectedFillColor:w,selectedFillOpacity:k,selectionFillMode:I,wellShape:d,highlightedWellIds:j??new Set,highlightBorderColor:F,flashWellId:T,flashWellKey:R},ge=Fe(V),me=d==="circle"?[]:Se(r,g,p),Ee=Ce(V),Le=L.useMemo(()=>{if(!M)return[];const i=[];for(let t=0;t<r.rows;t++)for(let f=0;f<r.columns;f++){const W=v.pos(t,f,r.columns);i.push(u.jsx("div",{style:{position:"absolute",left:c+f*g,top:c+t*g,width:g,height:g},"data-well-id":W,children:M(W,g)},W))}return i},[M,r.rows,r.columns,g]);return u.jsx("div",{ref:D,className:ee.cn("relative w-full select-none",$),"data-slot":"plate-paint-grid",children:u.jsxs("div",{className:ee.cn("relative inline-block",h&&"rounded-xl border bg-card p-3 shadow-sm"),"data-slot":"plate-paint-grid-frame",children:[u.jsxs("svg",{ref:C,width:J,height:Q,className:"block cursor-crosshair",onMouseDown:ce,onMouseMove:ie,onMouseUp:ae,onMouseLeave:ue,onDoubleClick:de,role:"group","aria-label":`${r.rows} row by ${r.columns} column plate map. Drag to select wells.`,children:[he,pe,ge,me,Ee]}),M?u.jsx("div",{className:"pointer-events-none absolute",style:{top:h?X:0,left:h?X:0,width:J,height:Q},"aria-hidden":!0,"data-slot":"plate-well-overlay",children:Le}):null]})})}exports.parsePos=v.parsePos;exports.PLATE_MAP_CELL_BORDER=le;exports.PLATE_MAP_EMPTY_WELL_FILL=ne;exports.PlatePaintGrid=We;
|
|
2
|
+
//# sourceMappingURL=PlatePaintGrid.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PlatePaintGrid.cjs","sources":["../../../../src/components/composed/PlateMapEditor/PlatePaintGrid.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport { parsePos, pos, rectPositions, resolveDimensions, rowLabel } from \"./wellGrid\";\n\nimport type { PlateDimensions, PlateFormat, WellId, WellRecord } from \"./types\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst DEFAULT_CELL = 34;\nconst DEFAULT_MIN_AUTO_CELL = 24;\nconst DEFAULT_MAX_AUTO_CELL = 72;\nconst DEFAULT_MAX_DENSE_AUTO_CELL = 36;\nconst LABEL_PAD = 26;\nconst FRAME_PADDING_PX = 12;\nconst FRAME_BORDER_PX = 1;\nconst LABEL_FONT_SIZE = 16;\nconst LABEL_TEXT_INSET = 9;\nconst LABEL_BASELINE_OFFSET = 5;\nconst WELL_INSET = 1;\nconst STROKE_DEFAULT = 4;\nconst STROKE_SELECTED = 4;\nconst STROKE_HIGHLIGHT = 3;\nconst STROKE_FLASH = 5;\nconst FLASH_DURATION_MS = 650;\nexport const PLATE_MAP_EMPTY_WELL_FILL = \"var(--surface-container)\";\nexport const PLATE_MAP_CELL_BORDER = \"var(--border)\";\n\nexport type WellShape = \"rect\" | \"circle\";\n\nexport interface PlatePaintGridProps<T extends WellRecord = WellRecord> {\n format: PlateFormat;\n rows?: number;\n columns?: number;\n values: Map<WellId, T>;\n selection: Set<WellId>;\n onSelectionChange: (next: Set<WellId>) => void;\n /** Returns the fill color for a given well record (or undefined if empty). */\n colorForWell: (well: T | undefined, wellId: WellId) => string;\n /** Fill color for empty wells. Pass `null` to delegate empty wells to `colorForWell`. */\n emptyWellFillColor?: string | null;\n /** Geometric shape of each well. `\"circle\"` matches scientific plate visuals. Defaults to `\"rect\"`. */\n wellShape?: WellShape;\n /**\n * When true, wraps the grid in a card-like surface (rounded, bordered,\n * padded, soft shadow) so it reads as a physical plate. Pairs well with\n * `wellShape=\"circle\"`.\n */\n framed?: boolean;\n /** Pixel size of each well cell. Defaults to 34 when fixed. */\n cellSize?: number;\n /** Resize wells to fill available width when `cellSize` is not fixed. */\n autoScale?: boolean;\n minCellSize?: number;\n /** Defaults to 72 for 96-well style plates and 36 for denser plates. */\n maxCellSize?: number;\n /** Stroke color for non-selected wells. Defaults to a light border. */\n borderColor?: string;\n /** Stroke color for selected wells. Defaults to the kit primary blue. */\n selectedBorderColor?: string;\n /** Fill color for selected wells. Defaults to the kit primary blue. */\n selectedFillColor?: string;\n /** Selected fill opacity. */\n selectedFillOpacity?: number;\n /** Whether selected wells use the selection fill or keep their assigned well color. */\n selectionFillMode?: \"selection\" | \"well\";\n /** Well id that should briefly flash, usually after a double-click assignment. */\n flashWellId?: WellId;\n /** Changing this value restarts the flash animation for the same well. */\n flashWellKey?: number;\n /**\n * Well ids that should render with a highlight ring. Used for cross-component\n * hover sync (e.g. hovering a legend item to highlight matching wells).\n */\n highlightedWellIds?: ReadonlySet<WellId>;\n /** Stroke color for highlighted wells. Defaults to the kit primary blue. */\n highlightBorderColor?: string;\n onWellHover?: (wellId: WellId | null) => void;\n onWellDoubleClick?: (wellId: WellId) => void;\n /**\n * Optional render-prop invoked once per well. The returned node is placed\n * inside an absolutely-positioned cell on top of the SVG, sized to\n * `cellSize`. The wrapper layer is `pointer-events: none` so the SVG keeps\n * pointer interaction by default; consumers wiring drop targets are expected\n * to set `pointer-events: auto` on their own element while a drag is active.\n */\n wrapWell?: (wellId: WellId, cellSize: number) => React.ReactNode;\n className?: string;\n}\n\ntype DragMode = \"replace\" | \"add\" | \"remove\";\n\ninterface DragState {\n start: { r: number; c: number };\n cur: { r: number; c: number };\n mode: DragMode;\n}\n\nfunction buildColumnLabels(columns: number, cellSize: number): React.ReactNode[] {\n const labels: React.ReactNode[] = [];\n for (let c = 0; c < columns; c++) {\n labels.push(\n <text\n key={`c${c}`}\n x={LABEL_PAD + c * cellSize + cellSize / 2}\n y={LABEL_PAD / 2}\n textAnchor=\"middle\"\n fontSize={LABEL_FONT_SIZE}\n className=\"fill-muted-foreground\"\n >\n {c + 1}\n </text>,\n );\n }\n return labels;\n}\n\nfunction buildRowLabels(rows: number, cellSize: number): React.ReactNode[] {\n const labels: React.ReactNode[] = [];\n for (let r = 0; r < rows; r++) {\n labels.push(\n <text\n key={`r${r}`}\n x={LABEL_PAD - LABEL_TEXT_INSET}\n y={LABEL_PAD + r * cellSize + cellSize / 2 + LABEL_BASELINE_OFFSET}\n textAnchor=\"end\"\n fontSize={LABEL_FONT_SIZE}\n className=\"fill-muted-foreground\"\n >\n {rowLabel(r)}\n </text>,\n );\n }\n return labels;\n}\n\ninterface BuildWellCellsArgs<T extends WellRecord> {\n dims: PlateDimensions;\n cellSize: number;\n values: Map<WellId, T>;\n selection: Set<WellId>;\n dragPositions: Set<WellId>;\n colorForWell: (well: T | undefined, wellId: WellId) => string;\n emptyWellFillColor: string | null;\n borderColor: string;\n selectedBorderColor: string;\n selectedFillColor: string;\n selectedFillOpacity: number;\n selectionFillMode: \"selection\" | \"well\";\n wellShape: WellShape;\n highlightedWellIds: ReadonlySet<WellId>;\n highlightBorderColor: string;\n flashWellId?: WellId;\n flashWellKey?: number;\n}\n\nfunction buildWellCell<T extends WellRecord>(\n args: BuildWellCellsArgs<T>,\n row: number,\n column: number,\n): React.ReactNode {\n const {\n dims,\n cellSize,\n values,\n selection,\n dragPositions,\n colorForWell,\n emptyWellFillColor,\n selectedFillColor,\n selectedFillOpacity,\n selectionFillMode,\n wellShape,\n } = args;\n const id = pos(row, column, dims.columns);\n const entry = values.get(id);\n const isSelected = selection.has(id) || dragPositions.has(id);\n const wellFill = entry === undefined && emptyWellFillColor !== null ? emptyWellFillColor : colorForWell(entry, id);\n const usesWellFill = isSelected && selectionFillMode === \"well\" && entry !== undefined;\n const usesSelectionFill = isSelected && !usesWellFill;\n const fill = usesSelectionFill ? selectedFillColor : wellFill;\n const fillOpacity = usesSelectionFill ? selectedFillOpacity : undefined;\n\n if (wellShape === \"circle\") {\n const cx = LABEL_PAD + column * cellSize + cellSize / 2;\n const cy = LABEL_PAD + row * cellSize + cellSize / 2;\n const r = cellSize / 2 - WELL_INSET;\n return (\n <circle\n key={id}\n cx={cx}\n cy={cy}\n r={r}\n fill={fill}\n fillOpacity={fillOpacity}\n stroke=\"none\"\n data-well={id}\n data-selected={isSelected ? \"true\" : undefined}\n />\n );\n }\n\n return (\n <rect\n key={id}\n x={LABEL_PAD + column * cellSize}\n y={LABEL_PAD + row * cellSize}\n width={cellSize}\n height={cellSize}\n fill={fill}\n fillOpacity={fillOpacity}\n stroke=\"none\"\n data-well={id}\n data-selected={isSelected ? \"true\" : undefined}\n />\n );\n}\n\nfunction buildWellCells<T extends WellRecord>({ dims, ...args }: BuildWellCellsArgs<T>): React.ReactNode[] {\n const cells: React.ReactNode[] = [];\n for (let r = 0; r < dims.rows; r++) {\n for (let c = 0; c < dims.columns; c++) {\n cells.push(buildWellCell({ dims, ...args }, r, c));\n }\n }\n return cells;\n}\n\nfunction buildGridLines(dims: PlateDimensions, cellSize: number, borderColor: string): React.ReactNode[] {\n const lines: React.ReactNode[] = [];\n const left = LABEL_PAD;\n const top = LABEL_PAD;\n const right = LABEL_PAD + dims.columns * cellSize;\n const bottom = LABEL_PAD + dims.rows * cellSize;\n\n for (let c = 0; c <= dims.columns; c++) {\n const x = LABEL_PAD + c * cellSize;\n lines.push(\n <line\n key={`grid-col-${c}`}\n x1={x}\n y1={top}\n x2={x}\n y2={bottom}\n stroke={borderColor}\n strokeWidth={STROKE_DEFAULT}\n vectorEffect=\"non-scaling-stroke\"\n shapeRendering=\"crispEdges\"\n pointerEvents=\"none\"\n data-plate-grid-line={`column-${c}`}\n />,\n );\n }\n\n for (let r = 0; r <= dims.rows; r++) {\n const y = LABEL_PAD + r * cellSize;\n lines.push(\n <line\n key={`grid-row-${r}`}\n x1={left}\n y1={y}\n x2={right}\n y2={y}\n stroke={borderColor}\n strokeWidth={STROKE_DEFAULT}\n vectorEffect=\"non-scaling-stroke\"\n shapeRendering=\"crispEdges\"\n pointerEvents=\"none\"\n data-plate-grid-line={`row-${r}`}\n />,\n );\n }\n\n return lines;\n}\n\nfunction buildWellOverlay<T extends WellRecord>(\n args: BuildWellCellsArgs<T>,\n row: number,\n column: number,\n): React.ReactNode {\n const {\n dims,\n cellSize,\n selection,\n dragPositions,\n selectedBorderColor,\n flashWellId,\n flashWellKey,\n wellShape,\n highlightedWellIds,\n highlightBorderColor,\n } = args;\n const id = pos(row, column, dims.columns);\n const isSelected = selection.has(id) || dragPositions.has(id);\n const isFlashing = flashWellId === id;\n const isHighlighted = !isSelected && highlightedWellIds.has(id);\n\n if (!isSelected && !isFlashing && !isHighlighted) return null;\n\n const x = LABEL_PAD + column * cellSize + WELL_INSET;\n const y = LABEL_PAD + row * cellSize + WELL_INSET;\n const size = cellSize - WELL_INSET * 2;\n const cx = LABEL_PAD + column * cellSize + cellSize / 2;\n const cy = LABEL_PAD + row * cellSize + cellSize / 2;\n const r = cellSize / 2 - WELL_INSET;\n const isCircle = wellShape === \"circle\";\n\n type WellShapeProps = Record<string, string | number | undefined>;\n const shapeProps: WellShapeProps = isCircle\n ? { cx, cy, r }\n : { x, y, width: size, height: size };\n\n const renderShape = (\n extraProps: Record<string, string | number | undefined>,\n children?: React.ReactNode,\n elementKey?: string | number,\n ): React.ReactNode => {\n const props = { ...shapeProps, ...extraProps, pointerEvents: \"none\" as const };\n return isCircle ? (\n <circle key={elementKey} {...props}>\n {children}\n </circle>\n ) : (\n <rect key={elementKey} {...props}>\n {children}\n </rect>\n );\n };\n\n const flashAnimations = (\n <>\n <animate attributeName=\"fill-opacity\" values=\"0.24;0.1;0\" dur={`${FLASH_DURATION_MS}ms`} fill=\"freeze\" />\n <animate attributeName=\"stroke-opacity\" values=\"0.92;0.42;0\" dur={`${FLASH_DURATION_MS}ms`} fill=\"freeze\" />\n <animate\n attributeName=\"stroke-width\"\n values={`${STROKE_FLASH};${STROKE_SELECTED}`}\n dur={`${FLASH_DURATION_MS}ms`}\n fill=\"freeze\"\n />\n </>\n );\n\n return (\n <g key={`overlay-${id}`}>\n {isSelected\n ? renderShape({\n fill: \"none\",\n stroke: selectedBorderColor,\n strokeWidth: STROKE_SELECTED,\n \"data-well-selection\": id,\n })\n : null}\n {isHighlighted\n ? renderShape({\n fill: \"none\",\n stroke: highlightBorderColor,\n strokeWidth: STROKE_HIGHLIGHT,\n \"data-well-highlight\": id,\n })\n : null}\n {isFlashing\n ? renderShape(\n {\n fill: selectedBorderColor,\n fillOpacity: 0.24,\n stroke: selectedBorderColor,\n strokeOpacity: 0.92,\n strokeWidth: STROKE_FLASH,\n \"data-well-flash\": id,\n },\n flashAnimations,\n `${id}-${flashWellKey}`,\n )\n : null}\n </g>\n );\n}\n\nfunction buildWellOverlays<T extends WellRecord>({ dims, ...args }: BuildWellCellsArgs<T>): React.ReactNode[] {\n const overlays: React.ReactNode[] = [];\n for (let r = 0; r < dims.rows; r++) {\n for (let c = 0; c < dims.columns; c++) {\n const overlay = buildWellOverlay({ dims, ...args }, r, c);\n if (overlay) overlays.push(overlay);\n }\n }\n return overlays;\n}\n\n/**\n * Interactive plate grid with drag-rectangle selection.\n * - Click & drag: replace selection\n * - Shift + drag: add to selection\n * - Alt + drag: remove from selection\n */\nexport function PlatePaintGrid<T extends WellRecord = WellRecord>({\n format,\n rows,\n columns,\n values,\n selection,\n onSelectionChange,\n colorForWell,\n emptyWellFillColor = PLATE_MAP_EMPTY_WELL_FILL,\n wellShape = \"rect\",\n framed = false,\n cellSize,\n autoScale = true,\n minCellSize = DEFAULT_MIN_AUTO_CELL,\n maxCellSize,\n borderColor = PLATE_MAP_CELL_BORDER,\n selectedBorderColor = \"var(--color-primary)\",\n selectedFillColor = \"var(--color-primary)\",\n selectedFillOpacity = 0.18,\n selectionFillMode = \"selection\",\n flashWellId,\n flashWellKey,\n highlightedWellIds,\n highlightBorderColor = \"var(--color-primary)\",\n onWellHover,\n onWellDoubleClick,\n wrapWell,\n className,\n}: PlatePaintGridProps<T>) {\n const dims = resolveDimensions(format, rows, columns);\n const containerRef = React.useRef<HTMLDivElement>(null);\n const svgRef = React.useRef<SVGSVGElement>(null);\n const [drag, setDrag] = React.useState<DragState | null>(null);\n const [containerWidth, setContainerWidth] = React.useState<number>();\n\n React.useLayoutEffect(() => {\n const node = containerRef.current;\n if (!node || !autoScale || cellSize !== undefined) return;\n\n const update = () => setContainerWidth(node.clientWidth);\n update();\n\n if (typeof ResizeObserver === \"undefined\") return;\n\n const observer = new ResizeObserver(update);\n observer.observe(node);\n return () => observer.disconnect();\n }, [autoScale, cellSize]);\n\n const resolvedCellSize = React.useMemo(() => {\n if (!autoScale || cellSize !== undefined || !containerWidth) {\n return cellSize ?? DEFAULT_CELL;\n }\n const autoMaxCellSize = maxCellSize ?? (dims.columns > 12 ? DEFAULT_MAX_DENSE_AUTO_CELL : DEFAULT_MAX_AUTO_CELL);\n const frameAdjust = framed ? (FRAME_PADDING_PX + FRAME_BORDER_PX) * 2 : 0;\n const fitSize = Math.floor((containerWidth - LABEL_PAD - frameAdjust) / dims.columns);\n return Math.max(minCellSize, Math.min(autoMaxCellSize, fitSize));\n }, [autoScale, cellSize, containerWidth, dims.columns, framed, maxCellSize, minCellSize]);\n\n const cellAt = React.useCallback(\n (evt: React.MouseEvent): { r: number; c: number } | null => {\n const svg = svgRef.current;\n if (!svg) return null;\n const rect = svg.getBoundingClientRect();\n const x = evt.clientX - rect.left - LABEL_PAD;\n const y = evt.clientY - rect.top - LABEL_PAD;\n const c = Math.floor(x / resolvedCellSize);\n const r = Math.floor(y / resolvedCellSize);\n if (r < 0 || r >= dims.rows || c < 0 || c >= dims.columns) return null;\n return { r, c };\n },\n [resolvedCellSize, dims.rows, dims.columns],\n );\n\n const handleDown = (e: React.MouseEvent) => {\n const cell = cellAt(e);\n if (!cell) return;\n const mode: DragMode = e.shiftKey ? \"add\" : e.altKey ? \"remove\" : \"replace\";\n setDrag({ start: cell, cur: cell, mode });\n };\n\n const handleMove = (e: React.MouseEvent) => {\n const cell = cellAt(e);\n if (cell && onWellHover) {\n onWellHover(pos(cell.r, cell.c, dims.columns));\n }\n if (!drag || !cell) return;\n setDrag({ ...drag, cur: cell });\n };\n\n const commitDrag = React.useCallback(() => {\n if (!drag) return;\n const positions = rectPositions(drag.start.r, drag.start.c, drag.cur.r, drag.cur.c, dims.columns);\n let next: Set<WellId>;\n if (drag.mode === \"replace\") {\n next = new Set(positions);\n } else if (drag.mode === \"add\") {\n next = new Set(selection);\n positions.forEach((p) => next.add(p));\n } else {\n next = new Set(selection);\n positions.forEach((p) => next.delete(p));\n }\n onSelectionChange(next);\n setDrag(null);\n }, [drag, dims.columns, onSelectionChange, selection]);\n\n const handleUp = () => commitDrag();\n const handleLeave = () => {\n commitDrag();\n onWellHover?.(null);\n };\n const handleDoubleClick = (e: React.MouseEvent) => {\n const cell = cellAt(e);\n if (!cell) return;\n onWellDoubleClick?.(pos(cell.r, cell.c, dims.columns));\n };\n\n const dragPositions = React.useMemo(() => {\n if (!drag) return new Set<WellId>();\n return new Set(rectPositions(drag.start.r, drag.start.c, drag.cur.r, drag.cur.c, dims.columns));\n }, [drag, dims.columns]);\n\n // Right and bottom edge strokes sit on the plate boundary; give the SVG room so they are not clipped.\n const edgeStrokePadding = STROKE_DEFAULT;\n const width = dims.columns * resolvedCellSize + LABEL_PAD + edgeStrokePadding;\n const height = dims.rows * resolvedCellSize + LABEL_PAD + edgeStrokePadding;\n\n const colLabels = buildColumnLabels(dims.columns, resolvedCellSize);\n const rowLabels = buildRowLabels(dims.rows, resolvedCellSize);\n const resolvedHighlightedWellIds: ReadonlySet<WellId> = highlightedWellIds ?? new Set();\n const wellRenderArgs = {\n dims,\n cellSize: resolvedCellSize,\n values,\n selection,\n dragPositions,\n colorForWell,\n emptyWellFillColor,\n borderColor,\n selectedBorderColor,\n selectedFillColor,\n selectedFillOpacity,\n selectionFillMode,\n wellShape,\n highlightedWellIds: resolvedHighlightedWellIds,\n highlightBorderColor,\n flashWellId,\n flashWellKey,\n };\n const wellCells = buildWellCells(wellRenderArgs);\n const gridLines = wellShape === \"circle\" ? [] : buildGridLines(dims, resolvedCellSize, borderColor);\n const wellOverlays = buildWellOverlays(wellRenderArgs);\n\n const overlayCells = React.useMemo<React.ReactNode[]>(() => {\n if (!wrapWell) return [];\n const cells: React.ReactNode[] = [];\n for (let r = 0; r < dims.rows; r++) {\n for (let c = 0; c < dims.columns; c++) {\n const id = pos(r, c, dims.columns);\n cells.push(\n <div\n key={id}\n style={{\n position: \"absolute\",\n left: LABEL_PAD + c * resolvedCellSize,\n top: LABEL_PAD + r * resolvedCellSize,\n width: resolvedCellSize,\n height: resolvedCellSize,\n }}\n data-well-id={id}\n >\n {wrapWell(id, resolvedCellSize)}\n </div>,\n );\n }\n }\n return cells;\n }, [wrapWell, dims.rows, dims.columns, resolvedCellSize]);\n\n return (\n <div\n ref={containerRef}\n className={cn(\"relative w-full select-none\", className)}\n data-slot=\"plate-paint-grid\"\n >\n <div\n className={cn(\n \"relative inline-block\",\n framed && \"rounded-xl border bg-card p-3 shadow-sm\",\n )}\n data-slot=\"plate-paint-grid-frame\"\n >\n <svg\n ref={svgRef}\n width={width}\n height={height}\n className=\"block cursor-crosshair\"\n onMouseDown={handleDown}\n onMouseMove={handleMove}\n onMouseUp={handleUp}\n onMouseLeave={handleLeave}\n onDoubleClick={handleDoubleClick}\n role=\"group\"\n aria-label={`${dims.rows} row by ${dims.columns} column plate map. Drag to select wells.`}\n >\n {colLabels}\n {rowLabels}\n {wellCells}\n {gridLines}\n {wellOverlays}\n </svg>\n {wrapWell ? (\n <div\n className=\"pointer-events-none absolute\"\n style={{\n top: framed ? FRAME_PADDING_PX : 0,\n left: framed ? FRAME_PADDING_PX : 0,\n width,\n height,\n }}\n aria-hidden\n data-slot=\"plate-well-overlay\"\n >\n {overlayCells}\n </div>\n ) : null}\n </div>\n </div>\n );\n}\n\nexport { parsePos };\n"],"names":["DEFAULT_CELL","DEFAULT_MIN_AUTO_CELL","DEFAULT_MAX_AUTO_CELL","DEFAULT_MAX_DENSE_AUTO_CELL","LABEL_PAD","FRAME_PADDING_PX","FRAME_BORDER_PX","LABEL_FONT_SIZE","LABEL_TEXT_INSET","LABEL_BASELINE_OFFSET","WELL_INSET","STROKE_DEFAULT","STROKE_SELECTED","STROKE_HIGHLIGHT","STROKE_FLASH","FLASH_DURATION_MS","PLATE_MAP_EMPTY_WELL_FILL","PLATE_MAP_CELL_BORDER","buildColumnLabels","columns","cellSize","labels","c","jsx","buildRowLabels","rows","r","buildWellCell","args","row","column","dims","values","selection","dragPositions","colorForWell","emptyWellFillColor","selectedFillColor","selectedFillOpacity","selectionFillMode","wellShape","id","pos","entry","isSelected","wellFill","usesSelectionFill","fill","fillOpacity","cx","cy","buildWellCells","cells","buildGridLines","borderColor","lines","left","top","right","bottom","x","y","buildWellOverlay","selectedBorderColor","flashWellId","flashWellKey","highlightedWellIds","highlightBorderColor","isFlashing","isHighlighted","size","isCircle","shapeProps","renderShape","extraProps","children","elementKey","props","flashAnimations","jsxs","Fragment","buildWellOverlays","overlays","overlay","PlatePaintGrid","format","onSelectionChange","framed","autoScale","minCellSize","maxCellSize","onWellHover","onWellDoubleClick","wrapWell","className","resolveDimensions","containerRef","React","svgRef","drag","setDrag","containerWidth","setContainerWidth","node","update","observer","resolvedCellSize","autoMaxCellSize","frameAdjust","fitSize","cellAt","evt","svg","rect","handleDown","e","cell","mode","handleMove","commitDrag","positions","rectPositions","next","p","handleUp","handleLeave","handleDoubleClick","edgeStrokePadding","width","height","colLabels","rowLabels","wellRenderArgs","wellCells","gridLines","wellOverlays","overlayCells","cn"],"mappings":"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"}
|