@crispy-seed/data-table 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -33,6 +33,7 @@ var textInput = require('@seed-design/css/recipes/text-input');
33
33
  var reactMenu = require('@seed-design/react-menu');
34
34
  var menu = require('@seed-design/css/recipes/menu');
35
35
  var menuItem = require('@seed-design/css/recipes/menu-item');
36
+ var tabs = require('@seed-design/css/recipes/tabs');
36
37
 
37
38
  function _interopNamespace(e) {
38
39
  if (e && e.__esModule) return e;
@@ -224,36 +225,8 @@ var Icon$b = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
224
225
  var IconDot3HorizontalLine = /*#__PURE__*/ React.forwardRef(Icon$b);
225
226
  var IconDot3HorizontalLine_default = IconDot3HorizontalLine;
226
227
 
227
- // src/IconEyeLine.tsx
228
- var Icon$a = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
229
- viewBox: "0 0 24 24",
230
- fill: "none",
231
- xmlns: "http://www.w3.org/2000/svg",
232
- "data-seed-icon": "true",
233
- width: size,
234
- height: size,
235
- ref,
236
- ...props,
237
- children: /* @__PURE__ */ jsxRuntime.jsx("g", {
238
- children: /* @__PURE__ */ jsxRuntime.jsxs("g", {
239
- children: [
240
- /* @__PURE__ */ jsxRuntime.jsx("path", {
241
- d: "M1.68785 14.0027C2.21223 14.1751 2.77712 13.8901 2.94996 13.3659C3.07236 13.0089 3.23163 12.663 3.39915 12.3254C3.7229 11.6731 4.22663 10.8054 4.94433 9.94191C6.3729 8.22305 8.6137 6.55273 12.0003 6.55273C15.3868 6.55273 17.6276 8.22305 19.0562 9.94191C19.7739 10.8054 20.2776 11.6731 20.6014 12.3254C20.7627 12.6505 20.8778 12.9189 20.9513 13.1024C21.0036 13.233 21.0295 13.302 21.0509 13.3669C21.2241 13.8904 21.7886 14.175 22.3127 14.0027C22.8373 13.8301 23.1225 13.264 22.9499 12.7394C22.9158 12.6366 22.8793 12.5369 22.8078 12.3586C22.717 12.1319 22.5805 11.8144 22.3929 11.4363C22.0187 10.6824 21.435 9.67502 20.5943 8.66356C18.9062 6.63242 16.147 4.55273 12.0003 4.55273C7.8535 4.55273 5.09431 6.63242 3.40621 8.66356C2.56557 9.67502 1.9818 10.6824 1.60765 11.4363C1.42001 11.8144 1.28353 12.1319 1.1927 12.3586C1.12106 12.5374 1.08448 12.6372 1.05033 12.7403C0.877781 13.2649 1.16321 13.8301 1.68785 14.0027Z",
242
- fill: "currentColor"
243
- }),
244
- /* @__PURE__ */ jsxRuntime.jsx("path", {
245
- d: "M12.576 9.10187C12.7569 9.46282 12.7029 9.89712 12.4393 10.2029C12.0675 10.634 11.8429 11.1792 11.8429 11.7727C11.8429 13.0923 12.9123 14.1613 14.2263 14.1613C14.8331 14.1613 15.3926 13.9188 15.8177 13.522C16.1163 13.2434 16.5544 13.1743 16.9243 13.3476C17.2941 13.5209 17.5214 13.9017 17.4984 14.3095C17.3348 17.2106 15.0545 19.5498 12.0068 19.5498C8.94739 19.5498 6.5 16.9571 6.5 14.0363C6.5 11.1187 8.76722 8.75104 11.6123 8.55225C12.015 8.52411 12.3952 8.74091 12.576 9.10187Z",
246
- fill: "currentColor"
247
- })
248
- ]
249
- })
250
- })
251
- });
252
- var IconEyeLine = /*#__PURE__*/ React.forwardRef(Icon$a);
253
- var IconEyeLine_default = IconEyeLine;
254
-
255
228
  // src/IconMagnifyingglassLine.tsx
256
- var Icon$9 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
229
+ var Icon$a = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
257
230
  viewBox: "0 0 24 24",
258
231
  fill: "none",
259
232
  xmlns: "http://www.w3.org/2000/svg",
@@ -271,11 +244,11 @@ var Icon$9 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
271
244
  })
272
245
  })
273
246
  });
274
- var IconMagnifyingglassLine = /*#__PURE__*/ React.forwardRef(Icon$9);
247
+ var IconMagnifyingglassLine = /*#__PURE__*/ React.forwardRef(Icon$a);
275
248
  var IconMagnifyingglassLine_default = IconMagnifyingglassLine;
276
249
 
277
250
  // src/IconSlider2HorizontalLine.tsx
278
- var Icon$8 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
251
+ var Icon$9 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
279
252
  viewBox: "0 0 24 24",
280
253
  fill: "none",
281
254
  xmlns: "http://www.w3.org/2000/svg",
@@ -303,11 +276,11 @@ var Icon$8 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
303
276
  })
304
277
  })
305
278
  });
306
- var IconSlider2HorizontalLine = /*#__PURE__*/ React.forwardRef(Icon$8);
279
+ var IconSlider2HorizontalLine = /*#__PURE__*/ React.forwardRef(Icon$9);
307
280
  var IconSlider2HorizontalLine_default = IconSlider2HorizontalLine;
308
281
 
309
282
  // src/IconPersonCircleLine.tsx
310
- var Icon$7 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
283
+ var Icon$8 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
311
284
  viewBox: "0 0 24 24",
312
285
  fill: "none",
313
286
  xmlns: "http://www.w3.org/2000/svg",
@@ -335,11 +308,11 @@ var Icon$7 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
335
308
  })
336
309
  })
337
310
  });
338
- var IconPersonCircleLine = /*#__PURE__*/ React.forwardRef(Icon$7);
311
+ var IconPersonCircleLine = /*#__PURE__*/ React.forwardRef(Icon$8);
339
312
  var IconPersonCircleLine_default = IconPersonCircleLine;
340
313
 
341
314
  // src/IconPlusLine.tsx
342
- var Icon$6 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
315
+ var Icon$7 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
343
316
  viewBox: "0 0 24 24",
344
317
  fill: "none",
345
318
  xmlns: "http://www.w3.org/2000/svg",
@@ -355,11 +328,11 @@ var Icon$6 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
355
328
  })
356
329
  })
357
330
  });
358
- var IconPlusLine = /*#__PURE__*/ React.forwardRef(Icon$6);
331
+ var IconPlusLine = /*#__PURE__*/ React.forwardRef(Icon$7);
359
332
  var IconPlusLine_default = IconPlusLine;
360
333
 
361
334
  // src/IconMinusLine.tsx
362
- var Icon$5 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
335
+ var Icon$6 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
363
336
  viewBox: "0 0 24 24",
364
337
  fill: "none",
365
338
  xmlns: "http://www.w3.org/2000/svg",
@@ -375,11 +348,11 @@ var Icon$5 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
375
348
  })
376
349
  })
377
350
  });
378
- var IconMinusLine = /*#__PURE__*/ React.forwardRef(Icon$5);
351
+ var IconMinusLine = /*#__PURE__*/ React.forwardRef(Icon$6);
379
352
  var IconMinusLine_default = IconMinusLine;
380
353
 
381
354
  // src/IconXmarkLine.tsx
382
- var Icon$4 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
355
+ var Icon$5 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
383
356
  viewBox: "0 0 24 24",
384
357
  fill: "none",
385
358
  xmlns: "http://www.w3.org/2000/svg",
@@ -395,11 +368,11 @@ var Icon$4 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
395
368
  })
396
369
  })
397
370
  });
398
- var IconXmarkLine = /*#__PURE__*/ React.forwardRef(Icon$4);
371
+ var IconXmarkLine = /*#__PURE__*/ React.forwardRef(Icon$5);
399
372
  var IconXmarkLine_default = IconXmarkLine;
400
373
 
401
374
  // src/IconCheckmarkLine.tsx
402
- var Icon$3 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
375
+ var Icon$4 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
403
376
  viewBox: "0 0 24 24",
404
377
  fill: "none",
405
378
  xmlns: "http://www.w3.org/2000/svg",
@@ -417,9 +390,45 @@ var Icon$3 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
417
390
  })
418
391
  })
419
392
  });
420
- var IconCheckmarkLine = /*#__PURE__*/ React.forwardRef(Icon$3);
393
+ var IconCheckmarkLine = /*#__PURE__*/ React.forwardRef(Icon$4);
421
394
  var IconCheckmarkLine_default = IconCheckmarkLine;
422
395
 
396
+ // src/IconCorner4OutwardLine.tsx
397
+ var Icon$3 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
398
+ viewBox: "0 0 24 24",
399
+ fill: "none",
400
+ xmlns: "http://www.w3.org/2000/svg",
401
+ "data-seed-icon": "true",
402
+ width: size,
403
+ height: size,
404
+ ref,
405
+ ...props,
406
+ children: /* @__PURE__ */ jsxRuntime.jsx("g", {
407
+ children: /* @__PURE__ */ jsxRuntime.jsxs("g", {
408
+ children: [
409
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
410
+ d: "M4.99961 4.00001L8.5 4C9.05228 4 9.5 3.55228 9.5 3C9.5 2.44772 9.05228 2 8.5 2L4.99959 2.00001C3.3426 2.00003 2 3.34333 2 4.99993V8.5C2 9.05228 2.44772 9.5 3 9.5C3.55228 9.5 4 9.05228 4 8.5V4.99993C4 4.44751 4.44757 4.00002 4.99961 4.00001Z",
411
+ fill: "currentColor"
412
+ }),
413
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
414
+ d: "M15.5 2C14.9477 2 14.5 2.44772 14.5 3C14.5 3.55228 14.9477 4 15.5 4H19C19.5525 4 20 4.44762 20 4.99974V8.5C20 9.05228 20.4477 9.5 21 9.5C21.5523 9.5 22 9.05228 22 8.5V4.99974C22 3.34271 20.6567 2 19 2H15.5Z",
415
+ fill: "currentColor"
416
+ }),
417
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
418
+ d: "M3.00001 14.5C3.5523 14.5 4.00001 14.9477 4.00001 15.5L4 19.0002C3.99999 19.5523 4.44759 20 5.00007 20H8.49995C9.05223 20 9.49995 20.4477 9.49995 21C9.49995 21.5523 9.05223 22 8.49995 22H5.00007C3.34331 22 1.99998 20.6572 2 19.0001L2.00001 15.5C2.00001 14.9477 2.44773 14.5 3.00001 14.5Z",
419
+ fill: "currentColor"
420
+ }),
421
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
422
+ d: "M22 15.5C22 14.9477 21.5523 14.5 21 14.5C20.4477 14.5 20 14.9477 20 15.5V19.0003C20 19.5524 19.5525 20 19 20H15.5C14.9477 20 14.5 20.4477 14.5 21C14.5 21.5523 14.9477 22 15.5 22H19C20.6567 22 22 20.6573 22 19.0003V15.5Z",
423
+ fill: "currentColor"
424
+ })
425
+ ]
426
+ })
427
+ })
428
+ });
429
+ var IconCorner4OutwardLine = /*#__PURE__*/ React.forwardRef(Icon$3);
430
+ var IconCorner4OutwardLine_default = IconCorner4OutwardLine;
431
+
423
432
  // src/IconChevronDownSmallLine.tsx
424
433
  var Icon$2 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
425
434
  viewBox: "0 0 24 24",
@@ -462,8 +471,8 @@ var Icon$1 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
462
471
  var IconCheckmarkFatFill = /*#__PURE__*/ React.forwardRef(Icon$1);
463
472
  var IconCheckmarkFatFill_default = IconCheckmarkFatFill;
464
473
 
