@tetrascience-npm/tetrascience-react-ui 1.0.0-beta.157.1 → 1.0.0-beta.159.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -0
- package/dist/components/ai/streamdown-plugins.cjs +1 -1
- package/dist/components/ai/streamdown-plugins.cjs.map +1 -1
- package/dist/components/ai/streamdown-plugins.d.ts +5 -1
- package/dist/components/ai/streamdown-plugins.js +22 -11
- package/dist/components/ai/streamdown-plugins.js.map +1 -1
- package/dist/components/ai/use-streamdown-plugins.cjs +1 -1
- package/dist/components/ai/use-streamdown-plugins.cjs.map +1 -1
- package/dist/components/ai/use-streamdown-plugins.js +7 -7
- package/dist/components/ai/use-streamdown-plugins.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.d.ts +5 -1
- package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.js +32 -30
- package/dist/components/composed/PlateMapEditor/ManifestFilterPopover.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.d.ts +269 -13
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.js +392 -310
- package/dist/components/composed/PlateMapEditor/PlateMapEditor.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapForm.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapForm.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapForm.d.ts +5 -1
- package/dist/components/composed/PlateMapEditor/PlateMapForm.js +19 -15
- package/dist/components/composed/PlateMapEditor/PlateMapForm.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.d.ts +12 -1
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.js +69 -63
- package/dist/components/composed/PlateMapEditor/PlateMapGrid.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapManifest.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapManifest.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlateMapManifest.d.ts +13 -4
- package/dist/components/composed/PlateMapEditor/PlateMapManifest.js +38 -29
- package/dist/components/composed/PlateMapEditor/PlateMapManifest.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlatePaintGrid.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/PlatePaintGrid.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/PlatePaintGrid.d.ts +6 -1
- package/dist/components/composed/PlateMapEditor/PlatePaintGrid.js +490 -275
- package/dist/components/composed/PlateMapEditor/PlatePaintGrid.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellLegend.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/WellLegend.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellLegend.d.ts +8 -2
- package/dist/components/composed/PlateMapEditor/WellLegend.js +8 -7
- package/dist/components/composed/PlateMapEditor/WellLegend.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellManifestTable.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/WellManifestTable.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellManifestTable.d.ts +6 -2
- package/dist/components/composed/PlateMapEditor/WellManifestTable.js +288 -256
- package/dist/components/composed/PlateMapEditor/WellManifestTable.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellMetadataForm.cjs +1 -1
- package/dist/components/composed/PlateMapEditor/WellMetadataForm.cjs.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellMetadataForm.d.ts +5 -1
- package/dist/components/composed/PlateMapEditor/WellMetadataForm.js +61 -66
- package/dist/components/composed/PlateMapEditor/WellMetadataForm.js.map +1 -1
- package/dist/components/composed/PlateMapEditor/WellQuickPaint.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/WellQuickPaint.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/WellQuickPaint.d.ts +23 -0
- package/dist/components/composed/PlateMapEditor/WellQuickPaint.js +75 -0
- package/dist/components/composed/PlateMapEditor/WellQuickPaint.js.map +1 -0
- package/dist/components/composed/PlateMapEditor/index.d.ts +6 -2
- package/dist/components/composed/PlateMapEditor/types.d.ts +65 -0
- package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.cjs +2 -0
- package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.cjs.map +1 -0
- package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.d.ts +109 -0
- package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.js +246 -0
- package/dist/components/composed/PlateMapEditor/usePlateMapEditorState.js.map +1 -0
- package/dist/composed/PlateMapEditor.cjs +1 -1
- package/dist/composed/PlateMapEditor.js +48 -42
- package/dist/composed/PlateMapEditor.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +701 -695
- package/dist/index.js.map +1 -1
- package/dist/index.scoped.css +1 -1
- package/dist/index.tailwind.css +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -306,6 +306,119 @@ For AI-assisted consuming apps, add a short instruction like this to the app's `
|
|
|
306
306
|
Use `ProcessFlow` from `@tetrascience-npm/tetrascience-react-ui` for multi-step workflow visualization. Do not build a custom stepper for upload, validation, review, approval, processing, or setup flows. Parent components own the workflow state and pass `steps: ProcessFlowStep[]`; each step status must be one of `PROCESS_FLOW_STEP_STATUSES`. Use `selectedStepId` only for the viewed/selected step. Keep completion/error side effects in the parent workflow code, not inside `ProcessFlow`.
|
|
307
307
|
```
|
|
308
308
|
|
|
309
|
+
#### PlateMapEditor
|
|
310
|
+
|
|
311
|
+
`PlateMapEditor` is the standard plate-map editing surface — a metadata form, an
|
|
312
|
+
interactive plate grid, and a sample manifest, wired together by a staged-edit
|
|
313
|
+
controller. Use it as-is for the default layout; tune it with props; and drop to
|
|
314
|
+
`usePlateMapEditorState` only when you need a layout the props can't express.
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
import { PlateMapEditor } from "@tetrascience-npm/tetrascience-react-ui";
|
|
318
|
+
|
|
319
|
+
function PlateScreen() {
|
|
320
|
+
const [values, setValues] = React.useState(new Map());
|
|
321
|
+
const [selection, setSelection] = React.useState(new Set());
|
|
322
|
+
|
|
323
|
+
return (
|
|
324
|
+
<PlateMapEditor
|
|
325
|
+
format="96"
|
|
326
|
+
values={values}
|
|
327
|
+
onChange={setValues}
|
|
328
|
+
selection={selection}
|
|
329
|
+
onSelectionChange={setSelection}
|
|
330
|
+
fields={FIELDS}
|
|
331
|
+
tableColumns={COLUMNS}
|
|
332
|
+
/>
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
##### Choosing a level
|
|
338
|
+
|
|
339
|
+
| You need | Use |
|
|
340
|
+
| --- | --- |
|
|
341
|
+
| The standard surface, tuned by props | `PlateMapEditor` |
|
|
342
|
+
| The form somewhere the editor can't reach | `PlateMapEditor` + `hideForm` + the imperative handle |
|
|
343
|
+
| A layout no prop combination expresses | `usePlateMapEditorState` + `PlateMapForm` / `PlateMapGrid` / `PlateMapManifest` |
|
|
344
|
+
|
|
345
|
+
Dropping to the hook keeps the apply/clear semantics, plate scoping, and barcode
|
|
346
|
+
stamping — you only take over layout. Do not re-implement staged edits by hand.
|
|
347
|
+
|
|
348
|
+
##### Customization
|
|
349
|
+
|
|
350
|
+
- **Layout** — `formPlacement` (`start` / `end` / `top` / `bottom`), `stackAt`,
|
|
351
|
+
`formWidth`, `hideForm`, `hideManifest`.
|
|
352
|
+
- **Slots** — `banner` (whole editor), `plateBanner` (plate card only),
|
|
353
|
+
`plateToolbar` (above grid), `plateFooter` (the plate card's footer),
|
|
354
|
+
`footer` (editor-wide action row), `legend` + `legendPlacement`,
|
|
355
|
+
`formExtras`, `formSlot`, `manifestSlot`.
|
|
356
|
+
- **Edit state** — `staged` / `onStagedChange` for a controlled staged record,
|
|
357
|
+
`mergeOnApply` for custom merge semantics, `applyScope="all-plates"` to write
|
|
358
|
+
across every plate at once.
|
|
359
|
+
- **Manifest** — one `manifest` object: `{ filterable, filterColumns, groupable,
|
|
360
|
+
defaultGroupBy, pageSize, pageSizeOptions, enableFillDown }`. Structural bits
|
|
361
|
+
stay top-level: `hideManifest`, `manifestTitle`, `manifestSlot`.
|
|
362
|
+
- **Labels** — one `labels` object covers every string the editor and its
|
|
363
|
+
manifest render, typed as the exported `PlateMapEditorLabels`, so an app's
|
|
364
|
+
translation table is a type error away from going stale:
|
|
365
|
+
`const fr: PlateMapEditorLabels = { … }`. `plateTitle` / `manifestTitle` and
|
|
366
|
+
the import/export menu labels are separate props (they take `ReactNode`, not
|
|
367
|
+
plain text).
|
|
368
|
+
- **Styling** — `className` / `style` on the root, plus one `classNames` map for
|
|
369
|
+
the regions (`PlateMapEditorClassNames`): `{ layout, formCard, plateCard,
|
|
370
|
+
manifestCard, form, grid, manifest }`. Each card also carries
|
|
371
|
+
`data-plate-map-region="form|plate|manifest"`, so plain CSS can target the
|
|
372
|
+
same regions without threading props.
|
|
373
|
+
|
|
374
|
+
##### Rendering the form outside the editor
|
|
375
|
+
|
|
376
|
+
Hide the built-in form and drive the staged state through the imperative handle.
|
|
377
|
+
The editor keeps owning selection and apply; your form just feeds it.
|
|
378
|
+
|
|
379
|
+
```tsx
|
|
380
|
+
const editor = React.useRef<PlateMapEditorHandle<MyWell>>(null);
|
|
381
|
+
|
|
382
|
+
<>
|
|
383
|
+
<MySidebarForm
|
|
384
|
+
onChange={(next) => editor.current?.setStaged(next)}
|
|
385
|
+
onApply={() => editor.current?.apply()}
|
|
386
|
+
onClear={() => editor.current?.clear()}
|
|
387
|
+
/>
|
|
388
|
+
<PlateMapEditor ref={editor} hideForm {...rest} />
|
|
389
|
+
</>;
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
##### Responsiveness
|
|
393
|
+
|
|
394
|
+
The editor responds to **its container's** width, not the viewport's, so it lays
|
|
395
|
+
out correctly inside a narrow panel, split pane, or drawer on a wide screen.
|
|
396
|
+
`stackAt` names a container width — `sm` 640 / `md` 768 (default) / `lg` 1024 /
|
|
397
|
+
`xl` 1280 / `never` — below which the form and grid stack full-width. A plate too
|
|
398
|
+
dense to fit scrolls inside its own container rather than widening the page.
|
|
399
|
+
|
|
400
|
+
##### Migration notes
|
|
401
|
+
|
|
402
|
+
Everything below is additive; existing code keeps working unchanged.
|
|
403
|
+
|
|
404
|
+
- **Layout now tracks container width, not viewport width.** This is the one
|
|
405
|
+
behavioural change. If your editor is full-width the result is effectively the
|
|
406
|
+
same; if it sits in a narrow panel on a wide screen it will now correctly
|
|
407
|
+
stack instead of rendering a cramped two-column layout. Tune with `stackAt`.
|
|
408
|
+
- `colorForWell` and `emptyEntry` are now **optional** — delete them for
|
|
409
|
+
read-only or single-category views and sensible defaults apply.
|
|
410
|
+
- `className` on the root always worked despite reports otherwise; `style` is
|
|
411
|
+
new.
|
|
412
|
+
- If you previously forked or re-assembled the primitives to change layout,
|
|
413
|
+
replace that with `formPlacement` / `stackAt` / `formWidth` and the slots, or
|
|
414
|
+
with `usePlateMapEditorState` if you still need custom structure. Hand-rolled
|
|
415
|
+
staged/apply logic should be deleted in favour of the hook.
|
|
416
|
+
- If you worked around the hardcoded 360px form column with a width utility,
|
|
417
|
+
switch to `formWidth` — it is the supported path and applies only at and above
|
|
418
|
+
`stackAt`.
|
|
419
|
+
- `manifestFilterable` and `manifestGroupable` still work but are deprecated in
|
|
420
|
+
favour of `manifest={{ filterable, groupable }}`.
|
|
421
|
+
|
|
309
422
|
### Charts (`charts/`)
|
|
310
423
|
|
|
311
424
|
Plotly.js-based data visualisations:
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("@streamdown/cjk"),c=require("./streamdown-code-plugin.cjs");function o(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const r in e)if(r!=="default"){const n=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,n.get?n:{enumerable:!0,get:()=>e[r]})}}return t.default=e,Object.freeze(t)}async function a(e){try{return await e()}catch{return}}async function u(){const[e,t]=await Promise.all([a(async()=>(await Promise.resolve().then(()=>o(require("@streamdown/math")))).math),a(async()=>(await Promise.resolve().then(()=>o(require("@streamdown/mermaid")))).mermaid)]);return{cjk:i.cjk,code:c.streamdownCodePlugin,...e?{math:e}:{},...t?{mermaid:t}:{}}}exports.getStreamdownPlugins=u;
|
|
2
2
|
//# sourceMappingURL=streamdown-plugins.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streamdown-plugins.cjs","sources":["../../../src/components/ai/streamdown-plugins.ts"],"sourcesContent":["/**\n * Full streamdown plugin set for `MessageResponse` / `Reasoning` markdown.\n *\n * This module is only ever reached through `useStreamdownPlugins`'s dynamic\n * import (SW-2007): mermaid, KaTeX, and the shiki grammars behind these\n * plugins land in a lazy chunk instead of the consumer's main bundle. Do not\n * import it statically from component code.\n
|
|
1
|
+
{"version":3,"file":"streamdown-plugins.cjs","sources":["../../../src/components/ai/streamdown-plugins.ts"],"sourcesContent":["/**\n * Full streamdown plugin set for `MessageResponse` / `Reasoning` markdown.\n *\n * This module is only ever reached through `useStreamdownPlugins`'s dynamic\n * import (SW-2007): mermaid, KaTeX, and the shiki grammars behind these\n * plugins land in a lazy chunk instead of the consumer's main bundle. Do not\n * import it statically from component code.\n *\n * `@streamdown/math` and `@streamdown/mermaid` are *optional* peer\n * dependencies, so they are resolved with `import()` rather than a static\n * import. A static import forces every consumer that bundles the root barrel\n * to install both — Rollup has to link the named export at build time, and\n * when the package is absent the specifier resolves to an empty\n * `__vite-optional-peer-dep:` stub and the build aborts (#190). Loading them\n * dynamically lets a consumer that omits them degrade at runtime instead:\n * markdown still renders, just without math or mermaid.\n */\nimport { cjk } from \"@streamdown/cjk\";\n\nimport { streamdownCodePlugin } from \"./streamdown-code-plugin\";\n\nimport type { PluginConfig } from \"streamdown\";\n\n/**\n * Resolve one optional peer's plugin export, or `undefined` when the package\n * is not installed. Mirrors how `plotly.js-dist`, `@rdkit/rdkit` and the\n * provider SDKs are loaded elsewhere in the kit.\n */\nasync function optionalPlugin<T>(load: () => Promise<T>): Promise<T | undefined> {\n try {\n return await load();\n } catch {\n return undefined;\n }\n}\n\n/**\n * Build the plugin set, including only the optional plugins that are actually\n * installed. Callers reach this through `useStreamdownPlugins`.\n */\nexport async function getStreamdownPlugins(): Promise<PluginConfig> {\n const [math, mermaid] = await Promise.all([\n optionalPlugin(async () => (await import(\"@streamdown/math\")).math),\n optionalPlugin(async () => (await import(\"@streamdown/mermaid\")).mermaid),\n ]);\n\n return {\n cjk,\n code: streamdownCodePlugin,\n ...(math ? { math } : {}),\n ...(mermaid ? { mermaid } : {}),\n };\n}\n"],"names":["optionalPlugin","load","getStreamdownPlugins","math","mermaid","_interopNamespaceDefault","cjk","streamdownCodePlugin"],"mappings":"0aA4BA,eAAeA,EAAkBC,EAAgD,CAC/E,GAAI,CACF,OAAO,MAAMA,EAAA,CACf,MAAQ,CACN,MACF,CACF,CAMA,eAAsBC,GAA8C,CAClE,KAAM,CAACC,EAAMC,CAAO,EAAI,MAAM,QAAQ,IAAI,CACxCJ,EAAe,UAAa,MAAM,qCAAO,kBAAkB,CAAA,CAAA,GAAG,IAAI,EAClEA,EAAe,UAAa,MAAM,QAAA,QAAA,EAAA,KAAA,IAAAK,EAAA,QAAO,qBAAqB,CAAA,CAAA,GAAG,OAAO,CAAA,CACzE,EAED,MAAO,CAAA,IACLC,EAAAA,IACA,KAAMC,EAAAA,qBACN,GAAIJ,EAAO,CAAE,KAAAA,CAAA,EAAS,CAAA,EACtB,GAAIC,EAAU,CAAE,QAAAA,GAAY,CAAA,CAAC,CAEjC"}
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
import { PluginConfig } from 'streamdown';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Build the plugin set, including only the optional plugins that are actually
|
|
4
|
+
* installed. Callers reach this through `useStreamdownPlugins`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getStreamdownPlugins(): Promise<PluginConfig>;
|
|
@@ -1,14 +1,25 @@
|
|
|
1
|
-
import { cjk as
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import { cjk as i } from "@streamdown/cjk";
|
|
2
|
+
import { streamdownCodePlugin as n } from "./streamdown-code-plugin.js";
|
|
3
|
+
async function r(t) {
|
|
4
|
+
try {
|
|
5
|
+
return await t();
|
|
6
|
+
} catch {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
async function e() {
|
|
11
|
+
const [t, a] = await Promise.all([
|
|
12
|
+
r(async () => (await import("@streamdown/math")).math),
|
|
13
|
+
r(async () => (await import("@streamdown/mermaid")).mermaid)
|
|
14
|
+
]);
|
|
15
|
+
return {
|
|
16
|
+
cjk: i,
|
|
17
|
+
code: n,
|
|
18
|
+
...t ? { math: t } : {},
|
|
19
|
+
...a ? { mermaid: a } : {}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
11
22
|
export {
|
|
12
|
-
|
|
23
|
+
e as getStreamdownPlugins
|
|
13
24
|
};
|
|
14
25
|
//# sourceMappingURL=streamdown-plugins.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streamdown-plugins.js","sources":["../../../src/components/ai/streamdown-plugins.ts"],"sourcesContent":["/**\n * Full streamdown plugin set for `MessageResponse` / `Reasoning` markdown.\n *\n * This module is only ever reached through `useStreamdownPlugins`'s dynamic\n * import (SW-2007): mermaid, KaTeX, and the shiki grammars behind these\n * plugins land in a lazy chunk instead of the consumer's main bundle. Do not\n * import it statically from component code.\n
|
|
1
|
+
{"version":3,"file":"streamdown-plugins.js","sources":["../../../src/components/ai/streamdown-plugins.ts"],"sourcesContent":["/**\n * Full streamdown plugin set for `MessageResponse` / `Reasoning` markdown.\n *\n * This module is only ever reached through `useStreamdownPlugins`'s dynamic\n * import (SW-2007): mermaid, KaTeX, and the shiki grammars behind these\n * plugins land in a lazy chunk instead of the consumer's main bundle. Do not\n * import it statically from component code.\n *\n * `@streamdown/math` and `@streamdown/mermaid` are *optional* peer\n * dependencies, so they are resolved with `import()` rather than a static\n * import. A static import forces every consumer that bundles the root barrel\n * to install both — Rollup has to link the named export at build time, and\n * when the package is absent the specifier resolves to an empty\n * `__vite-optional-peer-dep:` stub and the build aborts (#190). Loading them\n * dynamically lets a consumer that omits them degrade at runtime instead:\n * markdown still renders, just without math or mermaid.\n */\nimport { cjk } from \"@streamdown/cjk\";\n\nimport { streamdownCodePlugin } from \"./streamdown-code-plugin\";\n\nimport type { PluginConfig } from \"streamdown\";\n\n/**\n * Resolve one optional peer's plugin export, or `undefined` when the package\n * is not installed. Mirrors how `plotly.js-dist`, `@rdkit/rdkit` and the\n * provider SDKs are loaded elsewhere in the kit.\n */\nasync function optionalPlugin<T>(load: () => Promise<T>): Promise<T | undefined> {\n try {\n return await load();\n } catch {\n return undefined;\n }\n}\n\n/**\n * Build the plugin set, including only the optional plugins that are actually\n * installed. Callers reach this through `useStreamdownPlugins`.\n */\nexport async function getStreamdownPlugins(): Promise<PluginConfig> {\n const [math, mermaid] = await Promise.all([\n optionalPlugin(async () => (await import(\"@streamdown/math\")).math),\n optionalPlugin(async () => (await import(\"@streamdown/mermaid\")).mermaid),\n ]);\n\n return {\n cjk,\n code: streamdownCodePlugin,\n ...(math ? { math } : {}),\n ...(mermaid ? { mermaid } : {}),\n };\n}\n"],"names":["optionalPlugin","load","getStreamdownPlugins","math","mermaid","cjk","streamdownCodePlugin"],"mappings":";;AA4BA,eAAeA,EAAkBC,GAAgD;AAC/E,MAAI;AACF,WAAO,MAAMA,EAAA;AAAA,EACf,QAAQ;AACN;AAAA,EACF;AACF;AAMA,eAAsBC,IAA8C;AAClE,QAAM,CAACC,GAAMC,CAAO,IAAI,MAAM,QAAQ,IAAI;AAAA,IACxCJ,EAAe,aAAa,MAAM,OAAO,kBAAkB,GAAG,IAAI;AAAA,IAClEA,EAAe,aAAa,MAAM,OAAO,qBAAqB,GAAG,OAAO;AAAA,EAAA,CACzE;AAED,SAAO;AAAA,IACL,KAAAK;AAAA,IACA,MAAMC;AAAA,IACN,GAAIH,IAAO,EAAE,MAAAA,EAAA,IAAS,CAAA;AAAA,IACtB,GAAIC,IAAU,EAAE,SAAAA,MAAY,CAAA;AAAA,EAAC;AAEjC;"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("react");let n=null,t=null;function s(){return t??=Promise.resolve().then(()=>require("./streamdown-plugins.cjs")).then(e=>e.getStreamdownPlugins()).then(e=>(n=e,e)).catch(e=>{throw t=null,n=null,e}),t}function o(){const[e,l]=r.useState(n??void 0);return r.useEffect(()=>{if(e)return;let u=!1;return s().then(i=>{u||l(i)}),()=>{u=!0}},[e]),e}exports.useStreamdownPlugins=o;
|
|
2
2
|
//# sourceMappingURL=use-streamdown-plugins.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-streamdown-plugins.cjs","sources":["../../../src/components/ai/use-streamdown-plugins.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nimport type { PluginConfig } from \"streamdown\";\n\n// Module-level cache so the lazy chunk is fetched once and later mounts get\n// the plugins synchronously on first render (no highlight flash).\nlet cachedPlugins: PluginConfig | null = null;\nlet pluginsPromise: Promise<PluginConfig> | null = null;\n\nfunction loadStreamdownPlugins(): Promise<PluginConfig> {\n pluginsPromise ??= import(\"./streamdown-plugins\")\n .then((mod) => {\n cachedPlugins =
|
|
1
|
+
{"version":3,"file":"use-streamdown-plugins.cjs","sources":["../../../src/components/ai/use-streamdown-plugins.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nimport type { PluginConfig } from \"streamdown\";\n\n// Module-level cache so the lazy chunk is fetched once and later mounts get\n// the plugins synchronously on first render (no highlight flash).\nlet cachedPlugins: PluginConfig | null = null;\nlet pluginsPromise: Promise<PluginConfig> | null = null;\n\nfunction loadStreamdownPlugins(): Promise<PluginConfig> {\n pluginsPromise ??= import(\"./streamdown-plugins\")\n .then((mod) => mod.getStreamdownPlugins())\n .then((plugins) => {\n cachedPlugins = plugins;\n return plugins;\n })\n .catch((error) => {\n pluginsPromise = null;\n cachedPlugins = null;\n throw error;\n });\n return pluginsPromise;\n}\n\n/**\n * Lazily loads the streamdown plugin set (SW-2007). Markdown renders\n * immediately without plugins; code highlighting, math, and mermaid upgrade\n * in place once the lazy chunk arrives, keeping shiki/KaTeX/mermaid out of\n * the consumer's main bundle.\n */\nexport function useStreamdownPlugins(): PluginConfig | undefined {\n const [plugins, setPlugins] = useState<PluginConfig | undefined>(\n cachedPlugins ?? undefined,\n );\n\n useEffect(() => {\n if (plugins) return;\n let cancelled = false;\n void loadStreamdownPlugins().then((loaded) => {\n if (!cancelled) setPlugins(loaded);\n });\n return () => {\n cancelled = true;\n };\n }, [plugins]);\n\n return plugins;\n}\n"],"names":["cachedPlugins","pluginsPromise","loadStreamdownPlugins","mod","plugins","error","useStreamdownPlugins","setPlugins","useState","useEffect","cancelled","loaded"],"mappings":"yGAMA,IAAIA,EAAqC,KACrCC,EAA+C,KAEnD,SAASC,GAA+C,CACtD,OAAAD,IAAmB,QAAA,QAAA,EAAA,KAAA,IAAA,QAAO,0BAAsB,GAC7C,KAAME,GAAQA,EAAI,qBAAA,CAAsB,EACxC,KAAMC,IACLJ,EAAgBI,EACTA,EACR,EACA,MAAOC,GAAU,CAChB,MAAAJ,EAAiB,KACjBD,EAAgB,KACVK,CACR,CAAC,EACIJ,CACT,CAQO,SAASK,GAAiD,CAC/D,KAAM,CAACF,EAASG,CAAU,EAAIC,EAAAA,SAC5BR,GAAiB,MAAA,EAGnBS,OAAAA,EAAAA,UAAU,IAAM,CACd,GAAIL,EAAS,OACb,IAAIM,EAAY,GAChB,OAAKR,EAAA,EAAwB,KAAMS,GAAW,CACvCD,GAAWH,EAAWI,CAAM,CACnC,CAAC,EACM,IAAM,CACXD,EAAY,EACd,CACF,EAAG,CAACN,CAAO,CAAC,EAELA,CACT"}
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { useState as i, useEffect as o } from "react";
|
|
2
|
-
let
|
|
2
|
+
let n = null, t = null;
|
|
3
3
|
function s() {
|
|
4
|
-
return t ??= import("./streamdown-plugins.js").then((
|
|
5
|
-
throw t = null,
|
|
4
|
+
return t ??= import("./streamdown-plugins.js").then((e) => e.getStreamdownPlugins()).then((e) => (n = e, e)).catch((e) => {
|
|
5
|
+
throw t = null, n = null, e;
|
|
6
6
|
}), t;
|
|
7
7
|
}
|
|
8
8
|
function a() {
|
|
9
|
-
const [
|
|
10
|
-
|
|
9
|
+
const [e, u] = i(
|
|
10
|
+
n ?? void 0
|
|
11
11
|
);
|
|
12
12
|
return o(() => {
|
|
13
|
-
if (
|
|
13
|
+
if (e) return;
|
|
14
14
|
let l = !1;
|
|
15
15
|
return s().then((r) => {
|
|
16
16
|
l || u(r);
|
|
17
17
|
}), () => {
|
|
18
18
|
l = !0;
|
|
19
19
|
};
|
|
20
|
-
}, [
|
|
20
|
+
}, [e]), e;
|
|
21
21
|
}
|
|
22
22
|
export {
|
|
23
23
|
a as useStreamdownPlugins
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-streamdown-plugins.js","sources":["../../../src/components/ai/use-streamdown-plugins.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nimport type { PluginConfig } from \"streamdown\";\n\n// Module-level cache so the lazy chunk is fetched once and later mounts get\n// the plugins synchronously on first render (no highlight flash).\nlet cachedPlugins: PluginConfig | null = null;\nlet pluginsPromise: Promise<PluginConfig> | null = null;\n\nfunction loadStreamdownPlugins(): Promise<PluginConfig> {\n pluginsPromise ??= import(\"./streamdown-plugins\")\n .then((mod) => {\n cachedPlugins =
|
|
1
|
+
{"version":3,"file":"use-streamdown-plugins.js","sources":["../../../src/components/ai/use-streamdown-plugins.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\n\nimport type { PluginConfig } from \"streamdown\";\n\n// Module-level cache so the lazy chunk is fetched once and later mounts get\n// the plugins synchronously on first render (no highlight flash).\nlet cachedPlugins: PluginConfig | null = null;\nlet pluginsPromise: Promise<PluginConfig> | null = null;\n\nfunction loadStreamdownPlugins(): Promise<PluginConfig> {\n pluginsPromise ??= import(\"./streamdown-plugins\")\n .then((mod) => mod.getStreamdownPlugins())\n .then((plugins) => {\n cachedPlugins = plugins;\n return plugins;\n })\n .catch((error) => {\n pluginsPromise = null;\n cachedPlugins = null;\n throw error;\n });\n return pluginsPromise;\n}\n\n/**\n * Lazily loads the streamdown plugin set (SW-2007). Markdown renders\n * immediately without plugins; code highlighting, math, and mermaid upgrade\n * in place once the lazy chunk arrives, keeping shiki/KaTeX/mermaid out of\n * the consumer's main bundle.\n */\nexport function useStreamdownPlugins(): PluginConfig | undefined {\n const [plugins, setPlugins] = useState<PluginConfig | undefined>(\n cachedPlugins ?? undefined,\n );\n\n useEffect(() => {\n if (plugins) return;\n let cancelled = false;\n void loadStreamdownPlugins().then((loaded) => {\n if (!cancelled) setPlugins(loaded);\n });\n return () => {\n cancelled = true;\n };\n }, [plugins]);\n\n return plugins;\n}\n"],"names":["cachedPlugins","pluginsPromise","loadStreamdownPlugins","mod","plugins","error","useStreamdownPlugins","setPlugins","useState","useEffect","cancelled","loaded"],"mappings":";AAMA,IAAIA,IAAqC,MACrCC,IAA+C;AAEnD,SAASC,IAA+C;AACtD,SAAAD,MAAmB,OAAO,yBAAsB,EAC7C,KAAK,CAACE,MAAQA,EAAI,qBAAA,CAAsB,EACxC,KAAK,CAACC,OACLJ,IAAgBI,GACTA,EACR,EACA,MAAM,CAACC,MAAU;AAChB,UAAAJ,IAAiB,MACjBD,IAAgB,MACVK;AAAA,EACR,CAAC,GACIJ;AACT;AAQO,SAASK,IAAiD;AAC/D,QAAM,CAACF,GAASG,CAAU,IAAIC;AAAA,IAC5BR,KAAiB;AAAA,EAAA;AAGnB,SAAAS,EAAU,MAAM;AACd,QAAIL,EAAS;AACb,QAAIM,IAAY;AAChB,WAAKR,EAAA,EAAwB,KAAK,CAACS,MAAW;AAC5C,MAAKD,KAAWH,EAAWI,CAAM;AAAA,IACnC,CAAC,GACM,MAAM;AACX,MAAAD,IAAY;AAAA,IACd;AAAA,EACF,GAAG,CAACN,CAAO,CAAC,GAELA;AACT;"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),x=require("lucide-react"),u=require("../../../ui/button.cjs"),
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),x=require("lucide-react"),u=require("../../../ui/button.cjs"),F=require("../../../ui/input.cjs"),h=require("../../../ui/popover.cjs"),l=require("../../../ui/select.cjs"),P=require("../../../lib/utils.cjs"),R={contains:"contains",equals:"equals",not_equals:"not equals",starts_with:"starts with",ends_with:"ends with",is_empty:"is empty",is_not_empty:"is not empty"},O=["is_empty","is_not_empty"],f=["contains","equals","not_equals","starts_with","ends_with","is_empty","is_not_empty"];function A(n,d){const a=d?.[0]??"contains";return{id:crypto.randomUUID(),columnId:n,operator:a,value:""}}function E({columns:n,columnLabel:d,filters:a,onFiltersChange:i,triggerLabel:m="Filter",valuePlaceholder:g="Value…",clearAllLabel:_="Clear all",className:y}){const C=t=>d?.(t)??t,v=n[0],I=v?.columnId??"",N=()=>i([...a,A(I,v?.operators)]),S=t=>i(a.filter(c=>c.id!==t)),p=(t,c)=>i(a.map(r=>r.id===t?{...r,...c}:r)),b=()=>i([]),o=a.length;return e.jsxs(h.Popover,{children:[e.jsx(h.PopoverTrigger,{asChild:!0,children:e.jsxs(u.Button,{type:"button",variant:"outline",size:"sm","data-slot":"manifest-filter",className:P.cn(y),"aria-label":o>0?`${m} (${o} active)`:m,children:[e.jsx(x.ListFilterIcon,{className:"size-3.5"}),m,o>0?e.jsx("span",{className:"flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground",children:o}):null]})}),e.jsx(h.PopoverContent,{align:"end",className:"min-w-80","aria-label":"Filter conditions",children:e.jsxs("div",{className:"flex flex-col gap-2",children:[a.map(t=>{const r=n.find(s=>s.columnId===t.columnId)?.operators??f,q=O.includes(t.operator);return e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(l.Select,{value:t.columnId,onValueChange:s=>{const j=n.find(z=>z.columnId===s)?.operators??f,w=j.includes(t.operator)?t.operator:j[0]??"contains";p(t.id,{columnId:s,operator:w,value:""})},children:[e.jsx(l.SelectTrigger,{"aria-label":"Filter column",size:"sm",className:"w-36",children:e.jsx(l.SelectValue,{})}),e.jsx(l.SelectContent,{children:n.map(s=>e.jsx(l.SelectItem,{value:s.columnId,children:s.label??C(s.columnId)},s.columnId))})]}),e.jsxs(l.Select,{value:t.operator,onValueChange:s=>p(t.id,{operator:s,value:""}),children:[e.jsx(l.SelectTrigger,{"aria-label":"Filter operator",size:"sm",className:"w-32",children:e.jsx(l.SelectValue,{})}),e.jsx(l.SelectContent,{children:r.map(s=>e.jsx(l.SelectItem,{value:s,children:R[s]},s))})]}),q?e.jsx("div",{className:"h-8 w-40","aria-hidden":!0}):e.jsx(F.Input,{className:"w-40",placeholder:g,value:t.value,onChange:s=>p(t.id,{value:s.target.value})}),e.jsx(u.Button,{type:"button",variant:"ghost",size:"icon",className:"size-8 shrink-0 text-muted-foreground hover:text-foreground",onClick:()=>S(t.id),"aria-label":"Remove filter",children:e.jsx(x.XIcon,{className:"size-3.5"})})]},t.id)}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs(u.Button,{type:"button",variant:"outline",size:"sm",onClick:N,disabled:n.length===0,children:[e.jsx(x.PlusIcon,{className:"size-3.5"}),"Add filter"]}),a.length>0?e.jsx(u.Button,{type:"button",variant:"ghost",size:"sm",className:"text-muted-foreground",onClick:b,children:_}):null]})]})})]})}exports.ManifestFilterPopover=E;
|
|
2
2
|
//# sourceMappingURL=ManifestFilterPopover.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ManifestFilterPopover.cjs","sources":["../../../../src/components/composed/PlateMapEditor/ManifestFilterPopover.tsx"],"sourcesContent":["import { ListFilterIcon, PlusIcon, XIcon } from \"lucide-react\";\n\nimport type { FilterColumnConfig, FilterCondition, FilterOperator } from \"@/components/ui/data-table/data-table\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/ui/popover\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"@/components/ui/select\";\nimport { cn } from \"@/lib/utils\";\n\nconst OPERATOR_LABELS: Record<FilterOperator, string> = {\n contains: \"contains\",\n equals: \"equals\",\n not_equals: \"not equals\",\n starts_with: \"starts with\",\n ends_with: \"ends with\",\n is_empty: \"is empty\",\n is_not_empty: \"is not empty\",\n};\n\nconst VALUE_FREE_OPERATORS: FilterOperator[] = [\"is_empty\", \"is_not_empty\"];\n\nconst DEFAULT_OPERATORS: FilterOperator[] = [\n \"contains\",\n \"equals\",\n \"not_equals\",\n \"starts_with\",\n \"ends_with\",\n \"is_empty\",\n \"is_not_empty\",\n];\n\nfunction makeCondition(columnId: string, allowedOperators?: FilterOperator[]): FilterCondition {\n const operator = allowedOperators?.[0] ?? \"contains\";\n return { id: crypto.randomUUID(), columnId, operator, value: \"\" };\n}\n\nexport interface ManifestFilterPopoverProps {\n /** Filterable columns. Operator subsets can be specified per column. */\n columns: FilterColumnConfig[];\n /** Column id → display label resolver. */\n columnLabel?: (columnId: string) => string;\n filters: FilterCondition[];\n onFiltersChange: (next: FilterCondition[]) => void;\n triggerLabel?: string;\n className?: string;\n}\n\nexport function ManifestFilterPopover({\n columns,\n columnLabel,\n filters,\n onFiltersChange,\n triggerLabel = \"Filter\",\n className,\n}: ManifestFilterPopoverProps) {\n const labelFor = (columnId: string): string => columnLabel?.(columnId) ?? columnId;\n const firstColumn = columns[0];\n const firstColumnId = firstColumn?.columnId ?? \"\";\n\n const addFilter = () => onFiltersChange([...filters, makeCondition(firstColumnId, firstColumn?.operators)]);\n const removeFilter = (id: string) => onFiltersChange(filters.filter((cond) => cond.id !== id));\n const updateFilter = (id: string, patch: Partial<FilterCondition>) =>\n onFiltersChange(filters.map((cond) => (cond.id === id ? { ...cond, ...patch } : cond)));\n const clearAll = () => onFiltersChange([]);\n\n const activeCount = filters.length;\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n data-slot=\"manifest-filter\"\n className={cn(className)}\n aria-label={activeCount > 0 ? `${triggerLabel} (${activeCount} active)` : triggerLabel}\n >\n <ListFilterIcon className=\"size-3.5\" />\n {triggerLabel}\n {activeCount > 0 ? (\n <span className=\"flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground\">\n {activeCount}\n </span>\n ) : null}\n </Button>\n </PopoverTrigger>\n\n <PopoverContent align=\"end\" className=\"min-w-80\" aria-label=\"Filter conditions\">\n <div className=\"flex flex-col gap-2\">\n {filters.map((condition) => {\n const colConfig = columns.find((c) => c.columnId === condition.columnId);\n const operators = colConfig?.operators ?? DEFAULT_OPERATORS;\n const isValueFree = VALUE_FREE_OPERATORS.includes(condition.operator);\n return (\n <div key={condition.id} className=\"flex items-center gap-2\">\n <Select\n value={condition.columnId}\n onValueChange={(value) => {\n const nextConfig = columns.find((c) => c.columnId === value);\n const nextOperators = nextConfig?.operators ?? DEFAULT_OPERATORS;\n const nextOperator = nextOperators.includes(condition.operator)\n ? condition.operator\n : (nextOperators[0] ?? \"contains\");\n updateFilter(condition.id, { columnId: value, operator: nextOperator, value: \"\" });\n }}\n >\n <SelectTrigger aria-label=\"Filter column\" size=\"sm\" className=\"w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {columns.map((c) => (\n <SelectItem key={c.columnId} value={c.columnId}>\n {c.label ?? labelFor(c.columnId)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <Select\n value={condition.operator}\n onValueChange={(value) =>\n updateFilter(condition.id, { operator: value as FilterOperator, value: \"\" })\n }\n >\n <SelectTrigger aria-label=\"Filter operator\" size=\"sm\" className=\"w-32\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {OPERATOR_LABELS[op]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {isValueFree ? (\n <div className=\"h-8 w-40\" aria-hidden />\n ) : (\n <Input\n className=\"w-40\"\n placeholder=\"Value…\"\n value={condition.value}\n onChange={(event) => updateFilter(condition.id, { value: event.target.value })}\n />\n )}\n\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-8 shrink-0 text-muted-foreground hover:text-foreground\"\n onClick={() => removeFilter(condition.id)}\n aria-label=\"Remove filter\"\n >\n <XIcon className=\"size-3.5\" />\n </Button>\n </div>\n );\n })}\n\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={addFilter}\n disabled={columns.length === 0}\n >\n <PlusIcon className=\"size-3.5\" />\n Add filter\n </Button>\n {filters.length > 0 ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-muted-foreground\"\n onClick={clearAll}\n >\n Clear all\n </Button>\n ) : null}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n"],"names":["OPERATOR_LABELS","VALUE_FREE_OPERATORS","DEFAULT_OPERATORS","makeCondition","columnId","allowedOperators","operator","ManifestFilterPopover","columns","columnLabel","filters","onFiltersChange","triggerLabel","className","labelFor","firstColumn","firstColumnId","addFilter","removeFilter","id","cond","updateFilter","patch","clearAll","activeCount","Popover","jsx","PopoverTrigger","jsxs","Button","cn","ListFilterIcon","PopoverContent","condition","operators","c","isValueFree","Select","value","nextOperators","nextOperator","SelectTrigger","SelectValue","SelectContent","SelectItem","op","Input","event","XIcon","PlusIcon"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ManifestFilterPopover.cjs","sources":["../../../../src/components/composed/PlateMapEditor/ManifestFilterPopover.tsx"],"sourcesContent":["import { ListFilterIcon, PlusIcon, XIcon } from \"lucide-react\";\n\nimport type { FilterColumnConfig, FilterCondition, FilterOperator } from \"@/components/ui/data-table/data-table\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/ui/popover\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"@/components/ui/select\";\nimport { cn } from \"@/lib/utils\";\n\nconst OPERATOR_LABELS: Record<FilterOperator, string> = {\n contains: \"contains\",\n equals: \"equals\",\n not_equals: \"not equals\",\n starts_with: \"starts with\",\n ends_with: \"ends with\",\n is_empty: \"is empty\",\n is_not_empty: \"is not empty\",\n};\n\nconst VALUE_FREE_OPERATORS: FilterOperator[] = [\"is_empty\", \"is_not_empty\"];\n\nconst DEFAULT_OPERATORS: FilterOperator[] = [\n \"contains\",\n \"equals\",\n \"not_equals\",\n \"starts_with\",\n \"ends_with\",\n \"is_empty\",\n \"is_not_empty\",\n];\n\nfunction makeCondition(columnId: string, allowedOperators?: FilterOperator[]): FilterCondition {\n const operator = allowedOperators?.[0] ?? \"contains\";\n return { id: crypto.randomUUID(), columnId, operator, value: \"\" };\n}\n\nexport interface ManifestFilterPopoverProps {\n /** Filterable columns. Operator subsets can be specified per column. */\n columns: FilterColumnConfig[];\n /** Column id → display label resolver. */\n columnLabel?: (columnId: string) => string;\n filters: FilterCondition[];\n onFiltersChange: (next: FilterCondition[]) => void;\n triggerLabel?: string;\n /** Placeholder for a condition's value input. Defaults to `\"Value…\"`. */\n valuePlaceholder?: string;\n /** Label for the clear-every-filter button. Defaults to `\"Clear all\"`. */\n clearAllLabel?: string;\n className?: string;\n}\n\nexport function ManifestFilterPopover({\n columns,\n columnLabel,\n filters,\n onFiltersChange,\n triggerLabel = \"Filter\",\n valuePlaceholder = \"Value…\",\n clearAllLabel = \"Clear all\",\n className,\n}: ManifestFilterPopoverProps) {\n const labelFor = (columnId: string): string => columnLabel?.(columnId) ?? columnId;\n const firstColumn = columns[0];\n const firstColumnId = firstColumn?.columnId ?? \"\";\n\n const addFilter = () => onFiltersChange([...filters, makeCondition(firstColumnId, firstColumn?.operators)]);\n const removeFilter = (id: string) => onFiltersChange(filters.filter((cond) => cond.id !== id));\n const updateFilter = (id: string, patch: Partial<FilterCondition>) =>\n onFiltersChange(filters.map((cond) => (cond.id === id ? { ...cond, ...patch } : cond)));\n const clearAll = () => onFiltersChange([]);\n\n const activeCount = filters.length;\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n data-slot=\"manifest-filter\"\n className={cn(className)}\n aria-label={activeCount > 0 ? `${triggerLabel} (${activeCount} active)` : triggerLabel}\n >\n <ListFilterIcon className=\"size-3.5\" />\n {triggerLabel}\n {activeCount > 0 ? (\n <span className=\"flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground\">\n {activeCount}\n </span>\n ) : null}\n </Button>\n </PopoverTrigger>\n\n <PopoverContent align=\"end\" className=\"min-w-80\" aria-label=\"Filter conditions\">\n <div className=\"flex flex-col gap-2\">\n {filters.map((condition) => {\n const colConfig = columns.find((c) => c.columnId === condition.columnId);\n const operators = colConfig?.operators ?? DEFAULT_OPERATORS;\n const isValueFree = VALUE_FREE_OPERATORS.includes(condition.operator);\n return (\n <div key={condition.id} className=\"flex items-center gap-2\">\n <Select\n value={condition.columnId}\n onValueChange={(value) => {\n const nextConfig = columns.find((c) => c.columnId === value);\n const nextOperators = nextConfig?.operators ?? DEFAULT_OPERATORS;\n const nextOperator = nextOperators.includes(condition.operator)\n ? condition.operator\n : (nextOperators[0] ?? \"contains\");\n updateFilter(condition.id, { columnId: value, operator: nextOperator, value: \"\" });\n }}\n >\n <SelectTrigger aria-label=\"Filter column\" size=\"sm\" className=\"w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {columns.map((c) => (\n <SelectItem key={c.columnId} value={c.columnId}>\n {c.label ?? labelFor(c.columnId)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <Select\n value={condition.operator}\n onValueChange={(value) =>\n updateFilter(condition.id, { operator: value as FilterOperator, value: \"\" })\n }\n >\n <SelectTrigger aria-label=\"Filter operator\" size=\"sm\" className=\"w-32\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {OPERATOR_LABELS[op]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {isValueFree ? (\n <div className=\"h-8 w-40\" aria-hidden />\n ) : (\n <Input\n className=\"w-40\"\n placeholder={valuePlaceholder}\n value={condition.value}\n onChange={(event) => updateFilter(condition.id, { value: event.target.value })}\n />\n )}\n\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-8 shrink-0 text-muted-foreground hover:text-foreground\"\n onClick={() => removeFilter(condition.id)}\n aria-label=\"Remove filter\"\n >\n <XIcon className=\"size-3.5\" />\n </Button>\n </div>\n );\n })}\n\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={addFilter}\n disabled={columns.length === 0}\n >\n <PlusIcon className=\"size-3.5\" />\n Add filter\n </Button>\n {filters.length > 0 ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-muted-foreground\"\n onClick={clearAll}\n >\n {clearAllLabel}\n </Button>\n ) : null}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n"],"names":["OPERATOR_LABELS","VALUE_FREE_OPERATORS","DEFAULT_OPERATORS","makeCondition","columnId","allowedOperators","operator","ManifestFilterPopover","columns","columnLabel","filters","onFiltersChange","triggerLabel","valuePlaceholder","clearAllLabel","className","labelFor","firstColumn","firstColumnId","addFilter","removeFilter","id","cond","updateFilter","patch","clearAll","activeCount","Popover","jsx","PopoverTrigger","jsxs","Button","cn","ListFilterIcon","PopoverContent","condition","operators","c","isValueFree","Select","value","nextOperators","nextOperator","SelectTrigger","SelectValue","SelectContent","SelectItem","op","Input","event","XIcon","PlusIcon"],"mappings":"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"}
|
|
@@ -7,6 +7,10 @@ export interface ManifestFilterPopoverProps {
|
|
|
7
7
|
filters: FilterCondition[];
|
|
8
8
|
onFiltersChange: (next: FilterCondition[]) => void;
|
|
9
9
|
triggerLabel?: string;
|
|
10
|
+
/** Placeholder for a condition's value input. Defaults to `"Value…"`. */
|
|
11
|
+
valuePlaceholder?: string;
|
|
12
|
+
/** Label for the clear-every-filter button. Defaults to `"Clear all"`. */
|
|
13
|
+
clearAllLabel?: string;
|
|
10
14
|
className?: string;
|
|
11
15
|
}
|
|
12
|
-
export declare function ManifestFilterPopover({ columns, columnLabel, filters, onFiltersChange, triggerLabel, className, }: ManifestFilterPopoverProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare function ManifestFilterPopover({ columns, columnLabel, filters, onFiltersChange, triggerLabel, valuePlaceholder, clearAllLabel, className, }: ManifestFilterPopoverProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsxs as l, jsx as t } from "react/jsx-runtime";
|
|
2
|
-
import { ListFilterIcon as
|
|
2
|
+
import { ListFilterIcon as S, XIcon as q, PlusIcon as T } from "lucide-react";
|
|
3
3
|
import { Button as m } from "../../../ui/button.js";
|
|
4
|
-
import { Input as
|
|
5
|
-
import { Popover as
|
|
4
|
+
import { Input as V } from "../../../ui/input.js";
|
|
5
|
+
import { Popover as k, PopoverTrigger as U, PopoverContent as j } from "../../../ui/popover.js";
|
|
6
6
|
import { Select as v, SelectTrigger as g, SelectValue as _, SelectContent as x, SelectItem as y } from "../../../ui/select.js";
|
|
7
|
-
import { cn as
|
|
8
|
-
const
|
|
7
|
+
import { cn as L } from "../../../lib/utils.js";
|
|
8
|
+
const B = {
|
|
9
9
|
contains: "contains",
|
|
10
10
|
equals: "equals",
|
|
11
11
|
not_equals: "not equals",
|
|
@@ -13,7 +13,7 @@ const j = {
|
|
|
13
13
|
ends_with: "ends with",
|
|
14
14
|
is_empty: "is empty",
|
|
15
15
|
is_not_empty: "is not empty"
|
|
16
|
-
},
|
|
16
|
+
}, D = ["is_empty", "is_not_empty"], C = [
|
|
17
17
|
"contains",
|
|
18
18
|
"equals",
|
|
19
19
|
"not_equals",
|
|
@@ -22,51 +22,53 @@ const j = {
|
|
|
22
22
|
"is_empty",
|
|
23
23
|
"is_not_empty"
|
|
24
24
|
];
|
|
25
|
-
function
|
|
25
|
+
function $(n, u) {
|
|
26
26
|
const r = u?.[0] ?? "contains";
|
|
27
27
|
return { id: crypto.randomUUID(), columnId: n, operator: r, value: "" };
|
|
28
28
|
}
|
|
29
|
-
function
|
|
29
|
+
function Y({
|
|
30
30
|
columns: n,
|
|
31
31
|
columnLabel: u,
|
|
32
32
|
filters: r,
|
|
33
33
|
onFiltersChange: s,
|
|
34
34
|
triggerLabel: d = "Filter",
|
|
35
|
-
|
|
35
|
+
valuePlaceholder: I = "Value…",
|
|
36
|
+
clearAllLabel: N = "Clear all",
|
|
37
|
+
className: b
|
|
36
38
|
}) {
|
|
37
|
-
const
|
|
38
|
-
return /* @__PURE__ */ l(
|
|
39
|
-
/* @__PURE__ */ t(
|
|
39
|
+
const w = (e) => u?.(e) ?? e, h = n[0], z = h?.columnId ?? "", F = () => s([...r, $(z, h?.operators)]), A = (e) => s(r.filter((c) => c.id !== e)), p = (e, c) => s(r.map((o) => o.id === e ? { ...o, ...c } : o)), E = () => s([]), i = r.length;
|
|
40
|
+
return /* @__PURE__ */ l(k, { children: [
|
|
41
|
+
/* @__PURE__ */ t(U, { asChild: !0, children: /* @__PURE__ */ l(
|
|
40
42
|
m,
|
|
41
43
|
{
|
|
42
44
|
type: "button",
|
|
43
45
|
variant: "outline",
|
|
44
46
|
size: "sm",
|
|
45
47
|
"data-slot": "manifest-filter",
|
|
46
|
-
className:
|
|
48
|
+
className: L(b),
|
|
47
49
|
"aria-label": i > 0 ? `${d} (${i} active)` : d,
|
|
48
50
|
children: [
|
|
49
|
-
/* @__PURE__ */ t(
|
|
51
|
+
/* @__PURE__ */ t(S, { className: "size-3.5" }),
|
|
50
52
|
d,
|
|
51
53
|
i > 0 ? /* @__PURE__ */ t("span", { className: "flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground", children: i }) : null
|
|
52
54
|
]
|
|
53
55
|
}
|
|
54
56
|
) }),
|
|
55
|
-
/* @__PURE__ */ t(
|
|
57
|
+
/* @__PURE__ */ t(j, { align: "end", className: "min-w-80", "aria-label": "Filter conditions", children: /* @__PURE__ */ l("div", { className: "flex flex-col gap-2", children: [
|
|
56
58
|
r.map((e) => {
|
|
57
|
-
const o = n.find((a) => a.columnId === e.columnId)?.operators ?? C,
|
|
59
|
+
const o = n.find((a) => a.columnId === e.columnId)?.operators ?? C, O = D.includes(e.operator);
|
|
58
60
|
return /* @__PURE__ */ l("div", { className: "flex items-center gap-2", children: [
|
|
59
61
|
/* @__PURE__ */ l(
|
|
60
62
|
v,
|
|
61
63
|
{
|
|
62
64
|
value: e.columnId,
|
|
63
65
|
onValueChange: (a) => {
|
|
64
|
-
const f = n.find((
|
|
65
|
-
p(e.id, { columnId: a, operator:
|
|
66
|
+
const f = n.find((R) => R.columnId === a)?.operators ?? C, P = f.includes(e.operator) ? e.operator : f[0] ?? "contains";
|
|
67
|
+
p(e.id, { columnId: a, operator: P, value: "" });
|
|
66
68
|
},
|
|
67
69
|
children: [
|
|
68
70
|
/* @__PURE__ */ t(g, { "aria-label": "Filter column", size: "sm", className: "w-36", children: /* @__PURE__ */ t(_, {}) }),
|
|
69
|
-
/* @__PURE__ */ t(x, { children: n.map((a) => /* @__PURE__ */ t(y, { value: a.columnId, children: a.label ??
|
|
71
|
+
/* @__PURE__ */ t(x, { children: n.map((a) => /* @__PURE__ */ t(y, { value: a.columnId, children: a.label ?? w(a.columnId) }, a.columnId)) })
|
|
70
72
|
]
|
|
71
73
|
}
|
|
72
74
|
),
|
|
@@ -77,15 +79,15 @@ function Q({
|
|
|
77
79
|
onValueChange: (a) => p(e.id, { operator: a, value: "" }),
|
|
78
80
|
children: [
|
|
79
81
|
/* @__PURE__ */ t(g, { "aria-label": "Filter operator", size: "sm", className: "w-32", children: /* @__PURE__ */ t(_, {}) }),
|
|
80
|
-
/* @__PURE__ */ t(x, { children: o.map((a) => /* @__PURE__ */ t(y, { value: a, children:
|
|
82
|
+
/* @__PURE__ */ t(x, { children: o.map((a) => /* @__PURE__ */ t(y, { value: a, children: B[a] }, a)) })
|
|
81
83
|
]
|
|
82
84
|
}
|
|
83
85
|
),
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
O ? /* @__PURE__ */ t("div", { className: "h-8 w-40", "aria-hidden": !0 }) : /* @__PURE__ */ t(
|
|
87
|
+
V,
|
|
86
88
|
{
|
|
87
89
|
className: "w-40",
|
|
88
|
-
placeholder:
|
|
90
|
+
placeholder: I,
|
|
89
91
|
value: e.value,
|
|
90
92
|
onChange: (a) => p(e.id, { value: a.target.value })
|
|
91
93
|
}
|
|
@@ -97,9 +99,9 @@ function Q({
|
|
|
97
99
|
variant: "ghost",
|
|
98
100
|
size: "icon",
|
|
99
101
|
className: "size-8 shrink-0 text-muted-foreground hover:text-foreground",
|
|
100
|
-
onClick: () =>
|
|
102
|
+
onClick: () => A(e.id),
|
|
101
103
|
"aria-label": "Remove filter",
|
|
102
|
-
children: /* @__PURE__ */ t(
|
|
104
|
+
children: /* @__PURE__ */ t(q, { className: "size-3.5" })
|
|
103
105
|
}
|
|
104
106
|
)
|
|
105
107
|
] }, e.id);
|
|
@@ -111,10 +113,10 @@ function Q({
|
|
|
111
113
|
type: "button",
|
|
112
114
|
variant: "outline",
|
|
113
115
|
size: "sm",
|
|
114
|
-
onClick:
|
|
116
|
+
onClick: F,
|
|
115
117
|
disabled: n.length === 0,
|
|
116
118
|
children: [
|
|
117
|
-
/* @__PURE__ */ t(
|
|
119
|
+
/* @__PURE__ */ t(T, { className: "size-3.5" }),
|
|
118
120
|
"Add filter"
|
|
119
121
|
]
|
|
120
122
|
}
|
|
@@ -126,8 +128,8 @@ function Q({
|
|
|
126
128
|
variant: "ghost",
|
|
127
129
|
size: "sm",
|
|
128
130
|
className: "text-muted-foreground",
|
|
129
|
-
onClick:
|
|
130
|
-
children:
|
|
131
|
+
onClick: E,
|
|
132
|
+
children: N
|
|
131
133
|
}
|
|
132
134
|
) : null
|
|
133
135
|
] })
|
|
@@ -135,6 +137,6 @@ function Q({
|
|
|
135
137
|
] });
|
|
136
138
|
}
|
|
137
139
|
export {
|
|
138
|
-
|
|
140
|
+
Y as ManifestFilterPopover
|
|
139
141
|
};
|
|
140
142
|
//# sourceMappingURL=ManifestFilterPopover.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ManifestFilterPopover.js","sources":["../../../../src/components/composed/PlateMapEditor/ManifestFilterPopover.tsx"],"sourcesContent":["import { ListFilterIcon, PlusIcon, XIcon } from \"lucide-react\";\n\nimport type { FilterColumnConfig, FilterCondition, FilterOperator } from \"@/components/ui/data-table/data-table\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/ui/popover\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"@/components/ui/select\";\nimport { cn } from \"@/lib/utils\";\n\nconst OPERATOR_LABELS: Record<FilterOperator, string> = {\n contains: \"contains\",\n equals: \"equals\",\n not_equals: \"not equals\",\n starts_with: \"starts with\",\n ends_with: \"ends with\",\n is_empty: \"is empty\",\n is_not_empty: \"is not empty\",\n};\n\nconst VALUE_FREE_OPERATORS: FilterOperator[] = [\"is_empty\", \"is_not_empty\"];\n\nconst DEFAULT_OPERATORS: FilterOperator[] = [\n \"contains\",\n \"equals\",\n \"not_equals\",\n \"starts_with\",\n \"ends_with\",\n \"is_empty\",\n \"is_not_empty\",\n];\n\nfunction makeCondition(columnId: string, allowedOperators?: FilterOperator[]): FilterCondition {\n const operator = allowedOperators?.[0] ?? \"contains\";\n return { id: crypto.randomUUID(), columnId, operator, value: \"\" };\n}\n\nexport interface ManifestFilterPopoverProps {\n /** Filterable columns. Operator subsets can be specified per column. */\n columns: FilterColumnConfig[];\n /** Column id → display label resolver. */\n columnLabel?: (columnId: string) => string;\n filters: FilterCondition[];\n onFiltersChange: (next: FilterCondition[]) => void;\n triggerLabel?: string;\n className?: string;\n}\n\nexport function ManifestFilterPopover({\n columns,\n columnLabel,\n filters,\n onFiltersChange,\n triggerLabel = \"Filter\",\n className,\n}: ManifestFilterPopoverProps) {\n const labelFor = (columnId: string): string => columnLabel?.(columnId) ?? columnId;\n const firstColumn = columns[0];\n const firstColumnId = firstColumn?.columnId ?? \"\";\n\n const addFilter = () => onFiltersChange([...filters, makeCondition(firstColumnId, firstColumn?.operators)]);\n const removeFilter = (id: string) => onFiltersChange(filters.filter((cond) => cond.id !== id));\n const updateFilter = (id: string, patch: Partial<FilterCondition>) =>\n onFiltersChange(filters.map((cond) => (cond.id === id ? { ...cond, ...patch } : cond)));\n const clearAll = () => onFiltersChange([]);\n\n const activeCount = filters.length;\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n data-slot=\"manifest-filter\"\n className={cn(className)}\n aria-label={activeCount > 0 ? `${triggerLabel} (${activeCount} active)` : triggerLabel}\n >\n <ListFilterIcon className=\"size-3.5\" />\n {triggerLabel}\n {activeCount > 0 ? (\n <span className=\"flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground\">\n {activeCount}\n </span>\n ) : null}\n </Button>\n </PopoverTrigger>\n\n <PopoverContent align=\"end\" className=\"min-w-80\" aria-label=\"Filter conditions\">\n <div className=\"flex flex-col gap-2\">\n {filters.map((condition) => {\n const colConfig = columns.find((c) => c.columnId === condition.columnId);\n const operators = colConfig?.operators ?? DEFAULT_OPERATORS;\n const isValueFree = VALUE_FREE_OPERATORS.includes(condition.operator);\n return (\n <div key={condition.id} className=\"flex items-center gap-2\">\n <Select\n value={condition.columnId}\n onValueChange={(value) => {\n const nextConfig = columns.find((c) => c.columnId === value);\n const nextOperators = nextConfig?.operators ?? DEFAULT_OPERATORS;\n const nextOperator = nextOperators.includes(condition.operator)\n ? condition.operator\n : (nextOperators[0] ?? \"contains\");\n updateFilter(condition.id, { columnId: value, operator: nextOperator, value: \"\" });\n }}\n >\n <SelectTrigger aria-label=\"Filter column\" size=\"sm\" className=\"w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {columns.map((c) => (\n <SelectItem key={c.columnId} value={c.columnId}>\n {c.label ?? labelFor(c.columnId)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <Select\n value={condition.operator}\n onValueChange={(value) =>\n updateFilter(condition.id, { operator: value as FilterOperator, value: \"\" })\n }\n >\n <SelectTrigger aria-label=\"Filter operator\" size=\"sm\" className=\"w-32\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {OPERATOR_LABELS[op]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {isValueFree ? (\n <div className=\"h-8 w-40\" aria-hidden />\n ) : (\n <Input\n className=\"w-40\"\n placeholder=\"Value…\"\n value={condition.value}\n onChange={(event) => updateFilter(condition.id, { value: event.target.value })}\n />\n )}\n\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-8 shrink-0 text-muted-foreground hover:text-foreground\"\n onClick={() => removeFilter(condition.id)}\n aria-label=\"Remove filter\"\n >\n <XIcon className=\"size-3.5\" />\n </Button>\n </div>\n );\n })}\n\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={addFilter}\n disabled={columns.length === 0}\n >\n <PlusIcon className=\"size-3.5\" />\n Add filter\n </Button>\n {filters.length > 0 ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-muted-foreground\"\n onClick={clearAll}\n >\n Clear all\n </Button>\n ) : null}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n"],"names":["OPERATOR_LABELS","VALUE_FREE_OPERATORS","DEFAULT_OPERATORS","makeCondition","columnId","allowedOperators","operator","ManifestFilterPopover","columns","columnLabel","filters","onFiltersChange","triggerLabel","className","labelFor","firstColumn","firstColumnId","addFilter","removeFilter","id","cond","updateFilter","patch","clearAll","activeCount","Popover","jsx","PopoverTrigger","jsxs","Button","cn","ListFilterIcon","PopoverContent","condition","operators","c","isValueFree","Select","value","nextOperators","nextOperator","SelectTrigger","SelectValue","SelectContent","SelectItem","op","Input","event","XIcon","PlusIcon"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ManifestFilterPopover.js","sources":["../../../../src/components/composed/PlateMapEditor/ManifestFilterPopover.tsx"],"sourcesContent":["import { ListFilterIcon, PlusIcon, XIcon } from \"lucide-react\";\n\nimport type { FilterColumnConfig, FilterCondition, FilterOperator } from \"@/components/ui/data-table/data-table\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Popover, PopoverContent, PopoverTrigger } from \"@/components/ui/popover\";\nimport { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from \"@/components/ui/select\";\nimport { cn } from \"@/lib/utils\";\n\nconst OPERATOR_LABELS: Record<FilterOperator, string> = {\n contains: \"contains\",\n equals: \"equals\",\n not_equals: \"not equals\",\n starts_with: \"starts with\",\n ends_with: \"ends with\",\n is_empty: \"is empty\",\n is_not_empty: \"is not empty\",\n};\n\nconst VALUE_FREE_OPERATORS: FilterOperator[] = [\"is_empty\", \"is_not_empty\"];\n\nconst DEFAULT_OPERATORS: FilterOperator[] = [\n \"contains\",\n \"equals\",\n \"not_equals\",\n \"starts_with\",\n \"ends_with\",\n \"is_empty\",\n \"is_not_empty\",\n];\n\nfunction makeCondition(columnId: string, allowedOperators?: FilterOperator[]): FilterCondition {\n const operator = allowedOperators?.[0] ?? \"contains\";\n return { id: crypto.randomUUID(), columnId, operator, value: \"\" };\n}\n\nexport interface ManifestFilterPopoverProps {\n /** Filterable columns. Operator subsets can be specified per column. */\n columns: FilterColumnConfig[];\n /** Column id → display label resolver. */\n columnLabel?: (columnId: string) => string;\n filters: FilterCondition[];\n onFiltersChange: (next: FilterCondition[]) => void;\n triggerLabel?: string;\n /** Placeholder for a condition's value input. Defaults to `\"Value…\"`. */\n valuePlaceholder?: string;\n /** Label for the clear-every-filter button. Defaults to `\"Clear all\"`. */\n clearAllLabel?: string;\n className?: string;\n}\n\nexport function ManifestFilterPopover({\n columns,\n columnLabel,\n filters,\n onFiltersChange,\n triggerLabel = \"Filter\",\n valuePlaceholder = \"Value…\",\n clearAllLabel = \"Clear all\",\n className,\n}: ManifestFilterPopoverProps) {\n const labelFor = (columnId: string): string => columnLabel?.(columnId) ?? columnId;\n const firstColumn = columns[0];\n const firstColumnId = firstColumn?.columnId ?? \"\";\n\n const addFilter = () => onFiltersChange([...filters, makeCondition(firstColumnId, firstColumn?.operators)]);\n const removeFilter = (id: string) => onFiltersChange(filters.filter((cond) => cond.id !== id));\n const updateFilter = (id: string, patch: Partial<FilterCondition>) =>\n onFiltersChange(filters.map((cond) => (cond.id === id ? { ...cond, ...patch } : cond)));\n const clearAll = () => onFiltersChange([]);\n\n const activeCount = filters.length;\n\n return (\n <Popover>\n <PopoverTrigger asChild>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n data-slot=\"manifest-filter\"\n className={cn(className)}\n aria-label={activeCount > 0 ? `${triggerLabel} (${activeCount} active)` : triggerLabel}\n >\n <ListFilterIcon className=\"size-3.5\" />\n {triggerLabel}\n {activeCount > 0 ? (\n <span className=\"flex size-4 items-center justify-center rounded-full bg-primary text-[10px] font-medium text-primary-foreground\">\n {activeCount}\n </span>\n ) : null}\n </Button>\n </PopoverTrigger>\n\n <PopoverContent align=\"end\" className=\"min-w-80\" aria-label=\"Filter conditions\">\n <div className=\"flex flex-col gap-2\">\n {filters.map((condition) => {\n const colConfig = columns.find((c) => c.columnId === condition.columnId);\n const operators = colConfig?.operators ?? DEFAULT_OPERATORS;\n const isValueFree = VALUE_FREE_OPERATORS.includes(condition.operator);\n return (\n <div key={condition.id} className=\"flex items-center gap-2\">\n <Select\n value={condition.columnId}\n onValueChange={(value) => {\n const nextConfig = columns.find((c) => c.columnId === value);\n const nextOperators = nextConfig?.operators ?? DEFAULT_OPERATORS;\n const nextOperator = nextOperators.includes(condition.operator)\n ? condition.operator\n : (nextOperators[0] ?? \"contains\");\n updateFilter(condition.id, { columnId: value, operator: nextOperator, value: \"\" });\n }}\n >\n <SelectTrigger aria-label=\"Filter column\" size=\"sm\" className=\"w-36\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {columns.map((c) => (\n <SelectItem key={c.columnId} value={c.columnId}>\n {c.label ?? labelFor(c.columnId)}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n <Select\n value={condition.operator}\n onValueChange={(value) =>\n updateFilter(condition.id, { operator: value as FilterOperator, value: \"\" })\n }\n >\n <SelectTrigger aria-label=\"Filter operator\" size=\"sm\" className=\"w-32\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {operators.map((op) => (\n <SelectItem key={op} value={op}>\n {OPERATOR_LABELS[op]}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n\n {isValueFree ? (\n <div className=\"h-8 w-40\" aria-hidden />\n ) : (\n <Input\n className=\"w-40\"\n placeholder={valuePlaceholder}\n value={condition.value}\n onChange={(event) => updateFilter(condition.id, { value: event.target.value })}\n />\n )}\n\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"size-8 shrink-0 text-muted-foreground hover:text-foreground\"\n onClick={() => removeFilter(condition.id)}\n aria-label=\"Remove filter\"\n >\n <XIcon className=\"size-3.5\" />\n </Button>\n </div>\n );\n })}\n\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={addFilter}\n disabled={columns.length === 0}\n >\n <PlusIcon className=\"size-3.5\" />\n Add filter\n </Button>\n {filters.length > 0 ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-muted-foreground\"\n onClick={clearAll}\n >\n {clearAllLabel}\n </Button>\n ) : null}\n </div>\n </div>\n </PopoverContent>\n </Popover>\n );\n}\n"],"names":["OPERATOR_LABELS","VALUE_FREE_OPERATORS","DEFAULT_OPERATORS","makeCondition","columnId","allowedOperators","operator","ManifestFilterPopover","columns","columnLabel","filters","onFiltersChange","triggerLabel","valuePlaceholder","clearAllLabel","className","labelFor","firstColumn","firstColumnId","addFilter","removeFilter","id","cond","updateFilter","patch","clearAll","activeCount","Popover","jsx","PopoverTrigger","jsxs","Button","cn","ListFilterIcon","PopoverContent","condition","operators","c","isValueFree","Select","value","nextOperators","nextOperator","SelectTrigger","SelectValue","SelectContent","SelectItem","op","Input","event","XIcon","PlusIcon"],"mappings":"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"}
|