465
- var __defProp$3 = Object.defineProperty;
466
- var __name$3 = (target, value)=>__defProp$3(target, "name", {
474
+ var __defProp$5 = Object.defineProperty;
475
+ var __name$5 = (target, value)=>__defProp$5(target, "name", {
467
476
  value,
468
477
  configurable: true
469
478
  });
@@ -474,7 +483,7 @@ function setRef(ref, value) {
474
483
  ref.current = value;
475
484
  }
476
485
  }
477
- __name$3(setRef, "setRef");
486
+ __name$5(setRef, "setRef");
478
487
  function composeRefs(...refs) {
479
488
  return (node)=>{
480
489
  let hasCleanup = false;
@@ -499,14 +508,14 @@ function composeRefs(...refs) {
499
508
  }
500
509
  };
501
510
  }
502
- __name$3(composeRefs, "composeRefs");
511
+ __name$5(composeRefs, "composeRefs");
503
512
  function useComposedRefs(...refs) {
504
513
  return React__namespace.useCallback(composeRefs(...refs), refs);
505
514
  }
506
- __name$3(useComposedRefs, "useComposedRefs");
515
+ __name$5(useComposedRefs, "useComposedRefs");
507
516
 
508
- var __defProp$2 = Object.defineProperty;
509
- var __name$2 = (target, value)=>__defProp$2(target, "name", {
517
+ var __defProp$4 = Object.defineProperty;
518
+ var __name$4 = (target, value)=>__defProp$4(target, "name", {
510
519
  value,
511
520
  configurable: true
512
521
  });
@@ -560,18 +569,18 @@ function createSlot(ownerName) {
560
569
  Slot2.displayName = `${ownerName}.Slot`;
561
570
  return Slot2;
562
571
  }
563
- __name$2(createSlot, "createSlot");
572
+ __name$4(createSlot, "createSlot");
564
573
  var Slot = /* @__PURE__ */ createSlot("Slot");
565
574
  var SLOTTABLE_IDENTIFIER = Symbol.for("radix.slottable");
566
575
  // @__NO_SIDE_EFFECTS__
567
576
  function createSlottable(ownerName) {
568
- const Slottable2 = /* @__PURE__ */ __name$2((props)=>"child" in props ? props.children(props.child) : props.children, "Slottable");
577
+ const Slottable2 = /* @__PURE__ */ __name$4((props)=>"child" in props ? props.children(props.child) : props.children, "Slottable");
569
578
  Slottable2.displayName = `${ownerName}.Slottable`;
570
579
  Slottable2.__radixId = SLOTTABLE_IDENTIFIER;
571
580
  return Slottable2;
572
581
  }
573
- __name$2(createSlottable, "createSlottable");
574
- var getSlottableElementFromSlottable = /* @__PURE__ */ __name$2((slottable, child)=>{
582
+ __name$4(createSlottable, "createSlottable");
583
+ var getSlottableElementFromSlottable = /* @__PURE__ */ __name$4((slottable, child)=>{
575
584
  if ("child" in slottable.props) {
576
585
  const child2 = slottable.props.child;
577
586
  if (!/*#__PURE__*/ React__namespace.isValidElement(child2)) return null;
@@ -614,7 +623,7 @@ function mergeProps(slotProps, childProps) {
614
623
  ...overrideProps
615
624
  };
616
625
  }
617
- __name$2(mergeProps, "mergeProps");
626
+ __name$4(mergeProps, "mergeProps");
618
627
  function getElementRef(element) {
619
628
  let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
620
629
  let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
@@ -628,24 +637,24 @@ function getElementRef(element) {
628
637
  }
629
638
  return element.props.ref || element.ref;
630
639
  }
631
- __name$2(getElementRef, "getElementRef");
640
+ __name$4(getElementRef, "getElementRef");
632
641
  function isSlottable(child) {
633
642
  return /*#__PURE__*/ React__namespace.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
634
643
  }
635
- __name$2(isSlottable, "isSlottable");
644
+ __name$4(isSlottable, "isSlottable");
636
645
  var REACT_LAZY_TYPE = Symbol.for("react.lazy");
637
646
  function isLazyComponent(element) {
638
647
  return element != null && typeof element === "object" && "$$typeof" in element && element.$$typeof === REACT_LAZY_TYPE && "_payload" in element && isPromiseLike(element._payload);
639
648
  }
640
- __name$2(isLazyComponent, "isLazyComponent");
649
+ __name$4(isLazyComponent, "isLazyComponent");
641
650
  function isPromiseLike(value) {
642
651
  return typeof value === "object" && value !== null && "then" in value;
643
652
  }
644
- __name$2(isPromiseLike, "isPromiseLike");
645
- var createSlotError = /* @__PURE__ */ __name$2((ownerName)=>{
653
+ __name$4(isPromiseLike, "isPromiseLike");
654
+ var createSlotError = /* @__PURE__ */ __name$4((ownerName)=>{
646
655
  return `${ownerName} failed to slot onto its children. Expected a single React element child or \`Slottable\`.`;
647
656
  }, "createSlotError");
648
- var createSlottableError = /* @__PURE__ */ __name$2((ownerName)=>{
657
+ var createSlottableError = /* @__PURE__ */ __name$4((ownerName)=>{
649
658
  return `${ownerName} failed to slot onto its \`Slottable\`. Expected \`Slottable\` to receive a single React element child.`;
650
659
  }, "createSlottableError");
651
660
  var use = React__namespace[" use ".trim().toString()];
@@ -892,6 +901,40 @@ function useDataTableColumnCalc(props) {
892
901
  };
893
902
  }
894
903
 
904
+ function sameCell$1(a, id, key) {
905
+ return a !== null && a.id === id && a.key === key;
906
+ }
907
+ /**
908
+ * Tracks which single cell (if any) is showing its "show full content" overlay
909
+ * (DATATABLE-ATTIO-SPEC.md's cell-expand feature, #12) for the READ-ONLY case -- a
910
+ * non-`editable` column with truncated content, which has nothing to commit and so
911
+ * no reason to go through `useDataTableEdit`'s `editingCell`. An `editable` column's
912
+ * own truncated-cell overlay instead reuses `useDataTableEdit`'s `startEdit`/
913
+ * `editingCell` directly (the styled layer's `body-cells.tsx` picks which of the two
914
+ * a given activation opens) -- "for editable cells it's the editor" per the spec,
915
+ * not a second parallel state machine duplicating what editing already tracks.
916
+ *
917
+ * Only one cell can be peeked at a time, the same single-cell shape
918
+ * `useDataTableEdit`'s `editingCell` already uses -- this table has no multi-cell
919
+ * expand any more than it has multi-cell edit.
920
+ */ function useDataTableCellPeek() {
921
+ const [peekedCell, setPeekedCell] = React.useState(null);
922
+ const isPeeking = React.useCallback((id, key)=>sameCell$1(peekedCell, id, key), [
923
+ peekedCell
924
+ ]);
925
+ const openPeek = React.useCallback((id, key)=>setPeekedCell({
926
+ id,
927
+ key
928
+ }), []);
929
+ const closePeek = React.useCallback(()=>setPeekedCell(null), []);
930
+ return {
931
+ peekedCell,
932
+ isPeeking,
933
+ openPeek,
934
+ closePeek
935
+ };
936
+ }
937
+
895
938
  // Select's `dom.ts` reads state off REAL rendered elements (aria-disabled, data-value)
896
939
  // because its options register asynchronously through a floating-ui list and the DOM
897
940
  // is the one thing always in sync with `elementsRef`. DataTable's selection has no
@@ -1907,6 +1950,11 @@ function useDataTable(props) {
1907
1950
  const columnCalc = useDataTableColumnCalc(props);
1908
1951
  const scroll = useDataTableScroll();
1909
1952
  const edit = useDataTableEdit(props);
1953
+ // Feature C (#12) -- which single cell (if any) is showing its read-only "show
1954
+ // full content" overlay. Independent of `edit` above -- see
1955
+ // `useDataTableCellPeek.ts`'s own doc comment for why an `editable` column's
1956
+ // truncated-cell overlay reuses `edit` instead of this.
1957
+ const peek = useDataTableCellPeek();
1910
1958
  // Wired to `scroll`'s own scroll-container ref (not a fresh one of its own) so a
1911
1959
  // BOUNDED table's auto-load triggers off its own internal scroll, not the
1912
1960
  // browsing viewport -- see useDataTableInfiniteScroll.ts's own doc comment.
@@ -1924,6 +1972,7 @@ function useDataTable(props) {
1924
1972
  ...columnCalc,
1925
1973
  ...scroll,
1926
1974
  ...edit,
1975
+ ...peek,
1927
1976
  ...infiniteScroll,
1928
1977
  rootProps: domUtils.elementProps({
1929
1978
  role: "table"
@@ -2451,7 +2500,7 @@ function createRecipeContext(recipe) {
2451
2500
  }
2452
2501
 
2453
2502
  const { withContext: withGroupContext } = createRecipeContext(checkboxGroup.checkboxGroup);
2454
- const { ClassNamesProvider: ClassNamesProvider$3, withContext: withContext$5 } = createSlotRecipeContext(checkbox.checkbox);
2503
+ const { ClassNamesProvider: ClassNamesProvider$3, withContext: withContext$6 } = createSlotRecipeContext(checkbox.checkbox);
2455
2504
  const { withProvider: withCheckmarkProvider, useClassNames: useCheckmarkClassNames, PropsProvider: CheckmarkPropsProvider } = createSlotRecipeContext(checkmark.checkmark);
2456
2505
  const withStateProps$4 = createWithStateProps([
2457
2506
  reactCheckbox.useCheckboxContext
@@ -2505,14 +2554,14 @@ const CheckboxIndicator = /*#__PURE__*/ React.forwardRef(({ unchecked: unchecked
2505
2554
  return null;
2506
2555
  });
2507
2556
  CheckboxIndicator.displayName = "CheckboxIndicator";
2508
- const CheckboxLabel = withContext$5(withStateProps$4(Primitive.span), "label");
2557
+ const CheckboxLabel = withContext$6(withStateProps$4(Primitive.span), "label");
2509
2558
  const CheckboxHiddenInput = reactCheckbox.Checkbox.HiddenInput;
2510
2559
 
2511
- const { withProvider: withProvider$1, withContext: withContext$4 } = createSlotRecipeContext(avatar.avatar);
2512
- const AvatarRoot = withProvider$1(reactImage.Image.Root, "root");
2513
- const AvatarImage = withContext$4(reactImage.Image.Content, "image");
2514
- const AvatarFallback = withContext$4(reactImage.Image.Fallback, "fallback");
2515
- withContext$4(Primitive.div, "badge");
2560
+ const { withProvider: withProvider$2, withContext: withContext$5 } = createSlotRecipeContext(avatar.avatar);
2561
+ const AvatarRoot = withProvider$2(reactImage.Image.Root, "root");
2562
+ const AvatarImage = withContext$5(reactImage.Image.Content, "image");
2563
+ const AvatarFallback = withContext$5(reactImage.Image.Fallback, "fallback");
2564
+ withContext$5(Primitive.div, "badge");
2516
2565
 
2517
2566
  const Badge = /*#__PURE__*/ React.forwardRef(({ className, children, ...props }, ref)=>{
2518
2567
  const [variantProps, restProps] = badge.badge.splitVariantProps(props);
@@ -2564,6 +2613,41 @@ function toneFor(value) {
2564
2613
  }
2565
2614
  return BADGE_TONES[hash % BADGE_TONES.length];
2566
2615
  }
2616
+ // Feature A/B (#9/#8) -- Attio's own flat 12-swatch chip palette, ported from
2617
+ // crisp's `tokens/palette.ts` (`tagSwatch1..12`, measured live off Attio) rather
2618
+ // than seed's own 6-tone `Badge` (`BADGE_TONES` above): a swatch, unlike a Badge
2619
+ // tone, has no ring/border and its ink is a FLAT black on every swatch (crisp's own
2620
+ // `tagSwatchInk`, `#000000` -- every swatch background is already a pale tint, so
2621
+ // one dark foreground reads on all 12), not a per-tone contrast color. No seed token
2622
+ // lands on any of these 12 hex values, so -- same footnote as the qvism recipe's own
2623
+ // `ATTIO_BRAND_BLUE`/`RATING_FILLED` literals -- they stay literal here too.
2624
+ const CHIP_SWATCHES = [
2625
+ "#fff3cc",
2626
+ "#f4fbcb",
2627
+ "#fdf7c4",
2628
+ "#feecf3",
2629
+ "#e0fced",
2630
+ "#ddf7ff",
2631
+ "#f6f7f7",
2632
+ "#ffebeb",
2633
+ "#feeee1",
2634
+ "#e5eeff",
2635
+ "#f5f0ff",
2636
+ "#fdedff"
2637
+ ];
2638
+ const CHIP_SWATCH_INK = "#000000";
2639
+ /** Same stable multiply-and-add hash `toneFor` above already uses over seed's 6
2640
+ * Badge tones, just over this file's own 12-swatch palette instead -- same value,
2641
+ * same swatch, every render (crisp's own `toneFor`, `datatable.tsx`). */ function swatchIndexFor(value) {
2642
+ let hash = 0;
2643
+ for(let index = 0; index < value.length; index++){
2644
+ hash = hash * 31 + value.charCodeAt(index) >>> 0;
2645
+ }
2646
+ return hash % CHIP_SWATCHES.length;
2647
+ }
2648
+ function swatchColorFor(value) {
2649
+ return CHIP_SWATCHES[swatchIndexFor(value)];
2650
+ }
2567
2651
  function initialsFor(name) {
2568
2652
  const parts = name.trim().split(/\s+/);
2569
2653
  return ((parts[0]?.[0] ?? "") + (parts[1]?.[0] ?? "")).toUpperCase() || "?";
@@ -2647,7 +2731,7 @@ function EntityCell({ value, className, nameClassName, actionsClassName, actionC
2647
2731
  event.stopPropagation();
2648
2732
  column.onOpenPreview?.(row);
2649
2733
  },
2650
- children: /*#__PURE__*/ jsxRuntime.jsx(IconEyeLine_default, {
2734
+ children: /*#__PURE__*/ jsxRuntime.jsx(IconCorner4OutwardLine_default, {
2651
2735
  size: 16
2652
2736
  })
2653
2737
  })
@@ -2678,11 +2762,89 @@ function StatusCell({ value }) {
2678
2762
  children: status
2679
2763
  });
2680
2764
  }
2681
- function LinkCell({ column, value, className }) {
2765
+ /** One swatch chip -- `style` carries the per-value background/ink (computed by
2766
+ * `swatchColorFor`/`CHIP_SWATCH_INK` above) since neither is a recipe token, the
2767
+ * same "literal color as a computed prop" call `RatingCell`'s star fill already
2768
+ * makes. Shared by `ChipCell` (Feature A, single value) and `ChipListCell`/
2769
+ * editors.tsx's `ChipListEditor` (Feature B, multi value) so all three chip
2770
+ * renderings stay byte-identical. */ function SwatchChip({ value, className, children }) {
2771
+ return /*#__PURE__*/ jsxRuntime.jsxs("span", {
2772
+ className: className,
2773
+ style: {
2774
+ backgroundColor: swatchColorFor(value),
2775
+ color: CHIP_SWATCH_INK
2776
+ },
2777
+ children: [
2778
+ value,
2779
+ children
2780
+ ]
2781
+ });
2782
+ }
2783
+ /** `type: "chip"` (Feature A, #9) -- a single value rendered as one colored chip,
2784
+ * the color derived deterministically from the value string (`swatchColorFor`):
2785
+ * same value, same color, every render, every column. For a column like provider/
2786
+ * tenant/vendor/status/select where a plain `Badge` (seed's 6-tone `status`/`tags`
2787
+ * types above) doesn't need to be color-STABLE per value -- this one does. */ function ChipCell({ value, className }) {
2788
+ const text = value == null ? "" : String(value);
2789
+ if (!text) return null;
2790
+ return /*#__PURE__*/ jsxRuntime.jsx(SwatchChip, {
2791
+ value: text,
2792
+ className: className
2793
+ });
2794
+ }
2795
+ /** `type: "chip-list"`, READ-ONLY (Feature B, #8) -- multiple swatch chips, no add/
2796
+ * remove affordances. The editable case is editors.tsx's `ChipListEditor`, an
2797
+ * always-live widget (interacting with it IS the commit, the same `switch`/
2798
+ * `rating`/`checkbox` shape `body-cells.tsx`'s `isAlwaysLiveEditable` already
2799
+ * covers) -- this component only ever renders the REST display. */ function ChipListCell({ value, className, chipClassName }) {
2800
+ const tags = Array.isArray(value) ? value.filter((tag)=>!!tag) : [];
2801
+ if (!tags.length) return null;
2802
+ return /*#__PURE__*/ jsxRuntime.jsx("span", {
2803
+ className: className,
2804
+ children: tags.map((tag)=>/*#__PURE__*/ jsxRuntime.jsx(SwatchChip, {
2805
+ value: tag,
2806
+ className: chipClassName
2807
+ }, tag))
2808
+ });
2809
+ }
2810
+ /**
2811
+ * Feature D (#13) -- generic text+affix composition: `suffix` (if given) renders as
2812
+ * a `flex-shrink: 0` icon beside the text, and the TEXT itself (not the outer `<td>`)
2813
+ * gets its own `flex: 1 1 0%; min-width: 0` + ellipsis, so a long value truncates
2814
+ * while the affix stays visible instead of being squeezed out of the cell entirely
2815
+ * (the bug: composing text+icon inside a plain truncating `<td>` lets the icon
2816
+ * scroll/clip away with the text, since a flex child never inherits its PARENT's
2817
+ * own `text-overflow`). `data-seed-truncate` on the text span is what `truncate.ts`'s
2818
+ * `isCellTruncated` (Feature C's click-to-expand gate) measures instead of the
2819
+ * `<td>` itself for exactly the same reason -- see that file's own comment.
2820
+ * Any FUTURE cell composing a fixed leading/trailing glyph beside truncating text
2821
+ * should render through this component rather than re-deriving the same three-slot
2822
+ * flex shape.
2823
+ */ function CellWithSuffix({ text, suffix, rowClassName, textClassName, suffixClassName }) {
2824
+ if (!suffix) return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
2825
+ children: text
2826
+ });
2827
+ return /*#__PURE__*/ jsxRuntime.jsxs("span", {
2828
+ className: rowClassName,
2829
+ children: [
2830
+ /*#__PURE__*/ jsxRuntime.jsx("span", {
2831
+ className: textClassName,
2832
+ "data-seed-truncate": "",
2833
+ children: text
2834
+ }),
2835
+ /*#__PURE__*/ jsxRuntime.jsx("span", {
2836
+ className: suffixClassName,
2837
+ children: suffix
2838
+ })
2839
+ ]
2840
+ });
2841
+ }
2842
+ function LinkCell({ column, value, row, className, rowClassName, textClassName, suffixClassName }) {
2682
2843
  const text = value == null ? "" : String(value);
2683
2844
  if (!text) return null;
2684
2845
  const isUrl = column.type === "url";
2685
2846
  const href = isUrl ? /^https?:\/\//.test(text) ? text : `https://${text}` : `mailto:${text}`;
2847
+ const suffix = column.suffixIcon?.(row);
2686
2848
  return /*#__PURE__*/ jsxRuntime.jsx("a", {
2687
2849
  className: className,
2688
2850
  href: href,
@@ -2691,7 +2853,13 @@ function LinkCell({ column, value, className }) {
2691
2853
  // A link cell points somewhere other than "select this row" -- its own
2692
2854
  // navigation must not also fire a row click handler.
2693
2855
  onClick: (event)=>event.stopPropagation(),
2694
- children: text
2856
+ children: /*#__PURE__*/ jsxRuntime.jsx(CellWithSuffix, {
2857
+ text: text,
2858
+ suffix: suffix,
2859
+ rowClassName: rowClassName,
2860
+ textClassName: textClassName,
2861
+ suffixClassName: suffixClassName
2862
+ })
2695
2863
  });
2696
2864
  }
2697
2865
  // Attio's own measured 5-point star path (crisp's `RATING_STAR_D`, traced from the
@@ -2717,8 +2885,7 @@ function RatingCell({ value, maxRating = 5 }) {
2717
2885
  },
2718
2886
  children: Array.from({
2719
2887
  length: maxRating
2720
- }, (_, index)=>// biome-ignore lint/suspicious/noArrayIndexKey: a fixed-length star row has no other identity
2721
- /*#__PURE__*/ jsxRuntime.jsx("svg", {
2888
+ }, (_, index)=>/*#__PURE__*/ jsxRuntime.jsx("svg", {
2722
2889
  width: 16,
2723
2890
  height: 16,
2724
2891
  viewBox: "0 0 16 16",
@@ -3082,7 +3249,7 @@ const SelectHiddenSelect = /*#__PURE__*/ React__namespace.forwardRef((props, ref
3082
3249
  });
3083
3250
  SelectHiddenSelect.displayName = "SelectHiddenSelect";
3084
3251
 
3085
- const { withContext: withContext$3, ClassNamesProvider: ClassNamesProvider$2 } = createSlotRecipeContext(_switch.switchStyle);
3252
+ const { withContext: withContext$4, ClassNamesProvider: ClassNamesProvider$2 } = createSlotRecipeContext(_switch.switchStyle);
3086
3253
  const { withContext: withControlContext, PropsProvider: ControlPropsProvider, withProvider: withControlProvider } = createSlotRecipeContext(switchmark.switchmark);
3087
3254
  const withStateProps$2 = createWithStateProps([
3088
3255
  reactSwitch.useSwitchContext
@@ -3108,13 +3275,13 @@ const SwitchRoot = /*#__PURE__*/ React__namespace.default.forwardRef(({ classNam
3108
3275
  SwitchRoot.displayName = "SwitchRoot";
3109
3276
  const SwitchControl = withScaleFeedback(withControlProvider(reactSwitch.Switch.Control, "root"));
3110
3277
  const SwitchThumb = withControlContext(reactSwitch.Switch.Thumb, "thumb");
3111
- withContext$3(withStateProps$2(Primitive.span), "label");
3278
+ withContext$4(withStateProps$2(Primitive.span), "label");
3112
3279
  const SwitchHiddenInput = reactSwitch.Switch.HiddenInput;
3113
3280
 
3114
3281
  // src/use-layout-effect.tsx
3115
3282
  var useLayoutEffect2 = globalThis?.document ? React__namespace.useLayoutEffect : ()=>{};
3116
3283
 
3117
- const { withProvider, withContext: withContext$2, useClassNames: useClassNames$2 } = createSlotRecipeContext(textInput.textInput);
3284
+ const { withProvider: withProvider$1, withContext: withContext$3, useClassNames: useClassNames$2 } = createSlotRecipeContext(textInput.textInput);
3118
3285
  const withFieldStateProps = createWithStateProps([
3119
3286
  {
3120
3287
  useContext: reactField.useFieldContext,
@@ -3128,11 +3295,11 @@ const withStateProps$1 = createWithStateProps([
3128
3295
  strict: false
3129
3296
  }
3130
3297
  ]);
3131
- const TextFieldRoot = withProvider(withFieldStateProps(reactTextField.TextField.Root), "root");
3132
- withContext$2(withStateProps$1(InternalIcon), "prefixIcon");
3133
- withContext$2(withStateProps$1(Primitive.span), "prefixText");
3134
- withContext$2(withStateProps$1(InternalIcon), "suffixIcon");
3135
- withContext$2(withStateProps$1(Primitive.span), "suffixText");
3298
+ const TextFieldRoot = withProvider$1(withFieldStateProps(reactTextField.TextField.Root), "root");
3299
+ withContext$3(withStateProps$1(InternalIcon), "prefixIcon");
3300
+ withContext$3(withStateProps$1(Primitive.span), "prefixText");
3301
+ withContext$3(withStateProps$1(InternalIcon), "suffixIcon");
3302
+ withContext$3(withStateProps$1(Primitive.span), "suffixText");
3136
3303
  const TextFieldInput = /*#__PURE__*/ React.forwardRef(({ className, ...otherProps }, ref)=>{
3137
3304
  const classNames = useClassNames$2();
3138
3305
  const textFieldContext = reactTextField.useTextFieldContext();
@@ -3209,7 +3376,7 @@ const TextFieldTextarea = /*#__PURE__*/ React.forwardRef(({ className, autoresiz
3209
3376
  });
3210
3377
  TextFieldTextarea.displayName = "TextFieldTextarea";
3211
3378
 
3212
- __insertCSS(".seed-data-table__root{box-sizing:border-box;display:flex;flex-direction:column;width:100%;border-width:1px;border-style:solid;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.seed-data-table__table{width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed}.seed-data-table__headerCell{box-sizing:border-box;position:sticky;top:0;z-index:2;vertical-align:middle;text-align:start;padding:0;height:var(--seed-dimension-x10);background-color:var(--seed-color-bg-layer-default);border-top:1px solid var(--seed-color-stroke-neutral-muted);border-bottom:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-inline-start:none;color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-medium);letter-spacing:-.14px}.seed-data-table__headerCell:last-child{border-inline-end:none}.seed-data-table__headerCell[data-sortable]{cursor:pointer;user-select:none}.seed-data-table__headerCell [data-chevron]{opacity:0;transition:opacity .12s}.seed-data-table__headerCell[data-sortable]:hover [data-chevron]{opacity:.6}.seed-data-table__headerCell[data-sorted] [data-chevron]{opacity:1;color:var(--seed-color-fg-neutral)}.seed-data-table__headerCell:hover [data-column-menu]{opacity:1}.seed-data-table__headerCell [data-column-menu]:focus-visible,.seed-data-table__headerCell [data-column-menu][data-open]{opacity:1}.seed-data-table__headerCell[data-dragging]{opacity:.5}.seed-data-table__headerCell[data-sticky]{position:sticky;z-index:3;background-color:var(--seed-color-bg-layer-default)}.seed-data-table__headerCell{outline:var(--seed-dimension-x0_5) solid transparent;outline-offset:calc(var(--seed-dimension-x0_5) * -1)}.seed-data-table__headerCell:is(:focus-visible,[data-focus-visible]){outline:var(--seed-dimension-x0_5) solid var(--seed-color-stroke-focus-ring);outline-offset:calc(var(--seed-dimension-x0_5) * -1)}.seed-data-table__headerCell .seed-data-table-resize-handle{position:absolute;top:0;inset-inline-end:calc(7px / -2);width:7px;height:100%;z-index:4;display:flex;align-items:center;justify-content:center;cursor:col-resize;touch-action:none}.seed-data-table__headerCell .seed-data-table-resize-handle-inner{width:3px;height:100%;border-radius:3px;background-color:#0298d9;opacity:0;transition:opacity .1s}.seed-data-table__headerCell .seed-data-table-resize-handle:hover .seed-data-table-resize-handle-inner,.seed-data-table__headerCell .seed-data-table-resize-handle[data-resizing] .seed-data-table-resize-handle-inner{opacity:1}.seed-data-table__headerCellInner{box-sizing:border-box;display:flex;align-items:center;width:100%;height:100%;min-width:0;gap:var(--seed-dimension-x1_5);padding-inline:var(--seed-dimension-x3)}.seed-data-table__headerCellLabel{min-width:0;flex:1 1 0%;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__headerCellIcon{flex-shrink:0;margin-inline-start:auto;display:inline-flex;align-items:center;justify-content:center;width:var(--seed-dimension-x4);height:var(--seed-dimension-x4);color:var(--seed-color-fg-neutral-subtle);border:none;background:0 0;padding:0;font:inherit;cursor:pointer}.seed-data-table__headerCellIcon[data-active]{color:var(--seed-color-fg-neutral)}.seed-data-table__headerCellIcon[data-column-menu]{opacity:0;transition:opacity .12s}.seed-data-table__row{position:relative;background-color:var(--seed-color-bg-layer-default);--seed-data-table-row-bg:var(--seed-color-bg-layer-default)}.seed-data-table__row[data-clickable]{cursor:pointer}.seed-data-table__row:is([aria-selected=true],[data-selected]){background-color:color-mix(in srgb,#0298d9 10%,var(--seed-color-bg-layer-default));--seed-data-table-row-bg:color-mix(in srgb, #0298d9 10%, var(--seed-color-bg-layer-default))}.seed-data-table__row:is([aria-selected=true],[data-selected]):is(:hover,[data-hover]){background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 3%,color-mix(in srgb,#0298d9 10%,var(--seed-color-bg-layer-default)));--seed-data-table-row-bg:color-mix(in srgb, var(--seed-color-fg-neutral) 3%, color-mix(in srgb, #0298d9 10%, var(--seed-color-bg-layer-default)))}.seed-data-table__cell{box-sizing:border-box;position:relative;vertical-align:middle;text-align:start;height:var(--seed-dimension-x9);border-bottom:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-top:none;border-inline-start:none;padding-block:0;padding-inline:var(--seed-dimension-x3);color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-regular);letter-spacing:-.14px;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__cell:last-child{border-inline-end:none}tbody tr:last-child .seed-data-table__cell{border-bottom:none}.seed-data-table__cell[data-numeric]{text-align:end;font-variant-numeric:tabular-nums}.seed-data-table__cell[data-sticky]{position:sticky;z-index:1;background-color:var(--seed-data-table-row-bg)}.seed-data-table__cell[data-editable]{cursor:default}.seed-data-table__cell .seed-data-table-edit-picker{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);min-width:0}.seed-data-table__cell .seed-data-table-edit-pending{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);animation:rotate .6s linear infinite}.seed-data-table__cell .seed-data-table-edit-error{position:absolute;top:100%;inset-inline-start:var(--seed-dimension-x3);inset-inline-end:var(--seed-dimension-x3);z-index:2;padding-top:2px;font-size:12px;line-height:1.3;color:var(--seed-color-fg-critical);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__entity{display:flex;align-items:center;min-width:0;gap:var(--seed-dimension-x1_5)}.seed-data-table__entityName{min-width:0;font-weight:var(--seed-font-weight-medium);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__tagList{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);overflow:hidden;min-width:0}.seed-data-table__entityActions{display:flex;align-items:center;gap:6px;margin-inline-start:auto;flex-shrink:0;opacity:0}.seed-data-table__entityActions:focus-within,tr:hover .seed-data-table__entityActions{opacity:1}.seed-data-table__entityAction{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin:0;padding:0;border:none;border-radius:6px;background:0 0;font-family:inherit;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent);cursor:pointer;flex-shrink:0}.seed-data-table__entityAction:hover{background:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__link{color:var(--seed-color-fg-neutral);text-decoration:none;min-width:0;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__link:is(:hover,[data-hover]){text-decoration:underline}.seed-data-table__toolbar{box-sizing:border-box;display:flex;align-items:center;width:100%;flex-shrink:0;min-height:49px;gap:var(--seed-dimension-x2);padding-inline:var(--seed-dimension-x3);border-top:1px solid var(--seed-color-stroke-neutral-muted)}.seed-data-table__search{position:relative;display:inline-flex;align-items:center;flex:1 1 0%;max-width:280px}.seed-data-table__search svg{position:absolute;inset-inline-start:var(--seed-dimension-x2);width:var(--seed-dimension-x4);height:var(--seed-dimension-x4);color:var(--seed-color-fg-neutral-muted);pointer-events:none}.seed-data-table__search input{width:100%;height:var(--seed-dimension-x8);padding-inline-start:var(--seed-dimension-x7);padding-inline-end:var(--seed-dimension-x2);border:none;border-radius:var(--seed-radius-r2);background-color:var(--seed-color-bg-layer-default);box-shadow:inset 0 0 0 1px var(--seed-color-stroke-neutral-weak);font-family:inherit;font-size:var(--seed-font-size-t4);color:var(--seed-color-fg-neutral);outline:0;transition:box-shadow .1s var(--seed-timing-function-easing)}.seed-data-table__search input::placeholder{color:var(--seed-color-fg-neutral-subtle)}.seed-data-table__search input:focus{box-shadow:inset 0 0 0 2px var(--seed-color-stroke-neutral-contrast)}.seed-data-table__count{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);font-size:var(--seed-font-size-t3);font-variant-numeric:tabular-nums}.seed-data-table__toolbarEnd{flex-shrink:0;margin-inline-start:auto;display:inline-flex;align-items:center;gap:var(--seed-dimension-x2)}.seed-data-table__scrollArea{position:relative;display:flex;flex-direction:column;width:100%;min-width:0;flex:1 1 auto;min-height:0;overflow-x:auto;overflow-y:auto}.seed-data-table__scrollArea[data-scrolled-start] [data-sticky-edge]{clip-path:inset(0 -38px 0 0);box-shadow:6px 0 16px 4px rgba(0,0,0,.12)}.seed-data-table__scrollArea[data-scrolled-end] [data-sticky-edge-end]{clip-path:inset(0 0 0 -38px);box-shadow:-6px 0 16px 4px rgba(0,0,0,.12)}.seed-data-table__footerCell{box-sizing:border-box;position:sticky;bottom:0;z-index:2;vertical-align:middle;padding:0;height:var(--seed-dimension-x9);background-color:var(--seed-color-bg-layer-default);border-top:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-inline-start:none}.seed-data-table__footerCell:last-child{border-inline-end:none}.seed-data-table__footerCell .seed-data-table-column-calc-trigger{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);width:100%;height:100%;min-width:0;margin:0;padding:0 var(--seed-dimension-x3);border:none;background:0 0;color:var(--seed-color-fg-neutral-subtle);font-family:inherit;font-size:var(--seed-font-size-t4);font-weight:var(--seed-font-weight-medium);white-space:nowrap;overflow:clip;cursor:pointer;transition:background-color .12s,color .12s}.seed-data-table__footerCell .seed-data-table-column-calc-trigger:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__footerCell .seed-data-table-column-calc-trigger[data-align=right]{justify-content:flex-end}.seed-data-table__footerCell .seed-data-table-column-calc-trigger svg{flex-shrink:0;opacity:.7}.seed-data-table__footerCell .seed-data-table-column-calc-placeholder{overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__footerCell .seed-data-table-column-calc-value{min-width:0;flex-shrink:1;color:var(--seed-color-fg-neutral);font-variant-numeric:tabular-nums;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__footerCell .seed-data-table-column-calc-op{min-width:0;flex-shrink:1;color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__emptyState{box-sizing:border-box;text-align:center;padding-block:var(--seed-dimension-x8);padding-inline:var(--seed-dimension-x3);color:var(--seed-color-fg-neutral-subtle);font-size:var(--seed-font-size-t4)}.seed-data-table__loadingState{box-sizing:border-box;display:flex;align-items:center;justify-content:center;gap:10px;padding-block:var(--seed-dimension-x8);padding-inline:var(--seed-dimension-x3);color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-medium)}.seed-data-table__loadingState[data-inline]{padding-block:0;padding-inline:0;height:var(--seed-dimension-x9)}.seed-data-table__loadingState .seed-data-table-loading-spinner{flex-shrink:0;color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);animation:rotate .6s linear infinite}.seed-data-table__addRowCell{box-sizing:border-box;padding:0;border-top:none}.seed-data-table__addRow{box-sizing:border-box;display:flex;align-items:center;width:100%;margin:0;border:none;border-top:1px solid var(--seed-color-stroke-neutral-muted);background-color:var(--seed-color-bg-layer-default);height:var(--seed-dimension-x9);gap:var(--seed-dimension-x2);padding-inline:var(--seed-dimension-x2);color:var(--seed-color-fg-neutral-subtle);font-family:inherit;font-size:var(--seed-font-size-t4);cursor:pointer;transition:background-color .12s,color .12s}.seed-data-table__addRow:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,var(--seed-color-bg-layer-default));color:var(--seed-color-fg-neutral)}.seed-data-table__addRow svg{flex-shrink:0}.seed-data-table__selectionCell{text-align:center;padding:0}.seed-data-table__root--surface_card{border-color:var(--seed-color-stroke-neutral-muted);background-color:var(--seed-color-bg-layer-default);border-radius:var(--seed-radius-r3)}.seed-data-table__root--surface_bleed{border-color:var(--seed-color-bg-transparent);background-color:var(--seed-color-bg-transparent)}.seed-data-table__row--interactive_true{transition:background-color .12s}.seed-data-table__row--interactive_true:is(:hover,[data-hover]){background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 3%,transparent);--seed-data-table-row-bg:color-mix(in srgb, var(--seed-color-fg-neutral) 3%, var(--seed-color-bg-layer-default))}");
3379
+ __insertCSS(".seed-data-table__root{box-sizing:border-box;display:flex;flex-direction:column;width:100%;border-width:1px;border-style:solid;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.seed-data-table__table{width:100%;border-collapse:separate;border-spacing:0;table-layout:fixed}.seed-data-table__headerCell{box-sizing:border-box;position:sticky;top:0;z-index:2;vertical-align:middle;text-align:start;padding:0;height:var(--seed-dimension-x10);background-color:var(--seed-color-bg-layer-default);border-top:1px solid var(--seed-color-stroke-neutral-muted);border-bottom:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-inline-start:none;color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-medium);letter-spacing:-.14px}.seed-data-table__headerCell:last-child{border-inline-end:none}.seed-data-table__headerCell[data-sortable]{cursor:pointer;user-select:none}.seed-data-table__headerCell [data-chevron]{opacity:0;transition:opacity .12s}.seed-data-table__headerCell[data-sortable]:hover [data-chevron]{opacity:.6}.seed-data-table__headerCell[data-sorted] [data-chevron]{opacity:1;color:var(--seed-color-fg-neutral)}.seed-data-table__headerCell:hover [data-column-menu]{opacity:1}.seed-data-table__headerCell [data-column-menu]:focus-visible,.seed-data-table__headerCell [data-column-menu][data-open]{opacity:1}.seed-data-table__headerCell[data-dragging]{opacity:.5}.seed-data-table__headerCell[data-sticky]{position:sticky;z-index:3;background-color:var(--seed-color-bg-layer-default)}.seed-data-table__headerCell{outline:var(--seed-dimension-x0_5) solid transparent;outline-offset:calc(var(--seed-dimension-x0_5) * -1)}.seed-data-table__headerCell:is(:focus-visible,[data-focus-visible]){outline:var(--seed-dimension-x0_5) solid var(--seed-color-stroke-focus-ring);outline-offset:calc(var(--seed-dimension-x0_5) * -1)}.seed-data-table__headerCell .seed-data-table-resize-handle{position:absolute;top:0;inset-inline-end:calc(7px / -2);width:7px;height:100%;z-index:4;display:flex;align-items:center;justify-content:center;cursor:col-resize;touch-action:none}.seed-data-table__headerCell .seed-data-table-resize-handle-inner{width:3px;height:100%;border-radius:3px;background-color:#0298d9;opacity:0;transition:opacity .1s}.seed-data-table__headerCell .seed-data-table-resize-handle:hover .seed-data-table-resize-handle-inner,.seed-data-table__headerCell .seed-data-table-resize-handle[data-resizing] .seed-data-table-resize-handle-inner{opacity:1}.seed-data-table__headerCellInner{box-sizing:border-box;display:flex;align-items:center;width:100%;height:100%;min-width:0;gap:var(--seed-dimension-x1_5);padding-inline:var(--seed-dimension-x3)}.seed-data-table__headerCellLabel{min-width:0;flex:1 1 0%;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__headerCellIcon{flex-shrink:0;margin-inline-start:auto;display:inline-flex;align-items:center;justify-content:center;width:var(--seed-dimension-x4);height:var(--seed-dimension-x4);color:var(--seed-color-fg-neutral-subtle);border:none;background:0 0;padding:0;font:inherit;cursor:pointer}.seed-data-table__headerCellIcon[data-active]{color:var(--seed-color-fg-neutral)}.seed-data-table__headerCellIcon[data-column-menu]{opacity:0;transition:opacity .12s}.seed-data-table__row{position:relative;background-color:var(--seed-color-bg-layer-default);--seed-data-table-row-bg:var(--seed-color-bg-layer-default)}.seed-data-table__row[data-clickable]{cursor:pointer}.seed-data-table__row:is([aria-selected=true],[data-selected]){background-color:color-mix(in srgb,#0298d9 10%,var(--seed-color-bg-layer-default));--seed-data-table-row-bg:color-mix(in srgb, #0298d9 10%, var(--seed-color-bg-layer-default))}.seed-data-table__row:is([aria-selected=true],[data-selected]):is(:hover,[data-hover]){background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 3%,color-mix(in srgb,#0298d9 10%,var(--seed-color-bg-layer-default)));--seed-data-table-row-bg:color-mix(in srgb, var(--seed-color-fg-neutral) 3%, color-mix(in srgb, #0298d9 10%, var(--seed-color-bg-layer-default)))}.seed-data-table__cell{box-sizing:border-box;position:relative;vertical-align:middle;text-align:start;height:var(--seed-dimension-x9);border-bottom:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-top:none;border-inline-start:none;padding-block:0;padding-inline:var(--seed-dimension-x3);color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-regular);letter-spacing:-.14px;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__cell:last-child{border-inline-end:none}tbody tr:last-child .seed-data-table__cell{border-bottom:none}.seed-data-table__cell[data-numeric]{text-align:end;font-variant-numeric:tabular-nums}.seed-data-table__cell[data-sticky]{position:sticky;z-index:1;background-color:var(--seed-data-table-row-bg)}.seed-data-table__cell[data-editable]{cursor:default}.seed-data-table__cell .seed-data-table-edit-picker{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);min-width:0}.seed-data-table__cell .seed-data-table-edit-pending{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);animation:rotate .6s linear infinite}.seed-data-table__cell .seed-data-table-edit-error{position:absolute;top:100%;inset-inline-start:var(--seed-dimension-x3);inset-inline-end:var(--seed-dimension-x3);z-index:2;padding-top:2px;font-size:12px;line-height:1.3;color:var(--seed-color-fg-critical);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__entity{display:flex;align-items:center;min-width:0;gap:var(--seed-dimension-x1_5)}.seed-data-table__entityName{min-width:0;font-weight:var(--seed-font-weight-medium);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__tagList{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);overflow:hidden;min-width:0}.seed-data-table__entityActions{display:flex;align-items:center;gap:6px;margin-inline-start:auto;flex-shrink:0;opacity:0;transition:opacity .15s}tr:hover .seed-data-table__entityActions{opacity:1}.seed-data-table__entityActions:focus-within{opacity:1}.seed-data-table__entityAction{display:inline-flex;align-items:center;justify-content:center;width:20px;height:20px;margin:0;padding:2px;border:none;border-radius:6px;background:0 0;font-family:inherit;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent);cursor:pointer;flex-shrink:0;transition:background-color .2s,color .2s,box-shadow .2s}.seed-data-table__entityAction:hover{background:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__link{color:var(--seed-color-fg-neutral);text-decoration:none;min-width:0;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__link:is(:hover,[data-hover]){text-decoration:underline}.seed-data-table__chip{box-sizing:border-box;display:inline-flex;align-items:center;flex-shrink:0;max-width:100%;height:22px;padding:0 4px;border-radius:7px;font-size:10px;font-weight:var(--seed-font-weight-regular);line-height:22px;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__chipList{display:flex;align-items:center;flex-wrap:nowrap;gap:var(--seed-dimension-x1_5);overflow:hidden;min-width:0;width:100%}.seed-data-table__chipRemove{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:12px;height:12px;margin:0;margin-inline-start:2px;padding:0;border:none;border-radius:999px;background:0 0;color:inherit;opacity:.6;cursor:pointer;transition:opacity .12s,background-color .12s}.seed-data-table__chipRemove:hover{opacity:1;background-color:rgba(0,0,0,.08)}.seed-data-table__chipInput{flex:1 1 auto;min-width:40px;height:22px;margin:0;padding:0;border:none;outline:0;background:0 0;font-family:inherit;font-size:var(--seed-font-size-t4);color:var(--seed-color-fg-neutral);letter-spacing:-.14px}.seed-data-table__expandOverlay{box-sizing:border-box;position:absolute;top:-1px;inset-inline-start:-1px;inset-inline-end:-1px;z-index:10;min-height:calc(100% + 2px);display:flex;align-items:flex-start;padding-inline:var(--seed-dimension-x3);padding-block:var(--seed-dimension-x3);background-color:var(--seed-color-bg-layer-floating);border-radius:var(--seed-radius-r5);box-shadow:inset 0 0 0 1px var(--seed-color-stroke-neutral-weak),var(--seed-shadow-s3);color:var(--seed-color-fg-neutral);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);letter-spacing:-.14px;cursor:default}.seed-data-table__expandOverlayText{width:100%;min-width:0;white-space:normal;word-break:break-word}.seed-data-table__cellSuffixRow{display:flex;align-items:center;gap:4px;min-width:0;width:100%}.seed-data-table__cellSuffixText{flex:1 1 0%;min-width:0;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__cellSuffixIcon{flex-shrink:0;display:inline-flex;align-items:center;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent)}.seed-data-table__toolbar{box-sizing:border-box;display:flex;align-items:center;width:100%;flex-shrink:0;min-height:49px;gap:var(--seed-dimension-x2);padding-inline:var(--seed-dimension-x3);border-top:1px solid var(--seed-color-stroke-neutral-muted)}.seed-data-table__search{position:relative;display:inline-flex;align-items:center;flex:1 1 0%;max-width:280px}.seed-data-table__search svg{position:absolute;inset-inline-start:var(--seed-dimension-x2);width:var(--seed-dimension-x4);height:var(--seed-dimension-x4);color:var(--seed-color-fg-neutral-muted);pointer-events:none}.seed-data-table__search input{width:100%;height:var(--seed-dimension-x8);padding-inline-start:var(--seed-dimension-x7);padding-inline-end:var(--seed-dimension-x2);border:none;border-radius:var(--seed-radius-r2);background-color:var(--seed-color-bg-layer-default);box-shadow:inset 0 0 0 1px var(--seed-color-stroke-neutral-weak);font-family:inherit;font-size:var(--seed-font-size-t4);color:var(--seed-color-fg-neutral);outline:0;transition:box-shadow .1s var(--seed-timing-function-easing)}.seed-data-table__search input::placeholder{color:var(--seed-color-fg-neutral-subtle)}.seed-data-table__search input:focus{box-shadow:inset 0 0 0 2px var(--seed-color-stroke-neutral-contrast)}.seed-data-table__count{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);font-size:var(--seed-font-size-t3);font-variant-numeric:tabular-nums}.seed-data-table__toolbarEnd{flex-shrink:0;margin-inline-start:auto;display:inline-flex;align-items:center;gap:var(--seed-dimension-x2)}.seed-data-table__scrollArea{position:relative;display:flex;flex-direction:column;width:100%;min-width:0;flex:1 1 auto;min-height:0;overflow-x:auto;overflow-y:auto}.seed-data-table__scrollArea[data-scrolled-start] [data-sticky-edge]{clip-path:inset(0 -38px 0 0);box-shadow:6px 0 16px 4px rgba(0,0,0,.12)}.seed-data-table__scrollArea[data-scrolled-end] [data-sticky-edge-end]{clip-path:inset(0 0 0 -38px);box-shadow:-6px 0 16px 4px rgba(0,0,0,.12)}.seed-data-table__footerCell{box-sizing:border-box;position:sticky;bottom:0;z-index:2;vertical-align:middle;padding:0;height:var(--seed-dimension-x9);background-color:var(--seed-color-bg-layer-default);border-top:1px solid var(--seed-color-stroke-neutral-muted);border-inline-end:1px solid var(--seed-color-stroke-neutral-muted);border-inline-start:none}.seed-data-table__footerCell:last-child{border-inline-end:none}.seed-data-table__footerCell .seed-data-table-column-calc-trigger{display:flex;align-items:center;gap:var(--seed-dimension-x1_5);width:100%;height:100%;min-width:0;margin:0;padding:0 var(--seed-dimension-x3);border:none;background:0 0;color:var(--seed-color-fg-neutral-subtle);font-family:inherit;font-size:var(--seed-font-size-t4);font-weight:var(--seed-font-weight-medium);white-space:nowrap;overflow:clip;cursor:pointer;transition:background-color .12s,color .12s}.seed-data-table__footerCell .seed-data-table-column-calc-trigger:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,transparent);color:var(--seed-color-fg-neutral)}.seed-data-table__footerCell .seed-data-table-column-calc-trigger[data-align=right]{justify-content:flex-end}.seed-data-table__footerCell .seed-data-table-column-calc-trigger svg{flex-shrink:0;opacity:.7}.seed-data-table__footerCell .seed-data-table-column-calc-placeholder{overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__footerCell .seed-data-table-column-calc-value{min-width:0;flex-shrink:1;color:var(--seed-color-fg-neutral);font-variant-numeric:tabular-nums;overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__footerCell .seed-data-table-column-calc-op{min-width:0;flex-shrink:1;color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);overflow:clip;text-overflow:ellipsis;white-space:nowrap}.seed-data-table__emptyState{box-sizing:border-box;text-align:center;padding-block:var(--seed-dimension-x8);padding-inline:var(--seed-dimension-x3);color:var(--seed-color-fg-neutral-subtle);font-size:var(--seed-font-size-t4)}.seed-data-table__loadingState{box-sizing:border-box;display:flex;align-items:center;justify-content:center;gap:10px;padding-block:var(--seed-dimension-x8);padding-inline:var(--seed-dimension-x3);color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);font-size:var(--seed-font-size-t4);line-height:var(--seed-line-height-t4);font-weight:var(--seed-font-weight-medium)}.seed-data-table__loadingState[data-inline]{padding-block:0;padding-inline:0;height:var(--seed-dimension-x9)}.seed-data-table__loadingState .seed-data-table-loading-spinner{flex-shrink:0;color:color-mix(in srgb,var(--seed-color-fg-neutral) 63%,transparent);animation:rotate .6s linear infinite}.seed-data-table__addRowCell{box-sizing:border-box;padding:0;border-top:none}.seed-data-table__addRow{box-sizing:border-box;display:flex;align-items:center;width:100%;margin:0;border:none;border-top:1px solid var(--seed-color-stroke-neutral-muted);background-color:var(--seed-color-bg-layer-default);height:var(--seed-dimension-x9);gap:var(--seed-dimension-x2);padding-inline:var(--seed-dimension-x2);color:var(--seed-color-fg-neutral-subtle);font-family:inherit;font-size:var(--seed-font-size-t4);cursor:pointer;transition:background-color .12s,color .12s}.seed-data-table__addRow:hover{background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 4%,var(--seed-color-bg-layer-default));color:var(--seed-color-fg-neutral)}.seed-data-table__addRow svg{flex-shrink:0}.seed-data-table__selectionCell{text-align:center;padding:0}.seed-data-table__root--surface_card{border-color:var(--seed-color-stroke-neutral-muted);background-color:var(--seed-color-bg-layer-default);border-radius:var(--seed-radius-r3)}.seed-data-table__root--surface_bleed{border-color:var(--seed-color-bg-transparent);background-color:var(--seed-color-bg-transparent)}.seed-data-table__row--interactive_true{transition:background-color .12s}.seed-data-table__row--interactive_true:is(:hover,[data-hover]){background-color:color-mix(in srgb,var(--seed-color-fg-neutral) 3%,transparent);--seed-data-table-row-bg:color-mix(in srgb, var(--seed-color-fg-neutral) 3%, var(--seed-color-bg-layer-default))}");
3213
3380
 
3214
3381
  const createClassName = (className, variants, compoundVariants = [])=>{
3215
3382
  const variantKeys = Object.keys(variants);
@@ -3304,6 +3471,42 @@ const dataTableSlotNames = [
3304
3471
  "link",
3305
3472
  "seed-data-table__link"
3306
3473
  ],
3474
+ [
3475
+ "chip",
3476
+ "seed-data-table__chip"
3477
+ ],
3478
+ [
3479
+ "chipList",
3480
+ "seed-data-table__chipList"
3481
+ ],
3482
+ [
3483
+ "chipRemove",
3484
+ "seed-data-table__chipRemove"
3485
+ ],
3486
+ [
3487
+ "chipInput",
3488
+ "seed-data-table__chipInput"
3489
+ ],
3490
+ [
3491
+ "expandOverlay",
3492
+ "seed-data-table__expandOverlay"
3493
+ ],
3494
+ [
3495
+ "expandOverlayText",
3496
+ "seed-data-table__expandOverlayText"
3497
+ ],
3498
+ [
3499
+ "cellSuffixRow",
3500
+ "seed-data-table__cellSuffixRow"
3501
+ ],
3502
+ [
3503
+ "cellSuffixText",
3504
+ "seed-data-table__cellSuffixText"
3505
+ ],
3506
+ [
3507
+ "cellSuffixIcon",
3508
+ "seed-data-table__cellSuffixIcon"
3509
+ ],
3307
3510
  [
3308
3511
  "entityActions",
3309
3512
  "seed-data-table__entityActions"
@@ -3396,7 +3599,7 @@ Object.assign(dataTable, {
3396
3599
  // other file (`DataTable.tsx`, `column-menu.tsx`, `resize.tsx`, `column-calc.tsx`,
3397
3600
  // `toolbar.tsx`, `body-cells.tsx`, `editors.tsx`) imports its exports from here rather
3398
3601
  // than re-deriving its own.
3399
- const { ClassNamesProvider: ClassNamesProvider$1, withContext: withContext$1, useClassNames: useClassNames$1 } = createSlotRecipeContext(dataTable);
3602
+ const { ClassNamesProvider: ClassNamesProvider$1, withContext: withContext$2, useClassNames: useClassNames$1 } = createSlotRecipeContext(dataTable);
3400
3603
 
3401
3604
  /**
3402
3605
  * The pure filter itself, split out of `useSelectSearch` below so it can be
@@ -3878,6 +4081,103 @@ function RatingEditor({ column, value, pending, onCommit }) {
3878
4081
  ]
3879
4082
  });
3880
4083
  }
4084
+ ////////////////////////////////////////////////////////////////////////////////////
4085
+ // -- editable: "chip-list" (Feature B, #8) -- always-live, the same shape
4086
+ // `SwitchEditor`/`CheckboxEditor`/`RatingEditor` above already establish
4087
+ // (interacting with the widget IS the commit, no double-click gate, no separate
4088
+ // rest display -- `body-cells.tsx`'s `isAlwaysLiveEditable` covers it alongside
4089
+ // them). A gated open/close state (like `SelectEditor`'s) would fight adding
4090
+ // MULTIPLE tags in one sitting: `useDataTableEdit`'s `commitEdit` closes editing
4091
+ // the instant a SYNC commit resolves, so a gated chip editor would close itself
4092
+ // after the very first tag. Always-live sidesteps that entirely -- there is no
4093
+ // `editingCell` to close, the widget is simply always the cell's own content.
4094
+ // Every add/remove commits the FULL next array through the same one-string
4095
+ // contract every other editor already uses (`useDataTableEdit`'s own doc
4096
+ // comment): `JSON.stringify(nextTags)`, which the consumer's `onEdit` parses
4097
+ // back -- crisp/GRC's contract stays "one string in, no typed union", not a
4098
+ // second commit shape invented for just this column.
4099
+ function ChipListEditor({ column, value, pending, onCommit, classNames }) {
4100
+ const tags = Array.isArray(value) ? value.filter((tag)=>!!tag) : [];
4101
+ const [draft, setDraft] = React__namespace.useState("");
4102
+ function commitTags(next) {
4103
+ onCommit(JSON.stringify(next));
4104
+ }
4105
+ function addTag() {
4106
+ const tag = draft.trim();
4107
+ setDraft("");
4108
+ if (!tag || tags.includes(tag)) return;
4109
+ commitTags([
4110
+ ...tags,
4111
+ tag
4112
+ ]);
4113
+ }
4114
+ function removeTag(tag) {
4115
+ commitTags(tags.filter((existing)=>existing !== tag));
4116
+ }
4117
+ return(// biome-ignore lint/a11y/noStaticElementInteractions: not a widget -- this span never responds to interaction on ITSELF, only stops propagation of events that already occurred on a real interactive descendant (the remove buttons, the add-tag input). No role fits it because it isn't a control.
4118
+ /*#__PURE__*/ jsxRuntime.jsxs("span", {
4119
+ className: classNames.chipList,
4120
+ onClick: (event)=>event.stopPropagation(),
4121
+ children: [
4122
+ tags.map((tag)=>/*#__PURE__*/ jsxRuntime.jsx(SwatchChip, {
4123
+ value: tag,
4124
+ className: classNames.chip,
4125
+ children: /*#__PURE__*/ jsxRuntime.jsx("button", {
4126
+ type: "button",
4127
+ disabled: pending,
4128
+ "aria-label": `Remove ${tag}`,
4129
+ className: classNames.chipRemove,
4130
+ onClick: ()=>removeTag(tag),
4131
+ children: /*#__PURE__*/ jsxRuntime.jsx(IconXmarkLine_default, {
4132
+ size: 10
4133
+ })
4134
+ })
4135
+ }, tag)),
4136
+ /*#__PURE__*/ jsxRuntime.jsx("input", {
4137
+ className: classNames.chipInput,
4138
+ value: draft,
4139
+ disabled: pending,
4140
+ placeholder: tags.length ? "" : "Add…",
4141
+ "aria-label": `Add tag to ${column.key}`,
4142
+ onChange: (event)=>setDraft(event.target.value),
4143
+ onKeyDown: (event)=>{
4144
+ event.stopPropagation();
4145
+ if (event.key === "Enter") {
4146
+ event.preventDefault();
4147
+ addTag();
4148
+ } else if (event.key === "Backspace" && draft === "" && tags.length > 0) {
4149
+ removeTag(tags[tags.length - 1]);
4150
+ } else if (event.key === "Escape") {
4151
+ setDraft("");
4152
+ }
4153
+ }
4154
+ }),
4155
+ pending && /*#__PURE__*/ jsxRuntime.jsx(EditPendingIndicator, {})
4156
+ ]
4157
+ }));
4158
+ }
4159
+ ////////////////////////////////////////////////////////////////////////////////////
4160
+ // -- Feature C (#12): cell-expand "show full content" overlay -- an anchored box
4161
+ // (position:absolute over the cell it belongs to, grows in HEIGHT, elevated)
4162
+ // that wraps whatever a truncated cell would otherwise show inline: an editable
4163
+ // column's OWN editor node (unchanged, just relocated into this box instead of
4164
+ // rendered flush in the 36px row) for the editable case, or the plain full value
4165
+ // for a read-only one. `body-cells.tsx` decides WHEN to render this (a click on a
4166
+ // cell whose content is actually truncated, `truncate.ts`'s `isCellTruncated`);
4167
+ // this component only supplies the box.
4168
+ function CellExpandOverlay({ className, children }) {
4169
+ return(// `stopPropagation` -- an overlay sits on top of (and past) its own cell/row;
4170
+ // a click anywhere inside it must never ALSO fire the row's own `onRowClick`,
4171
+ // the same guard every other interactive cell affordance in this directory
4172
+ // already applies (`LinkCell`, `EntityCell`'s preview button, editors.tsx's
4173
+ // `TextEditor` root).
4174
+ // biome-ignore lint/a11y/noStaticElementInteractions: not a widget -- this div never responds to interaction on ITSELF, only stops propagation of events that already occurred on a real interactive descendant (the editor/text it wraps). No role fits it because it isn't a control.
4175
+ /*#__PURE__*/ jsxRuntime.jsx("div", {
4176
+ className: className,
4177
+ onClick: (event)=>event.stopPropagation(),
4178
+ children: children
4179
+ }));
4180
+ }
3881
4181
 
3882
4182
  // Row-narrowing for the toolbar's search box -- split out of DataTable.tsx the way
3883
4183
  // cells.tsx splits out cell-type rendering: a self-contained, independently testable
@@ -3917,49 +4217,167 @@ function RatingEditor({ column, value, pending, onCommit }) {
3917
4217
  return data.filter((row)=>searchableColumns.some((column)=>matchesSearch(getColumnValue(column, row), query)));
3918
4218
  }
3919
4219
 
4220
+ // Feature C (#12) -- the click-to-expand gate: measures whether a cell's rendered
4221
+ // content is ACTUALLY truncated right now, so a single click only opens the
4222
+ // anchored overlay (editors.tsx's `CellExpandOverlay`) when there's real hidden
4223
+ // content to show, never on a short value that already fits. Isolated out of
4224
+ // body-cells.tsx the same way select-search.tsx/filtering.ts already split their own
4225
+ // concerns out of the styled layer, so this one pure DOM read is unit-testable on
4226
+ // its own (a plain `<td>`'s own truncate CSS vs. a suffix-icon cell's nested flex
4227
+ // child need two different elements measured -- see below).
4228
+ //
4229
+ // This one genuinely reads a real, laid-out DOM element (`scrollWidth`/
4230
+ // `clientWidth`), unlike react-headless/data-table/src/dom.ts's own helpers (plain
4231
+ // data in, plain data out) -- it lives in the REACT (styled) package rather than
4232
+ // react-headless for that reason: measuring actual layout is a rendering concern,
4233
+ // not table state.
4234
+ function isCellTruncated(cell) {
4235
+ // The common case: a plain cell's own `white-space: nowrap; overflow: clip` (the
4236
+ // `cell` recipe slot) makes the `<td>` ITSELF overflow once its text is longer
4237
+ // than the column is wide.
4238
+ if (cell.scrollWidth > cell.clientWidth) return true;
4239
+ // A cell composing a suffix icon beside its text (cells.tsx's `CellWithSuffix`,
4240
+ // Feature D) wraps the text in its OWN `flex: 1 1 0%; min-width: 0` child instead
4241
+ // -- that flex child never lets the `<td>` itself overflow (it's constrained to
4242
+ // the remaining row width by construction), so the truncating element to measure
4243
+ // is the flex child, marked `data-seed-truncate` by whichever cell renderer
4244
+ // applies its own ellipsis (`CellWithSuffix`'s text span today; any future
4245
+ // truncating inner element should carry the same marker).
4246
+ const targets = cell.querySelectorAll("[data-seed-truncate]");
4247
+ for (const target of targets){
4248
+ if (target.scrollWidth > target.clientWidth) return true;
4249
+ }
4250
+ return false;
4251
+ }
4252
+
3920
4253
  /** `editable` + one of these shapes needs no double-click gate and no separate rest
3921
4254
  * display -- interacting with the widget itself IS the commit gesture (crisp's own
3922
4255
  * #453/#760/#761: `switch`/`rating`/`checkbox` never had a meaningfully different
3923
- * "rest" state to show). Every other `editable` column opens on double-click. */ function isAlwaysLiveEditable(column) {
4256
+ * "rest" state to show). `chip-list` (Feature B, #8) joins them for a different
4257
+ * reason -- see `ChipListEditor`'s own doc comment (editors.tsx): a GATED chip
4258
+ * editor would close itself the instant the first tag committed. Every other
4259
+ * `editable` column opens on double-click. */ function isAlwaysLiveEditable(column) {
3924
4260
  if (!column.editable) return false;
3925
- return column.editor === "switch" || column.type === "rating" || column.type === "checkbox";
4261
+ return column.editor === "switch" || column.type === "rating" || column.type === "checkbox" || column.type === "chip-list";
3926
4262
  }
3927
4263
  function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyFirst, stickyLast }) {
3928
4264
  const effective = useEffectiveColumns(columns, {
3929
4265
  selectable,
3930
4266
  stickyFirst
3931
4267
  });
3932
- const { startEdit } = useDataTableContext();
3933
4268
  return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
3934
4269
  children: effective.map((entry, index)=>{
3935
4270
  const { column } = entry;
3936
4271
  const isLast = index === effective.length - 1;
3937
4272
  const sticky = pinnedCellProps(entry, stickyLast, isLast);
3938
- // A gated `editable` column (everything except the always-live widgets)
3939
- // opens on double-click -- Attio's own measured gesture (crisp's #411),
3940
- // matched here rather than a single click, which stays "select this cell"
3941
- // in crisp's own spreadsheet model and would otherwise be indistinguishable
3942
- // from this table's plain row click.
3943
- const gated = column.editable && !isAlwaysLiveEditable(column);
3944
- return /*#__PURE__*/ jsxRuntime.jsx(DataTableCell, {
3945
- "data-numeric": domUtils.dataAttr(column.type === "number" || column.type === "currency"),
3946
- "data-editable": domUtils.dataAttr(!!column.editable),
3947
- "data-sticky": sticky["data-sticky"],
3948
- "data-sticky-edge": sticky["data-sticky-edge"],
3949
- "data-sticky-edge-end": sticky["data-sticky-edge-end"],
3950
- style: sticky.style,
3951
- onDoubleClick: gated ? ()=>startEdit(row.id, column.key) : undefined,
3952
- children: /*#__PURE__*/ jsxRuntime.jsx(CellContent, {
3953
- column: column,
3954
- row: row,
3955
- rowId: row.id,
3956
- classNames: classNames,
3957
- locale: locale
3958
- })
4273
+ return /*#__PURE__*/ jsxRuntime.jsx(DataTableBodyCell, {
4274
+ column: column,
4275
+ row: row,
4276
+ classNames: classNames,
4277
+ locale: locale,
4278
+ sticky: sticky
3959
4279
  }, column.key);
3960
4280
  })
3961
4281
  });
3962
4282
  }
4283
+ /**
4284
+ * One `<td>` -- split out of `ColumnBodyCells`'s own map (which now only computes
4285
+ * per-column LAYOUT, `pinnedCellProps`) so this component can own its own per-cell
4286
+ * "is the Feature C overlay open" local state (`overlayOpen`) without re-deriving it
4287
+ * on every row's render. React keeps this instance stable across re-renders because
4288
+ * `ColumnBodyCells` keys it by `column.key` the same way it always keyed the bare
4289
+ * `<DataTableCell>` before this split.
4290
+ */ function DataTableBodyCell({ column, row, classNames, locale, sticky }) {
4291
+ const { startEdit, isEditing, isPeeking, openPeek, closePeek } = useDataTableContext();
4292
+ const cellRef = React__namespace.useRef(null);
4293
+ // Feature C (#12) -- whether THIS activation (click or double-click) should
4294
+ // render as the anchored overlay rather than plain inline. Frozen at the moment
4295
+ // of activation (re-measured every time, never left stale) rather than derived
4296
+ // from `isEditing`/`isPeeking` alone, because those two states don't carry "was
4297
+ // the content actually truncated when this opened" on their own -- a SHORT
4298
+ // editable cell's existing double-click-to-edit (tranche 5, unchanged) must keep
4299
+ // rendering exactly as it always has, not suddenly gain overlay chrome.
4300
+ const [overlayOpen, setOverlayOpen] = React__namespace.useState(false);
4301
+ const alwaysLive = isAlwaysLiveEditable(column);
4302
+ // A gated `editable` column (everything except the always-live widgets) opens on
4303
+ // double-click -- Attio's own measured gesture (crisp's #411), matched here
4304
+ // rather than a single click, which stays "select this cell" in crisp's own
4305
+ // spreadsheet model and would otherwise be indistinguishable from this table's
4306
+ // plain row click. Feature C ADDS a single click as a SECOND way in, but only
4307
+ // when the cell's content is actually truncated (see `handleClick` below) --
4308
+ // double-click keeps working unconditionally, same as before this feature.
4309
+ const gated = column.editable && !alwaysLive;
4310
+ function handleDoubleClick(event) {
4311
+ if (!gated) return;
4312
+ setOverlayOpen(isCellTruncated(event.currentTarget));
4313
+ startEdit(row.id, column.key);
4314
+ }
4315
+ function handleClick(event) {
4316
+ if (!isCellTruncated(event.currentTarget)) return;
4317
+ if (gated) {
4318
+ setOverlayOpen(true);
4319
+ startEdit(row.id, column.key);
4320
+ } else if (alwaysLive) {
4321
+ // No separate open/edit state to start -- the always-live widget already
4322
+ // renders unconditionally; this only switches ITS OWN box to the anchored
4323
+ // overlay so a long chip list (etc.) gets room to show every chip.
4324
+ setOverlayOpen(true);
4325
+ } else {
4326
+ setOverlayOpen(true);
4327
+ openPeek(row.id, column.key);
4328
+ }
4329
+ }
4330
+ // Closes the overlay on Escape or an outside click/pointerdown -- a gated
4331
+ // editor's OWN Escape/blur handling already ends `isEditing` on its own (see
4332
+ // `TextEditor`/`SelectEditor`'s existing onCancel/onOpenChange wiring), which
4333
+ // alone is enough to hide a GATED cell's overlay (its render below is gated on
4334
+ // `isEditing` too); this listener is what the PEEK and ALWAYS-LIVE cases need,
4335
+ // since neither has any other mechanism that ever calls `closePeek`/resets
4336
+ // `overlayOpen` on its own.
4337
+ React__namespace.useEffect(()=>{
4338
+ if (!overlayOpen) return;
4339
+ function handlePointerDown(event) {
4340
+ if (cellRef.current && !cellRef.current.contains(event.target)) {
4341
+ setOverlayOpen(false);
4342
+ closePeek();
4343
+ }
4344
+ }
4345
+ function handleKeyDown(event) {
4346
+ if (event.key !== "Escape") return;
4347
+ setOverlayOpen(false);
4348
+ closePeek();
4349
+ }
4350
+ document.addEventListener("pointerdown", handlePointerDown);
4351
+ document.addEventListener("keydown", handleKeyDown);
4352
+ return ()=>{
4353
+ document.removeEventListener("pointerdown", handlePointerDown);
4354
+ document.removeEventListener("keydown", handleKeyDown);
4355
+ };
4356
+ }, [
4357
+ overlayOpen,
4358
+ closePeek
4359
+ ]);
4360
+ const overlayActive = overlayOpen && (alwaysLive || isEditing(row.id, column.key) || isPeeking(row.id, column.key));
4361
+ return /*#__PURE__*/ jsxRuntime.jsx(DataTableCell, {
4362
+ ref: cellRef,
4363
+ "data-numeric": domUtils.dataAttr(column.type === "number" || column.type === "currency"),
4364
+ "data-editable": domUtils.dataAttr(!!column.editable),
4365
+ "data-sticky": sticky["data-sticky"],
4366
+ "data-sticky-edge": sticky["data-sticky-edge"],
4367
+ "data-sticky-edge-end": sticky["data-sticky-edge-end"],
4368
+ style: sticky.style,
4369
+ onClick: handleClick,
4370
+ onDoubleClick: handleDoubleClick,
4371
+ children: /*#__PURE__*/ jsxRuntime.jsx(CellContent, {
4372
+ column: column,
4373
+ row: row,
4374
+ rowId: row.id,
4375
+ classNames: classNames,
4376
+ locale: locale,
4377
+ overlayActive: overlayActive
4378
+ })
4379
+ });
4380
+ }
3963
4381
  /**
3964
4382
  * Dispatches to whichever of cells.tsx's static renderers or editors.tsx's live
3965
4383
  * editors applies to THIS column/row right now -- always-live first (no rest state to
@@ -3967,7 +4385,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
3967
4385
  * display (a custom `render` wins there, same as before `editable` existed; it never
3968
4386
  * overrides an actual editing/always-live state, matching crisp's own split between
3969
4387
  * `renderTyped`'s rest-state branch and the editing branches above it).
3970
- */ function CellContent({ column, row, rowId, classNames, locale }) {
4388
+ */ function CellContent({ column, row, rowId, classNames, locale, overlayActive }) {
3971
4389
  const { isEditing, isPending, getEditError, commitEdit, cancelEdit } = useDataTableContext();
3972
4390
  const rawValue = getColumnValue(column, row);
3973
4391
  const pending = isPending(rowId, column.key);
@@ -3996,6 +4414,14 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
3996
4414
  pending: pending,
3997
4415
  onCommit: commit
3998
4416
  });
4417
+ } else if (column.editable && column.type === "chip-list") {
4418
+ node = /*#__PURE__*/ jsxRuntime.jsx(ChipListEditor, {
4419
+ column: column,
4420
+ value: resolveEditValue(column, row, rawValue),
4421
+ pending: pending,
4422
+ onCommit: commit,
4423
+ classNames: classNames
4424
+ });
3999
4425
  } else if (column.editable && isEditing(rowId, column.key)) {
4000
4426
  if (column.editor === "select") {
4001
4427
  node = /*#__PURE__*/ jsxRuntime.jsx(SelectEditor, {
@@ -4031,6 +4457,10 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4031
4457
  });
4032
4458
  }
4033
4459
  } else {
4460
+ // Feature C (#12), read-only case: `DataTableBodyCell`'s own `isPeeking` already
4461
+ // decided whether the overlay should be open (folded into `overlayActive`
4462
+ // below) -- this branch always renders the SAME `DisplayCellContent` either
4463
+ // way, just possibly relocated into the overlay box.
4034
4464
  node = /*#__PURE__*/ jsxRuntime.jsx(DisplayCellContent, {
4035
4465
  column: column,
4036
4466
  row: row,
@@ -4041,7 +4471,13 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4041
4471
  }
4042
4472
  return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
4043
4473
  children: [
4044
- node,
4474
+ overlayActive ? /*#__PURE__*/ jsxRuntime.jsx(CellExpandOverlay, {
4475
+ className: classNames.expandOverlay,
4476
+ children: column.editable ? node : /*#__PURE__*/ jsxRuntime.jsx("span", {
4477
+ className: classNames.expandOverlayText,
4478
+ children: node
4479
+ })
4480
+ }) : node,
4045
4481
  error && /*#__PURE__*/ jsxRuntime.jsx(EditError, {
4046
4482
  message: error
4047
4483
  })
@@ -4072,7 +4508,11 @@ function DisplayCellContent({ column, row, value, classNames, locale }) {
4072
4508
  return /*#__PURE__*/ jsxRuntime.jsx(LinkCell, {
4073
4509
  column: column,
4074
4510
  value: value,
4075
- className: classNames.link
4511
+ row: row,
4512
+ className: classNames.link,
4513
+ rowClassName: classNames.cellSuffixRow,
4514
+ textClassName: classNames.cellSuffixText,
4515
+ suffixClassName: classNames.cellSuffixIcon
4076
4516
  });
4077
4517
  case "tags":
4078
4518
  return /*#__PURE__*/ jsxRuntime.jsx(TagsCell, {
@@ -4083,6 +4523,17 @@ function DisplayCellContent({ column, row, value, classNames, locale }) {
4083
4523
  return /*#__PURE__*/ jsxRuntime.jsx(StatusCell, {
4084
4524
  value: value
4085
4525
  });
4526
+ case "chip":
4527
+ return /*#__PURE__*/ jsxRuntime.jsx(ChipCell, {
4528
+ value: value,
4529
+ className: classNames.chip
4530
+ });
4531
+ case "chip-list":
4532
+ return /*#__PURE__*/ jsxRuntime.jsx(ChipListCell, {
4533
+ value: value,
4534
+ className: classNames.chipList,
4535
+ chipClassName: classNames.chip
4536
+ });
4086
4537
  case "number":
4087
4538
  return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
4088
4539
  children: formatNumberCell(value, locale)
@@ -4109,13 +4560,27 @@ function DisplayCellContent({ column, row, value, classNames, locale }) {
4109
4560
  value: value
4110
4561
  });
4111
4562
  default:
4112
- return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
4113
- children: value == null ? null : String(value)
4114
- });
4563
+ {
4564
+ const text = value == null ? null : String(value);
4565
+ const suffix = column.suffixIcon?.(row);
4566
+ // Feature D (#13) -- a plain text column carrying a `suffixIcon` (e.g. a
4567
+ // location column's pin glyph) composes through the same `CellWithSuffix`
4568
+ // LinkCell already uses, so the icon never gets squeezed out by a long value.
4569
+ if (!suffix || text == null) return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
4570
+ children: text
4571
+ });
4572
+ return /*#__PURE__*/ jsxRuntime.jsx(CellWithSuffix, {
4573
+ text: text,
4574
+ suffix: suffix,
4575
+ rowClassName: classNames.cellSuffixRow,
4576
+ textClassName: classNames.cellSuffixText,
4577
+ suffixClassName: classNames.cellSuffixIcon
4578
+ });
4579
+ }
4115
4580
  }
4116
4581
  }
4117
4582
 
4118
- const { ClassNamesProvider, withContext, useClassNames } = createSlotRecipeContext(menu.menu);
4583
+ const { ClassNamesProvider, withContext: withContext$1, useClassNames } = createSlotRecipeContext(menu.menu);
4119
4584
  const { PropsProvider: ItemPropsProvider, useProps: useItemProps, withContext: withItemContext, ClassNamesProvider: ItemClassNamesProvider } = createSlotRecipeContext(menuItem.menuItem);
4120
4585
  const withStateProps = createWithStateProps([
4121
4586
  reactMenu.useMenuContext
@@ -4149,10 +4614,10 @@ const MenuPositioner = /*#__PURE__*/ React__namespace.forwardRef(({ className, .
4149
4614
  });
4150
4615
  });
4151
4616
  MenuPositioner.displayName = "MenuPositioner";
4152
- const MenuContent = withContext(reactMenu.Menu.Content, "content");
4153
- const MenuScrollArea = withContext(withStateProps(Primitive.div), "scrollArea");
4154
- withContext(reactMenu.Menu.Group, "group");
4155
- withContext(reactMenu.Menu.GroupLabel, "groupLabel");
4617
+ const MenuContent = withContext$1(reactMenu.Menu.Content, "content");
4618
+ const MenuScrollArea = withContext$1(withStateProps(Primitive.div), "scrollArea");
4619
+ withContext$1(reactMenu.Menu.Group, "group");
4620
+ withContext$1(reactMenu.Menu.GroupLabel, "groupLabel");
4156
4621
  const MenuItem = /*#__PURE__*/ React__namespace.forwardRef(({ className: propClassName, ...props }, ref)=>{
4157
4622
  const [variantProps, otherProps] = menuItem.menuItem.splitVariantProps(props);
4158
4623
  const parentProps = useItemProps();
@@ -5917,8 +6382,8 @@ Object.assign(datePicker, {
5917
6382
  splitVariantProps: (props)=>splitVariantProps(props, datePickerVariantMap)
5918
6383
  }); // @recipe(seed): date-picker
5919
6384
 
5920
- var __defProp$1 = Object.defineProperty;
5921
- var __name$1 = (target, value)=>__defProp$1(target, "name", {
6385
+ var __defProp$3 = Object.defineProperty;
6386
+ var __name$3 = (target, value)=>__defProp$3(target, "name", {
5922
6387
  value,
5923
6388
  configurable: true
5924
6389
  });
@@ -5942,15 +6407,15 @@ function useEffectEvent(callback) {
5942
6407
  }
5943
6408
  return React__namespace.useMemo(()=>(...args)=>ref.current?.(...args), []);
5944
6409
  }
5945
- __name$1(useEffectEvent, "useEffectEvent");
6410
+ __name$3(useEffectEvent, "useEffectEvent");
5946
6411
 
5947
- var __defProp = Object.defineProperty;
5948
- var __name = (target, value)=>__defProp(target, "name", {
6412
+ var __defProp$2 = Object.defineProperty;
6413
+ var __name$2 = (target, value)=>__defProp$2(target, "name", {
5949
6414
  value,
5950
6415
  configurable: true
5951
6416
  });
5952
6417
  var useInsertionEffect = React__namespace[" useInsertionEffect ".trim().toString()] || useLayoutEffect2;
5953
- function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __name(()=>{}, "onChange"), caller }) {
6418
+ function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __name$2(()=>{}, "onChange"), caller }) {
5954
6419
  const [uncontrolledProp, setUncontrolledProp, onChangeRef] = useUncontrolledState({
5955
6420
  defaultProp,
5956
6421
  onChange
@@ -5977,7 +6442,7 @@ function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __
5977
6442
  setValue
5978
6443
  ];
5979
6444
  }
5980
- __name(useControllableState, "useControllableState");
6445
+ __name$2(useControllableState, "useControllableState");
5981
6446
  function useUncontrolledState({ defaultProp, onChange }) {
5982
6447
  const [value, setValue] = React__namespace.useState(defaultProp);
5983
6448
  const prevValueRef = React__namespace.useRef(value);
@@ -6002,11 +6467,11 @@ function useUncontrolledState({ defaultProp, onChange }) {
6002
6467
  onChangeRef
6003
6468
  ];
6004
6469
  }
6005
- __name(useUncontrolledState, "useUncontrolledState");
6470
+ __name$2(useUncontrolledState, "useUncontrolledState");
6006
6471
  function isFunction(value) {
6007
6472
  return typeof value === "function";
6008
6473
  }
6009
- __name(isFunction, "isFunction");
6474
+ __name$2(isFunction, "isFunction");
6010
6475
  var SYNC_STATE = Symbol("RADIX:SYNC_STATE");
6011
6476
  function useControllableStateReducer(reducer, userArgs, initialArg, init) {
6012
6477
  const { prop: controlledState, defaultProp, onChange: onChangeProp, caller } = userArgs;
@@ -6078,7 +6543,7 @@ function useControllableStateReducer(reducer, userArgs, initialArg, init) {
6078
6543
  dispatch
6079
6544
  ];
6080
6545
  }
6081
- __name(useControllableStateReducer, "useControllableStateReducer");
6546
+ __name$2(useControllableStateReducer, "useControllableStateReducer");
6082
6547
 
6083
6548
  /*
6084
6549
  * Copyright 2020 Adobe. All rights reserved.
@@ -10284,8 +10749,8 @@ const formatFilterDate = (iso)=>{
10284
10749
  });
10285
10750
  }
10286
10751
 
10287
- const DataTableToolbar = withContext$1(Primitive.div, "toolbar");
10288
- const DataTableCount = withContext$1(Primitive.span, "count");
10752
+ const DataTableToolbar = withContext$2(Primitive.div, "toolbar");
10753
+ const DataTableCount = withContext$2(Primitive.span, "count");
10289
10754
  /**
10290
10755
  * The toolbar's own right-hand group -- `count`'s `marginInlineStart: auto` moved
10291
10756
  * here (off the `<span>` itself) so a consumer's `toolbarActions` and the row count
@@ -10296,7 +10761,7 @@ const DataTableCount = withContext$1(Primitive.span, "count");
10296
10761
  * it. `DataTable.tsx` always renders this wrapper once either `toolbarActions` or
10297
10762
  * `showCount` is on, so it degrades to exactly the old single-count layout when no
10298
10763
  * `toolbarActions` is passed.
10299
- */ const DataTableToolbarEnd = withContext$1(Primitive.div, "toolbarEnd");
10764
+ */ const DataTableToolbarEnd = withContext$2(Primitive.div, "toolbarEnd");
10300
10765
  /**
10301
10766
  * A compact, borderless search box -- crisp's own measured `.crisp-datatable-search`
10302
10767
  * (32px, `bgSurface` background, a leading icon, a soft rest-state shadow, a 1px brand
@@ -10718,7 +11183,7 @@ DataTableInner.displayName = "DataTable";
10718
11183
  // is a SIBLING of the toolbar (both inside `DataTablePrimitiveRoot`'s card `<div>`)
10719
11184
  // rather than `DataTablePrimitiveRoot` itself: a `<table>` cannot host the toolbar as a
10720
11185
  // child without the HTML parser foster-parenting it back out.
10721
- const DataTableTable = withContext$1(DataTableTable$1, "table");
11186
+ const DataTableTable = withContext$2(DataTableTable$1, "table");
10722
11187
  /** One `<col>` per rendered column (plus the 40px selection column, if any) -- the
10723
11188
  * SINGLE place a column's width is ever set now. Every row's header/body/footer cell
10724
11189
  * reads its own width off this shared `<colgroup>` by construction (native table
@@ -10755,15 +11220,15 @@ const DataTableTable = withContext$1(DataTableTable$1, "table");
10755
11220
  ]
10756
11221
  });
10757
11222
  }
10758
- const DataTableHeader = withContext$1(DataTableHeader$1, "header");
10759
- const DataTableHeaderCell = withContext$1(DataTableHeaderCell$1, "headerCell");
11223
+ const DataTableHeader = withContext$2(DataTableHeader$1, "header");
11224
+ const DataTableHeaderCell = withContext$2(DataTableHeaderCell$1, "headerCell");
10760
11225
  // `.crisp-datatable-th-inner` -- the flex row INSIDE a `<th>` (label + filter trigger +
10761
11226
  // sort chevron + column menu). See qvism-preset's own `headerCellInner` slot comment
10762
11227
  // for why this lives one level below the cell itself rather than on it.
10763
- const DataTableHeaderCellInner = withContext$1(Primitive.div, "headerCellInner");
11228
+ const DataTableHeaderCellInner = withContext$2(Primitive.div, "headerCellInner");
10764
11229
  /** The label text specifically -- the one thing in `DataTableHeaderCellInner` that
10765
11230
  * truncates; the trailing filter/sort/menu icons stay fully visible regardless of how
10766
- * long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$1(Primitive.span, "headerCellLabel");
11231
+ * long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$2(Primitive.span, "headerCellLabel");
10767
11232
  /**
10768
11233
  * A selection `<th>`/`<td>`/footer `<td>` composes ITS base slot (`headerCell`/`cell`/
10769
11234
  * `footerCell`) together with the `selectionCell` MODIFIER, rather than replacing it --
@@ -10785,12 +11250,12 @@ const DataTableHeaderCellInner = withContext$1(Primitive.div, "headerCellInner")
10785
11250
  return StyledComponent;
10786
11251
  }
10787
11252
  const DataTableSelectionHeaderCell = withSelectionCell(DataTableHeaderCell$1, "headerCell");
10788
- const DataTableHeaderCellIcon = withContext$1(Primitive.span, "headerCellIcon");
11253
+ const DataTableHeaderCellIcon = withContext$2(Primitive.span, "headerCellIcon");
10789
11254
  // body has no CSS slot of its own in the rootage spec -- it is a plain structural
10790
11255
  // `<tbody>`, so it is not wrapped in `withContext`.
10791
11256
  const DataTableBody = DataTableBody$1;
10792
- const DataTableRow = withContext$1(DataTableRow$1, "row");
10793
- const DataTableCell = withContext$1(DataTableCell$1, "cell");
11257
+ const DataTableRow = withContext$2(DataTableRow$1, "row");
11258
+ const DataTableCell = withContext$2(DataTableCell$1, "cell");
10794
11259
  const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
10795
11260
  // Tranche 4 -- column menu + reorder + resize + sticky columns. A scroll boundary
10796
11261
  // wrapping the whole `<table>`: `overflowX: auto` is what makes a wide table (more
@@ -10800,7 +11265,7 @@ const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
10800
11265
  // stays out of this slot's own base css (nothing here has a bounded height of its own
10801
11266
  // to clip), so it never introduces a second vertical scrollbar next to whatever
10802
11267
  // ambient container the whole DataTable already sits in.
10803
- const DataTableScrollBodyBase = withContext$1(Primitive.div, "scrollArea");
11268
+ const DataTableScrollBodyBase = withContext$2(Primitive.div, "scrollArea");
10804
11269
  function DataTableScrollBody({ children, scrollProps }) {
10805
11270
  const { scrollAreaRef, scrolledStart, scrolledEnd, onScroll } = useDataTableContext();
10806
11271
  const { className: scrollClassName, style: scrollStyle, ...otherScrollProps } = scrollProps ?? {};
@@ -10815,7 +11280,7 @@ function DataTableScrollBody({ children, scrollProps }) {
10815
11280
  children: children
10816
11281
  });
10817
11282
  }
10818
- const DataTableFooterBase = withContext$1(Primitive.tfoot, "footer");
11283
+ const DataTableFooterBase = withContext$2(Primitive.tfoot, "footer");
10819
11284
  function DataTableFooter({ children, ...props }) {
10820
11285
  return /*#__PURE__*/ jsxRuntime.jsx(DataTableFooterBase, {
10821
11286
  ...props,
@@ -10824,7 +11289,7 @@ function DataTableFooter({ children, ...props }) {
10824
11289
  })
10825
11290
  });
10826
11291
  }
10827
- const DataTableFooterCell = withContext$1(Primitive.td, "footerCell");
11292
+ const DataTableFooterCell = withContext$2(Primitive.td, "footerCell");
10828
11293
  // The selection column's OWN sticky wrappers -- one each for header/body/footer,
10829
11294
  // reading the live pinned set via `useSelectionStickyFirst` (column-layout.ts) instead
10830
11295
  // of the `stickyFirst` PROP alone, so the checkbox column sticks the moment ANY column
@@ -11176,7 +11641,7 @@ DataTableViewsControl.displayName = "DataTableViewsControl";
11176
11641
  });
11177
11642
  }
11178
11643
 
11179
- __insertCSS(".seed-resizable-panel__root{position:relative;display:flex;height:100%}.seed-resizable-panel__spacer{flex:0 0 auto;height:100%;transition:width .14s}.seed-resizable-panel__panel{position:absolute;top:0;bottom:0;box-sizing:border-box;overflow:hidden;background:var(--seed-color-bg-layer-default)}.seed-resizable-panel__panel[data-side=left]{left:0}.seed-resizable-panel__panel[data-side=right]{right:0}.seed-resizable-panel__panel[data-floating=false]{border-radius:0;box-shadow:none}.seed-resizable-panel__panel[data-floating=false][data-side=left]{border-inline-end:1px solid var(--seed-color-stroke-neutral-muted)}.seed-resizable-panel__panel[data-floating=false][data-side=right]{border-inline-start:1px solid var(--seed-color-stroke-neutral-muted)}.seed-resizable-panel__panel[data-floating=true]{position:absolute;top:8px;bottom:8px;z-index:20;border-inline-end:none;border-inline-start:none;border-radius:12px;box-shadow:0 0 0 1px rgba(28,40,64,.04),0 4px 8px -4px rgba(28,40,64,.12),0 4px 12px -2px rgba(24,41,75,.16);transform:none;opacity:1}.seed-resizable-panel__panel[data-floating=true][data-side=left]{left:8px}.seed-resizable-panel__panel[data-floating=true][data-side=right]{right:8px}.seed-resizable-panel__panel[data-floating=true][data-side=left][data-state=open]{animation:seed-enter;animation-timing-function:cubic-bezier(0.16,1,0.3,1);animation-duration:.18s;--seed-enter-translate-x:calc(-100% - 8px);--seed-enter-translate-y:0;--seed-enter-opacity:0;--seed-enter-scale:1}@media (prefers-reduced-motion:reduce){.seed-resizable-panel__panel[data-floating=true][data-side=left][data-state=open]{--seed-enter-translate-x:0}}.seed-resizable-panel__panel[data-floating=true][data-side=left][data-state=closed]{animation:seed-exit;animation-timing-function:cubic-bezier(0.4,0,1,1);animation-duration:.14s;animation-fill-mode:forwards;--seed-exit-translate-x:calc(-100% - 8px);--seed-exit-translate-y:0;--seed-exit-opacity:0;--seed-exit-scale:1}@media (prefers-reduced-motion:reduce){.seed-resizable-panel__panel[data-floating=true][data-side=left][data-state=closed]{--seed-exit-translate-x:0}}.seed-resizable-panel__panel[data-floating=true][data-side=right][data-state=open]{animation:seed-enter;animation-timing-function:cubic-bezier(0.16,1,0.3,1);animation-duration:.18s;--seed-enter-translate-x:calc(100% + 8px);--seed-enter-translate-y:0;--seed-enter-opacity:0;--seed-enter-scale:1}@media (prefers-reduced-motion:reduce){.seed-resizable-panel__panel[data-floating=true][data-side=right][data-state=open]{--seed-enter-translate-x:0}}.seed-resizable-panel__panel[data-floating=true][data-side=right][data-state=closed]{animation:seed-exit;animation-timing-function:cubic-bezier(0.4,0,1,1);animation-duration:.14s;animation-fill-mode:forwards;--seed-exit-translate-x:calc(100% + 8px);--seed-exit-translate-y:0;--seed-exit-opacity:0;--seed-exit-scale:1}@media (prefers-reduced-motion:reduce){.seed-resizable-panel__panel[data-floating=true][data-side=right][data-state=closed]{--seed-exit-translate-x:0}}.seed-resizable-panel__inner{height:100%}.seed-resizable-panel__handle{position:absolute;top:0;width:10px;height:100%;z-index:2;cursor:col-resize;background:0 0;touch-action:none}.seed-resizable-panel__handle[data-side=left]{right:0}.seed-resizable-panel__handle[data-side=right]{left:0}.seed-resizable-panel__handle::before{content:\"\";position:absolute;top:0;width:1px;height:100%;background:var(--seed-color-stroke-neutral-muted);transition:background-color .16s,height .16s}.seed-resizable-panel__handle[data-side=left]::before{right:0}.seed-resizable-panel__handle[data-side=right]::before{left:0}.seed-resizable-panel__handle:hover::before,.seed-resizable-panel__handle[data-dragging]::before{width:2px;background:#0298d9}.seed-resizable-panel__handle.crisp-seed-resizable-panel-handle--column{display:flex;align-items:center;justify-content:center;width:7px;left:-3px;right:-3px;z-index:1;cursor:col-resize}.seed-resizable-panel__handle.crisp-seed-resizable-panel-handle--column::before{content:none}.seed-resizable-panel__handleInner{width:3px;height:100%;border-radius:3px;background:#0298d9;opacity:0;transition:opacity .1s .1s}.seed-resizable-panel__handle:hover .seed-resizable-panel__handleInner,.seed-resizable-panel__handle[data-dragging] .seed-resizable-panel__handleInner{opacity:1}");
11644
+ __insertCSS(".seed-resizable-panel__root{position:relative;display:flex;height:100%}.seed-resizable-panel__root[data-floating=true]{position:absolute;inset:0;display:block;height:auto;pointer-events:none}.seed-resizable-panel__spacer{flex:0 0 auto;height:100%;transition:width .14s}.seed-resizable-panel__panel{position:absolute;top:0;bottom:0;box-sizing:border-box;overflow:hidden;background:var(--seed-color-bg-layer-default)}.seed-resizable-panel__panel[data-side=left]{left:0}.seed-resizable-panel__panel[data-side=right]{right:0}.seed-resizable-panel__panel[data-floating=false]{border-radius:0;box-shadow:none}.seed-resizable-panel__panel[data-floating=false][data-side=left]{border-inline-end:1px solid var(--seed-color-stroke-neutral-muted)}.seed-resizable-panel__panel[data-floating=false][data-side=right]{border-inline-start:1px solid var(--seed-color-stroke-neutral-muted)}.seed-resizable-panel__panel[data-floating=true]{position:absolute;top:0;bottom:0;z-index:20;pointer-events:auto;border-inline-end:none;border-inline-start:none;transform:none;opacity:1}.seed-resizable-panel__panel[data-floating=true][data-side=left]{left:0;border-top-right-radius:12px;border-bottom-right-radius:12px;box-shadow:1px 0 0 rgba(28,40,64,.04),8px 0 16px -6px rgba(28,40,64,.12),8px 0 24px -4px rgba(24,41,75,.16)}.seed-resizable-panel__panel[data-floating=true][data-side=right]{right:0;border-top-left-radius:12px;border-bottom-left-radius:12px;box-shadow:-1px 0 0 rgba(28,40,64,.04),-8px 0 16px -6px rgba(28,40,64,.12),-8px 0 24px -4px rgba(24,41,75,.16)}.seed-resizable-panel__panel[data-floating=true][data-side=left][data-state=open]{animation:seed-enter;animation-timing-function:cubic-bezier(0.16,1,0.3,1);animation-duration:.18s;--seed-enter-translate-x:-100%;--seed-enter-translate-y:0;--seed-enter-opacity:0;--seed-enter-scale:1}@media (prefers-reduced-motion:reduce){.seed-resizable-panel__panel[data-floating=true][data-side=left][data-state=open]{--seed-enter-translate-x:0}}.seed-resizable-panel__panel[data-floating=true][data-side=left][data-state=closed]{animation:seed-exit;animation-timing-function:cubic-bezier(0.4,0,1,1);animation-duration:.14s;animation-fill-mode:forwards;--seed-exit-translate-x:-100%;--seed-exit-translate-y:0;--seed-exit-opacity:0;--seed-exit-scale:1}@media (prefers-reduced-motion:reduce){.seed-resizable-panel__panel[data-floating=true][data-side=left][data-state=closed]{--seed-exit-translate-x:0}}.seed-resizable-panel__panel[data-floating=true][data-side=right][data-state=open]{animation:seed-enter;animation-timing-function:cubic-bezier(0.16,1,0.3,1);animation-duration:.18s;--seed-enter-translate-x:100%;--seed-enter-translate-y:0;--seed-enter-opacity:0;--seed-enter-scale:1}@media (prefers-reduced-motion:reduce){.seed-resizable-panel__panel[data-floating=true][data-side=right][data-state=open]{--seed-enter-translate-x:0}}.seed-resizable-panel__panel[data-floating=true][data-side=right][data-state=closed]{animation:seed-exit;animation-timing-function:cubic-bezier(0.4,0,1,1);animation-duration:.14s;animation-fill-mode:forwards;--seed-exit-translate-x:100%;--seed-exit-translate-y:0;--seed-exit-opacity:0;--seed-exit-scale:1}@media (prefers-reduced-motion:reduce){.seed-resizable-panel__panel[data-floating=true][data-side=right][data-state=closed]{--seed-exit-translate-x:0}}.seed-resizable-panel__inner{height:100%}.seed-resizable-panel__backdrop{position:absolute;inset:0;z-index:19;pointer-events:auto;background:rgba(15,19,32,.32);opacity:0;transition:opacity .16s cubic-bezier(.4, 0, 1, 1)}.seed-resizable-panel__backdrop[data-state=open]{opacity:1;transition-timing-function:cubic-bezier(0.16,1,0.3,1)}.seed-resizable-panel__backdrop[data-state=closed]{opacity:0}.seed-resizable-panel__handle{position:absolute;top:0;width:10px;height:100%;z-index:2;cursor:col-resize;background:0 0;touch-action:none}.seed-resizable-panel__handle[data-side=left]{right:0}.seed-resizable-panel__handle[data-side=right]{left:0}.seed-resizable-panel__handle::before{content:\"\";position:absolute;top:0;width:1px;height:100%;background:var(--seed-color-stroke-neutral-muted);transition:background-color .16s,height .16s}.seed-resizable-panel__handle[data-side=left]::before{right:0}.seed-resizable-panel__handle[data-side=right]::before{left:0}.seed-resizable-panel__handle:hover::before,.seed-resizable-panel__handle[data-dragging]::before{width:2px;background:#0298d9}.seed-resizable-panel__handle.crisp-seed-resizable-panel-handle--column{display:flex;align-items:center;justify-content:center;width:7px;left:-3px;right:-3px;z-index:1;cursor:col-resize}.seed-resizable-panel__handle.crisp-seed-resizable-panel-handle--column::before{content:none}.seed-resizable-panel__handleInner{width:3px;height:100%;border-radius:3px;background:#0298d9;opacity:0;transition:opacity .1s .1s}.seed-resizable-panel__handle:hover .seed-resizable-panel__handleInner,.seed-resizable-panel__handle[data-dragging] .seed-resizable-panel__handleInner{opacity:1}");
11180
11645
 
11181
11646
  const resizablePanelSlotNames = [
11182
11647
  [
@@ -11202,6 +11667,10 @@ const resizablePanelSlotNames = [
11202
11667
  [
11203
11668
  "handleInner",
11204
11669
  "seed-resizable-panel__handleInner"
11670
+ ],
11671
+ [
11672
+ "backdrop",
11673
+ "seed-resizable-panel__backdrop"
11205
11674
  ]
11206
11675
  ];
11207
11676
  const defaultVariant$1 = {};
@@ -11380,26 +11849,20 @@ Object.assign(resizablePanel, {
11380
11849
  // selector), so this component and that recipe stay in lockstep without a
11381
11850
  // second source of truth for the string.
11382
11851
  const COLUMN_HANDLE_CLASS = "crisp-seed-resizable-panel-handle--column";
11383
- // Kept in lock-step with `resizable-panel.ts`'s own `FLOATING_GUTTER` (a
11384
- // literal there too, for the same reason) -- the floating/inset-card variant
11385
- // needs an outward-facing gutter that DOCKED never reserves. Read on at
11386
- // `spacer`'s own render below for why this can't just be one more CSS rule.
11387
- const FLOATING_GUTTER_PX = 8;
11388
11852
  /**
11389
- * An adjustable-panel primitive: a spacer that reserves layout width plus a
11390
- * panel that overlays it, so it can resize (or animate open/closed) without
11391
- * a reflow glitch on the rest of the layout -- the spacer always reserves
11392
- * the panel's real width, in BOTH the docked and floating visual variants
11393
- * (floating changes only the panel's own look -- gutter, radius, shadow --
11394
- * never its `position`, so a consumer laying this out as a row-flex sibling
11395
- * of e.g. a table sees that table genuinely shrink to make room, matching
11396
- * Attio's own measured peek). Drag the edge handle to resize; constraints
11397
- * are CSS `min-width`/`max-width`, never re-clamped in JS. Ported from
11398
- * your-moon/crisp's `ResizablePanel` (`components/resizablepanel/`);
11399
- * state/drag behavior lives in `useResizablePanel`
11400
- * (`@seed-design/react-resizable-panel`), this component owns the DOM +
11401
- * recipe classes on top of it.
11402
- */ const ResizablePanel = /*#__PURE__*/ React.forwardRef(({ side = "left", width: sizeProp, defaultSize = 280, onWidthChange, min = 200, max, floating = false, handleVariant = "panel", resizable = true, storageKey, animationState, panelElementRef, className, children, ...rest }, forwardedRef)=>{
11853
+ * An adjustable-panel primitive with two layout models (`floating`'s own doc
11854
+ * comment above has the full contrast). Docked: a `spacer` reserves layout
11855
+ * width and the panel overlays exactly that reservation, so a resize drag
11856
+ * never causes a reflow glitch on the rest of the layout. Floating: no
11857
+ * spacer at all -- this component's own outer element becomes a
11858
+ * `position: absolute; inset: 0` overlay reserving zero width, with the
11859
+ * panel itself pinned to one edge and spanning the full height of that box.
11860
+ * Drag the edge handle to resize; constraints are CSS `min-width`/
11861
+ * `max-width`, never re-clamped in JS. Ported from your-moon/crisp's
11862
+ * `ResizablePanel` (`components/resizablepanel/`); state/drag behavior
11863
+ * lives in `useResizablePanel` (`@seed-design/react-resizable-panel`), this
11864
+ * component owns the DOM + recipe classes on top of it.
11865
+ */ const ResizablePanel = /*#__PURE__*/ React.forwardRef(({ side = "left", width: sizeProp, defaultSize = 280, onWidthChange, min = 200, max, floating = false, backdrop = false, onBackdropClick, handleVariant = "panel", resizable = true, storageKey, animationState, panelElementRef, className, children, ...rest }, forwardedRef)=>{
11403
11866
  const { width, dragging, containerRef, panelRef, handleHandlers } = useResizablePanel({
11404
11867
  side,
11405
11868
  size: sizeProp,
@@ -11409,27 +11872,12 @@ const FLOATING_GUTTER_PX = 8;
11409
11872
  });
11410
11873
  const composedPanelRef = useComposedRefs(panelRef, panelElementRef);
11411
11874
  const classNames = resizablePanel();
11412
- // The floating/inset-card variant sits INSET from `root`'s own outward
11413
- // edge by `FLOATING_GUTTER_PX`, flush against the inward edge (Attio's
11414
- // own peek: no gap on the side facing the table it shrinks). `width`
11415
- // (the prop a consumer sets, e.g. `RecordPreview`'s own 497px default)
11416
- // is the RESERVED FOOTPRINT -- what the table beside it actually gives
11417
- // up -- so the panel's own rendered card shrinks by the gutter INSIDE
11418
- // that footprint, rather than the footprint growing past it: reserving
11419
- // the panel's full width AND adding an outward gutter on top of it
11420
- // over-claims space the surrounding layout was never asked for --
11421
- // confirmed live, it pushed the whole page 8px into horizontal
11422
- // overflow. Docked (no gutter) keeps `width` as both the reservation
11423
- // and the render, as before.
11424
- const panelGutter = floating ? FLOATING_GUTTER_PX : 0;
11875
+ // Floating spans its full container edge to edge -- no gutter, no
11876
+ // shrink -- so the panel's own size style is the SAME reservation a
11877
+ // docked spacer would use; only whether `spacer` itself renders differs
11878
+ // below.
11425
11879
  const maxStyle = max == null ? undefined : typeof max === "number" ? `${max}px` : max;
11426
- const panelMaxStyle = panelGutter === 0 || maxStyle == null ? maxStyle : `calc(${maxStyle} - ${panelGutter}px)`;
11427
11880
  const panelSizeStyle = {
11428
- width: width - panelGutter,
11429
- minWidth: min - panelGutter,
11430
- maxWidth: panelMaxStyle
11431
- };
11432
- const spacerSizeStyle = {
11433
11881
  width,
11434
11882
  minWidth: min,
11435
11883
  maxWidth: maxStyle
@@ -11441,11 +11889,21 @@ const FLOATING_GUTTER_PX = 8;
11441
11889
  else if (forwardedRef) forwardedRef.current = node;
11442
11890
  },
11443
11891
  className: clsx(classNames.root, className),
11892
+ "data-floating": floating ? "true" : "false",
11444
11893
  ...rest,
11445
11894
  children: [
11446
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11895
+ !floating && /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11447
11896
  className: classNames.spacer,
11448
- style: spacerSizeStyle
11897
+ style: {
11898
+ width,
11899
+ minWidth: min,
11900
+ maxWidth: maxStyle
11901
+ }
11902
+ }),
11903
+ floating && backdrop && /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11904
+ className: classNames.backdrop,
11905
+ "data-state": animationState,
11906
+ onClick: onBackdropClick
11449
11907
  }),
11450
11908
  /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11451
11909
  ref: composedPanelRef,
@@ -11477,7 +11935,7 @@ const FLOATING_GUTTER_PX = 8;
11477
11935
  });
11478
11936
  ResizablePanel.displayName = "ResizablePanel";
11479
11937
 
11480
- __insertCSS(".seed-record-preview__root{display:flex;flex-direction:column;height:100%;overflow:hidden;background:var(--seed-color-bg-layer-default);font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.seed-record-preview__toolbar{display:flex;align-items:center;height:49px;flex-shrink:0;padding:8px 10px;border-bottom:1px solid var(--seed-color-stroke-neutral-muted)}.seed-record-preview__toolbarSpacer{flex:1}.seed-record-preview__pager{display:flex;align-items:center;gap:6px;margin-inline-start:12px}.seed-record-preview__counter{margin-inline-start:12px;font-size:12px;font-weight:500;color:var(--seed-color-fg-neutral-subtle);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.seed-record-preview__iconbtn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;flex-shrink:0;padding:0;border:none;border-radius:6px;background:0 0;color:var(--seed-color-fg-neutral-subtle);font-family:inherit;cursor:pointer;transition:background-color .12s}.seed-record-preview__iconbtn:hover{background:var(--seed-color-bg-layer-fill);color:var(--seed-color-fg-neutral)}.seed-record-preview__navbtn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;flex-shrink:0;padding:0;border:none;border-radius:6px;background:var(--seed-color-bg-layer-default);box-shadow:inset 0 0 0 1px rgba(0,0,0,.06),0 0 2px rgba(28,40,64,.18),0 1px 3px rgba(0,0,0,.04);color:var(--seed-color-fg-neutral);font-family:inherit;cursor:pointer}.seed-record-preview__navbtn:hover:not(:disabled){background:var(--seed-color-bg-layer-fill)}.seed-record-preview__navbtn:disabled{opacity:.4;cursor:not-allowed}.seed-record-preview__header{display:flex;align-items:center;gap:8px;height:52px;flex-shrink:0;padding:0 12px}.seed-record-preview__fav{width:22px;height:22px;border-radius:4px;flex-shrink:0;object-fit:cover;background:var(--seed-color-bg-layer-fill)}.seed-record-preview__title{flex:1;min-width:0;font-size:16px;font-weight:600;letter-spacing:-.2px;color:var(--seed-color-fg-neutral);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.seed-record-preview__body{flex:1;min-height:0;overflow-y:auto;padding:8px 12px}.seed-record-preview__section{padding:8px 4px 0}.seed-record-preview__sectionHead{display:flex;align-items:center;justify-content:space-between;gap:8px;height:20px;margin-bottom:8px}.seed-record-preview__sectionLabel{font-size:14px;font-weight:500;color:var(--seed-color-fg-neutral-subtle)}.seed-record-preview__sectionAction{border:none;background:0 0;padding:0;font-family:inherit;font-size:10px;font-weight:400;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent);cursor:pointer}.seed-record-preview__sectionAction:hover{color:var(--seed-color-fg-neutral)}.seed-record-preview__highlights{display:grid;grid-template-columns:1fr 1fr;gap:8px}.seed-record-preview__highlight{display:flex;flex-direction:column;gap:6px;min-width:0}.seed-record-preview__highlightLabel{display:flex;align-items:center;gap:4px;font-size:12px;font-weight:500;color:var(--seed-color-fg-neutral-subtle)}.seed-record-preview__highlightLabel span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.seed-record-preview__highlightLabel svg{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle)}.seed-record-preview__highlightValue{font-size:14px;font-weight:500;color:var(--seed-color-fg-neutral);overflow:hidden;text-overflow:ellipsis}.seed-record-preview__activity{font-size:14px}.seed-record-preview__sections{display:flex;flex-direction:column;padding-top:8px}.seed-record-preview__sectionTrigger{display:flex;align-items:center;gap:4px;width:100%;height:20px;padding:0 8px 0 12px;border:none;background:0 0;font-family:inherit;text-align:start;cursor:pointer}.seed-record-preview__sectionTrigger[data-inert]{cursor:default}.seed-record-preview__sectionRowLabel{font-size:14px;font-weight:500;color:var(--seed-color-fg-neutral)}.seed-record-preview__sectionRowCount{font-size:14px;color:var(--seed-color-fg-neutral-subtle)}.seed-record-preview__sectionChevron{margin-inline-start:auto;flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);transition:transform .15s ease}.seed-record-preview__sectionChevron[data-open]{transform:rotate(180deg)}.seed-record-preview__sectionContent{padding:8px 8px 8px 12px}.seed-record-preview__footer{flex-shrink:0;display:flex;align-items:center;gap:8px;padding:8px 12px;border-top:1px solid var(--seed-color-stroke-neutral-muted)}");
11938
+ __insertCSS(".seed-record-preview__root{display:flex;flex-direction:column;height:100%;overflow:hidden;background:var(--seed-color-bg-layer-default);font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}.seed-record-preview__toolbar{display:flex;align-items:center;height:49px;flex-shrink:0;padding:8px 10px;border-bottom:1px solid var(--seed-color-stroke-neutral-muted)}.seed-record-preview__toolbarSpacer{flex:1}.seed-record-preview__pager{display:flex;align-items:center;gap:6px;margin-inline-start:12px}.seed-record-preview__counter{margin-inline-start:12px;font-size:12px;font-weight:500;color:var(--seed-color-fg-neutral-subtle);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.seed-record-preview__iconbtn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;flex-shrink:0;padding:0;border:none;border-radius:6px;background:0 0;color:var(--seed-color-fg-neutral-subtle);font-family:inherit;cursor:pointer;transition:background-color .12s}.seed-record-preview__iconbtn:hover{background:var(--seed-color-bg-layer-fill);color:var(--seed-color-fg-neutral)}.seed-record-preview__navbtn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;flex-shrink:0;padding:0;border:none;border-radius:6px;background:var(--seed-color-bg-layer-default);box-shadow:inset 0 0 0 1px rgba(0,0,0,.06),0 0 2px rgba(28,40,64,.18),0 1px 3px rgba(0,0,0,.04);color:var(--seed-color-fg-neutral);font-family:inherit;cursor:pointer}.seed-record-preview__navbtn:hover:not(:disabled){background:var(--seed-color-bg-layer-fill)}.seed-record-preview__navbtn:disabled{opacity:.4;cursor:not-allowed}.seed-record-preview__header{display:flex;align-items:center;gap:8px;height:52px;flex-shrink:0;padding:0 12px}.seed-record-preview__fav{width:22px;height:22px;border-radius:4px;flex-shrink:0;object-fit:cover;background:var(--seed-color-bg-layer-fill)}.seed-record-preview__title{flex:1;min-width:0;font-size:16px;font-weight:600;letter-spacing:-.2px;color:var(--seed-color-fg-neutral);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.seed-record-preview__body{flex:1;min-height:0;overflow-y:auto;padding:8px 12px}.seed-record-preview__section{padding:8px 4px 0}.seed-record-preview__sectionHead{display:flex;align-items:center;justify-content:space-between;gap:8px;height:20px;margin-bottom:8px}.seed-record-preview__sectionLabel{font-size:14px;font-weight:500;color:var(--seed-color-fg-neutral-subtle)}.seed-record-preview__sectionAction{border:none;background:0 0;padding:0;font-family:inherit;font-size:10px;font-weight:400;color:color-mix(in srgb,var(--seed-color-fg-neutral) 55%,transparent);cursor:pointer}.seed-record-preview__sectionAction:hover{color:var(--seed-color-fg-neutral)}.seed-record-preview__highlights{display:grid;grid-template-columns:1fr 1fr;gap:8px}.seed-record-preview__highlight{display:flex;flex-direction:column;gap:6px;min-width:0}.seed-record-preview__highlightLabel{display:flex;align-items:center;gap:4px;font-size:12px;font-weight:500;color:var(--seed-color-fg-neutral-subtle)}.seed-record-preview__highlightLabel span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.seed-record-preview__highlightLabel svg{flex-shrink:0;color:var(--seed-color-fg-neutral-subtle)}.seed-record-preview__highlightValue{font-size:14px;font-weight:500;color:var(--seed-color-fg-neutral);overflow:hidden;text-overflow:ellipsis}.seed-record-preview__activity{font-size:14px}.seed-record-preview__sections{display:flex;flex-direction:column;padding-top:8px}.seed-record-preview__sectionTrigger{display:flex;align-items:center;gap:4px;width:100%;height:20px;padding:0 8px 0 12px;border:none;background:0 0;font-family:inherit;text-align:start;cursor:pointer}.seed-record-preview__sectionTrigger[data-inert]{cursor:default}.seed-record-preview__sectionRowLabel{font-size:14px;font-weight:500;color:var(--seed-color-fg-neutral)}.seed-record-preview__sectionRowCount{font-size:14px;color:var(--seed-color-fg-neutral-subtle)}.seed-record-preview__sectionChevron{margin-inline-start:auto;flex-shrink:0;color:var(--seed-color-fg-neutral-subtle);transition:transform .15s ease}.seed-record-preview__sectionChevron[data-open]{transform:rotate(180deg)}.seed-record-preview__sectionContent{padding:8px 8px 8px 12px}.seed-record-preview__footer{flex-shrink:0;display:flex;align-items:center;gap:8px;padding:8px 12px;border-top:1px solid var(--seed-color-stroke-neutral-muted)}.seed-record-preview__tabsRoot{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.seed-record-preview__tabPanel{flex:1;min-height:0;overflow-y:auto;padding:8px 12px}.seed-record-preview__tabBadge{margin-inline-start:6px}");
11481
11939
 
11482
11940
  const recordPreviewSlotNames = [
11483
11941
  [
@@ -11587,6 +12045,18 @@ const recordPreviewSlotNames = [
11587
12045
  [
11588
12046
  "footer",
11589
12047
  "seed-record-preview__footer"
12048
+ ],
12049
+ [
12050
+ "tabsRoot",
12051
+ "seed-record-preview__tabsRoot"
12052
+ ],
12053
+ [
12054
+ "tabPanel",
12055
+ "seed-record-preview__tabPanel"
12056
+ ],
12057
+ [
12058
+ "tabBadge",
12059
+ "seed-record-preview__tabBadge"
11590
12060
  ]
11591
12061
  ];
11592
12062
  const defaultVariant = {};
@@ -11748,30 +12218,2684 @@ function useStateMachine(initialState, machine) {
11748
12218
  const PresenceContext = /*#__PURE__*/ React.createContext(null);
11749
12219
  PresenceContext.Provider;
11750
12220
 
11751
- const RESIZE_MIN_WIDTH = 360;
11752
- const RESIZE_MAX_WIDTH = 720;
11753
- /**
11754
- * Right-side record peek panel -- Attio's own peek shows a Highlights card
11755
- * area and an Activity feed (measured live). Ported from your-moon/crisp's
11756
- * `RecordPanel` (`product/recordpanel/`), built ON `ResizablePanel`
11757
- * (`side="right" floating`) rather than crisp's own duplicate absolute-
11758
- * positioning/slide-in/resize-handle implementation -- the outer shell (the
11759
- * enter/exit keyframe animation, the floating elevation, the drag handle
11760
- * itself) all come from that primitive; this component only renders the
11761
- * CONTENT inside it.
11762
- *
11763
- * Enter/exit: closing this panel is a state change (`open` -> false) that
11764
- * typically clears `record` in the SAME update (see the DataTable story's
11765
- * own `onClose={() => setPreviewRow(null)}`), so by the time an exit
11766
- * animation should play, the values this component would render are
11767
- * already gone. `usePresence` (`@seed-design/react-presence`, the same
11768
- * idiom Dialog/Drawer already use for their own floating layers) keeps the
11769
- * tree mounted for exactly the exit animation's duration; the snapshot
11770
- * below keeps the LAST visible props around for that same window, since
11771
- * without it the panel would animate out empty.
11772
- */ function RecordPreview(props) {
11773
- const shouldRender = props.open && props.record != null;
11774
- const { isPresent, ref: panelElementRef } = usePresence(shouldRender);
12221
+ var __defProp$1 = Object.defineProperty;
12222
+ var __name$1 = (target, value)=>__defProp$1(target, "name", {
12223
+ value,
12224
+ configurable: true
12225
+ });
12226
+ function useSize(element) {
12227
+ const [size, setSize] = React__namespace.useState(void 0);
12228
+ useLayoutEffect2(()=>{
12229
+ if (element) {
12230
+ setSize({
12231
+ width: element.offsetWidth,
12232
+ height: element.offsetHeight
12233
+ });
12234
+ const resizeObserver = new ResizeObserver((entries)=>{
12235
+ if (!Array.isArray(entries)) {
12236
+ return;
12237
+ }
12238
+ if (!entries.length) {
12239
+ return;
12240
+ }
12241
+ const entry = entries[0];
12242
+ let width;
12243
+ let height;
12244
+ if ("borderBoxSize" in entry) {
12245
+ const borderSizeEntry = entry["borderBoxSize"];
12246
+ const borderSize = Array.isArray(borderSizeEntry) ? borderSizeEntry[0] : borderSizeEntry;
12247
+ width = borderSize["inlineSize"];
12248
+ height = borderSize["blockSize"];
12249
+ } else {
12250
+ width = element.offsetWidth;
12251
+ height = element.offsetHeight;
12252
+ }
12253
+ setSize({
12254
+ width,
12255
+ height
12256
+ });
12257
+ });
12258
+ resizeObserver.observe(element, {
12259
+ box: "border-box"
12260
+ });
12261
+ return ()=>resizeObserver.unobserve(element);
12262
+ } else {
12263
+ setSize(void 0);
12264
+ }
12265
+ }, [
12266
+ element
12267
+ ]);
12268
+ return size;
12269
+ }
12270
+ __name$1(useSize, "useSize");
12271
+
12272
+ function useSupports(selector) {
12273
+ const [isSupported, setIsSupported] = React.useState(null);
12274
+ React.useEffect(()=>{
12275
+ setIsSupported(CSS.supports(selector));
12276
+ }, [
12277
+ selector
12278
+ ]);
12279
+ return isSupported;
12280
+ }
12281
+
12282
+ var __defProp = Object.defineProperty;
12283
+ var __name = (target, value)=>__defProp(target, "name", {
12284
+ value,
12285
+ configurable: true
12286
+ });
12287
+ function useCallbackRef(callback) {
12288
+ const callbackRef = React__namespace.useRef(callback);
12289
+ React__namespace.useEffect(()=>{
12290
+ callbackRef.current = callback;
12291
+ });
12292
+ return React__namespace.useMemo(()=>(...args)=>callbackRef.current?.(...args), []);
12293
+ }
12294
+ __name(useCallbackRef, "useCallbackRef");
12295
+
12296
+ function AutoHeight(userOptions = {}) {
12297
+ let emblaApi;
12298
+ let slideHeights = [];
12299
+ const heightEvents = [
12300
+ 'select',
12301
+ 'slideFocus'
12302
+ ];
12303
+ function init(emblaApiInstance) {
12304
+ emblaApi = emblaApiInstance;
12305
+ const { options: { axis }, slideRects } = emblaApi.internalEngine();
12306
+ if (axis === 'y') return;
12307
+ slideHeights = slideRects.map((slideRect)=>slideRect.height);
12308
+ heightEvents.forEach((evt)=>emblaApi.on(evt, setContainerHeight));
12309
+ setContainerHeight();
12310
+ }
12311
+ function destroy() {
12312
+ heightEvents.forEach((evt)=>emblaApi.off(evt, setContainerHeight));
12313
+ const container = emblaApi.containerNode();
12314
+ container.style.height = '';
12315
+ if (!container.getAttribute('style')) container.removeAttribute('style');
12316
+ }
12317
+ function highestInView() {
12318
+ const { slideRegistry } = emblaApi.internalEngine();
12319
+ const selectedIndexes = slideRegistry[emblaApi.selectedScrollSnap()];
12320
+ if (!selectedIndexes) return null;
12321
+ return selectedIndexes.map((index)=>slideHeights[index]).reduce((a, b)=>Math.max(a, b), 0);
12322
+ }
12323
+ function setContainerHeight() {
12324
+ const height = highestInView();
12325
+ if (height === null) return;
12326
+ emblaApi.containerNode().style.height = `${highestInView()}px`;
12327
+ }
12328
+ const self = {
12329
+ name: 'autoHeight',
12330
+ options: userOptions,
12331
+ init,
12332
+ destroy
12333
+ };
12334
+ return self;
12335
+ }
12336
+ AutoHeight.globalOptions = undefined;
12337
+
12338
+ function isObject$1(subject) {
12339
+ return Object.prototype.toString.call(subject) === '[object Object]';
12340
+ }
12341
+ function isRecord(subject) {
12342
+ return isObject$1(subject) || Array.isArray(subject);
12343
+ }
12344
+ function canUseDOM() {
12345
+ return !!(typeof window !== 'undefined' && window.document && window.document.createElement);
12346
+ }
12347
+ function areOptionsEqual(optionsA, optionsB) {
12348
+ const optionsAKeys = Object.keys(optionsA);
12349
+ const optionsBKeys = Object.keys(optionsB);
12350
+ if (optionsAKeys.length !== optionsBKeys.length) return false;
12351
+ const breakpointsA = JSON.stringify(Object.keys(optionsA.breakpoints || {}));
12352
+ const breakpointsB = JSON.stringify(Object.keys(optionsB.breakpoints || {}));
12353
+ if (breakpointsA !== breakpointsB) return false;
12354
+ return optionsAKeys.every((key)=>{
12355
+ const valueA = optionsA[key];
12356
+ const valueB = optionsB[key];
12357
+ if (typeof valueA === 'function') return `${valueA}` === `${valueB}`;
12358
+ if (!isRecord(valueA) || !isRecord(valueB)) return valueA === valueB;
12359
+ return areOptionsEqual(valueA, valueB);
12360
+ });
12361
+ }
12362
+ function sortAndMapPluginToOptions(plugins) {
12363
+ return plugins.concat().sort((a, b)=>a.name > b.name ? 1 : -1).map((plugin)=>plugin.options);
12364
+ }
12365
+ function arePluginsEqual(pluginsA, pluginsB) {
12366
+ if (pluginsA.length !== pluginsB.length) return false;
12367
+ const optionsA = sortAndMapPluginToOptions(pluginsA);
12368
+ const optionsB = sortAndMapPluginToOptions(pluginsB);
12369
+ return optionsA.every((optionA, index)=>{
12370
+ const optionB = optionsB[index];
12371
+ return areOptionsEqual(optionA, optionB);
12372
+ });
12373
+ }
12374
+
12375
+ function isNumber(subject) {
12376
+ return typeof subject === 'number';
12377
+ }
12378
+ function isString(subject) {
12379
+ return typeof subject === 'string';
12380
+ }
12381
+ function isBoolean(subject) {
12382
+ return typeof subject === 'boolean';
12383
+ }
12384
+ function isObject(subject) {
12385
+ return Object.prototype.toString.call(subject) === '[object Object]';
12386
+ }
12387
+ function mathAbs(n) {
12388
+ return Math.abs(n);
12389
+ }
12390
+ function mathSign(n) {
12391
+ return Math.sign(n);
12392
+ }
12393
+ function deltaAbs(valueB, valueA) {
12394
+ return mathAbs(valueB - valueA);
12395
+ }
12396
+ function factorAbs(valueB, valueA) {
12397
+ if (valueB === 0 || valueA === 0) return 0;
12398
+ if (mathAbs(valueB) <= mathAbs(valueA)) return 0;
12399
+ const diff = deltaAbs(mathAbs(valueB), mathAbs(valueA));
12400
+ return mathAbs(diff / valueB);
12401
+ }
12402
+ function roundToTwoDecimals(num) {
12403
+ return Math.round(num * 100) / 100;
12404
+ }
12405
+ function arrayKeys(array) {
12406
+ return objectKeys(array).map(Number);
12407
+ }
12408
+ function arrayLast(array) {
12409
+ return array[arrayLastIndex(array)];
12410
+ }
12411
+ function arrayLastIndex(array) {
12412
+ return Math.max(0, array.length - 1);
12413
+ }
12414
+ function arrayIsLastIndex(array, index) {
12415
+ return index === arrayLastIndex(array);
12416
+ }
12417
+ function arrayFromNumber(n, startAt = 0) {
12418
+ return Array.from(Array(n), (_, i)=>startAt + i);
12419
+ }
12420
+ function objectKeys(object) {
12421
+ return Object.keys(object);
12422
+ }
12423
+ function objectsMergeDeep(objectA, objectB) {
12424
+ return [
12425
+ objectA,
12426
+ objectB
12427
+ ].reduce((mergedObjects, currentObject)=>{
12428
+ objectKeys(currentObject).forEach((key)=>{
12429
+ const valueA = mergedObjects[key];
12430
+ const valueB = currentObject[key];
12431
+ const areObjects = isObject(valueA) && isObject(valueB);
12432
+ mergedObjects[key] = areObjects ? objectsMergeDeep(valueA, valueB) : valueB;
12433
+ });
12434
+ return mergedObjects;
12435
+ }, {});
12436
+ }
12437
+ function isMouseEvent(evt, ownerWindow) {
12438
+ return typeof ownerWindow.MouseEvent !== 'undefined' && evt instanceof ownerWindow.MouseEvent;
12439
+ }
12440
+ function Alignment(align, viewSize) {
12441
+ const predefined = {
12442
+ start,
12443
+ center,
12444
+ end
12445
+ };
12446
+ function start() {
12447
+ return 0;
12448
+ }
12449
+ function center(n) {
12450
+ return end(n) / 2;
12451
+ }
12452
+ function end(n) {
12453
+ return viewSize - n;
12454
+ }
12455
+ function measure(n, index) {
12456
+ if (isString(align)) return predefined[align](n);
12457
+ return align(viewSize, n, index);
12458
+ }
12459
+ const self = {
12460
+ measure
12461
+ };
12462
+ return self;
12463
+ }
12464
+ function EventStore() {
12465
+ let listeners = [];
12466
+ function add(node, type, handler, options = {
12467
+ passive: true
12468
+ }) {
12469
+ let removeListener;
12470
+ if ('addEventListener' in node) {
12471
+ node.addEventListener(type, handler, options);
12472
+ removeListener = ()=>node.removeEventListener(type, handler, options);
12473
+ } else {
12474
+ const legacyMediaQueryList = node;
12475
+ legacyMediaQueryList.addListener(handler);
12476
+ removeListener = ()=>legacyMediaQueryList.removeListener(handler);
12477
+ }
12478
+ listeners.push(removeListener);
12479
+ return self;
12480
+ }
12481
+ function clear() {
12482
+ listeners = listeners.filter((remove)=>remove());
12483
+ }
12484
+ const self = {
12485
+ add,
12486
+ clear
12487
+ };
12488
+ return self;
12489
+ }
12490
+ function Animations(ownerDocument, ownerWindow, update, render) {
12491
+ const documentVisibleHandler = EventStore();
12492
+ const fixedTimeStep = 1000 / 60;
12493
+ let lastTimeStamp = null;
12494
+ let accumulatedTime = 0;
12495
+ let animationId = 0;
12496
+ function init() {
12497
+ documentVisibleHandler.add(ownerDocument, 'visibilitychange', ()=>{
12498
+ if (ownerDocument.hidden) reset();
12499
+ });
12500
+ }
12501
+ function destroy() {
12502
+ stop();
12503
+ documentVisibleHandler.clear();
12504
+ }
12505
+ function animate(timeStamp) {
12506
+ if (!animationId) return;
12507
+ if (!lastTimeStamp) {
12508
+ lastTimeStamp = timeStamp;
12509
+ update();
12510
+ update();
12511
+ }
12512
+ const timeElapsed = timeStamp - lastTimeStamp;
12513
+ lastTimeStamp = timeStamp;
12514
+ accumulatedTime += timeElapsed;
12515
+ while(accumulatedTime >= fixedTimeStep){
12516
+ update();
12517
+ accumulatedTime -= fixedTimeStep;
12518
+ }
12519
+ const alpha = accumulatedTime / fixedTimeStep;
12520
+ render(alpha);
12521
+ if (animationId) {
12522
+ animationId = ownerWindow.requestAnimationFrame(animate);
12523
+ }
12524
+ }
12525
+ function start() {
12526
+ if (animationId) return;
12527
+ animationId = ownerWindow.requestAnimationFrame(animate);
12528
+ }
12529
+ function stop() {
12530
+ ownerWindow.cancelAnimationFrame(animationId);
12531
+ lastTimeStamp = null;
12532
+ accumulatedTime = 0;
12533
+ animationId = 0;
12534
+ }
12535
+ function reset() {
12536
+ lastTimeStamp = null;
12537
+ accumulatedTime = 0;
12538
+ }
12539
+ const self = {
12540
+ init,
12541
+ destroy,
12542
+ start,
12543
+ stop,
12544
+ update,
12545
+ render
12546
+ };
12547
+ return self;
12548
+ }
12549
+ function Axis(axis, contentDirection) {
12550
+ const isRightToLeft = contentDirection === 'rtl';
12551
+ const isVertical = axis === 'y';
12552
+ const scroll = isVertical ? 'y' : 'x';
12553
+ const cross = isVertical ? 'x' : 'y';
12554
+ const sign = !isVertical && isRightToLeft ? -1 : 1;
12555
+ const startEdge = getStartEdge();
12556
+ const endEdge = getEndEdge();
12557
+ function measureSize(nodeRect) {
12558
+ const { height, width } = nodeRect;
12559
+ return isVertical ? height : width;
12560
+ }
12561
+ function getStartEdge() {
12562
+ if (isVertical) return 'top';
12563
+ return isRightToLeft ? 'right' : 'left';
12564
+ }
12565
+ function getEndEdge() {
12566
+ if (isVertical) return 'bottom';
12567
+ return isRightToLeft ? 'left' : 'right';
12568
+ }
12569
+ function direction(n) {
12570
+ return n * sign;
12571
+ }
12572
+ const self = {
12573
+ scroll,
12574
+ cross,
12575
+ startEdge,
12576
+ endEdge,
12577
+ measureSize,
12578
+ direction
12579
+ };
12580
+ return self;
12581
+ }
12582
+ function Limit(min = 0, max = 0) {
12583
+ const length = mathAbs(min - max);
12584
+ function reachedMin(n) {
12585
+ return n < min;
12586
+ }
12587
+ function reachedMax(n) {
12588
+ return n > max;
12589
+ }
12590
+ function reachedAny(n) {
12591
+ return reachedMin(n) || reachedMax(n);
12592
+ }
12593
+ function constrain(n) {
12594
+ if (!reachedAny(n)) return n;
12595
+ return reachedMin(n) ? min : max;
12596
+ }
12597
+ function removeOffset(n) {
12598
+ if (!length) return n;
12599
+ return n - length * Math.ceil((n - max) / length);
12600
+ }
12601
+ const self = {
12602
+ length,
12603
+ max,
12604
+ min,
12605
+ constrain,
12606
+ reachedAny,
12607
+ reachedMax,
12608
+ reachedMin,
12609
+ removeOffset
12610
+ };
12611
+ return self;
12612
+ }
12613
+ function Counter(max, start, loop) {
12614
+ const { constrain } = Limit(0, max);
12615
+ const loopEnd = max + 1;
12616
+ let counter = withinLimit(start);
12617
+ function withinLimit(n) {
12618
+ return !loop ? constrain(n) : mathAbs((loopEnd + n) % loopEnd);
12619
+ }
12620
+ function get() {
12621
+ return counter;
12622
+ }
12623
+ function set(n) {
12624
+ counter = withinLimit(n);
12625
+ return self;
12626
+ }
12627
+ function add(n) {
12628
+ return clone().set(get() + n);
12629
+ }
12630
+ function clone() {
12631
+ return Counter(max, get(), loop);
12632
+ }
12633
+ const self = {
12634
+ get,
12635
+ set,
12636
+ add,
12637
+ clone
12638
+ };
12639
+ return self;
12640
+ }
12641
+ function DragHandler(axis, rootNode, ownerDocument, ownerWindow, target, dragTracker, location, animation, scrollTo, scrollBody, scrollTarget, index, eventHandler, percentOfView, dragFree, dragThreshold, skipSnaps, baseFriction, watchDrag) {
12642
+ const { cross: crossAxis, direction } = axis;
12643
+ const focusNodes = [
12644
+ 'INPUT',
12645
+ 'SELECT',
12646
+ 'TEXTAREA'
12647
+ ];
12648
+ const nonPassiveEvent = {
12649
+ passive: false
12650
+ };
12651
+ const initEvents = EventStore();
12652
+ const dragEvents = EventStore();
12653
+ const goToNextThreshold = Limit(50, 225).constrain(percentOfView.measure(20));
12654
+ const snapForceBoost = {
12655
+ mouse: 300,
12656
+ touch: 400
12657
+ };
12658
+ const freeForceBoost = {
12659
+ mouse: 500,
12660
+ touch: 600
12661
+ };
12662
+ const baseSpeed = dragFree ? 43 : 25;
12663
+ let isMoving = false;
12664
+ let startScroll = 0;
12665
+ let startCross = 0;
12666
+ let pointerIsDown = false;
12667
+ let preventScroll = false;
12668
+ let preventClick = false;
12669
+ let isMouse = false;
12670
+ function init(emblaApi) {
12671
+ if (!watchDrag) return;
12672
+ function downIfAllowed(evt) {
12673
+ if (isBoolean(watchDrag) || watchDrag(emblaApi, evt)) down(evt);
12674
+ }
12675
+ const node = rootNode;
12676
+ initEvents.add(node, 'dragstart', (evt)=>evt.preventDefault(), nonPassiveEvent).add(node, 'touchmove', ()=>undefined, nonPassiveEvent).add(node, 'touchend', ()=>undefined).add(node, 'touchstart', downIfAllowed).add(node, 'mousedown', downIfAllowed).add(node, 'touchcancel', up).add(node, 'contextmenu', up).add(node, 'click', click, true);
12677
+ }
12678
+ function destroy() {
12679
+ initEvents.clear();
12680
+ dragEvents.clear();
12681
+ }
12682
+ function addDragEvents() {
12683
+ const node = isMouse ? ownerDocument : rootNode;
12684
+ dragEvents.add(node, 'touchmove', move, nonPassiveEvent).add(node, 'touchend', up).add(node, 'mousemove', move, nonPassiveEvent).add(node, 'mouseup', up);
12685
+ }
12686
+ function isFocusNode(node) {
12687
+ const nodeName = node.nodeName || '';
12688
+ return focusNodes.includes(nodeName);
12689
+ }
12690
+ function forceBoost() {
12691
+ const boost = dragFree ? freeForceBoost : snapForceBoost;
12692
+ const type = isMouse ? 'mouse' : 'touch';
12693
+ return boost[type];
12694
+ }
12695
+ function allowedForce(force, targetChanged) {
12696
+ const next = index.add(mathSign(force) * -1);
12697
+ const baseForce = scrollTarget.byDistance(force, !dragFree).distance;
12698
+ if (dragFree || mathAbs(force) < goToNextThreshold) return baseForce;
12699
+ if (skipSnaps && targetChanged) return baseForce * 0.5;
12700
+ return scrollTarget.byIndex(next.get(), 0).distance;
12701
+ }
12702
+ function down(evt) {
12703
+ const isMouseEvt = isMouseEvent(evt, ownerWindow);
12704
+ isMouse = isMouseEvt;
12705
+ preventClick = dragFree && isMouseEvt && !evt.buttons && isMoving;
12706
+ isMoving = deltaAbs(target.get(), location.get()) >= 2;
12707
+ if (isMouseEvt && evt.button !== 0) return;
12708
+ if (isFocusNode(evt.target)) return;
12709
+ pointerIsDown = true;
12710
+ dragTracker.pointerDown(evt);
12711
+ scrollBody.useFriction(0).useDuration(0);
12712
+ target.set(location);
12713
+ addDragEvents();
12714
+ startScroll = dragTracker.readPoint(evt);
12715
+ startCross = dragTracker.readPoint(evt, crossAxis);
12716
+ eventHandler.emit('pointerDown');
12717
+ }
12718
+ function move(evt) {
12719
+ const isTouchEvt = !isMouseEvent(evt, ownerWindow);
12720
+ if (isTouchEvt && evt.touches.length >= 2) return up(evt);
12721
+ const lastScroll = dragTracker.readPoint(evt);
12722
+ const lastCross = dragTracker.readPoint(evt, crossAxis);
12723
+ const diffScroll = deltaAbs(lastScroll, startScroll);
12724
+ const diffCross = deltaAbs(lastCross, startCross);
12725
+ if (!preventScroll && !isMouse) {
12726
+ if (!evt.cancelable) return up(evt);
12727
+ preventScroll = diffScroll > diffCross;
12728
+ if (!preventScroll) return up(evt);
12729
+ }
12730
+ const diff = dragTracker.pointerMove(evt);
12731
+ if (diffScroll > dragThreshold) preventClick = true;
12732
+ scrollBody.useFriction(0.3).useDuration(0.75);
12733
+ animation.start();
12734
+ target.add(direction(diff));
12735
+ evt.preventDefault();
12736
+ }
12737
+ function up(evt) {
12738
+ const currentLocation = scrollTarget.byDistance(0, false);
12739
+ const targetChanged = currentLocation.index !== index.get();
12740
+ const rawForce = dragTracker.pointerUp(evt) * forceBoost();
12741
+ const force = allowedForce(direction(rawForce), targetChanged);
12742
+ const forceFactor = factorAbs(rawForce, force);
12743
+ const speed = baseSpeed - 10 * forceFactor;
12744
+ const friction = baseFriction + forceFactor / 50;
12745
+ preventScroll = false;
12746
+ pointerIsDown = false;
12747
+ dragEvents.clear();
12748
+ scrollBody.useDuration(speed).useFriction(friction);
12749
+ scrollTo.distance(force, !dragFree);
12750
+ isMouse = false;
12751
+ eventHandler.emit('pointerUp');
12752
+ }
12753
+ function click(evt) {
12754
+ if (preventClick) {
12755
+ evt.stopPropagation();
12756
+ evt.preventDefault();
12757
+ preventClick = false;
12758
+ }
12759
+ }
12760
+ function pointerDown() {
12761
+ return pointerIsDown;
12762
+ }
12763
+ const self = {
12764
+ init,
12765
+ destroy,
12766
+ pointerDown
12767
+ };
12768
+ return self;
12769
+ }
12770
+ function DragTracker(axis, ownerWindow) {
12771
+ const logInterval = 170;
12772
+ let startEvent;
12773
+ let lastEvent;
12774
+ function readTime(evt) {
12775
+ return evt.timeStamp;
12776
+ }
12777
+ function readPoint(evt, evtAxis) {
12778
+ const property = evtAxis || axis.scroll;
12779
+ const coord = `client${property === 'x' ? 'X' : 'Y'}`;
12780
+ return (isMouseEvent(evt, ownerWindow) ? evt : evt.touches[0])[coord];
12781
+ }
12782
+ function pointerDown(evt) {
12783
+ startEvent = evt;
12784
+ lastEvent = evt;
12785
+ return readPoint(evt);
12786
+ }
12787
+ function pointerMove(evt) {
12788
+ const diff = readPoint(evt) - readPoint(lastEvent);
12789
+ const expired = readTime(evt) - readTime(startEvent) > logInterval;
12790
+ lastEvent = evt;
12791
+ if (expired) startEvent = evt;
12792
+ return diff;
12793
+ }
12794
+ function pointerUp(evt) {
12795
+ if (!startEvent || !lastEvent) return 0;
12796
+ const diffDrag = readPoint(lastEvent) - readPoint(startEvent);
12797
+ const diffTime = readTime(evt) - readTime(startEvent);
12798
+ const expired = readTime(evt) - readTime(lastEvent) > logInterval;
12799
+ const force = diffDrag / diffTime;
12800
+ const isFlick = diffTime && !expired && mathAbs(force) > 0.1;
12801
+ return isFlick ? force : 0;
12802
+ }
12803
+ const self = {
12804
+ pointerDown,
12805
+ pointerMove,
12806
+ pointerUp,
12807
+ readPoint
12808
+ };
12809
+ return self;
12810
+ }
12811
+ function NodeRects() {
12812
+ function measure(node) {
12813
+ const { offsetTop, offsetLeft, offsetWidth, offsetHeight } = node;
12814
+ const offset = {
12815
+ top: offsetTop,
12816
+ right: offsetLeft + offsetWidth,
12817
+ bottom: offsetTop + offsetHeight,
12818
+ left: offsetLeft,
12819
+ width: offsetWidth,
12820
+ height: offsetHeight
12821
+ };
12822
+ return offset;
12823
+ }
12824
+ const self = {
12825
+ measure
12826
+ };
12827
+ return self;
12828
+ }
12829
+ function PercentOfView(viewSize) {
12830
+ function measure(n) {
12831
+ return viewSize * (n / 100);
12832
+ }
12833
+ const self = {
12834
+ measure
12835
+ };
12836
+ return self;
12837
+ }
12838
+ function ResizeHandler(container, eventHandler, ownerWindow, slides, axis, watchResize, nodeRects) {
12839
+ const observeNodes = [
12840
+ container
12841
+ ].concat(slides);
12842
+ let resizeObserver;
12843
+ let containerSize;
12844
+ let slideSizes = [];
12845
+ let destroyed = false;
12846
+ function readSize(node) {
12847
+ return axis.measureSize(nodeRects.measure(node));
12848
+ }
12849
+ function init(emblaApi) {
12850
+ if (!watchResize) return;
12851
+ containerSize = readSize(container);
12852
+ slideSizes = slides.map(readSize);
12853
+ function defaultCallback(entries) {
12854
+ for (const entry of entries){
12855
+ if (destroyed) return;
12856
+ const isContainer = entry.target === container;
12857
+ const slideIndex = slides.indexOf(entry.target);
12858
+ const lastSize = isContainer ? containerSize : slideSizes[slideIndex];
12859
+ const newSize = readSize(isContainer ? container : slides[slideIndex]);
12860
+ const diffSize = mathAbs(newSize - lastSize);
12861
+ if (diffSize >= 0.5) {
12862
+ emblaApi.reInit();
12863
+ eventHandler.emit('resize');
12864
+ break;
12865
+ }
12866
+ }
12867
+ }
12868
+ resizeObserver = new ResizeObserver((entries)=>{
12869
+ if (isBoolean(watchResize) || watchResize(emblaApi, entries)) {
12870
+ defaultCallback(entries);
12871
+ }
12872
+ });
12873
+ ownerWindow.requestAnimationFrame(()=>{
12874
+ observeNodes.forEach((node)=>resizeObserver.observe(node));
12875
+ });
12876
+ }
12877
+ function destroy() {
12878
+ destroyed = true;
12879
+ if (resizeObserver) resizeObserver.disconnect();
12880
+ }
12881
+ const self = {
12882
+ init,
12883
+ destroy
12884
+ };
12885
+ return self;
12886
+ }
12887
+ function ScrollBody(location, offsetLocation, previousLocation, target, baseDuration, baseFriction) {
12888
+ let scrollVelocity = 0;
12889
+ let scrollDirection = 0;
12890
+ let scrollDuration = baseDuration;
12891
+ let scrollFriction = baseFriction;
12892
+ let rawLocation = location.get();
12893
+ let rawLocationPrevious = 0;
12894
+ function seek() {
12895
+ const displacement = target.get() - location.get();
12896
+ const isInstant = !scrollDuration;
12897
+ let scrollDistance = 0;
12898
+ if (isInstant) {
12899
+ scrollVelocity = 0;
12900
+ previousLocation.set(target);
12901
+ location.set(target);
12902
+ scrollDistance = displacement;
12903
+ } else {
12904
+ previousLocation.set(location);
12905
+ scrollVelocity += displacement / scrollDuration;
12906
+ scrollVelocity *= scrollFriction;
12907
+ rawLocation += scrollVelocity;
12908
+ location.add(scrollVelocity);
12909
+ scrollDistance = rawLocation - rawLocationPrevious;
12910
+ }
12911
+ scrollDirection = mathSign(scrollDistance);
12912
+ rawLocationPrevious = rawLocation;
12913
+ return self;
12914
+ }
12915
+ function settled() {
12916
+ const diff = target.get() - offsetLocation.get();
12917
+ return mathAbs(diff) < 0.001;
12918
+ }
12919
+ function duration() {
12920
+ return scrollDuration;
12921
+ }
12922
+ function direction() {
12923
+ return scrollDirection;
12924
+ }
12925
+ function velocity() {
12926
+ return scrollVelocity;
12927
+ }
12928
+ function useBaseDuration() {
12929
+ return useDuration(baseDuration);
12930
+ }
12931
+ function useBaseFriction() {
12932
+ return useFriction(baseFriction);
12933
+ }
12934
+ function useDuration(n) {
12935
+ scrollDuration = n;
12936
+ return self;
12937
+ }
12938
+ function useFriction(n) {
12939
+ scrollFriction = n;
12940
+ return self;
12941
+ }
12942
+ const self = {
12943
+ direction,
12944
+ duration,
12945
+ velocity,
12946
+ seek,
12947
+ settled,
12948
+ useBaseFriction,
12949
+ useBaseDuration,
12950
+ useFriction,
12951
+ useDuration
12952
+ };
12953
+ return self;
12954
+ }
12955
+ function ScrollBounds(limit, location, target, scrollBody, percentOfView) {
12956
+ const pullBackThreshold = percentOfView.measure(10);
12957
+ const edgeOffsetTolerance = percentOfView.measure(50);
12958
+ const frictionLimit = Limit(0.1, 0.99);
12959
+ let disabled = false;
12960
+ function shouldConstrain() {
12961
+ if (disabled) return false;
12962
+ if (!limit.reachedAny(target.get())) return false;
12963
+ if (!limit.reachedAny(location.get())) return false;
12964
+ return true;
12965
+ }
12966
+ function constrain(pointerDown) {
12967
+ if (!shouldConstrain()) return;
12968
+ const edge = limit.reachedMin(location.get()) ? 'min' : 'max';
12969
+ const diffToEdge = mathAbs(limit[edge] - location.get());
12970
+ const diffToTarget = target.get() - location.get();
12971
+ const friction = frictionLimit.constrain(diffToEdge / edgeOffsetTolerance);
12972
+ target.subtract(diffToTarget * friction);
12973
+ if (!pointerDown && mathAbs(diffToTarget) < pullBackThreshold) {
12974
+ target.set(limit.constrain(target.get()));
12975
+ scrollBody.useDuration(25).useBaseFriction();
12976
+ }
12977
+ }
12978
+ function toggleActive(active) {
12979
+ disabled = !active;
12980
+ }
12981
+ const self = {
12982
+ shouldConstrain,
12983
+ constrain,
12984
+ toggleActive
12985
+ };
12986
+ return self;
12987
+ }
12988
+ function ScrollContain(viewSize, contentSize, snapsAligned, containScroll, pixelTolerance) {
12989
+ const scrollBounds = Limit(-contentSize + viewSize, 0);
12990
+ const snapsBounded = measureBounded();
12991
+ const scrollContainLimit = findScrollContainLimit();
12992
+ const snapsContained = measureContained();
12993
+ function usePixelTolerance(bound, snap) {
12994
+ return deltaAbs(bound, snap) <= 1;
12995
+ }
12996
+ function findScrollContainLimit() {
12997
+ const startSnap = snapsBounded[0];
12998
+ const endSnap = arrayLast(snapsBounded);
12999
+ const min = snapsBounded.lastIndexOf(startSnap);
13000
+ const max = snapsBounded.indexOf(endSnap) + 1;
13001
+ return Limit(min, max);
13002
+ }
13003
+ function measureBounded() {
13004
+ return snapsAligned.map((snapAligned, index)=>{
13005
+ const { min, max } = scrollBounds;
13006
+ const snap = scrollBounds.constrain(snapAligned);
13007
+ const isFirst = !index;
13008
+ const isLast = arrayIsLastIndex(snapsAligned, index);
13009
+ if (isFirst) return max;
13010
+ if (isLast) return min;
13011
+ if (usePixelTolerance(min, snap)) return min;
13012
+ if (usePixelTolerance(max, snap)) return max;
13013
+ return snap;
13014
+ }).map((scrollBound)=>parseFloat(scrollBound.toFixed(3)));
13015
+ }
13016
+ function measureContained() {
13017
+ if (contentSize <= viewSize + pixelTolerance) return [
13018
+ scrollBounds.max
13019
+ ];
13020
+ if (containScroll === 'keepSnaps') return snapsBounded;
13021
+ const { min, max } = scrollContainLimit;
13022
+ return snapsBounded.slice(min, max);
13023
+ }
13024
+ const self = {
13025
+ snapsContained,
13026
+ scrollContainLimit
13027
+ };
13028
+ return self;
13029
+ }
13030
+ function ScrollLimit(contentSize, scrollSnaps, loop) {
13031
+ const max = scrollSnaps[0];
13032
+ const min = loop ? max - contentSize : arrayLast(scrollSnaps);
13033
+ const limit = Limit(min, max);
13034
+ const self = {
13035
+ limit
13036
+ };
13037
+ return self;
13038
+ }
13039
+ function ScrollLooper(contentSize, limit, location, vectors) {
13040
+ const jointSafety = 0.1;
13041
+ const min = limit.min + jointSafety;
13042
+ const max = limit.max + jointSafety;
13043
+ const { reachedMin, reachedMax } = Limit(min, max);
13044
+ function shouldLoop(direction) {
13045
+ if (direction === 1) return reachedMax(location.get());
13046
+ if (direction === -1) return reachedMin(location.get());
13047
+ return false;
13048
+ }
13049
+ function loop(direction) {
13050
+ if (!shouldLoop(direction)) return;
13051
+ const loopDistance = contentSize * (direction * -1);
13052
+ vectors.forEach((v)=>v.add(loopDistance));
13053
+ }
13054
+ const self = {
13055
+ loop
13056
+ };
13057
+ return self;
13058
+ }
13059
+ function ScrollProgress(limit) {
13060
+ const { max, length } = limit;
13061
+ function get(n) {
13062
+ const currentLocation = n - max;
13063
+ return length ? currentLocation / -length : 0;
13064
+ }
13065
+ const self = {
13066
+ get
13067
+ };
13068
+ return self;
13069
+ }
13070
+ function ScrollSnaps(axis, alignment, containerRect, slideRects, slidesToScroll) {
13071
+ const { startEdge, endEdge } = axis;
13072
+ const { groupSlides } = slidesToScroll;
13073
+ const alignments = measureSizes().map(alignment.measure);
13074
+ const snaps = measureUnaligned();
13075
+ const snapsAligned = measureAligned();
13076
+ function measureSizes() {
13077
+ return groupSlides(slideRects).map((rects)=>arrayLast(rects)[endEdge] - rects[0][startEdge]).map(mathAbs);
13078
+ }
13079
+ function measureUnaligned() {
13080
+ return slideRects.map((rect)=>containerRect[startEdge] - rect[startEdge]).map((snap)=>-mathAbs(snap));
13081
+ }
13082
+ function measureAligned() {
13083
+ return groupSlides(snaps).map((g)=>g[0]).map((snap, index)=>snap + alignments[index]);
13084
+ }
13085
+ const self = {
13086
+ snaps,
13087
+ snapsAligned
13088
+ };
13089
+ return self;
13090
+ }
13091
+ function SlideRegistry(containSnaps, containScroll, scrollSnaps, scrollContainLimit, slidesToScroll, slideIndexes) {
13092
+ const { groupSlides } = slidesToScroll;
13093
+ const { min, max } = scrollContainLimit;
13094
+ const slideRegistry = createSlideRegistry();
13095
+ function createSlideRegistry() {
13096
+ const groupedSlideIndexes = groupSlides(slideIndexes);
13097
+ const doNotContain = !containSnaps || containScroll === 'keepSnaps';
13098
+ if (scrollSnaps.length === 1) return [
13099
+ slideIndexes
13100
+ ];
13101
+ if (doNotContain) return groupedSlideIndexes;
13102
+ return groupedSlideIndexes.slice(min, max).map((group, index, groups)=>{
13103
+ const isFirst = !index;
13104
+ const isLast = arrayIsLastIndex(groups, index);
13105
+ if (isFirst) {
13106
+ const range = arrayLast(groups[0]) + 1;
13107
+ return arrayFromNumber(range);
13108
+ }
13109
+ if (isLast) {
13110
+ const range = arrayLastIndex(slideIndexes) - arrayLast(groups)[0] + 1;
13111
+ return arrayFromNumber(range, arrayLast(groups)[0]);
13112
+ }
13113
+ return group;
13114
+ });
13115
+ }
13116
+ const self = {
13117
+ slideRegistry
13118
+ };
13119
+ return self;
13120
+ }
13121
+ function ScrollTarget(loop, scrollSnaps, contentSize, limit, targetVector) {
13122
+ const { reachedAny, removeOffset, constrain } = limit;
13123
+ function minDistance(distances) {
13124
+ return distances.concat().sort((a, b)=>mathAbs(a) - mathAbs(b))[0];
13125
+ }
13126
+ function findTargetSnap(target) {
13127
+ const distance = loop ? removeOffset(target) : constrain(target);
13128
+ const ascDiffsToSnaps = scrollSnaps.map((snap, index)=>({
13129
+ diff: shortcut(snap - distance, 0),
13130
+ index
13131
+ })).sort((d1, d2)=>mathAbs(d1.diff) - mathAbs(d2.diff));
13132
+ const { index } = ascDiffsToSnaps[0];
13133
+ return {
13134
+ index,
13135
+ distance
13136
+ };
13137
+ }
13138
+ function shortcut(target, direction) {
13139
+ const targets = [
13140
+ target,
13141
+ target + contentSize,
13142
+ target - contentSize
13143
+ ];
13144
+ if (!loop) return target;
13145
+ if (!direction) return minDistance(targets);
13146
+ const matchingTargets = targets.filter((t)=>mathSign(t) === direction);
13147
+ if (matchingTargets.length) return minDistance(matchingTargets);
13148
+ return arrayLast(targets) - contentSize;
13149
+ }
13150
+ function byIndex(index, direction) {
13151
+ const diffToSnap = scrollSnaps[index] - targetVector.get();
13152
+ const distance = shortcut(diffToSnap, direction);
13153
+ return {
13154
+ index,
13155
+ distance
13156
+ };
13157
+ }
13158
+ function byDistance(distance, snap) {
13159
+ const target = targetVector.get() + distance;
13160
+ const { index, distance: targetSnapDistance } = findTargetSnap(target);
13161
+ const reachedBound = !loop && reachedAny(target);
13162
+ if (!snap || reachedBound) return {
13163
+ index,
13164
+ distance
13165
+ };
13166
+ const diffToSnap = scrollSnaps[index] - targetSnapDistance;
13167
+ const snapDistance = distance + shortcut(diffToSnap, 0);
13168
+ return {
13169
+ index,
13170
+ distance: snapDistance
13171
+ };
13172
+ }
13173
+ const self = {
13174
+ byDistance,
13175
+ byIndex,
13176
+ shortcut
13177
+ };
13178
+ return self;
13179
+ }
13180
+ function ScrollTo(animation, indexCurrent, indexPrevious, scrollBody, scrollTarget, targetVector, eventHandler) {
13181
+ function scrollTo(target) {
13182
+ const distanceDiff = target.distance;
13183
+ const indexDiff = target.index !== indexCurrent.get();
13184
+ targetVector.add(distanceDiff);
13185
+ if (distanceDiff) {
13186
+ if (scrollBody.duration()) {
13187
+ animation.start();
13188
+ } else {
13189
+ animation.update();
13190
+ animation.render(1);
13191
+ animation.update();
13192
+ }
13193
+ }
13194
+ if (indexDiff) {
13195
+ indexPrevious.set(indexCurrent.get());
13196
+ indexCurrent.set(target.index);
13197
+ eventHandler.emit('select');
13198
+ }
13199
+ }
13200
+ function distance(n, snap) {
13201
+ const target = scrollTarget.byDistance(n, snap);
13202
+ scrollTo(target);
13203
+ }
13204
+ function index(n, direction) {
13205
+ const targetIndex = indexCurrent.clone().set(n);
13206
+ const target = scrollTarget.byIndex(targetIndex.get(), direction);
13207
+ scrollTo(target);
13208
+ }
13209
+ const self = {
13210
+ distance,
13211
+ index
13212
+ };
13213
+ return self;
13214
+ }
13215
+ function SlideFocus(root, slides, slideRegistry, scrollTo, scrollBody, eventStore, eventHandler, watchFocus) {
13216
+ const focusListenerOptions = {
13217
+ passive: true,
13218
+ capture: true
13219
+ };
13220
+ let lastTabPressTime = 0;
13221
+ function init(emblaApi) {
13222
+ if (!watchFocus) return;
13223
+ function defaultCallback(index) {
13224
+ const nowTime = new Date().getTime();
13225
+ const diffTime = nowTime - lastTabPressTime;
13226
+ if (diffTime > 10) return;
13227
+ eventHandler.emit('slideFocusStart');
13228
+ root.scrollLeft = 0;
13229
+ const group = slideRegistry.findIndex((group)=>group.includes(index));
13230
+ if (!isNumber(group)) return;
13231
+ scrollBody.useDuration(0);
13232
+ scrollTo.index(group, 0);
13233
+ eventHandler.emit('slideFocus');
13234
+ }
13235
+ eventStore.add(document, 'keydown', registerTabPress, false);
13236
+ slides.forEach((slide, slideIndex)=>{
13237
+ eventStore.add(slide, 'focus', (evt)=>{
13238
+ if (isBoolean(watchFocus) || watchFocus(emblaApi, evt)) {
13239
+ defaultCallback(slideIndex);
13240
+ }
13241
+ }, focusListenerOptions);
13242
+ });
13243
+ }
13244
+ function registerTabPress(event) {
13245
+ if (event.code === 'Tab') lastTabPressTime = new Date().getTime();
13246
+ }
13247
+ const self = {
13248
+ init
13249
+ };
13250
+ return self;
13251
+ }
13252
+ function Vector1D(initialValue) {
13253
+ let value = initialValue;
13254
+ function get() {
13255
+ return value;
13256
+ }
13257
+ function set(n) {
13258
+ value = normalizeInput(n);
13259
+ }
13260
+ function add(n) {
13261
+ value += normalizeInput(n);
13262
+ }
13263
+ function subtract(n) {
13264
+ value -= normalizeInput(n);
13265
+ }
13266
+ function normalizeInput(n) {
13267
+ return isNumber(n) ? n : n.get();
13268
+ }
13269
+ const self = {
13270
+ get,
13271
+ set,
13272
+ add,
13273
+ subtract
13274
+ };
13275
+ return self;
13276
+ }
13277
+ function Translate(axis, container) {
13278
+ const translate = axis.scroll === 'x' ? x : y;
13279
+ const containerStyle = container.style;
13280
+ let previousTarget = null;
13281
+ let disabled = false;
13282
+ function x(n) {
13283
+ return `translate3d(${n}px,0px,0px)`;
13284
+ }
13285
+ function y(n) {
13286
+ return `translate3d(0px,${n}px,0px)`;
13287
+ }
13288
+ function to(target) {
13289
+ if (disabled) return;
13290
+ const newTarget = roundToTwoDecimals(axis.direction(target));
13291
+ if (newTarget === previousTarget) return;
13292
+ containerStyle.transform = translate(newTarget);
13293
+ previousTarget = newTarget;
13294
+ }
13295
+ function toggleActive(active) {
13296
+ disabled = !active;
13297
+ }
13298
+ function clear() {
13299
+ if (disabled) return;
13300
+ containerStyle.transform = '';
13301
+ if (!container.getAttribute('style')) container.removeAttribute('style');
13302
+ }
13303
+ const self = {
13304
+ clear,
13305
+ to,
13306
+ toggleActive
13307
+ };
13308
+ return self;
13309
+ }
13310
+ function SlideLooper(axis, viewSize, contentSize, slideSizes, slideSizesWithGaps, snaps, scrollSnaps, location, slides) {
13311
+ const roundingSafety = 0.5;
13312
+ const ascItems = arrayKeys(slideSizesWithGaps);
13313
+ const descItems = arrayKeys(slideSizesWithGaps).reverse();
13314
+ const loopPoints = startPoints().concat(endPoints());
13315
+ function removeSlideSizes(indexes, from) {
13316
+ return indexes.reduce((a, i)=>{
13317
+ return a - slideSizesWithGaps[i];
13318
+ }, from);
13319
+ }
13320
+ function slidesInGap(indexes, gap) {
13321
+ return indexes.reduce((a, i)=>{
13322
+ const remainingGap = removeSlideSizes(a, gap);
13323
+ return remainingGap > 0 ? a.concat([
13324
+ i
13325
+ ]) : a;
13326
+ }, []);
13327
+ }
13328
+ function findSlideBounds(offset) {
13329
+ return snaps.map((snap, index)=>({
13330
+ start: snap - slideSizes[index] + roundingSafety + offset,
13331
+ end: snap + viewSize - roundingSafety + offset
13332
+ }));
13333
+ }
13334
+ function findLoopPoints(indexes, offset, isEndEdge) {
13335
+ const slideBounds = findSlideBounds(offset);
13336
+ return indexes.map((index)=>{
13337
+ const initial = isEndEdge ? 0 : -contentSize;
13338
+ const altered = isEndEdge ? contentSize : 0;
13339
+ const boundEdge = isEndEdge ? 'end' : 'start';
13340
+ const loopPoint = slideBounds[index][boundEdge];
13341
+ return {
13342
+ index,
13343
+ loopPoint,
13344
+ slideLocation: Vector1D(-1),
13345
+ translate: Translate(axis, slides[index]),
13346
+ target: ()=>location.get() > loopPoint ? initial : altered
13347
+ };
13348
+ });
13349
+ }
13350
+ function startPoints() {
13351
+ const gap = scrollSnaps[0];
13352
+ const indexes = slidesInGap(descItems, gap);
13353
+ return findLoopPoints(indexes, contentSize, false);
13354
+ }
13355
+ function endPoints() {
13356
+ const gap = viewSize - scrollSnaps[0] - 1;
13357
+ const indexes = slidesInGap(ascItems, gap);
13358
+ return findLoopPoints(indexes, -contentSize, true);
13359
+ }
13360
+ function canLoop() {
13361
+ return loopPoints.every(({ index })=>{
13362
+ const otherIndexes = ascItems.filter((i)=>i !== index);
13363
+ return removeSlideSizes(otherIndexes, viewSize) <= 0.1;
13364
+ });
13365
+ }
13366
+ function loop() {
13367
+ loopPoints.forEach((loopPoint)=>{
13368
+ const { target, translate, slideLocation } = loopPoint;
13369
+ const shiftLocation = target();
13370
+ if (shiftLocation === slideLocation.get()) return;
13371
+ translate.to(shiftLocation);
13372
+ slideLocation.set(shiftLocation);
13373
+ });
13374
+ }
13375
+ function clear() {
13376
+ loopPoints.forEach((loopPoint)=>loopPoint.translate.clear());
13377
+ }
13378
+ const self = {
13379
+ canLoop,
13380
+ clear,
13381
+ loop,
13382
+ loopPoints
13383
+ };
13384
+ return self;
13385
+ }
13386
+ function SlidesHandler(container, eventHandler, watchSlides) {
13387
+ let mutationObserver;
13388
+ let destroyed = false;
13389
+ function init(emblaApi) {
13390
+ if (!watchSlides) return;
13391
+ function defaultCallback(mutations) {
13392
+ for (const mutation of mutations){
13393
+ if (mutation.type === 'childList') {
13394
+ emblaApi.reInit();
13395
+ eventHandler.emit('slidesChanged');
13396
+ break;
13397
+ }
13398
+ }
13399
+ }
13400
+ mutationObserver = new MutationObserver((mutations)=>{
13401
+ if (destroyed) return;
13402
+ if (isBoolean(watchSlides) || watchSlides(emblaApi, mutations)) {
13403
+ defaultCallback(mutations);
13404
+ }
13405
+ });
13406
+ mutationObserver.observe(container, {
13407
+ childList: true
13408
+ });
13409
+ }
13410
+ function destroy() {
13411
+ if (mutationObserver) mutationObserver.disconnect();
13412
+ destroyed = true;
13413
+ }
13414
+ const self = {
13415
+ init,
13416
+ destroy
13417
+ };
13418
+ return self;
13419
+ }
13420
+ function SlidesInView(container, slides, eventHandler, threshold) {
13421
+ const intersectionEntryMap = {};
13422
+ let inViewCache = null;
13423
+ let notInViewCache = null;
13424
+ let intersectionObserver;
13425
+ let destroyed = false;
13426
+ function init() {
13427
+ intersectionObserver = new IntersectionObserver((entries)=>{
13428
+ if (destroyed) return;
13429
+ entries.forEach((entry)=>{
13430
+ const index = slides.indexOf(entry.target);
13431
+ intersectionEntryMap[index] = entry;
13432
+ });
13433
+ inViewCache = null;
13434
+ notInViewCache = null;
13435
+ eventHandler.emit('slidesInView');
13436
+ }, {
13437
+ root: container.parentElement,
13438
+ threshold
13439
+ });
13440
+ slides.forEach((slide)=>intersectionObserver.observe(slide));
13441
+ }
13442
+ function destroy() {
13443
+ if (intersectionObserver) intersectionObserver.disconnect();
13444
+ destroyed = true;
13445
+ }
13446
+ function createInViewList(inView) {
13447
+ return objectKeys(intersectionEntryMap).reduce((list, slideIndex)=>{
13448
+ const index = parseInt(slideIndex);
13449
+ const { isIntersecting } = intersectionEntryMap[index];
13450
+ const inViewMatch = inView && isIntersecting;
13451
+ const notInViewMatch = !inView && !isIntersecting;
13452
+ if (inViewMatch || notInViewMatch) list.push(index);
13453
+ return list;
13454
+ }, []);
13455
+ }
13456
+ function get(inView = true) {
13457
+ if (inView && inViewCache) return inViewCache;
13458
+ if (!inView && notInViewCache) return notInViewCache;
13459
+ const slideIndexes = createInViewList(inView);
13460
+ if (inView) inViewCache = slideIndexes;
13461
+ if (!inView) notInViewCache = slideIndexes;
13462
+ return slideIndexes;
13463
+ }
13464
+ const self = {
13465
+ init,
13466
+ destroy,
13467
+ get
13468
+ };
13469
+ return self;
13470
+ }
13471
+ function SlideSizes(axis, containerRect, slideRects, slides, readEdgeGap, ownerWindow) {
13472
+ const { measureSize, startEdge, endEdge } = axis;
13473
+ const withEdgeGap = slideRects[0] && readEdgeGap;
13474
+ const startGap = measureStartGap();
13475
+ const endGap = measureEndGap();
13476
+ const slideSizes = slideRects.map(measureSize);
13477
+ const slideSizesWithGaps = measureWithGaps();
13478
+ function measureStartGap() {
13479
+ if (!withEdgeGap) return 0;
13480
+ const slideRect = slideRects[0];
13481
+ return mathAbs(containerRect[startEdge] - slideRect[startEdge]);
13482
+ }
13483
+ function measureEndGap() {
13484
+ if (!withEdgeGap) return 0;
13485
+ const style = ownerWindow.getComputedStyle(arrayLast(slides));
13486
+ return parseFloat(style.getPropertyValue(`margin-${endEdge}`));
13487
+ }
13488
+ function measureWithGaps() {
13489
+ return slideRects.map((rect, index, rects)=>{
13490
+ const isFirst = !index;
13491
+ const isLast = arrayIsLastIndex(rects, index);
13492
+ if (isFirst) return slideSizes[index] + startGap;
13493
+ if (isLast) return slideSizes[index] + endGap;
13494
+ return rects[index + 1][startEdge] - rect[startEdge];
13495
+ }).map(mathAbs);
13496
+ }
13497
+ const self = {
13498
+ slideSizes,
13499
+ slideSizesWithGaps,
13500
+ startGap,
13501
+ endGap
13502
+ };
13503
+ return self;
13504
+ }
13505
+ function SlidesToScroll(axis, viewSize, slidesToScroll, loop, containerRect, slideRects, startGap, endGap, pixelTolerance) {
13506
+ const { startEdge, endEdge, direction } = axis;
13507
+ const groupByNumber = isNumber(slidesToScroll);
13508
+ function byNumber(array, groupSize) {
13509
+ return arrayKeys(array).filter((i)=>i % groupSize === 0).map((i)=>array.slice(i, i + groupSize));
13510
+ }
13511
+ function bySize(array) {
13512
+ if (!array.length) return [];
13513
+ return arrayKeys(array).reduce((groups, rectB, index)=>{
13514
+ const rectA = arrayLast(groups) || 0;
13515
+ const isFirst = rectA === 0;
13516
+ const isLast = rectB === arrayLastIndex(array);
13517
+ const edgeA = containerRect[startEdge] - slideRects[rectA][startEdge];
13518
+ const edgeB = containerRect[startEdge] - slideRects[rectB][endEdge];
13519
+ const gapA = !loop && isFirst ? direction(startGap) : 0;
13520
+ const gapB = !loop && isLast ? direction(endGap) : 0;
13521
+ const chunkSize = mathAbs(edgeB - gapB - (edgeA + gapA));
13522
+ if (index && chunkSize > viewSize + pixelTolerance) groups.push(rectB);
13523
+ if (isLast) groups.push(array.length);
13524
+ return groups;
13525
+ }, []).map((currentSize, index, groups)=>{
13526
+ const previousSize = Math.max(groups[index - 1] || 0);
13527
+ return array.slice(previousSize, currentSize);
13528
+ });
13529
+ }
13530
+ function groupSlides(array) {
13531
+ return groupByNumber ? byNumber(array, slidesToScroll) : bySize(array);
13532
+ }
13533
+ const self = {
13534
+ groupSlides
13535
+ };
13536
+ return self;
13537
+ }
13538
+ function Engine(root, container, slides, ownerDocument, ownerWindow, options, eventHandler) {
13539
+ // Options
13540
+ const { align, axis: scrollAxis, direction, startIndex, loop, duration, dragFree, dragThreshold, inViewThreshold, slidesToScroll: groupSlides, skipSnaps, containScroll, watchResize, watchSlides, watchDrag, watchFocus } = options;
13541
+ // Measurements
13542
+ const pixelTolerance = 2;
13543
+ const nodeRects = NodeRects();
13544
+ const containerRect = nodeRects.measure(container);
13545
+ const slideRects = slides.map(nodeRects.measure);
13546
+ const axis = Axis(scrollAxis, direction);
13547
+ const viewSize = axis.measureSize(containerRect);
13548
+ const percentOfView = PercentOfView(viewSize);
13549
+ const alignment = Alignment(align, viewSize);
13550
+ const containSnaps = !loop && !!containScroll;
13551
+ const readEdgeGap = loop || !!containScroll;
13552
+ const { slideSizes, slideSizesWithGaps, startGap, endGap } = SlideSizes(axis, containerRect, slideRects, slides, readEdgeGap, ownerWindow);
13553
+ const slidesToScroll = SlidesToScroll(axis, viewSize, groupSlides, loop, containerRect, slideRects, startGap, endGap, pixelTolerance);
13554
+ const { snaps, snapsAligned } = ScrollSnaps(axis, alignment, containerRect, slideRects, slidesToScroll);
13555
+ const contentSize = -arrayLast(snaps) + arrayLast(slideSizesWithGaps);
13556
+ const { snapsContained, scrollContainLimit } = ScrollContain(viewSize, contentSize, snapsAligned, containScroll, pixelTolerance);
13557
+ const scrollSnaps = containSnaps ? snapsContained : snapsAligned;
13558
+ const { limit } = ScrollLimit(contentSize, scrollSnaps, loop);
13559
+ // Indexes
13560
+ const index = Counter(arrayLastIndex(scrollSnaps), startIndex, loop);
13561
+ const indexPrevious = index.clone();
13562
+ const slideIndexes = arrayKeys(slides);
13563
+ // Animation
13564
+ const update = ({ dragHandler, scrollBody, scrollBounds, options: { loop } })=>{
13565
+ if (!loop) scrollBounds.constrain(dragHandler.pointerDown());
13566
+ scrollBody.seek();
13567
+ };
13568
+ const render = ({ scrollBody, translate, location, offsetLocation, previousLocation, scrollLooper, slideLooper, dragHandler, animation, eventHandler, scrollBounds, options: { loop } }, alpha)=>{
13569
+ const shouldSettle = scrollBody.settled();
13570
+ const withinBounds = !scrollBounds.shouldConstrain();
13571
+ const hasSettled = loop ? shouldSettle : shouldSettle && withinBounds;
13572
+ const hasSettledAndIdle = hasSettled && !dragHandler.pointerDown();
13573
+ if (hasSettledAndIdle) animation.stop();
13574
+ const interpolatedLocation = location.get() * alpha + previousLocation.get() * (1 - alpha);
13575
+ offsetLocation.set(interpolatedLocation);
13576
+ if (loop) {
13577
+ scrollLooper.loop(scrollBody.direction());
13578
+ slideLooper.loop();
13579
+ }
13580
+ translate.to(offsetLocation.get());
13581
+ if (hasSettledAndIdle) eventHandler.emit('settle');
13582
+ if (!hasSettled) eventHandler.emit('scroll');
13583
+ };
13584
+ const animation = Animations(ownerDocument, ownerWindow, ()=>update(engine), (alpha)=>render(engine, alpha));
13585
+ // Shared
13586
+ const friction = 0.68;
13587
+ const startLocation = scrollSnaps[index.get()];
13588
+ const location = Vector1D(startLocation);
13589
+ const previousLocation = Vector1D(startLocation);
13590
+ const offsetLocation = Vector1D(startLocation);
13591
+ const target = Vector1D(startLocation);
13592
+ const scrollBody = ScrollBody(location, offsetLocation, previousLocation, target, duration, friction);
13593
+ const scrollTarget = ScrollTarget(loop, scrollSnaps, contentSize, limit, target);
13594
+ const scrollTo = ScrollTo(animation, index, indexPrevious, scrollBody, scrollTarget, target, eventHandler);
13595
+ const scrollProgress = ScrollProgress(limit);
13596
+ const eventStore = EventStore();
13597
+ const slidesInView = SlidesInView(container, slides, eventHandler, inViewThreshold);
13598
+ const { slideRegistry } = SlideRegistry(containSnaps, containScroll, scrollSnaps, scrollContainLimit, slidesToScroll, slideIndexes);
13599
+ const slideFocus = SlideFocus(root, slides, slideRegistry, scrollTo, scrollBody, eventStore, eventHandler, watchFocus);
13600
+ // Engine
13601
+ const engine = {
13602
+ ownerDocument,
13603
+ ownerWindow,
13604
+ eventHandler,
13605
+ containerRect,
13606
+ slideRects,
13607
+ animation,
13608
+ axis,
13609
+ dragHandler: DragHandler(axis, root, ownerDocument, ownerWindow, target, DragTracker(axis, ownerWindow), location, animation, scrollTo, scrollBody, scrollTarget, index, eventHandler, percentOfView, dragFree, dragThreshold, skipSnaps, friction, watchDrag),
13610
+ eventStore,
13611
+ percentOfView,
13612
+ index,
13613
+ indexPrevious,
13614
+ limit,
13615
+ location,
13616
+ offsetLocation,
13617
+ previousLocation,
13618
+ options,
13619
+ resizeHandler: ResizeHandler(container, eventHandler, ownerWindow, slides, axis, watchResize, nodeRects),
13620
+ scrollBody,
13621
+ scrollBounds: ScrollBounds(limit, offsetLocation, target, scrollBody, percentOfView),
13622
+ scrollLooper: ScrollLooper(contentSize, limit, offsetLocation, [
13623
+ location,
13624
+ offsetLocation,
13625
+ previousLocation,
13626
+ target
13627
+ ]),
13628
+ scrollProgress,
13629
+ scrollSnapList: scrollSnaps.map(scrollProgress.get),
13630
+ scrollSnaps,
13631
+ scrollTarget,
13632
+ scrollTo,
13633
+ slideLooper: SlideLooper(axis, viewSize, contentSize, slideSizes, slideSizesWithGaps, snaps, scrollSnaps, offsetLocation, slides),
13634
+ slideFocus,
13635
+ slidesHandler: SlidesHandler(container, eventHandler, watchSlides),
13636
+ slidesInView,
13637
+ slideIndexes,
13638
+ slideRegistry,
13639
+ slidesToScroll,
13640
+ target,
13641
+ translate: Translate(axis, container)
13642
+ };
13643
+ return engine;
13644
+ }
13645
+ function EventHandler() {
13646
+ let listeners = {};
13647
+ let api;
13648
+ function init(emblaApi) {
13649
+ api = emblaApi;
13650
+ }
13651
+ function getListeners(evt) {
13652
+ return listeners[evt] || [];
13653
+ }
13654
+ function emit(evt) {
13655
+ getListeners(evt).forEach((e)=>e(api, evt));
13656
+ return self;
13657
+ }
13658
+ function on(evt, cb) {
13659
+ listeners[evt] = getListeners(evt).concat([
13660
+ cb
13661
+ ]);
13662
+ return self;
13663
+ }
13664
+ function off(evt, cb) {
13665
+ listeners[evt] = getListeners(evt).filter((e)=>e !== cb);
13666
+ return self;
13667
+ }
13668
+ function clear() {
13669
+ listeners = {};
13670
+ }
13671
+ const self = {
13672
+ init,
13673
+ emit,
13674
+ off,
13675
+ on,
13676
+ clear
13677
+ };
13678
+ return self;
13679
+ }
13680
+ const defaultOptions = {
13681
+ align: 'center',
13682
+ axis: 'x',
13683
+ container: null,
13684
+ slides: null,
13685
+ containScroll: 'trimSnaps',
13686
+ direction: 'ltr',
13687
+ slidesToScroll: 1,
13688
+ inViewThreshold: 0,
13689
+ breakpoints: {},
13690
+ dragFree: false,
13691
+ dragThreshold: 10,
13692
+ loop: false,
13693
+ skipSnaps: false,
13694
+ duration: 25,
13695
+ startIndex: 0,
13696
+ active: true,
13697
+ watchDrag: true,
13698
+ watchResize: true,
13699
+ watchSlides: true,
13700
+ watchFocus: true
13701
+ };
13702
+ function OptionsHandler(ownerWindow) {
13703
+ function mergeOptions(optionsA, optionsB) {
13704
+ return objectsMergeDeep(optionsA, optionsB || {});
13705
+ }
13706
+ function optionsAtMedia(options) {
13707
+ const optionsAtMedia = options.breakpoints || {};
13708
+ const matchedMediaOptions = objectKeys(optionsAtMedia).filter((media)=>ownerWindow.matchMedia(media).matches).map((media)=>optionsAtMedia[media]).reduce((a, mediaOption)=>mergeOptions(a, mediaOption), {});
13709
+ return mergeOptions(options, matchedMediaOptions);
13710
+ }
13711
+ function optionsMediaQueries(optionsList) {
13712
+ return optionsList.map((options)=>objectKeys(options.breakpoints || {})).reduce((acc, mediaQueries)=>acc.concat(mediaQueries), []).map(ownerWindow.matchMedia);
13713
+ }
13714
+ const self = {
13715
+ mergeOptions,
13716
+ optionsAtMedia,
13717
+ optionsMediaQueries
13718
+ };
13719
+ return self;
13720
+ }
13721
+ function PluginsHandler(optionsHandler) {
13722
+ let activePlugins = [];
13723
+ function init(emblaApi, plugins) {
13724
+ activePlugins = plugins.filter(({ options })=>optionsHandler.optionsAtMedia(options).active !== false);
13725
+ activePlugins.forEach((plugin)=>plugin.init(emblaApi, optionsHandler));
13726
+ return plugins.reduce((map, plugin)=>Object.assign(map, {
13727
+ [plugin.name]: plugin
13728
+ }), {});
13729
+ }
13730
+ function destroy() {
13731
+ activePlugins = activePlugins.filter((plugin)=>plugin.destroy());
13732
+ }
13733
+ const self = {
13734
+ init,
13735
+ destroy
13736
+ };
13737
+ return self;
13738
+ }
13739
+ function EmblaCarousel(root, userOptions, userPlugins) {
13740
+ const ownerDocument = root.ownerDocument;
13741
+ const ownerWindow = ownerDocument.defaultView;
13742
+ const optionsHandler = OptionsHandler(ownerWindow);
13743
+ const pluginsHandler = PluginsHandler(optionsHandler);
13744
+ const mediaHandlers = EventStore();
13745
+ const eventHandler = EventHandler();
13746
+ const { mergeOptions, optionsAtMedia, optionsMediaQueries } = optionsHandler;
13747
+ const { on, off, emit } = eventHandler;
13748
+ const reInit = reActivate;
13749
+ let destroyed = false;
13750
+ let engine;
13751
+ let optionsBase = mergeOptions(defaultOptions, EmblaCarousel.globalOptions);
13752
+ let options = mergeOptions(optionsBase);
13753
+ let pluginList = [];
13754
+ let pluginApis;
13755
+ let container;
13756
+ let slides;
13757
+ function storeElements() {
13758
+ const { container: userContainer, slides: userSlides } = options;
13759
+ const customContainer = isString(userContainer) ? root.querySelector(userContainer) : userContainer;
13760
+ container = customContainer || root.children[0];
13761
+ const customSlides = isString(userSlides) ? container.querySelectorAll(userSlides) : userSlides;
13762
+ slides = [].slice.call(customSlides || container.children);
13763
+ }
13764
+ function createEngine(options) {
13765
+ const engine = Engine(root, container, slides, ownerDocument, ownerWindow, options, eventHandler);
13766
+ if (options.loop && !engine.slideLooper.canLoop()) {
13767
+ const optionsWithoutLoop = Object.assign({}, options, {
13768
+ loop: false
13769
+ });
13770
+ return createEngine(optionsWithoutLoop);
13771
+ }
13772
+ return engine;
13773
+ }
13774
+ function activate(withOptions, withPlugins) {
13775
+ if (destroyed) return;
13776
+ optionsBase = mergeOptions(optionsBase, withOptions);
13777
+ options = optionsAtMedia(optionsBase);
13778
+ pluginList = withPlugins || pluginList;
13779
+ storeElements();
13780
+ engine = createEngine(options);
13781
+ optionsMediaQueries([
13782
+ optionsBase,
13783
+ ...pluginList.map(({ options })=>options)
13784
+ ]).forEach((query)=>mediaHandlers.add(query, 'change', reActivate));
13785
+ if (!options.active) return;
13786
+ engine.translate.to(engine.location.get());
13787
+ engine.animation.init();
13788
+ engine.slidesInView.init();
13789
+ engine.slideFocus.init(self);
13790
+ engine.eventHandler.init(self);
13791
+ engine.resizeHandler.init(self);
13792
+ engine.slidesHandler.init(self);
13793
+ if (engine.options.loop) engine.slideLooper.loop();
13794
+ if (container.offsetParent && slides.length) engine.dragHandler.init(self);
13795
+ pluginApis = pluginsHandler.init(self, pluginList);
13796
+ }
13797
+ function reActivate(withOptions, withPlugins) {
13798
+ const startIndex = selectedScrollSnap();
13799
+ deActivate();
13800
+ activate(mergeOptions({
13801
+ startIndex
13802
+ }, withOptions), withPlugins);
13803
+ eventHandler.emit('reInit');
13804
+ }
13805
+ function deActivate() {
13806
+ engine.dragHandler.destroy();
13807
+ engine.eventStore.clear();
13808
+ engine.translate.clear();
13809
+ engine.slideLooper.clear();
13810
+ engine.resizeHandler.destroy();
13811
+ engine.slidesHandler.destroy();
13812
+ engine.slidesInView.destroy();
13813
+ engine.animation.destroy();
13814
+ pluginsHandler.destroy();
13815
+ mediaHandlers.clear();
13816
+ }
13817
+ function destroy() {
13818
+ if (destroyed) return;
13819
+ destroyed = true;
13820
+ mediaHandlers.clear();
13821
+ deActivate();
13822
+ eventHandler.emit('destroy');
13823
+ eventHandler.clear();
13824
+ }
13825
+ function scrollTo(index, jump, direction) {
13826
+ if (!options.active || destroyed) return;
13827
+ engine.scrollBody.useBaseFriction().useDuration(jump === true ? 0 : options.duration);
13828
+ engine.scrollTo.index(index, direction || 0);
13829
+ }
13830
+ function scrollNext(jump) {
13831
+ const next = engine.index.add(1).get();
13832
+ scrollTo(next, jump, -1);
13833
+ }
13834
+ function scrollPrev(jump) {
13835
+ const prev = engine.index.add(-1).get();
13836
+ scrollTo(prev, jump, 1);
13837
+ }
13838
+ function canScrollNext() {
13839
+ const next = engine.index.add(1).get();
13840
+ return next !== selectedScrollSnap();
13841
+ }
13842
+ function canScrollPrev() {
13843
+ const prev = engine.index.add(-1).get();
13844
+ return prev !== selectedScrollSnap();
13845
+ }
13846
+ function scrollSnapList() {
13847
+ return engine.scrollSnapList;
13848
+ }
13849
+ function scrollProgress() {
13850
+ return engine.scrollProgress.get(engine.offsetLocation.get());
13851
+ }
13852
+ function selectedScrollSnap() {
13853
+ return engine.index.get();
13854
+ }
13855
+ function previousScrollSnap() {
13856
+ return engine.indexPrevious.get();
13857
+ }
13858
+ function slidesInView() {
13859
+ return engine.slidesInView.get();
13860
+ }
13861
+ function slidesNotInView() {
13862
+ return engine.slidesInView.get(false);
13863
+ }
13864
+ function plugins() {
13865
+ return pluginApis;
13866
+ }
13867
+ function internalEngine() {
13868
+ return engine;
13869
+ }
13870
+ function rootNode() {
13871
+ return root;
13872
+ }
13873
+ function containerNode() {
13874
+ return container;
13875
+ }
13876
+ function slideNodes() {
13877
+ return slides;
13878
+ }
13879
+ const self = {
13880
+ canScrollNext,
13881
+ canScrollPrev,
13882
+ containerNode,
13883
+ internalEngine,
13884
+ destroy,
13885
+ off,
13886
+ on,
13887
+ emit,
13888
+ plugins,
13889
+ previousScrollSnap,
13890
+ reInit,
13891
+ rootNode,
13892
+ scrollNext,
13893
+ scrollPrev,
13894
+ scrollProgress,
13895
+ scrollSnapList,
13896
+ scrollTo,
13897
+ selectedScrollSnap,
13898
+ slideNodes,
13899
+ slidesInView,
13900
+ slidesNotInView
13901
+ };
13902
+ activate(userOptions, userPlugins);
13903
+ setTimeout(()=>eventHandler.emit('init'), 0);
13904
+ return self;
13905
+ }
13906
+ EmblaCarousel.globalOptions = undefined;
13907
+
13908
+ function useEmblaCarousel(options = {}, plugins = []) {
13909
+ const storedOptions = React.useRef(options);
13910
+ const storedPlugins = React.useRef(plugins);
13911
+ const [emblaApi, setEmblaApi] = React.useState();
13912
+ const [viewport, setViewport] = React.useState();
13913
+ const reInit = React.useCallback(()=>{
13914
+ if (emblaApi) emblaApi.reInit(storedOptions.current, storedPlugins.current);
13915
+ }, [
13916
+ emblaApi
13917
+ ]);
13918
+ React.useEffect(()=>{
13919
+ if (areOptionsEqual(storedOptions.current, options)) return;
13920
+ storedOptions.current = options;
13921
+ reInit();
13922
+ }, [
13923
+ options,
13924
+ reInit
13925
+ ]);
13926
+ React.useEffect(()=>{
13927
+ if (arePluginsEqual(storedPlugins.current, plugins)) return;
13928
+ storedPlugins.current = plugins;
13929
+ reInit();
13930
+ }, [
13931
+ plugins,
13932
+ reInit
13933
+ ]);
13934
+ React.useEffect(()=>{
13935
+ if (canUseDOM() && viewport) {
13936
+ EmblaCarousel.globalOptions = useEmblaCarousel.globalOptions;
13937
+ const newEmblaApi = EmblaCarousel(viewport, storedOptions.current, storedPlugins.current);
13938
+ setEmblaApi(newEmblaApi);
13939
+ return ()=>newEmblaApi.destroy();
13940
+ } else {
13941
+ setEmblaApi(undefined);
13942
+ }
13943
+ }, [
13944
+ viewport,
13945
+ setEmblaApi
13946
+ ]);
13947
+ return [
13948
+ setViewport,
13949
+ emblaApi
13950
+ ];
13951
+ }
13952
+ useEmblaCarousel.globalOptions = undefined;
13953
+
13954
+ function useRenderStrategy(props) {
13955
+ const wasEverPresent = React.useRef(false);
13956
+ if (props.present) {
13957
+ wasEverPresent.current = true;
13958
+ }
13959
+ return {
13960
+ unmounted: !props.present && !wasEverPresent.current && props.lazyMount || props.unmountOnExit && !props.present && wasEverPresent.current
13961
+ };
13962
+ }
13963
+ const RenderStrategyPropsContext = /*#__PURE__*/ React.createContext(null);
13964
+ const RenderStrategyPropsProvider = RenderStrategyPropsContext.Provider;
13965
+ function useRenderStrategyPropsContext({ strict = true } = {}) {
13966
+ const context = React.useContext(RenderStrategyPropsContext);
13967
+ if (!context && strict) {
13968
+ throw new Error("useRenderStrategyPropsContext must be used within a RenderStrategyPropsProvider");
13969
+ }
13970
+ return context;
13971
+ }
13972
+ /* ID -----------------------------------------------------------------------
13973
+ -------------------------------------------------------------------------------- */ const getTriggerId = (value, id)=>`tabs:${value}:${id}:trigger-root`;
13974
+ const getListId = (id)=>`tabs:${id}:list`;
13975
+ const getContentId = (value, id)=>`tabs:${value}:${id}:content`;
13976
+ /* Query -----------------------------------------------------------------------
13977
+ -------------------------------------------------------------------------------- */ const getTriggerByValue = (listEl, value)=>{
13978
+ return listEl.querySelector(`[data-value="${value}"]`);
13979
+ };
13980
+ const getEnabledValues = (el)=>{
13981
+ if (!el) return [];
13982
+ return Array.from(el.children).filter((child)=>!child.hasAttribute("aria-disabled")).map((child)=>child.getAttribute("data-value")).filter(Boolean);
13983
+ };
13984
+ /* Prevent Drag -----------------------------------------------------------------------
13985
+ -------------------------------------------------------------------------------- */ const PREVENT_DRAG_ATTRIBUTE = "data-embla-prevent-drag";
13986
+ const isDragPrevented = (el)=>{
13987
+ return el.closest(`[${PREVENT_DRAG_ATTRIBUTE}]`) != null;
13988
+ };
13989
+ function getPrevIndex(index, length) {
13990
+ if (length <= 0) {
13991
+ throw new Error("Length must be a positive number.");
13992
+ }
13993
+ return (index - 1 + length) % length;
13994
+ }
13995
+ function getNextIndex(index, length) {
13996
+ if (length <= 0) {
13997
+ throw new Error("Length must be a positive number.");
13998
+ }
13999
+ return (index + 1) % length;
14000
+ }
14001
+ // Helper function to scroll tab into view
14002
+ function scrollTabIntoView(tabElement, listElement, options) {
14003
+ const isHorizontal = listElement.getAttribute("aria-orientation") !== "vertical";
14004
+ // Default padding is 16px if not specified
14005
+ const scrollPadding = options?.scrollPadding;
14006
+ if (isHorizontal) {
14007
+ const tabLeft = tabElement.offsetLeft;
14008
+ const tabRight = tabLeft + tabElement.offsetWidth;
14009
+ const listScrollLeft = listElement.scrollLeft;
14010
+ const listWidth = listElement.clientWidth;
14011
+ const listRight = listScrollLeft + listWidth;
14012
+ if (tabLeft < listScrollLeft) {
14013
+ // Tab is to the left of the visible area - scroll a bit more to the left
14014
+ listElement.scrollTo({
14015
+ left: Math.max(0, tabLeft - scrollPadding),
14016
+ behavior: "smooth"
14017
+ });
14018
+ } else if (tabRight > listRight) {
14019
+ // Tab is to the right of the visible area - scroll a bit more to the right
14020
+ listElement.scrollTo({
14021
+ left: tabRight - listWidth + scrollPadding,
14022
+ behavior: "smooth"
14023
+ });
14024
+ }
14025
+ } else {
14026
+ const tabTop = tabElement.offsetTop;
14027
+ const tabBottom = tabTop + tabElement.offsetHeight;
14028
+ const listScrollTop = listElement.scrollTop;
14029
+ const listHeight = listElement.clientHeight;
14030
+ const listBottom = listScrollTop + listHeight;
14031
+ if (tabTop < listScrollTop) {
14032
+ // Tab is above the visible area - scroll a bit more up
14033
+ listElement.scrollTo({
14034
+ top: Math.max(0, tabTop - scrollPadding),
14035
+ behavior: "smooth"
14036
+ });
14037
+ } else if (tabBottom > listBottom) {
14038
+ // Tab is below the visible area - scroll a bit more down
14039
+ listElement.scrollTo({
14040
+ top: tabBottom - listHeight + scrollPadding,
14041
+ behavior: "smooth"
14042
+ });
14043
+ }
14044
+ }
14045
+ }
14046
+ function useIsSSR() {
14047
+ const [isSSR, setIsSSR] = React.useState(true);
14048
+ React.useEffect(()=>{
14049
+ setIsSSR(false);
14050
+ }, []);
14051
+ return isSSR;
14052
+ }
14053
+ function useTabsState(props) {
14054
+ const [interactionState, setInteractionState] = React.useState("idle");
14055
+ const [value, setValue] = useControllableState$1({
14056
+ prop: props.value,
14057
+ defaultProp: props.defaultValue ?? undefined,
14058
+ onChange: props.onValueChange
14059
+ });
14060
+ const [focusedValue, setFocusedValue] = React.useState(null);
14061
+ const [isFocusVisible, setIsFocusVisible] = React.useState(false);
14062
+ const isFocusVisibleSupported = useSupports("selector(:focus-visible)");
14063
+ const [listEl, listRef] = React.useState(null);
14064
+ const [selectedTriggerEl, setSelectedTriggerEl] = React.useState(null);
14065
+ const selectedTriggerSize = useSize(selectedTriggerEl);
14066
+ const enabledValues = React.useMemo(()=>listEl ? getEnabledValues(listEl) : [], [
14067
+ listEl
14068
+ ]);
14069
+ const contentIndex = value ? enabledValues.indexOf(value) : -1;
14070
+ const prevIndex = contentIndex >= 0 ? getPrevIndex(contentIndex, enabledValues.length) : -1;
14071
+ const nextIndex = contentIndex >= 0 ? getNextIndex(contentIndex, enabledValues.length) : -1;
14072
+ // Scroll selected tab into view when it changes
14073
+ // TODO: this implementation is temporary, we should create some sort of hook or plugin system
14074
+ // to allow users to customize the scroll behavior.
14075
+ React.useEffect(()=>{
14076
+ if (selectedTriggerEl && listEl) {
14077
+ scrollTabIntoView(selectedTriggerEl, listEl, {
14078
+ scrollPadding: 16
14079
+ });
14080
+ }
14081
+ }, [
14082
+ selectedTriggerEl,
14083
+ listEl
14084
+ ]);
14085
+ const focusTriggerByValue = React.useCallback((value)=>{
14086
+ if (listEl) {
14087
+ getTriggerByValue(listEl, value)?.focus();
14088
+ }
14089
+ }, [
14090
+ listEl
14091
+ ]);
14092
+ const selectPrevAction = React.useCallback(()=>{
14093
+ const prevValue = enabledValues[prevIndex];
14094
+ if (!prevValue) return;
14095
+ setValue(prevValue);
14096
+ }, [
14097
+ enabledValues,
14098
+ prevIndex,
14099
+ setValue
14100
+ ]);
14101
+ const selectNextAction = React.useCallback(()=>{
14102
+ const nextValue = enabledValues[nextIndex];
14103
+ if (!nextValue) return;
14104
+ setValue(nextValue);
14105
+ }, [
14106
+ enabledValues,
14107
+ nextIndex,
14108
+ setValue
14109
+ ]);
14110
+ const selectFirstAction = React.useCallback(()=>{
14111
+ const firstValue = enabledValues[0];
14112
+ if (!firstValue) return;
14113
+ setValue(firstValue);
14114
+ }, [
14115
+ enabledValues,
14116
+ setValue
14117
+ ]);
14118
+ const selectLastAction = React.useCallback(()=>{
14119
+ const lastValue = enabledValues[enabledValues.length - 1];
14120
+ if (!lastValue) return;
14121
+ setValue(lastValue);
14122
+ }, [
14123
+ enabledValues,
14124
+ setValue
14125
+ ]);
14126
+ const setFocusedValueAction = React.useCallback((value)=>{
14127
+ setFocusedValue(value);
14128
+ }, []);
14129
+ const clearFocusedValueAction = React.useCallback(()=>{
14130
+ setFocusedValue(null);
14131
+ }, []);
14132
+ const setValueAction = React.useCallback((value)=>{
14133
+ setValue(value);
14134
+ }, [
14135
+ setValue
14136
+ ]);
14137
+ const actions = {
14138
+ selectPrev: selectPrevAction,
14139
+ selectNext: selectNextAction,
14140
+ selectFirst: selectFirstAction,
14141
+ selectLast: selectLastAction,
14142
+ setFocusedValue: setFocusedValueAction,
14143
+ clearFocusedValue: clearFocusedValueAction,
14144
+ setValue: setValueAction
14145
+ };
14146
+ const arrowPrevEvent = React.useCallback(()=>{
14147
+ if (interactionState === "focused") {
14148
+ actions.selectPrev();
14149
+ focusTriggerByValue(enabledValues[prevIndex] ?? "");
14150
+ if (isFocusVisibleSupported) {
14151
+ setIsFocusVisible(true);
14152
+ }
14153
+ }
14154
+ }, [
14155
+ interactionState,
14156
+ actions.selectPrev,
14157
+ focusTriggerByValue,
14158
+ enabledValues,
14159
+ prevIndex,
14160
+ isFocusVisibleSupported
14161
+ ]);
14162
+ const arrowNextEvent = React.useCallback(()=>{
14163
+ if (interactionState === "focused") {
14164
+ actions.selectNext();
14165
+ focusTriggerByValue(enabledValues[nextIndex] ?? "");
14166
+ if (isFocusVisibleSupported) {
14167
+ setIsFocusVisible(true);
14168
+ }
14169
+ }
14170
+ }, [
14171
+ interactionState,
14172
+ actions.selectNext,
14173
+ focusTriggerByValue,
14174
+ enabledValues,
14175
+ nextIndex,
14176
+ isFocusVisibleSupported
14177
+ ]);
14178
+ const arrowUpEvent = React.useCallback(()=>{
14179
+ if (interactionState === "focused") {
14180
+ actions.selectPrev();
14181
+ focusTriggerByValue(enabledValues[prevIndex] ?? "");
14182
+ if (isFocusVisibleSupported) {
14183
+ setIsFocusVisible(true);
14184
+ }
14185
+ }
14186
+ }, [
14187
+ interactionState,
14188
+ actions.selectPrev,
14189
+ focusTriggerByValue,
14190
+ enabledValues,
14191
+ prevIndex,
14192
+ isFocusVisibleSupported
14193
+ ]);
14194
+ const arrowDownEvent = React.useCallback(()=>{
14195
+ if (interactionState === "focused") {
14196
+ actions.selectNext();
14197
+ focusTriggerByValue(enabledValues[nextIndex] ?? "");
14198
+ if (isFocusVisibleSupported) {
14199
+ setIsFocusVisible(true);
14200
+ }
14201
+ }
14202
+ }, [
14203
+ interactionState,
14204
+ actions.selectNext,
14205
+ focusTriggerByValue,
14206
+ enabledValues,
14207
+ nextIndex,
14208
+ isFocusVisibleSupported
14209
+ ]);
14210
+ const homeEvent = React.useCallback(()=>{
14211
+ if (interactionState === "focused") {
14212
+ actions.selectFirst();
14213
+ focusTriggerByValue(enabledValues[0] ?? "");
14214
+ if (isFocusVisibleSupported) {
14215
+ setIsFocusVisible(true);
14216
+ }
14217
+ }
14218
+ }, [
14219
+ interactionState,
14220
+ actions.selectFirst,
14221
+ focusTriggerByValue,
14222
+ enabledValues,
14223
+ isFocusVisibleSupported
14224
+ ]);
14225
+ const endEvent = React.useCallback(()=>{
14226
+ if (interactionState === "focused") {
14227
+ actions.selectLast();
14228
+ focusTriggerByValue(enabledValues[enabledValues.length - 1] ?? "");
14229
+ if (isFocusVisibleSupported) {
14230
+ setIsFocusVisible(true);
14231
+ }
14232
+ }
14233
+ }, [
14234
+ interactionState,
14235
+ actions.selectLast,
14236
+ focusTriggerByValue,
14237
+ enabledValues,
14238
+ isFocusVisibleSupported
14239
+ ]);
14240
+ const tabFocusEvent = React.useCallback((value)=>{
14241
+ actions.setFocusedValue(value);
14242
+ if (interactionState === "idle") {
14243
+ setInteractionState("focused");
14244
+ }
14245
+ }, [
14246
+ interactionState,
14247
+ actions.setFocusedValue
14248
+ ]);
14249
+ const tabBlurEvent = React.useCallback(()=>{
14250
+ if (interactionState === "focused") {
14251
+ actions.clearFocusedValue();
14252
+ setInteractionState("idle");
14253
+ }
14254
+ }, [
14255
+ interactionState,
14256
+ actions.clearFocusedValue
14257
+ ]);
14258
+ const tabClickEvent = React.useCallback((value)=>{
14259
+ actions.setValue(value);
14260
+ if (interactionState === "idle") {
14261
+ setInteractionState("focused");
14262
+ }
14263
+ }, [
14264
+ interactionState,
14265
+ actions.setValue
14266
+ ]);
14267
+ const setValueEvent = React.useCallback((value)=>{
14268
+ actions.setValue(value);
14269
+ }, [
14270
+ actions.setValue
14271
+ ]);
14272
+ const selectNextEvent = React.useCallback(()=>{
14273
+ if (interactionState === "focused") {
14274
+ actions.selectNext();
14275
+ if (isFocusVisibleSupported) {
14276
+ setIsFocusVisible(true);
14277
+ }
14278
+ }
14279
+ }, [
14280
+ interactionState,
14281
+ actions.selectNext,
14282
+ isFocusVisibleSupported
14283
+ ]);
14284
+ const selectPrevEvent = React.useCallback(()=>{
14285
+ if (interactionState === "focused") {
14286
+ actions.selectPrev();
14287
+ if (isFocusVisibleSupported) {
14288
+ setIsFocusVisible(true);
14289
+ }
14290
+ }
14291
+ }, [
14292
+ interactionState,
14293
+ actions.selectPrev,
14294
+ isFocusVisibleSupported
14295
+ ]);
14296
+ const setContentIndexEvent = React.useCallback((index)=>{
14297
+ const valueFromIndex = enabledValues[index];
14298
+ if (!valueFromIndex) return;
14299
+ actions.setValue(valueFromIndex);
14300
+ }, [
14301
+ actions.setValue,
14302
+ enabledValues
14303
+ ]);
14304
+ const setIsFocusVisibleEvent = React.useCallback((isFocusVisible)=>{
14305
+ setIsFocusVisible(isFocusVisible);
14306
+ }, []);
14307
+ const setSelectedTriggerElEvent = React.useCallback((element)=>{
14308
+ setSelectedTriggerEl(element);
14309
+ }, []);
14310
+ const events = {
14311
+ arrowPrev: arrowPrevEvent,
14312
+ arrowNext: arrowNextEvent,
14313
+ arrowUp: arrowUpEvent,
14314
+ arrowDown: arrowDownEvent,
14315
+ home: homeEvent,
14316
+ end: endEvent,
14317
+ tabFocus: tabFocusEvent,
14318
+ tabBlur: tabBlurEvent,
14319
+ tabClick: tabClickEvent,
14320
+ setValue: setValueEvent,
14321
+ selectNext: selectNextEvent,
14322
+ selectPrev: selectPrevEvent,
14323
+ setContentIndex: setContentIndexEvent,
14324
+ setIsFocusVisible: setIsFocusVisibleEvent,
14325
+ setSelectedTriggerEl: setSelectedTriggerElEvent
14326
+ };
14327
+ const refs = React.useMemo(()=>({
14328
+ list: listRef
14329
+ }), []);
14330
+ const triggerRect = React.useMemo(()=>({
14331
+ width: selectedTriggerSize?.width ?? selectedTriggerEl?.offsetWidth ?? 0,
14332
+ left: selectedTriggerEl?.offsetLeft ?? 0
14333
+ }), [
14334
+ selectedTriggerSize,
14335
+ selectedTriggerEl
14336
+ ]);
14337
+ const isSSR = useIsSSR();
14338
+ return {
14339
+ refs,
14340
+ interactionState,
14341
+ value,
14342
+ isSSR,
14343
+ triggerRect,
14344
+ focusedValue,
14345
+ isFocusVisible,
14346
+ contentIndex,
14347
+ events,
14348
+ isFocusVisibleSupported
14349
+ };
14350
+ }
14351
+ function useTabs(props) {
14352
+ const autoId = React.useId();
14353
+ const { refs, interactionState, value, isSSR, events, triggerRect, focusedValue, isFocusVisible, contentIndex, isFocusVisibleSupported } = useTabsState(props);
14354
+ const { orientation = "horizontal" } = props;
14355
+ const focused = interactionState === "focused";
14356
+ const stateProps = React.useMemo(()=>domUtils.elementProps({
14357
+ "data-orientation": orientation,
14358
+ "data-focus": domUtils.dataAttr(focused),
14359
+ "data-ssr": domUtils.dataAttr(isSSR)
14360
+ }), [
14361
+ orientation,
14362
+ focused,
14363
+ isSSR
14364
+ ]);
14365
+ return {
14366
+ refs,
14367
+ value,
14368
+ contentIndex,
14369
+ triggerRect,
14370
+ selectNext: events.selectNext,
14371
+ selectPrev: events.selectPrev,
14372
+ setValue: events.setValue,
14373
+ setContentIndex: events.setContentIndex,
14374
+ stateProps,
14375
+ rootProps: domUtils.elementProps({
14376
+ ...stateProps,
14377
+ style: {
14378
+ "--indicator-left": `${triggerRect.left}px`,
14379
+ "--indicator-width": `${triggerRect.width}px`
14380
+ }
14381
+ }),
14382
+ listProps: domUtils.elementProps({
14383
+ id: getListId(autoId),
14384
+ role: "tablist",
14385
+ "aria-orientation": orientation,
14386
+ ...stateProps,
14387
+ onKeyDown (event) {
14388
+ if (event.defaultPrevented) return;
14389
+ if (event.nativeEvent.isComposing) return;
14390
+ // TODO: support activationMode="manual"
14391
+ switch(event.key){
14392
+ case "ArrowLeft":
14393
+ if (orientation !== "horizontal") return;
14394
+ event.preventDefault();
14395
+ events.arrowPrev();
14396
+ break;
14397
+ case "ArrowRight":
14398
+ if (orientation !== "horizontal") return;
14399
+ event.preventDefault();
14400
+ events.arrowNext();
14401
+ break;
14402
+ case "ArrowUp":
14403
+ if (orientation !== "vertical") return;
14404
+ event.preventDefault();
14405
+ events.arrowPrev();
14406
+ break;
14407
+ case "ArrowDown":
14408
+ if (orientation !== "vertical") return;
14409
+ event.preventDefault();
14410
+ events.arrowNext();
14411
+ break;
14412
+ case "Home":
14413
+ {
14414
+ event.preventDefault();
14415
+ events.home();
14416
+ break;
14417
+ }
14418
+ case "End":
14419
+ {
14420
+ event.preventDefault();
14421
+ events.end();
14422
+ break;
14423
+ }
14424
+ }
14425
+ }
14426
+ }),
14427
+ getTriggerProps: (props)=>{
14428
+ const { disabled: isDisabled, value: triggerValue } = props;
14429
+ const itemState = {
14430
+ isDisabled,
14431
+ isSelected: value === triggerValue,
14432
+ isFocused: focusedValue === triggerValue
14433
+ };
14434
+ const itemStateProps = {
14435
+ "data-focus": domUtils.dataAttr(itemState.isFocused),
14436
+ "data-focus-visible": domUtils.dataAttr(itemState.isFocused && isFocusVisible),
14437
+ "data-selected": domUtils.dataAttr(itemState.isSelected),
14438
+ "data-disabled": domUtils.dataAttr(itemState.isDisabled),
14439
+ "data-ssr": domUtils.dataAttr(isSSR),
14440
+ "aria-disabled": domUtils.ariaAttr(itemState.isDisabled),
14441
+ "aria-selected": domUtils.ariaAttr(itemState.isSelected)
14442
+ };
14443
+ const ref = (element)=>{
14444
+ if (element && triggerValue === value) {
14445
+ events.setSelectedTriggerEl(element);
14446
+ }
14447
+ };
14448
+ return {
14449
+ ...itemState,
14450
+ refs: {
14451
+ root: ref
14452
+ },
14453
+ stateProps: itemStateProps,
14454
+ rootProps: domUtils.buttonProps({
14455
+ id: getTriggerId(triggerValue, autoId),
14456
+ role: "tab",
14457
+ type: "button",
14458
+ disabled: isDisabled,
14459
+ tabIndex: itemState.isSelected ? 0 : -1,
14460
+ ...itemStateProps,
14461
+ "data-value": triggerValue,
14462
+ "data-orientation": orientation,
14463
+ "data-ownedby": getListId(autoId),
14464
+ "aria-controls": getContentId(triggerValue, autoId),
14465
+ onClick (event) {
14466
+ if (itemState.isDisabled) return;
14467
+ if (event.defaultPrevented) return;
14468
+ events.tabClick(triggerValue);
14469
+ },
14470
+ onFocus (event) {
14471
+ events.tabFocus(props.value);
14472
+ if (isFocusVisibleSupported) {
14473
+ events.setIsFocusVisible(event.target.matches(":focus-visible"));
14474
+ }
14475
+ },
14476
+ onBlur (event) {
14477
+ const target = event.relatedTarget;
14478
+ if (target?.getAttribute("role") !== "tab") {
14479
+ events.tabBlur();
14480
+ }
14481
+ if (isFocusVisibleSupported) {
14482
+ events.setIsFocusVisible(false);
14483
+ }
14484
+ }
14485
+ })
14486
+ };
14487
+ },
14488
+ getContentProps: (props)=>{
14489
+ const { value: contentValue } = props;
14490
+ const triggerId = getTriggerId(contentValue, autoId);
14491
+ const isSelected = value === contentValue;
14492
+ return domUtils.elementProps({
14493
+ id: getContentId(contentValue, autoId),
14494
+ tabIndex: -1,
14495
+ role: "tabpanel",
14496
+ "aria-labelledby": triggerId,
14497
+ "aria-selected": domUtils.ariaAttr(isSelected),
14498
+ "aria-hidden": !isSelected,
14499
+ "data-selected": domUtils.dataAttr(isSelected),
14500
+ "data-orientation": orientation,
14501
+ "data-ownedby": getListId(autoId),
14502
+ "data-ssr": domUtils.dataAttr(isSSR)
14503
+ });
14504
+ },
14505
+ indicatorProps: domUtils.elementProps({
14506
+ ...stateProps
14507
+ })
14508
+ };
14509
+ }
14510
+ const TabsContext = /*#__PURE__*/ React.createContext(null);
14511
+ const TabsProvider = TabsContext.Provider;
14512
+ function useTabsContext({ strict = true } = {}) {
14513
+ const context = React.useContext(TabsContext);
14514
+ if (!context && strict) {
14515
+ throw new Error("useTabsContext must be used within a Tabs");
14516
+ }
14517
+ return context;
14518
+ }
14519
+ const autoHeight = AutoHeight();
14520
+ const plugins = [
14521
+ autoHeight
14522
+ ];
14523
+ const useTabsCarouselState = (props)=>{
14524
+ const api = useTabsContext();
14525
+ const slideHeights = React.useRef(new WeakMap());
14526
+ const [emblaRef, emblaApi] = useEmblaCarousel({
14527
+ loop: props.loop,
14528
+ dragThreshold: props.dragThreshold,
14529
+ duration: 20,
14530
+ watchDrag: (_, event)=>{
14531
+ if (event.target instanceof HTMLElement) {
14532
+ if (isDragPrevented(event.target)) {
14533
+ return false;
14534
+ }
14535
+ }
14536
+ return true;
14537
+ },
14538
+ // Workaround for Embla v8 AutoHeight plugin not detecting lazy-loaded content height changes.
14539
+ // ref: https://github.com/davidjerleke/embla-carousel/discussions/1142
14540
+ // Embla's default resize handler only reInits on axis-size (width, for a horizontal carousel)
14541
+ // changes, so a slide growing taller (e.g. after lazy mount) never triggers a recalculation.
14542
+ // We reInit manually, but ONLY when a slide's *height* actually changes — reInit-ing on every
14543
+ // slide resize (including width fluctuations during horizontal scroll) re-applies Embla's
14544
+ // translate3d mid-scroll and breaks momentum scrolling in WKWebView/Safari.
14545
+ watchResize: (emblaApi, entries)=>{
14546
+ const heights = slideHeights.current;
14547
+ const slides = emblaApi.slideNodes();
14548
+ for (const entry of entries){
14549
+ const slide = slides.find((node)=>node === entry.target);
14550
+ if (!slide) continue;
14551
+ const newHeight = slide.offsetHeight;
14552
+ const prevHeight = heights.get(slide);
14553
+ heights.set(slide, newHeight);
14554
+ if (prevHeight !== undefined && newHeight !== prevHeight) {
14555
+ emblaApi.reInit();
14556
+ return false;
14557
+ }
14558
+ }
14559
+ return true;
14560
+ }
14561
+ }, plugins);
14562
+ const onSettle = useCallbackRef(props.onSettle);
14563
+ const onSwipeStart = useCallbackRef(props.onSwipeStart);
14564
+ const onSwipeEnd = useCallbackRef(props.onSwipeEnd);
14565
+ React.useEffect(()=>{
14566
+ const select = emblaApi?.on("select", ()=>{
14567
+ const contentIndex = emblaApi.selectedScrollSnap();
14568
+ api.setContentIndex(contentIndex);
14569
+ });
14570
+ const settle = emblaApi?.on("settle", ()=>{
14571
+ onSettle?.();
14572
+ });
14573
+ const pointerDown = emblaApi?.on("pointerDown", ()=>{
14574
+ onSwipeStart?.();
14575
+ });
14576
+ const pointerUp = emblaApi?.on("pointerUp", ()=>{
14577
+ onSwipeEnd?.();
14578
+ });
14579
+ return ()=>{
14580
+ select?.clear();
14581
+ settle?.clear();
14582
+ pointerDown?.clear();
14583
+ pointerUp?.clear();
14584
+ };
14585
+ }, [
14586
+ emblaApi,
14587
+ api.setContentIndex,
14588
+ onSettle,
14589
+ onSwipeStart,
14590
+ onSwipeEnd
14591
+ ]);
14592
+ const getContentIndex = useCallbackRef(()=>api.contentIndex);
14593
+ React.useEffect(()=>{
14594
+ const reInit = emblaApi?.on("reInit", ()=>{
14595
+ emblaApi?.scrollTo(getContentIndex(), true);
14596
+ });
14597
+ return ()=>{
14598
+ reInit?.clear();
14599
+ };
14600
+ }, [
14601
+ getContentIndex,
14602
+ emblaApi
14603
+ ]);
14604
+ React.useEffect(()=>{
14605
+ if (!emblaApi) return;
14606
+ const { dragHandler } = emblaApi.internalEngine();
14607
+ if (props.swipeable) {
14608
+ dragHandler.init(emblaApi);
14609
+ } else {
14610
+ dragHandler.destroy();
14611
+ }
14612
+ }, [
14613
+ emblaApi,
14614
+ props.swipeable
14615
+ ]);
14616
+ const isInitialScroll = React.useRef(true);
14617
+ React.useEffect(()=>{
14618
+ if (emblaApi && api.contentIndex !== emblaApi.selectedScrollSnap()) {
14619
+ const engine = emblaApi.internalEngine();
14620
+ /**
14621
+ * We want the content to quickly snap into place when a tab changes.
14622
+ * However, using a very small duration can result in an underdamped spring, which produces a rubber-band (oscillatory) effect.
14623
+ *
14624
+ * To avoid that, we intentionally use a low duration and friction so that the motion feels fast (snappy)
14625
+ * without overshooting. The underlying idea is to map our parameters to a physical spring model—a damped harmonic oscillator—
14626
+ * so we can control the dynamics in a physically intuitive way.
14627
+ *
14628
+ * In embla, the acceleration is modeled as:
14629
+ * a = (displacement * friction) / duration - velocity * (1 - friction)
14630
+ *
14631
+ * In a classical damped harmonic oscillator (assuming unit mass, m = 1), the acceleration is:
14632
+ * a = (stiffness * displacement - damping * velocity)
14633
+ *
14634
+ * By comparing the two equations, we can equate the coefficients for displacement and velocity:
14635
+ *
14636
+ * For the displacement term:
14637
+ * stiffness = friction / duration
14638
+ *
14639
+ * For the velocity term:
14640
+ * damping = 1 - friction
14641
+ *
14642
+ * Additionally, the damping ratio (zeta) for a damped oscillator is defined as:
14643
+ * zeta = damping / (2 * sqrt(stiffness))
14644
+ *
14645
+ * Substituting the derived expressions for stiffness and damping gives:
14646
+ * zeta = (1 - friction) / (2 * sqrt(friction / duration))
14647
+ *
14648
+ * This damping ratio (zeta) tells us the nature of the response:
14649
+ * - zeta < 1: Underdamped (oscillatory, rubber-band effect)
14650
+ * - zeta = 1: Critically damped (fast snap without overshoot)
14651
+ * - zeta > 1: Overdamped (slow response)
14652
+ *
14653
+ * For our current configuration, we use:
14654
+ *
14655
+ * duration = 4
14656
+ * friction = 0.4
14657
+ *
14658
+ * Which gives:
14659
+ *
14660
+ * stiffness = 0.4 / 4 = 0.1
14661
+ * damping = 1 - 0.4 = 0.6
14662
+ *
14663
+ * And the damping ratio becomes:
14664
+ *
14665
+ * zeta = 0.6 / (2 * sqrt(0.1))
14666
+ * = 0.6 / (2 * 0.31623...)
14667
+ * = 0.6 / 0.63246...
14668
+ * = 0.94868...
14669
+ *
14670
+ * Since zeta is close to 1, this spring is nearly critically damped. This is enough for our usage,
14671
+ * ensuring that the animation snaps quickly without overshooting (avoiding the rubber-band effect)
14672
+ * when switching tabs.
14673
+ */ if (isInitialScroll.current) {
14674
+ engine.scrollBody.useDuration(0);
14675
+ isInitialScroll.current = false;
14676
+ } else {
14677
+ engine.scrollBody.useDuration(4).useFriction(0.4);
14678
+ }
14679
+ engine.scrollTo.index(api.contentIndex, 0);
14680
+ }
14681
+ }, [
14682
+ emblaApi,
14683
+ api.contentIndex
14684
+ ]);
14685
+ return {
14686
+ refs: {
14687
+ root: emblaRef
14688
+ },
14689
+ autoHeight: props.autoHeight
14690
+ };
14691
+ };
14692
+ const useTabsCarousel = (props)=>{
14693
+ const { refs, autoHeight } = useTabsCarouselState(props);
14694
+ const stateProps = domUtils.elementProps({
14695
+ "data-carousel": "",
14696
+ "data-auto-height": domUtils.dataAttr(autoHeight)
14697
+ });
14698
+ return {
14699
+ refs,
14700
+ stateProps,
14701
+ rootProps: domUtils.elementProps({
14702
+ ...stateProps
14703
+ }),
14704
+ cameraProps: domUtils.elementProps({
14705
+ ...stateProps
14706
+ })
14707
+ };
14708
+ };
14709
+ const TabsCarouselContext = /*#__PURE__*/ React.createContext(null);
14710
+ const TabsCarouselProvider = TabsCarouselContext.Provider;
14711
+ function useTabsCarouselContext({ strict = true } = {}) {
14712
+ const context = React.useContext(TabsCarouselContext);
14713
+ if (!context && strict) {
14714
+ throw new Error("useTabsCarouselContext must be used within a TabsCarousel");
14715
+ }
14716
+ return context;
14717
+ }
14718
+ const TabsTriggerContext = /*#__PURE__*/ React.createContext(null);
14719
+ const TabsTriggerProvider = TabsTriggerContext.Provider;
14720
+ const TabsRoot$1 = /*#__PURE__*/ React.forwardRef((props, ref)=>{
14721
+ const { defaultValue, value, onValueChange, orientation, lazyMount, unmountOnExit, ...otherProps } = props;
14722
+ const api = useTabs({
14723
+ defaultValue,
14724
+ value,
14725
+ onValueChange,
14726
+ orientation
14727
+ });
14728
+ return /*#__PURE__*/ jsxRuntime.jsx(TabsProvider, {
14729
+ value: api,
14730
+ children: /*#__PURE__*/ jsxRuntime.jsx(RenderStrategyPropsProvider, {
14731
+ value: React.useMemo(()=>({
14732
+ lazyMount,
14733
+ unmountOnExit
14734
+ }), [
14735
+ lazyMount,
14736
+ unmountOnExit
14737
+ ]),
14738
+ children: /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14739
+ ref: ref,
14740
+ ...domUtils.mergeProps(api.rootProps, otherProps)
14741
+ })
14742
+ })
14743
+ });
14744
+ });
14745
+ TabsRoot$1.displayName = "TabsRoot";
14746
+ const TabsList$1 = /*#__PURE__*/ React.forwardRef((props, ref)=>{
14747
+ const api = useTabsContext();
14748
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14749
+ ref: composeRefs(api.refs.list, ref),
14750
+ ...domUtils.mergeProps(api.listProps, props)
14751
+ });
14752
+ });
14753
+ TabsList$1.displayName = "TabsList";
14754
+ const TabsTrigger$1 = /*#__PURE__*/ React.forwardRef((props, ref)=>{
14755
+ const { value, disabled, ...otherProps } = props;
14756
+ const api = useTabsContext();
14757
+ const triggerApi = api.getTriggerProps({
14758
+ value,
14759
+ disabled
14760
+ });
14761
+ return /*#__PURE__*/ jsxRuntime.jsx(TabsTriggerProvider, {
14762
+ value: triggerApi,
14763
+ children: /*#__PURE__*/ jsxRuntime.jsx(Primitive.button, {
14764
+ ref: composeRefs(triggerApi.refs.root, ref),
14765
+ ...domUtils.mergeProps(triggerApi.rootProps, otherProps)
14766
+ })
14767
+ });
14768
+ });
14769
+ TabsTrigger$1.displayName = "TabsTrigger";
14770
+ const TabsContent$1 = /*#__PURE__*/ React.forwardRef((props, ref)=>{
14771
+ const { value, children, ...otherProps } = props;
14772
+ const api = useTabsContext();
14773
+ const carouselApi = useTabsCarouselContext({
14774
+ strict: false
14775
+ });
14776
+ const inCarousel = carouselApi != null;
14777
+ const renderStrategyProps = useRenderStrategyPropsContext();
14778
+ const { unmounted } = useRenderStrategy({
14779
+ ...renderStrategyProps,
14780
+ present: api.value === value
14781
+ });
14782
+ // Outside a carousel, an unmounted tab renders nothing at all.
14783
+ // Inside a carousel, every tab must keep its slide slot even while unmounted:
14784
+ // the carousel syncs by numeric index, so collapsing the slide set (returning
14785
+ // null for lazy/exited tabs) makes embla's snap index drift from the tab's
14786
+ // logical index, and clicking/swiping to a far tab lands on the wrong one.
14787
+ // We keep the slot but still honor lazyMount/unmountOnExit for the children,
14788
+ // so expensive content stays lazy — only the empty slide wrapper persists.
14789
+ if (unmounted && !inCarousel) return null;
14790
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14791
+ ref: ref,
14792
+ ...domUtils.mergeProps(api.getContentProps({
14793
+ value
14794
+ }), carouselApi?.stateProps ?? {}, otherProps),
14795
+ children: unmounted ? null : children
14796
+ });
14797
+ });
14798
+ TabsContent$1.displayName = "TabsContent";
14799
+ const TabsIndicator$1 = /*#__PURE__*/ React.forwardRef((props, ref)=>{
14800
+ const api = useTabsContext();
14801
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14802
+ ref: ref,
14803
+ ...domUtils.mergeProps(api.indicatorProps, props)
14804
+ });
14805
+ });
14806
+ const TabsCarousel = /*#__PURE__*/ React.forwardRef((props, ref)=>{
14807
+ const { dragThreshold, loop, swipeable, autoHeight, onSettle, onSwipeStart, onSwipeEnd, ...otherProps } = props;
14808
+ const api = useTabsCarousel({
14809
+ dragThreshold,
14810
+ loop,
14811
+ swipeable,
14812
+ onSettle,
14813
+ autoHeight,
14814
+ onSwipeStart,
14815
+ onSwipeEnd
14816
+ });
14817
+ return /*#__PURE__*/ jsxRuntime.jsx(TabsCarouselProvider, {
14818
+ value: api,
14819
+ children: /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14820
+ ref: composeRefs(api.refs.root, ref),
14821
+ ...domUtils.mergeProps(api.rootProps, otherProps),
14822
+ children: props.children
14823
+ })
14824
+ });
14825
+ });
14826
+ TabsCarousel.displayName = "TabsCarousel";
14827
+ const TabsCarouselCamera = /*#__PURE__*/ React.forwardRef((props, ref)=>{
14828
+ const api = useTabsCarouselContext();
14829
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14830
+ ref: ref,
14831
+ ...domUtils.mergeProps(api.cameraProps, props)
14832
+ });
14833
+ });
14834
+ TabsCarouselCamera.displayName = "TabsCarouselCamera";
14835
+
14836
+ var Tabs_namespace = {
14837
+ __proto__: null,
14838
+ Carousel: TabsCarousel,
14839
+ CarouselCamera: TabsCarouselCamera,
14840
+ Content: TabsContent$1,
14841
+ Indicator: TabsIndicator$1,
14842
+ List: TabsList$1,
14843
+ Root: TabsRoot$1,
14844
+ Trigger: TabsTrigger$1};
14845
+
14846
+ const { withProvider, withContext } = createSlotRecipeContext(tabs.tabs);
14847
+ const TabsRoot = withProvider(Tabs_namespace.Root, "root");
14848
+ const TabsList = withContext(Tabs_namespace.List, "list");
14849
+ const TabsTrigger = withScaleFeedback(withContext(Tabs_namespace.Trigger, "trigger"));
14850
+ const TabsIndicator = withContext(Tabs_namespace.Indicator, "indicator");
14851
+ const TabsContent = withContext(Tabs_namespace.Content, "content");
14852
+ withContext(Tabs_namespace.Carousel, "carousel");
14853
+ withContext(Tabs_namespace.CarouselCamera, "carouselCamera");
14854
+
14855
+ const RESIZE_MIN_WIDTH = 360;
14856
+ const RESIZE_MAX_WIDTH = 720;
14857
+ /**
14858
+ * Right-side record peek panel -- Attio's own peek shows a Highlights card
14859
+ * area and an Activity feed (measured live). Ported from your-moon/crisp's
14860
+ * `RecordPanel` (`product/recordpanel/`), built ON `ResizablePanel`
14861
+ * (`side="right" floating`) rather than crisp's own duplicate absolute-
14862
+ * positioning/slide-in/resize-handle implementation -- the outer shell (the
14863
+ * enter/exit keyframe animation, the full-height overlay, the drag handle
14864
+ * itself) all come from that primitive; this component only renders the
14865
+ * CONTENT inside it.
14866
+ *
14867
+ * OVERLAY, not docked: `floating` (on the `ResizablePanel` this is built on)
14868
+ * now means a real `position: absolute` overlay that reserves ZERO layout
14869
+ * width -- opening this panel never shrinks or reflows a table beside it
14870
+ * (your-moon/crisp#448 reversed an earlier in-flow/docked attempt at this
14871
+ * exact spot, reported live as "table's action buttons moving"). Because of
14872
+ * that, THIS COMPONENT OWNS ITS OWN POSITIONING -- a consumer does not wrap
14873
+ * it in a flex row with the table; it renders `<RecordPreview>` as an
14874
+ * ordinary sibling of the table. The one requirement: the shared parent
14875
+ * (the table region's own wrapper) must itself be a positioned element
14876
+ * (typically `position: relative`) with a real height, since that's the
14877
+ * containing block this panel's `inset: 0` resolves against. See
14878
+ * `DataTable.stories.tsx`'s own Showcase for the pattern:
14879
+ *
14880
+ * ```tsx
14881
+ * <div style={{ position: "relative", flex: 1, minHeight: 0 }}>
14882
+ * <DataTable style={{ width: "100%", height: "100%" }} ... />
14883
+ * <RecordPreview open={open} record={record} onClose={onClose} ... />
14884
+ * </div>
14885
+ * ```
14886
+ *
14887
+ * Enter/exit: closing this panel is a state change (`open` -> false) that
14888
+ * typically clears `record` in the SAME update (see the DataTable story's
14889
+ * own `onClose={() => setPreviewRow(null)}`), so by the time an exit
14890
+ * animation should play, the values this component would render are
14891
+ * already gone. `usePresence` (`@seed-design/react-presence`, the same
14892
+ * idiom Dialog/Drawer already use for their own floating layers) keeps the
14893
+ * tree mounted for exactly the exit animation's duration; the snapshot
14894
+ * below keeps the LAST visible props around for that same window, since
14895
+ * without it the panel would animate out empty.
14896
+ */ function RecordPreview(props) {
14897
+ const shouldRender = props.open && props.record != null;
14898
+ const { isPresent, ref: panelElementRef } = usePresence(shouldRender);
11775
14899
  const lastVisiblePropsRef = React.useRef(props);
11776
14900
  if (shouldRender) lastVisiblePropsRef.current = props;
11777
14901
  if (!isPresent) return null;
@@ -11782,7 +14906,7 @@ const RESIZE_MAX_WIDTH = 720;
11782
14906
  });
11783
14907
  }
11784
14908
  RecordPreview.displayName = "RecordPreview";
11785
- function RecordPreviewPanel({ open: _open, record, onClose, className, animationState, panelElementRef, closeLabel = "Close panel", onOpenRecord, openRecordLabel = "Open record", onPrevious, previousRecordLabel = "Previous record", onNext, nextRecordLabel = "Next record", title, avatar, highlightsLabel = "Highlights", highlights, counterLabel, resizable = false, width, defaultWidth = 497, onWidthChange, minWidth = RESIZE_MIN_WIDTH, maxWidth = RESIZE_MAX_WIDTH, activityLabel = "Activity", viewAllActivityLabel = "View all", onViewAllActivity, activity, sections, footer, ...rest }) {
14909
+ function RecordPreviewPanel({ open: _open, record, onClose, className, animationState, panelElementRef, closeLabel = "Close panel", onOpenRecord, openRecordLabel = "Open record", onPrevious, previousRecordLabel = "Previous record", onNext, nextRecordLabel = "Next record", title, avatar, highlightsLabel = "Highlights", highlights, counterLabel, resizable = false, width, defaultWidth = 497, onWidthChange, minWidth = RESIZE_MIN_WIDTH, maxWidth = RESIZE_MAX_WIDTH, activityLabel = "Activity", viewAllActivityLabel = "View all", onViewAllActivity, activity, sections, tabs, activeTab, defaultActiveTab, onTabChange, overviewTabLabel = "Overview", footer, backdrop = false, ...rest }) {
11786
14910
  const titleId = React.useId();
11787
14911
  const classNames = recordPreview();
11788
14912
  // Which sections (your-moon/crisp#413) are expanded -- uncontrolled and
@@ -11822,9 +14946,143 @@ function RecordPreviewPanel({ open: _open, record, onClose, className, animation
11822
14946
  // Defensive only -- `RecordPreview` never mounts this without a record.
11823
14947
  if (!record) return null;
11824
14948
  const heading = title ?? record.name ?? "Record";
14949
+ // The plain (non-tabs) layout -- unchanged from before `tabs` existed.
14950
+ // Extracted so it can ALSO become the synthesized "Overview" tab's own
14951
+ // content below, rather than being re-authored as JSX twice.
14952
+ const overviewContent = /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
14953
+ children: [
14954
+ highlights && highlights.length > 0 && /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
14955
+ className: classNames.section,
14956
+ children: [
14957
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14958
+ className: classNames.sectionHead,
14959
+ children: /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
14960
+ className: classNames.sectionLabel,
14961
+ children: highlightsLabel
14962
+ })
14963
+ }),
14964
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14965
+ className: classNames.highlights,
14966
+ children: highlights.map((h)=>/*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
14967
+ className: classNames.highlight,
14968
+ children: [
14969
+ /*#__PURE__*/ jsxRuntime.jsxs(Primitive.span, {
14970
+ className: classNames.highlightLabel,
14971
+ children: [
14972
+ /*#__PURE__*/ jsxRuntime.jsx("span", {
14973
+ children: h.label
14974
+ }),
14975
+ h.icon ? /*#__PURE__*/ jsxRuntime.jsx(h.icon, {
14976
+ size: 16
14977
+ }) : null
14978
+ ]
14979
+ }),
14980
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
14981
+ className: classNames.highlightValue,
14982
+ children: h.value
14983
+ })
14984
+ ]
14985
+ }, h.key))
14986
+ })
14987
+ ]
14988
+ }),
14989
+ activity != null && /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
14990
+ className: classNames.section,
14991
+ children: [
14992
+ /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
14993
+ className: classNames.sectionHead,
14994
+ children: [
14995
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
14996
+ className: classNames.sectionLabel,
14997
+ children: activityLabel
14998
+ }),
14999
+ onViewAllActivity && /*#__PURE__*/ jsxRuntime.jsx(Primitive.button, {
15000
+ type: "button",
15001
+ className: classNames.sectionAction,
15002
+ onClick: onViewAllActivity,
15003
+ children: viewAllActivityLabel
15004
+ })
15005
+ ]
15006
+ }),
15007
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
15008
+ className: classNames.activity,
15009
+ children: activity
15010
+ })
15011
+ ]
15012
+ }),
15013
+ sections && sections.length > 0 && /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
15014
+ className: classNames.sections,
15015
+ children: sections.map((s)=>{
15016
+ const isOpen = openSections.has(s.key);
15017
+ const panelId = `${titleId}-section-${s.key}`;
15018
+ return /*#__PURE__*/ jsxRuntime.jsxs("div", {
15019
+ children: [
15020
+ s.content ? /*#__PURE__*/ jsxRuntime.jsxs(Primitive.button, {
15021
+ type: "button",
15022
+ className: classNames.sectionTrigger,
15023
+ "aria-expanded": isOpen,
15024
+ "aria-controls": panelId,
15025
+ onClick: ()=>toggleSection(s.key),
15026
+ children: [
15027
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
15028
+ className: classNames.sectionRowLabel,
15029
+ children: s.label
15030
+ }),
15031
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
15032
+ className: classNames.sectionRowCount,
15033
+ children: s.count
15034
+ }),
15035
+ /*#__PURE__*/ jsxRuntime.jsx(IconChevronDownLine_default, {
15036
+ className: classNames.sectionChevron,
15037
+ size: 14,
15038
+ "data-open": isOpen ? "" : undefined,
15039
+ "aria-hidden": "true"
15040
+ })
15041
+ ]
15042
+ }) : /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
15043
+ className: classNames.sectionTrigger,
15044
+ "data-inert": "",
15045
+ children: [
15046
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
15047
+ className: classNames.sectionRowLabel,
15048
+ children: s.label
15049
+ }),
15050
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
15051
+ className: classNames.sectionRowCount,
15052
+ children: s.count
15053
+ })
15054
+ ]
15055
+ }),
15056
+ s.content && isOpen && /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
15057
+ id: panelId,
15058
+ className: classNames.sectionContent,
15059
+ children: s.content
15060
+ })
15061
+ ]
15062
+ }, s.key);
15063
+ })
15064
+ })
15065
+ ]
15066
+ });
15067
+ // Combine (`tabs`'s own doc comment): when there's ALSO plain overview
15068
+ // content given alongside `tabs`, prepend a synthesized tab carrying it,
15069
+ // ahead of the consumer's own tabs -- so `tabs` and the simpler Highlights/
15070
+ // Activity/sections props stay usable TOGETHER rather than one silently
15071
+ // replacing the other.
15072
+ const hasOverviewContent = highlights && highlights.length > 0 || activity != null || sections && sections.length > 0;
15073
+ const allTabs = tabs && hasOverviewContent ? [
15074
+ {
15075
+ key: "__overview",
15076
+ label: overviewTabLabel,
15077
+ content: overviewContent
15078
+ },
15079
+ ...tabs
15080
+ ] : tabs ?? [];
11825
15081
  return /*#__PURE__*/ jsxRuntime.jsx(ResizablePanel, {
11826
15082
  side: "right",
11827
15083
  floating: true,
15084
+ backdrop: backdrop,
15085
+ onBackdropClick: onClose,
11828
15086
  animationState: animationState,
11829
15087
  panelElementRef: panelElementRef,
11830
15088
  resizable: resizable,
@@ -11914,121 +15172,49 @@ function RecordPreviewPanel({ open: _open, record, onClose, className, animation
11914
15172
  })
11915
15173
  ]
11916
15174
  }),
11917
- /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11918
- className: classNames.body,
11919
- children: [
11920
- highlights && highlights.length > 0 && /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11921
- className: classNames.section,
11922
- children: [
11923
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11924
- className: classNames.sectionHead,
11925
- children: /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
11926
- className: classNames.sectionLabel,
11927
- children: highlightsLabel
11928
- })
11929
- }),
11930
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11931
- className: classNames.highlights,
11932
- children: highlights.map((h)=>/*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11933
- className: classNames.highlight,
11934
- children: [
11935
- /*#__PURE__*/ jsxRuntime.jsxs(Primitive.span, {
11936
- className: classNames.highlightLabel,
11937
- children: [
11938
- /*#__PURE__*/ jsxRuntime.jsx("span", {
11939
- children: h.label
11940
- }),
11941
- h.icon ? /*#__PURE__*/ jsxRuntime.jsx(h.icon, {
11942
- size: 16
11943
- }) : null
11944
- ]
11945
- }),
11946
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
11947
- className: classNames.highlightValue,
11948
- children: h.value
11949
- })
11950
- ]
11951
- }, h.key))
11952
- })
11953
- ]
11954
- }),
11955
- activity != null && /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11956
- className: classNames.section,
11957
- children: [
11958
- /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11959
- className: classNames.sectionHead,
11960
- children: [
11961
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
11962
- className: classNames.sectionLabel,
11963
- children: activityLabel
11964
- }),
11965
- onViewAllActivity && /*#__PURE__*/ jsxRuntime.jsx(Primitive.button, {
11966
- type: "button",
11967
- className: classNames.sectionAction,
11968
- onClick: onViewAllActivity,
11969
- children: viewAllActivityLabel
11970
- })
11971
- ]
11972
- }),
11973
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11974
- className: classNames.activity,
11975
- children: activity
11976
- })
11977
- ]
11978
- }),
11979
- sections && sections.length > 0 && /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11980
- className: classNames.sections,
11981
- children: sections.map((s)=>{
11982
- const isOpen = openSections.has(s.key);
11983
- const panelId = `${titleId}-section-${s.key}`;
11984
- return /*#__PURE__*/ jsxRuntime.jsxs("div", {
11985
- children: [
11986
- s.content ? /*#__PURE__*/ jsxRuntime.jsxs(Primitive.button, {
11987
- type: "button",
11988
- className: classNames.sectionTrigger,
11989
- "aria-expanded": isOpen,
11990
- "aria-controls": panelId,
11991
- onClick: ()=>toggleSection(s.key),
11992
- children: [
11993
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
11994
- className: classNames.sectionRowLabel,
11995
- children: s.label
11996
- }),
11997
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
11998
- className: classNames.sectionRowCount,
11999
- children: s.count
12000
- }),
12001
- /*#__PURE__*/ jsxRuntime.jsx(IconChevronDownLine_default, {
12002
- className: classNames.sectionChevron,
12003
- size: 14,
12004
- "data-open": isOpen ? "" : undefined,
12005
- "aria-hidden": "true"
12006
- })
12007
- ]
12008
- }) : /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
12009
- className: classNames.sectionTrigger,
12010
- "data-inert": "",
15175
+ tabs && tabs.length > 0 ? /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
15176
+ className: classNames.tabsRoot,
15177
+ children: /*#__PURE__*/ jsxRuntime.jsxs(TabsRoot, {
15178
+ value: activeTab,
15179
+ defaultValue: defaultActiveTab ?? allTabs[0]?.key,
15180
+ onValueChange: onTabChange,
15181
+ style: {
15182
+ display: "flex",
15183
+ flexDirection: "column",
15184
+ flex: "1 1 auto",
15185
+ minHeight: 0,
15186
+ overflow: "hidden"
15187
+ },
15188
+ children: [
15189
+ /*#__PURE__*/ jsxRuntime.jsxs(TabsList, {
15190
+ children: [
15191
+ allTabs.map((tab)=>/*#__PURE__*/ jsxRuntime.jsxs(TabsTrigger, {
15192
+ value: tab.key,
12011
15193
  children: [
12012
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
12013
- className: classNames.sectionRowLabel,
12014
- children: s.label
12015
- }),
12016
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
12017
- className: classNames.sectionRowCount,
12018
- children: s.count
15194
+ tab.label,
15195
+ tab.badge != null && /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
15196
+ className: classNames.tabBadge,
15197
+ children: tab.badge
12019
15198
  })
12020
15199
  ]
12021
- }),
12022
- s.content && isOpen && /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
12023
- id: panelId,
12024
- className: classNames.sectionContent,
12025
- children: s.content
12026
- })
12027
- ]
12028
- }, s.key);
12029
- })
12030
- })
12031
- ]
15200
+ }, tab.key)),
15201
+ /*#__PURE__*/ jsxRuntime.jsx(TabsIndicator, {})
15202
+ ]
15203
+ }),
15204
+ allTabs.map((tab)=>/*#__PURE__*/ jsxRuntime.jsx(TabsContent, {
15205
+ value: tab.key,
15206
+ className: classNames.tabPanel,
15207
+ style: {
15208
+ flex: "1 1 auto",
15209
+ minHeight: 0
15210
+ },
15211
+ children: tab.content
15212
+ }, tab.key))
15213
+ ]
15214
+ })
15215
+ }) : /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
15216
+ className: classNames.body,
15217
+ children: overviewContent
12032
15218
  }),
12033
15219
  footer != null && /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
12034
15220
  className: classNames.footer,