@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.js CHANGED
@@ -10,8 +10,8 @@ function __insertCSS(code) {
10
10
 
11
11
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
12
12
  import * as React from 'react';
13
- import React__default, { forwardRef, useCallback, useMemo, useState, useRef, useEffect, createContext, useContext, useId } from 'react';
14
- import { dataAttr, elementProps, ariaAttr, mergeProps as mergeProps$1 } from '@seed-design/dom-utils';
13
+ import React__default, { forwardRef, useCallback, useState, useMemo, useRef, useEffect, createContext, useContext, useId } from 'react';
14
+ import { dataAttr, elementProps, ariaAttr, mergeProps as mergeProps$1, buttonProps } from '@seed-design/dom-utils';
15
15
  import { checkbox } from '@seed-design/css/recipes/checkbox';
16
16
  import { checkmark } from '@seed-design/css/recipes/checkmark';
17
17
  import { Checkbox, useCheckboxContext } from '@seed-design/react-checkbox';
@@ -34,6 +34,7 @@ import { textInput } from '@seed-design/css/recipes/text-input';
34
34
  import { Menu, useMenuContext, useMenuItemContext } from '@seed-design/react-menu';
35
35
  import { menu } from '@seed-design/css/recipes/menu';
36
36
  import { menuItem } from '@seed-design/css/recipes/menu-item';
37
+ import { tabs } from '@seed-design/css/recipes/tabs';
37
38
 
38
39
  // src/IconArrowUpLine.tsx
39
40
  var Icon$h = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
@@ -205,36 +206,8 @@ var Icon$b = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
205
206
  var IconDot3HorizontalLine = /*#__PURE__*/ forwardRef(Icon$b);
206
207
  var IconDot3HorizontalLine_default = IconDot3HorizontalLine;
207
208
 
208
- // src/IconEyeLine.tsx
209
- var Icon$a = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
210
- viewBox: "0 0 24 24",
211
- fill: "none",
212
- xmlns: "http://www.w3.org/2000/svg",
213
- "data-seed-icon": "true",
214
- width: size,
215
- height: size,
216
- ref,
217
- ...props,
218
- children: /* @__PURE__ */ jsx("g", {
219
- children: /* @__PURE__ */ jsxs("g", {
220
- children: [
221
- /* @__PURE__ */ jsx("path", {
222
- 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",
223
- fill: "currentColor"
224
- }),
225
- /* @__PURE__ */ jsx("path", {
226
- 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",
227
- fill: "currentColor"
228
- })
229
- ]
230
- })
231
- })
232
- });
233
- var IconEyeLine = /*#__PURE__*/ forwardRef(Icon$a);
234
- var IconEyeLine_default = IconEyeLine;
235
-
236
209
  // src/IconMagnifyingglassLine.tsx
237
- var Icon$9 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
210
+ var Icon$a = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
238
211
  viewBox: "0 0 24 24",
239
212
  fill: "none",
240
213
  xmlns: "http://www.w3.org/2000/svg",
@@ -252,11 +225,11 @@ var Icon$9 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
252
225
  })
253
226
  })
254
227
  });
255
- var IconMagnifyingglassLine = /*#__PURE__*/ forwardRef(Icon$9);
228
+ var IconMagnifyingglassLine = /*#__PURE__*/ forwardRef(Icon$a);
256
229
  var IconMagnifyingglassLine_default = IconMagnifyingglassLine;
257
230
 
258
231
  // src/IconSlider2HorizontalLine.tsx
259
- var Icon$8 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
232
+ var Icon$9 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
260
233
  viewBox: "0 0 24 24",
261
234
  fill: "none",
262
235
  xmlns: "http://www.w3.org/2000/svg",
@@ -284,11 +257,11 @@ var Icon$8 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
284
257
  })
285
258
  })
286
259
  });
287
- var IconSlider2HorizontalLine = /*#__PURE__*/ forwardRef(Icon$8);
260
+ var IconSlider2HorizontalLine = /*#__PURE__*/ forwardRef(Icon$9);
288
261
  var IconSlider2HorizontalLine_default = IconSlider2HorizontalLine;
289
262
 
290
263
  // src/IconPersonCircleLine.tsx
291
- var Icon$7 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
264
+ var Icon$8 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
292
265
  viewBox: "0 0 24 24",
293
266
  fill: "none",
294
267
  xmlns: "http://www.w3.org/2000/svg",
@@ -316,11 +289,11 @@ var Icon$7 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
316
289
  })
317
290
  })
318
291
  });
319
- var IconPersonCircleLine = /*#__PURE__*/ forwardRef(Icon$7);
292
+ var IconPersonCircleLine = /*#__PURE__*/ forwardRef(Icon$8);
320
293
  var IconPersonCircleLine_default = IconPersonCircleLine;
321
294
 
322
295
  // src/IconPlusLine.tsx
323
- var Icon$6 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
296
+ var Icon$7 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
324
297
  viewBox: "0 0 24 24",
325
298
  fill: "none",
326
299
  xmlns: "http://www.w3.org/2000/svg",
@@ -336,11 +309,11 @@ var Icon$6 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
336
309
  })
337
310
  })
338
311
  });
339
- var IconPlusLine = /*#__PURE__*/ forwardRef(Icon$6);
312
+ var IconPlusLine = /*#__PURE__*/ forwardRef(Icon$7);
340
313
  var IconPlusLine_default = IconPlusLine;
341
314
 
342
315
  // src/IconMinusLine.tsx
343
- var Icon$5 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
316
+ var Icon$6 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
344
317
  viewBox: "0 0 24 24",
345
318
  fill: "none",
346
319
  xmlns: "http://www.w3.org/2000/svg",
@@ -356,11 +329,11 @@ var Icon$5 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
356
329
  })
357
330
  })
358
331
  });
359
- var IconMinusLine = /*#__PURE__*/ forwardRef(Icon$5);
332
+ var IconMinusLine = /*#__PURE__*/ forwardRef(Icon$6);
360
333
  var IconMinusLine_default = IconMinusLine;
361
334
 
362
335
  // src/IconXmarkLine.tsx
363
- var Icon$4 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
336
+ var Icon$5 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
364
337
  viewBox: "0 0 24 24",
365
338
  fill: "none",
366
339
  xmlns: "http://www.w3.org/2000/svg",
@@ -376,11 +349,11 @@ var Icon$4 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
376
349
  })
377
350
  })
378
351
  });
379
- var IconXmarkLine = /*#__PURE__*/ forwardRef(Icon$4);
352
+ var IconXmarkLine = /*#__PURE__*/ forwardRef(Icon$5);
380
353
  var IconXmarkLine_default = IconXmarkLine;
381
354
 
382
355
  // src/IconCheckmarkLine.tsx
383
- var Icon$3 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
356
+ var Icon$4 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
384
357
  viewBox: "0 0 24 24",
385
358
  fill: "none",
386
359
  xmlns: "http://www.w3.org/2000/svg",
@@ -398,9 +371,45 @@ var Icon$3 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
398
371
  })
399
372
  })
400
373
  });
401
- var IconCheckmarkLine = /*#__PURE__*/ forwardRef(Icon$3);
374
+ var IconCheckmarkLine = /*#__PURE__*/ forwardRef(Icon$4);
402
375
  var IconCheckmarkLine_default = IconCheckmarkLine;
403
376
 
377
+ // src/IconCorner4OutwardLine.tsx
378
+ var Icon$3 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
379
+ viewBox: "0 0 24 24",
380
+ fill: "none",
381
+ xmlns: "http://www.w3.org/2000/svg",
382
+ "data-seed-icon": "true",
383
+ width: size,
384
+ height: size,
385
+ ref,
386
+ ...props,
387
+ children: /* @__PURE__ */ jsx("g", {
388
+ children: /* @__PURE__ */ jsxs("g", {
389
+ children: [
390
+ /* @__PURE__ */ jsx("path", {
391
+ 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",
392
+ fill: "currentColor"
393
+ }),
394
+ /* @__PURE__ */ jsx("path", {
395
+ 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",
396
+ fill: "currentColor"
397
+ }),
398
+ /* @__PURE__ */ jsx("path", {
399
+ 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",
400
+ fill: "currentColor"
401
+ }),
402
+ /* @__PURE__ */ jsx("path", {
403
+ 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",
404
+ fill: "currentColor"
405
+ })
406
+ ]
407
+ })
408
+ })
409
+ });
410
+ var IconCorner4OutwardLine = /*#__PURE__*/ forwardRef(Icon$3);
411
+ var IconCorner4OutwardLine_default = IconCorner4OutwardLine;
412
+
404
413
  // src/IconChevronDownSmallLine.tsx
405
414
  var Icon$2 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
406
415
  viewBox: "0 0 24 24",
@@ -443,8 +452,8 @@ var Icon$1 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsx("svg", {
443
452
  var IconCheckmarkFatFill = /*#__PURE__*/ forwardRef(Icon$1);
444
453
  var IconCheckmarkFatFill_default = IconCheckmarkFatFill;
445
454
 
446
- var __defProp$3 = Object.defineProperty;
447
- var __name$3 = (target, value)=>__defProp$3(target, "name", {
455
+ var __defProp$5 = Object.defineProperty;
456
+ var __name$5 = (target, value)=>__defProp$5(target, "name", {
448
457
  value,
449
458
  configurable: true
450
459
  });
@@ -455,7 +464,7 @@ function setRef(ref, value) {
455
464
  ref.current = value;
456
465
  }
457
466
  }
458
- __name$3(setRef, "setRef");
467
+ __name$5(setRef, "setRef");
459
468
  function composeRefs(...refs) {
460
469
  return (node)=>{
461
470
  let hasCleanup = false;
@@ -480,14 +489,14 @@ function composeRefs(...refs) {
480
489
  }
481
490
  };
482
491
  }
483
- __name$3(composeRefs, "composeRefs");
492
+ __name$5(composeRefs, "composeRefs");
484
493
  function useComposedRefs(...refs) {
485
494
  return React.useCallback(composeRefs(...refs), refs);
486
495
  }
487
- __name$3(useComposedRefs, "useComposedRefs");
496
+ __name$5(useComposedRefs, "useComposedRefs");
488
497
 
489
- var __defProp$2 = Object.defineProperty;
490
- var __name$2 = (target, value)=>__defProp$2(target, "name", {
498
+ var __defProp$4 = Object.defineProperty;
499
+ var __name$4 = (target, value)=>__defProp$4(target, "name", {
491
500
  value,
492
501
  configurable: true
493
502
  });
@@ -541,18 +550,18 @@ function createSlot(ownerName) {
541
550
  Slot2.displayName = `${ownerName}.Slot`;
542
551
  return Slot2;
543
552
  }
544
- __name$2(createSlot, "createSlot");
553
+ __name$4(createSlot, "createSlot");
545
554
  var Slot = /* @__PURE__ */ createSlot("Slot");
546
555
  var SLOTTABLE_IDENTIFIER = Symbol.for("radix.slottable");
547
556
  // @__NO_SIDE_EFFECTS__
548
557
  function createSlottable(ownerName) {
549
- const Slottable2 = /* @__PURE__ */ __name$2((props)=>"child" in props ? props.children(props.child) : props.children, "Slottable");
558
+ const Slottable2 = /* @__PURE__ */ __name$4((props)=>"child" in props ? props.children(props.child) : props.children, "Slottable");
550
559
  Slottable2.displayName = `${ownerName}.Slottable`;
551
560
  Slottable2.__radixId = SLOTTABLE_IDENTIFIER;
552
561
  return Slottable2;
553
562
  }
554
- __name$2(createSlottable, "createSlottable");
555
- var getSlottableElementFromSlottable = /* @__PURE__ */ __name$2((slottable, child)=>{
563
+ __name$4(createSlottable, "createSlottable");
564
+ var getSlottableElementFromSlottable = /* @__PURE__ */ __name$4((slottable, child)=>{
556
565
  if ("child" in slottable.props) {
557
566
  const child2 = slottable.props.child;
558
567
  if (!/*#__PURE__*/ React.isValidElement(child2)) return null;
@@ -595,7 +604,7 @@ function mergeProps(slotProps, childProps) {
595
604
  ...overrideProps
596
605
  };
597
606
  }
598
- __name$2(mergeProps, "mergeProps");
607
+ __name$4(mergeProps, "mergeProps");
599
608
  function getElementRef(element) {
600
609
  let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
601
610
  let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
@@ -609,24 +618,24 @@ function getElementRef(element) {
609
618
  }
610
619
  return element.props.ref || element.ref;
611
620
  }
612
- __name$2(getElementRef, "getElementRef");
621
+ __name$4(getElementRef, "getElementRef");
613
622
  function isSlottable(child) {
614
623
  return /*#__PURE__*/ React.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
615
624
  }
616
- __name$2(isSlottable, "isSlottable");
625
+ __name$4(isSlottable, "isSlottable");
617
626
  var REACT_LAZY_TYPE = Symbol.for("react.lazy");
618
627
  function isLazyComponent(element) {
619
628
  return element != null && typeof element === "object" && "$$typeof" in element && element.$$typeof === REACT_LAZY_TYPE && "_payload" in element && isPromiseLike(element._payload);
620
629
  }
621
- __name$2(isLazyComponent, "isLazyComponent");
630
+ __name$4(isLazyComponent, "isLazyComponent");
622
631
  function isPromiseLike(value) {
623
632
  return typeof value === "object" && value !== null && "then" in value;
624
633
  }
625
- __name$2(isPromiseLike, "isPromiseLike");
626
- var createSlotError = /* @__PURE__ */ __name$2((ownerName)=>{
634
+ __name$4(isPromiseLike, "isPromiseLike");
635
+ var createSlotError = /* @__PURE__ */ __name$4((ownerName)=>{
627
636
  return `${ownerName} failed to slot onto its children. Expected a single React element child or \`Slottable\`.`;
628
637
  }, "createSlotError");
629
- var createSlottableError = /* @__PURE__ */ __name$2((ownerName)=>{
638
+ var createSlottableError = /* @__PURE__ */ __name$4((ownerName)=>{
630
639
  return `${ownerName} failed to slot onto its \`Slottable\`. Expected \`Slottable\` to receive a single React element child.`;
631
640
  }, "createSlottableError");
632
641
  var use = React[" use ".trim().toString()];
@@ -873,6 +882,40 @@ function useDataTableColumnCalc(props) {
873
882
  };
874
883
  }
875
884
 
885
+ function sameCell$1(a, id, key) {
886
+ return a !== null && a.id === id && a.key === key;
887
+ }
888
+ /**
889
+ * Tracks which single cell (if any) is showing its "show full content" overlay
890
+ * (DATATABLE-ATTIO-SPEC.md's cell-expand feature, #12) for the READ-ONLY case -- a
891
+ * non-`editable` column with truncated content, which has nothing to commit and so
892
+ * no reason to go through `useDataTableEdit`'s `editingCell`. An `editable` column's
893
+ * own truncated-cell overlay instead reuses `useDataTableEdit`'s `startEdit`/
894
+ * `editingCell` directly (the styled layer's `body-cells.tsx` picks which of the two
895
+ * a given activation opens) -- "for editable cells it's the editor" per the spec,
896
+ * not a second parallel state machine duplicating what editing already tracks.
897
+ *
898
+ * Only one cell can be peeked at a time, the same single-cell shape
899
+ * `useDataTableEdit`'s `editingCell` already uses -- this table has no multi-cell
900
+ * expand any more than it has multi-cell edit.
901
+ */ function useDataTableCellPeek() {
902
+ const [peekedCell, setPeekedCell] = useState(null);
903
+ const isPeeking = useCallback((id, key)=>sameCell$1(peekedCell, id, key), [
904
+ peekedCell
905
+ ]);
906
+ const openPeek = useCallback((id, key)=>setPeekedCell({
907
+ id,
908
+ key
909
+ }), []);
910
+ const closePeek = useCallback(()=>setPeekedCell(null), []);
911
+ return {
912
+ peekedCell,
913
+ isPeeking,
914
+ openPeek,
915
+ closePeek
916
+ };
917
+ }
918
+
876
919
  // Select's `dom.ts` reads state off REAL rendered elements (aria-disabled, data-value)
877
920
  // because its options register asynchronously through a floating-ui list and the DOM
878
921
  // is the one thing always in sync with `elementsRef`. DataTable's selection has no
@@ -1888,6 +1931,11 @@ function useDataTable(props) {
1888
1931
  const columnCalc = useDataTableColumnCalc(props);
1889
1932
  const scroll = useDataTableScroll();
1890
1933
  const edit = useDataTableEdit(props);
1934
+ // Feature C (#12) -- which single cell (if any) is showing its read-only "show
1935
+ // full content" overlay. Independent of `edit` above -- see
1936
+ // `useDataTableCellPeek.ts`'s own doc comment for why an `editable` column's
1937
+ // truncated-cell overlay reuses `edit` instead of this.
1938
+ const peek = useDataTableCellPeek();
1891
1939
  // Wired to `scroll`'s own scroll-container ref (not a fresh one of its own) so a
1892
1940
  // BOUNDED table's auto-load triggers off its own internal scroll, not the
1893
1941
  // browsing viewport -- see useDataTableInfiniteScroll.ts's own doc comment.
@@ -1905,6 +1953,7 @@ function useDataTable(props) {
1905
1953
  ...columnCalc,
1906
1954
  ...scroll,
1907
1955
  ...edit,
1956
+ ...peek,
1908
1957
  ...infiniteScroll,
1909
1958
  rootProps: elementProps({
1910
1959
  role: "table"
@@ -2432,7 +2481,7 @@ function createRecipeContext(recipe) {
2432
2481
  }
2433
2482
 
2434
2483
  const { withContext: withGroupContext } = createRecipeContext(checkboxGroup);
2435
- const { ClassNamesProvider: ClassNamesProvider$3, withContext: withContext$5 } = createSlotRecipeContext(checkbox);
2484
+ const { ClassNamesProvider: ClassNamesProvider$3, withContext: withContext$6 } = createSlotRecipeContext(checkbox);
2436
2485
  const { withProvider: withCheckmarkProvider, useClassNames: useCheckmarkClassNames, PropsProvider: CheckmarkPropsProvider } = createSlotRecipeContext(checkmark);
2437
2486
  const withStateProps$4 = createWithStateProps([
2438
2487
  useCheckboxContext
@@ -2486,14 +2535,14 @@ const CheckboxIndicator = /*#__PURE__*/ forwardRef(({ unchecked: uncheckedSvg, c
2486
2535
  return null;
2487
2536
  });
2488
2537
  CheckboxIndicator.displayName = "CheckboxIndicator";
2489
- const CheckboxLabel = withContext$5(withStateProps$4(Primitive.span), "label");
2538
+ const CheckboxLabel = withContext$6(withStateProps$4(Primitive.span), "label");
2490
2539
  const CheckboxHiddenInput = Checkbox.HiddenInput;
2491
2540
 
2492
- const { withProvider: withProvider$1, withContext: withContext$4 } = createSlotRecipeContext(avatar);
2493
- const AvatarRoot = withProvider$1(Image.Root, "root");
2494
- const AvatarImage = withContext$4(Image.Content, "image");
2495
- const AvatarFallback = withContext$4(Image.Fallback, "fallback");
2496
- withContext$4(Primitive.div, "badge");
2541
+ const { withProvider: withProvider$2, withContext: withContext$5 } = createSlotRecipeContext(avatar);
2542
+ const AvatarRoot = withProvider$2(Image.Root, "root");
2543
+ const AvatarImage = withContext$5(Image.Content, "image");
2544
+ const AvatarFallback = withContext$5(Image.Fallback, "fallback");
2545
+ withContext$5(Primitive.div, "badge");
2497
2546
 
2498
2547
  const Badge = /*#__PURE__*/ forwardRef(({ className, children, ...props }, ref)=>{
2499
2548
  const [variantProps, restProps] = badge.splitVariantProps(props);
@@ -2545,6 +2594,41 @@ function toneFor(value) {
2545
2594
  }
2546
2595
  return BADGE_TONES[hash % BADGE_TONES.length];
2547
2596
  }
2597
+ // Feature A/B (#9/#8) -- Attio's own flat 12-swatch chip palette, ported from
2598
+ // crisp's `tokens/palette.ts` (`tagSwatch1..12`, measured live off Attio) rather
2599
+ // than seed's own 6-tone `Badge` (`BADGE_TONES` above): a swatch, unlike a Badge
2600
+ // tone, has no ring/border and its ink is a FLAT black on every swatch (crisp's own
2601
+ // `tagSwatchInk`, `#000000` -- every swatch background is already a pale tint, so
2602
+ // one dark foreground reads on all 12), not a per-tone contrast color. No seed token
2603
+ // lands on any of these 12 hex values, so -- same footnote as the qvism recipe's own
2604
+ // `ATTIO_BRAND_BLUE`/`RATING_FILLED` literals -- they stay literal here too.
2605
+ const CHIP_SWATCHES = [
2606
+ "#fff3cc",
2607
+ "#f4fbcb",
2608
+ "#fdf7c4",
2609
+ "#feecf3",
2610
+ "#e0fced",
2611
+ "#ddf7ff",
2612
+ "#f6f7f7",
2613
+ "#ffebeb",
2614
+ "#feeee1",
2615
+ "#e5eeff",
2616
+ "#f5f0ff",
2617
+ "#fdedff"
2618
+ ];
2619
+ const CHIP_SWATCH_INK = "#000000";
2620
+ /** Same stable multiply-and-add hash `toneFor` above already uses over seed's 6
2621
+ * Badge tones, just over this file's own 12-swatch palette instead -- same value,
2622
+ * same swatch, every render (crisp's own `toneFor`, `datatable.tsx`). */ function swatchIndexFor(value) {
2623
+ let hash = 0;
2624
+ for(let index = 0; index < value.length; index++){
2625
+ hash = hash * 31 + value.charCodeAt(index) >>> 0;
2626
+ }
2627
+ return hash % CHIP_SWATCHES.length;
2628
+ }
2629
+ function swatchColorFor(value) {
2630
+ return CHIP_SWATCHES[swatchIndexFor(value)];
2631
+ }
2548
2632
  function initialsFor(name) {
2549
2633
  const parts = name.trim().split(/\s+/);
2550
2634
  return ((parts[0]?.[0] ?? "") + (parts[1]?.[0] ?? "")).toUpperCase() || "?";
@@ -2628,7 +2712,7 @@ function EntityCell({ value, className, nameClassName, actionsClassName, actionC
2628
2712
  event.stopPropagation();
2629
2713
  column.onOpenPreview?.(row);
2630
2714
  },
2631
- children: /*#__PURE__*/ jsx(IconEyeLine_default, {
2715
+ children: /*#__PURE__*/ jsx(IconCorner4OutwardLine_default, {
2632
2716
  size: 16
2633
2717
  })
2634
2718
  })
@@ -2659,11 +2743,89 @@ function StatusCell({ value }) {
2659
2743
  children: status
2660
2744
  });
2661
2745
  }
2662
- function LinkCell({ column, value, className }) {
2746
+ /** One swatch chip -- `style` carries the per-value background/ink (computed by
2747
+ * `swatchColorFor`/`CHIP_SWATCH_INK` above) since neither is a recipe token, the
2748
+ * same "literal color as a computed prop" call `RatingCell`'s star fill already
2749
+ * makes. Shared by `ChipCell` (Feature A, single value) and `ChipListCell`/
2750
+ * editors.tsx's `ChipListEditor` (Feature B, multi value) so all three chip
2751
+ * renderings stay byte-identical. */ function SwatchChip({ value, className, children }) {
2752
+ return /*#__PURE__*/ jsxs("span", {
2753
+ className: className,
2754
+ style: {
2755
+ backgroundColor: swatchColorFor(value),
2756
+ color: CHIP_SWATCH_INK
2757
+ },
2758
+ children: [
2759
+ value,
2760
+ children
2761
+ ]
2762
+ });
2763
+ }
2764
+ /** `type: "chip"` (Feature A, #9) -- a single value rendered as one colored chip,
2765
+ * the color derived deterministically from the value string (`swatchColorFor`):
2766
+ * same value, same color, every render, every column. For a column like provider/
2767
+ * tenant/vendor/status/select where a plain `Badge` (seed's 6-tone `status`/`tags`
2768
+ * types above) doesn't need to be color-STABLE per value -- this one does. */ function ChipCell({ value, className }) {
2769
+ const text = value == null ? "" : String(value);
2770
+ if (!text) return null;
2771
+ return /*#__PURE__*/ jsx(SwatchChip, {
2772
+ value: text,
2773
+ className: className
2774
+ });
2775
+ }
2776
+ /** `type: "chip-list"`, READ-ONLY (Feature B, #8) -- multiple swatch chips, no add/
2777
+ * remove affordances. The editable case is editors.tsx's `ChipListEditor`, an
2778
+ * always-live widget (interacting with it IS the commit, the same `switch`/
2779
+ * `rating`/`checkbox` shape `body-cells.tsx`'s `isAlwaysLiveEditable` already
2780
+ * covers) -- this component only ever renders the REST display. */ function ChipListCell({ value, className, chipClassName }) {
2781
+ const tags = Array.isArray(value) ? value.filter((tag)=>!!tag) : [];
2782
+ if (!tags.length) return null;
2783
+ return /*#__PURE__*/ jsx("span", {
2784
+ className: className,
2785
+ children: tags.map((tag)=>/*#__PURE__*/ jsx(SwatchChip, {
2786
+ value: tag,
2787
+ className: chipClassName
2788
+ }, tag))
2789
+ });
2790
+ }
2791
+ /**
2792
+ * Feature D (#13) -- generic text+affix composition: `suffix` (if given) renders as
2793
+ * a `flex-shrink: 0` icon beside the text, and the TEXT itself (not the outer `<td>`)
2794
+ * gets its own `flex: 1 1 0%; min-width: 0` + ellipsis, so a long value truncates
2795
+ * while the affix stays visible instead of being squeezed out of the cell entirely
2796
+ * (the bug: composing text+icon inside a plain truncating `<td>` lets the icon
2797
+ * scroll/clip away with the text, since a flex child never inherits its PARENT's
2798
+ * own `text-overflow`). `data-seed-truncate` on the text span is what `truncate.ts`'s
2799
+ * `isCellTruncated` (Feature C's click-to-expand gate) measures instead of the
2800
+ * `<td>` itself for exactly the same reason -- see that file's own comment.
2801
+ * Any FUTURE cell composing a fixed leading/trailing glyph beside truncating text
2802
+ * should render through this component rather than re-deriving the same three-slot
2803
+ * flex shape.
2804
+ */ function CellWithSuffix({ text, suffix, rowClassName, textClassName, suffixClassName }) {
2805
+ if (!suffix) return /*#__PURE__*/ jsx(Fragment, {
2806
+ children: text
2807
+ });
2808
+ return /*#__PURE__*/ jsxs("span", {
2809
+ className: rowClassName,
2810
+ children: [
2811
+ /*#__PURE__*/ jsx("span", {
2812
+ className: textClassName,
2813
+ "data-seed-truncate": "",
2814
+ children: text
2815
+ }),
2816
+ /*#__PURE__*/ jsx("span", {
2817
+ className: suffixClassName,
2818
+ children: suffix
2819
+ })
2820
+ ]
2821
+ });
2822
+ }
2823
+ function LinkCell({ column, value, row, className, rowClassName, textClassName, suffixClassName }) {
2663
2824
  const text = value == null ? "" : String(value);
2664
2825
  if (!text) return null;
2665
2826
  const isUrl = column.type === "url";
2666
2827
  const href = isUrl ? /^https?:\/\//.test(text) ? text : `https://${text}` : `mailto:${text}`;
2828
+ const suffix = column.suffixIcon?.(row);
2667
2829
  return /*#__PURE__*/ jsx("a", {
2668
2830
  className: className,
2669
2831
  href: href,
@@ -2672,7 +2834,13 @@ function LinkCell({ column, value, className }) {
2672
2834
  // A link cell points somewhere other than "select this row" -- its own
2673
2835
  // navigation must not also fire a row click handler.
2674
2836
  onClick: (event)=>event.stopPropagation(),
2675
- children: text
2837
+ children: /*#__PURE__*/ jsx(CellWithSuffix, {
2838
+ text: text,
2839
+ suffix: suffix,
2840
+ rowClassName: rowClassName,
2841
+ textClassName: textClassName,
2842
+ suffixClassName: suffixClassName
2843
+ })
2676
2844
  });
2677
2845
  }
2678
2846
  // Attio's own measured 5-point star path (crisp's `RATING_STAR_D`, traced from the
@@ -2698,8 +2866,7 @@ function RatingCell({ value, maxRating = 5 }) {
2698
2866
  },
2699
2867
  children: Array.from({
2700
2868
  length: maxRating
2701
- }, (_, index)=>// biome-ignore lint/suspicious/noArrayIndexKey: a fixed-length star row has no other identity
2702
- /*#__PURE__*/ jsx("svg", {
2869
+ }, (_, index)=>/*#__PURE__*/ jsx("svg", {
2703
2870
  width: 16,
2704
2871
  height: 16,
2705
2872
  viewBox: "0 0 16 16",
@@ -3063,7 +3230,7 @@ const SelectHiddenSelect = /*#__PURE__*/ React.forwardRef((props, ref)=>{
3063
3230
  });
3064
3231
  SelectHiddenSelect.displayName = "SelectHiddenSelect";
3065
3232
 
3066
- const { withContext: withContext$3, ClassNamesProvider: ClassNamesProvider$2 } = createSlotRecipeContext(switchStyle);
3233
+ const { withContext: withContext$4, ClassNamesProvider: ClassNamesProvider$2 } = createSlotRecipeContext(switchStyle);
3067
3234
  const { withContext: withControlContext, PropsProvider: ControlPropsProvider, withProvider: withControlProvider } = createSlotRecipeContext(switchmark);
3068
3235
  const withStateProps$2 = createWithStateProps([
3069
3236
  useSwitchContext
@@ -3089,13 +3256,13 @@ const SwitchRoot = /*#__PURE__*/ React__default.forwardRef(({ className, ...prop
3089
3256
  SwitchRoot.displayName = "SwitchRoot";
3090
3257
  const SwitchControl = withScaleFeedback(withControlProvider(Switch.Control, "root"));
3091
3258
  const SwitchThumb = withControlContext(Switch.Thumb, "thumb");
3092
- withContext$3(withStateProps$2(Primitive.span), "label");
3259
+ withContext$4(withStateProps$2(Primitive.span), "label");
3093
3260
  const SwitchHiddenInput = Switch.HiddenInput;
3094
3261
 
3095
3262
  // src/use-layout-effect.tsx
3096
3263
  var useLayoutEffect2 = globalThis?.document ? React.useLayoutEffect : ()=>{};
3097
3264
 
3098
- const { withProvider, withContext: withContext$2, useClassNames: useClassNames$2 } = createSlotRecipeContext(textInput);
3265
+ const { withProvider: withProvider$1, withContext: withContext$3, useClassNames: useClassNames$2 } = createSlotRecipeContext(textInput);
3099
3266
  const withFieldStateProps = createWithStateProps([
3100
3267
  {
3101
3268
  useContext: useFieldContext,
@@ -3109,11 +3276,11 @@ const withStateProps$1 = createWithStateProps([
3109
3276
  strict: false
3110
3277
  }
3111
3278
  ]);
3112
- const TextFieldRoot = withProvider(withFieldStateProps(TextField.Root), "root");
3113
- withContext$2(withStateProps$1(InternalIcon), "prefixIcon");
3114
- withContext$2(withStateProps$1(Primitive.span), "prefixText");
3115
- withContext$2(withStateProps$1(InternalIcon), "suffixIcon");
3116
- withContext$2(withStateProps$1(Primitive.span), "suffixText");
3279
+ const TextFieldRoot = withProvider$1(withFieldStateProps(TextField.Root), "root");
3280
+ withContext$3(withStateProps$1(InternalIcon), "prefixIcon");
3281
+ withContext$3(withStateProps$1(Primitive.span), "prefixText");
3282
+ withContext$3(withStateProps$1(InternalIcon), "suffixIcon");
3283
+ withContext$3(withStateProps$1(Primitive.span), "suffixText");
3117
3284
  const TextFieldInput = /*#__PURE__*/ forwardRef(({ className, ...otherProps }, ref)=>{
3118
3285
  const classNames = useClassNames$2();
3119
3286
  const textFieldContext = useTextFieldContext();
@@ -3190,7 +3357,7 @@ const TextFieldTextarea = /*#__PURE__*/ forwardRef(({ className, autoresize = tr
3190
3357
  });
3191
3358
  TextFieldTextarea.displayName = "TextFieldTextarea";
3192
3359
 
3193
- __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))}");
3360
+ __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))}");
3194
3361
 
3195
3362
  const createClassName = (className, variants, compoundVariants = [])=>{
3196
3363
  const variantKeys = Object.keys(variants);
@@ -3285,6 +3452,42 @@ const dataTableSlotNames = [
3285
3452
  "link",
3286
3453
  "seed-data-table__link"
3287
3454
  ],
3455
+ [
3456
+ "chip",
3457
+ "seed-data-table__chip"
3458
+ ],
3459
+ [
3460
+ "chipList",
3461
+ "seed-data-table__chipList"
3462
+ ],
3463
+ [
3464
+ "chipRemove",
3465
+ "seed-data-table__chipRemove"
3466
+ ],
3467
+ [
3468
+ "chipInput",
3469
+ "seed-data-table__chipInput"
3470
+ ],
3471
+ [
3472
+ "expandOverlay",
3473
+ "seed-data-table__expandOverlay"
3474
+ ],
3475
+ [
3476
+ "expandOverlayText",
3477
+ "seed-data-table__expandOverlayText"
3478
+ ],
3479
+ [
3480
+ "cellSuffixRow",
3481
+ "seed-data-table__cellSuffixRow"
3482
+ ],
3483
+ [
3484
+ "cellSuffixText",
3485
+ "seed-data-table__cellSuffixText"
3486
+ ],
3487
+ [
3488
+ "cellSuffixIcon",
3489
+ "seed-data-table__cellSuffixIcon"
3490
+ ],
3288
3491
  [
3289
3492
  "entityActions",
3290
3493
  "seed-data-table__entityActions"
@@ -3377,7 +3580,7 @@ Object.assign(dataTable, {
3377
3580
  // other file (`DataTable.tsx`, `column-menu.tsx`, `resize.tsx`, `column-calc.tsx`,
3378
3581
  // `toolbar.tsx`, `body-cells.tsx`, `editors.tsx`) imports its exports from here rather
3379
3582
  // than re-deriving its own.
3380
- const { ClassNamesProvider: ClassNamesProvider$1, withContext: withContext$1, useClassNames: useClassNames$1 } = createSlotRecipeContext(dataTable);
3583
+ const { ClassNamesProvider: ClassNamesProvider$1, withContext: withContext$2, useClassNames: useClassNames$1 } = createSlotRecipeContext(dataTable);
3381
3584
 
3382
3585
  /**
3383
3586
  * The pure filter itself, split out of `useSelectSearch` below so it can be
@@ -3859,6 +4062,103 @@ function RatingEditor({ column, value, pending, onCommit }) {
3859
4062
  ]
3860
4063
  });
3861
4064
  }
4065
+ ////////////////////////////////////////////////////////////////////////////////////
4066
+ // -- editable: "chip-list" (Feature B, #8) -- always-live, the same shape
4067
+ // `SwitchEditor`/`CheckboxEditor`/`RatingEditor` above already establish
4068
+ // (interacting with the widget IS the commit, no double-click gate, no separate
4069
+ // rest display -- `body-cells.tsx`'s `isAlwaysLiveEditable` covers it alongside
4070
+ // them). A gated open/close state (like `SelectEditor`'s) would fight adding
4071
+ // MULTIPLE tags in one sitting: `useDataTableEdit`'s `commitEdit` closes editing
4072
+ // the instant a SYNC commit resolves, so a gated chip editor would close itself
4073
+ // after the very first tag. Always-live sidesteps that entirely -- there is no
4074
+ // `editingCell` to close, the widget is simply always the cell's own content.
4075
+ // Every add/remove commits the FULL next array through the same one-string
4076
+ // contract every other editor already uses (`useDataTableEdit`'s own doc
4077
+ // comment): `JSON.stringify(nextTags)`, which the consumer's `onEdit` parses
4078
+ // back -- crisp/GRC's contract stays "one string in, no typed union", not a
4079
+ // second commit shape invented for just this column.
4080
+ function ChipListEditor({ column, value, pending, onCommit, classNames }) {
4081
+ const tags = Array.isArray(value) ? value.filter((tag)=>!!tag) : [];
4082
+ const [draft, setDraft] = React.useState("");
4083
+ function commitTags(next) {
4084
+ onCommit(JSON.stringify(next));
4085
+ }
4086
+ function addTag() {
4087
+ const tag = draft.trim();
4088
+ setDraft("");
4089
+ if (!tag || tags.includes(tag)) return;
4090
+ commitTags([
4091
+ ...tags,
4092
+ tag
4093
+ ]);
4094
+ }
4095
+ function removeTag(tag) {
4096
+ commitTags(tags.filter((existing)=>existing !== tag));
4097
+ }
4098
+ 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.
4099
+ /*#__PURE__*/ jsxs("span", {
4100
+ className: classNames.chipList,
4101
+ onClick: (event)=>event.stopPropagation(),
4102
+ children: [
4103
+ tags.map((tag)=>/*#__PURE__*/ jsx(SwatchChip, {
4104
+ value: tag,
4105
+ className: classNames.chip,
4106
+ children: /*#__PURE__*/ jsx("button", {
4107
+ type: "button",
4108
+ disabled: pending,
4109
+ "aria-label": `Remove ${tag}`,
4110
+ className: classNames.chipRemove,
4111
+ onClick: ()=>removeTag(tag),
4112
+ children: /*#__PURE__*/ jsx(IconXmarkLine_default, {
4113
+ size: 10
4114
+ })
4115
+ })
4116
+ }, tag)),
4117
+ /*#__PURE__*/ jsx("input", {
4118
+ className: classNames.chipInput,
4119
+ value: draft,
4120
+ disabled: pending,
4121
+ placeholder: tags.length ? "" : "Add…",
4122
+ "aria-label": `Add tag to ${column.key}`,
4123
+ onChange: (event)=>setDraft(event.target.value),
4124
+ onKeyDown: (event)=>{
4125
+ event.stopPropagation();
4126
+ if (event.key === "Enter") {
4127
+ event.preventDefault();
4128
+ addTag();
4129
+ } else if (event.key === "Backspace" && draft === "" && tags.length > 0) {
4130
+ removeTag(tags[tags.length - 1]);
4131
+ } else if (event.key === "Escape") {
4132
+ setDraft("");
4133
+ }
4134
+ }
4135
+ }),
4136
+ pending && /*#__PURE__*/ jsx(EditPendingIndicator, {})
4137
+ ]
4138
+ }));
4139
+ }
4140
+ ////////////////////////////////////////////////////////////////////////////////////
4141
+ // -- Feature C (#12): cell-expand "show full content" overlay -- an anchored box
4142
+ // (position:absolute over the cell it belongs to, grows in HEIGHT, elevated)
4143
+ // that wraps whatever a truncated cell would otherwise show inline: an editable
4144
+ // column's OWN editor node (unchanged, just relocated into this box instead of
4145
+ // rendered flush in the 36px row) for the editable case, or the plain full value
4146
+ // for a read-only one. `body-cells.tsx` decides WHEN to render this (a click on a
4147
+ // cell whose content is actually truncated, `truncate.ts`'s `isCellTruncated`);
4148
+ // this component only supplies the box.
4149
+ function CellExpandOverlay({ className, children }) {
4150
+ return(// `stopPropagation` -- an overlay sits on top of (and past) its own cell/row;
4151
+ // a click anywhere inside it must never ALSO fire the row's own `onRowClick`,
4152
+ // the same guard every other interactive cell affordance in this directory
4153
+ // already applies (`LinkCell`, `EntityCell`'s preview button, editors.tsx's
4154
+ // `TextEditor` root).
4155
+ // 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.
4156
+ /*#__PURE__*/ jsx("div", {
4157
+ className: className,
4158
+ onClick: (event)=>event.stopPropagation(),
4159
+ children: children
4160
+ }));
4161
+ }
3862
4162
 
3863
4163
  // Row-narrowing for the toolbar's search box -- split out of DataTable.tsx the way
3864
4164
  // cells.tsx splits out cell-type rendering: a self-contained, independently testable
@@ -3898,49 +4198,167 @@ function RatingEditor({ column, value, pending, onCommit }) {
3898
4198
  return data.filter((row)=>searchableColumns.some((column)=>matchesSearch(getColumnValue(column, row), query)));
3899
4199
  }
3900
4200
 
4201
+ // Feature C (#12) -- the click-to-expand gate: measures whether a cell's rendered
4202
+ // content is ACTUALLY truncated right now, so a single click only opens the
4203
+ // anchored overlay (editors.tsx's `CellExpandOverlay`) when there's real hidden
4204
+ // content to show, never on a short value that already fits. Isolated out of
4205
+ // body-cells.tsx the same way select-search.tsx/filtering.ts already split their own
4206
+ // concerns out of the styled layer, so this one pure DOM read is unit-testable on
4207
+ // its own (a plain `<td>`'s own truncate CSS vs. a suffix-icon cell's nested flex
4208
+ // child need two different elements measured -- see below).
4209
+ //
4210
+ // This one genuinely reads a real, laid-out DOM element (`scrollWidth`/
4211
+ // `clientWidth`), unlike react-headless/data-table/src/dom.ts's own helpers (plain
4212
+ // data in, plain data out) -- it lives in the REACT (styled) package rather than
4213
+ // react-headless for that reason: measuring actual layout is a rendering concern,
4214
+ // not table state.
4215
+ function isCellTruncated(cell) {
4216
+ // The common case: a plain cell's own `white-space: nowrap; overflow: clip` (the
4217
+ // `cell` recipe slot) makes the `<td>` ITSELF overflow once its text is longer
4218
+ // than the column is wide.
4219
+ if (cell.scrollWidth > cell.clientWidth) return true;
4220
+ // A cell composing a suffix icon beside its text (cells.tsx's `CellWithSuffix`,
4221
+ // Feature D) wraps the text in its OWN `flex: 1 1 0%; min-width: 0` child instead
4222
+ // -- that flex child never lets the `<td>` itself overflow (it's constrained to
4223
+ // the remaining row width by construction), so the truncating element to measure
4224
+ // is the flex child, marked `data-seed-truncate` by whichever cell renderer
4225
+ // applies its own ellipsis (`CellWithSuffix`'s text span today; any future
4226
+ // truncating inner element should carry the same marker).
4227
+ const targets = cell.querySelectorAll("[data-seed-truncate]");
4228
+ for (const target of targets){
4229
+ if (target.scrollWidth > target.clientWidth) return true;
4230
+ }
4231
+ return false;
4232
+ }
4233
+
3901
4234
  /** `editable` + one of these shapes needs no double-click gate and no separate rest
3902
4235
  * display -- interacting with the widget itself IS the commit gesture (crisp's own
3903
4236
  * #453/#760/#761: `switch`/`rating`/`checkbox` never had a meaningfully different
3904
- * "rest" state to show). Every other `editable` column opens on double-click. */ function isAlwaysLiveEditable(column) {
4237
+ * "rest" state to show). `chip-list` (Feature B, #8) joins them for a different
4238
+ * reason -- see `ChipListEditor`'s own doc comment (editors.tsx): a GATED chip
4239
+ * editor would close itself the instant the first tag committed. Every other
4240
+ * `editable` column opens on double-click. */ function isAlwaysLiveEditable(column) {
3905
4241
  if (!column.editable) return false;
3906
- return column.editor === "switch" || column.type === "rating" || column.type === "checkbox";
4242
+ return column.editor === "switch" || column.type === "rating" || column.type === "checkbox" || column.type === "chip-list";
3907
4243
  }
3908
4244
  function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyFirst, stickyLast }) {
3909
4245
  const effective = useEffectiveColumns(columns, {
3910
4246
  selectable,
3911
4247
  stickyFirst
3912
4248
  });
3913
- const { startEdit } = useDataTableContext();
3914
4249
  return /*#__PURE__*/ jsx(Fragment, {
3915
4250
  children: effective.map((entry, index)=>{
3916
4251
  const { column } = entry;
3917
4252
  const isLast = index === effective.length - 1;
3918
4253
  const sticky = pinnedCellProps(entry, stickyLast, isLast);
3919
- // A gated `editable` column (everything except the always-live widgets)
3920
- // opens on double-click -- Attio's own measured gesture (crisp's #411),
3921
- // matched here rather than a single click, which stays "select this cell"
3922
- // in crisp's own spreadsheet model and would otherwise be indistinguishable
3923
- // from this table's plain row click.
3924
- const gated = column.editable && !isAlwaysLiveEditable(column);
3925
- return /*#__PURE__*/ jsx(DataTableCell, {
3926
- "data-numeric": dataAttr(column.type === "number" || column.type === "currency"),
3927
- "data-editable": dataAttr(!!column.editable),
3928
- "data-sticky": sticky["data-sticky"],
3929
- "data-sticky-edge": sticky["data-sticky-edge"],
3930
- "data-sticky-edge-end": sticky["data-sticky-edge-end"],
3931
- style: sticky.style,
3932
- onDoubleClick: gated ? ()=>startEdit(row.id, column.key) : undefined,
3933
- children: /*#__PURE__*/ jsx(CellContent, {
3934
- column: column,
3935
- row: row,
3936
- rowId: row.id,
3937
- classNames: classNames,
3938
- locale: locale
3939
- })
4254
+ return /*#__PURE__*/ jsx(DataTableBodyCell, {
4255
+ column: column,
4256
+ row: row,
4257
+ classNames: classNames,
4258
+ locale: locale,
4259
+ sticky: sticky
3940
4260
  }, column.key);
3941
4261
  })
3942
4262
  });
3943
4263
  }
4264
+ /**
4265
+ * One `<td>` -- split out of `ColumnBodyCells`'s own map (which now only computes
4266
+ * per-column LAYOUT, `pinnedCellProps`) so this component can own its own per-cell
4267
+ * "is the Feature C overlay open" local state (`overlayOpen`) without re-deriving it
4268
+ * on every row's render. React keeps this instance stable across re-renders because
4269
+ * `ColumnBodyCells` keys it by `column.key` the same way it always keyed the bare
4270
+ * `<DataTableCell>` before this split.
4271
+ */ function DataTableBodyCell({ column, row, classNames, locale, sticky }) {
4272
+ const { startEdit, isEditing, isPeeking, openPeek, closePeek } = useDataTableContext();
4273
+ const cellRef = React.useRef(null);
4274
+ // Feature C (#12) -- whether THIS activation (click or double-click) should
4275
+ // render as the anchored overlay rather than plain inline. Frozen at the moment
4276
+ // of activation (re-measured every time, never left stale) rather than derived
4277
+ // from `isEditing`/`isPeeking` alone, because those two states don't carry "was
4278
+ // the content actually truncated when this opened" on their own -- a SHORT
4279
+ // editable cell's existing double-click-to-edit (tranche 5, unchanged) must keep
4280
+ // rendering exactly as it always has, not suddenly gain overlay chrome.
4281
+ const [overlayOpen, setOverlayOpen] = React.useState(false);
4282
+ const alwaysLive = isAlwaysLiveEditable(column);
4283
+ // A gated `editable` column (everything except the always-live widgets) opens on
4284
+ // double-click -- Attio's own measured gesture (crisp's #411), matched here
4285
+ // rather than a single click, which stays "select this cell" in crisp's own
4286
+ // spreadsheet model and would otherwise be indistinguishable from this table's
4287
+ // plain row click. Feature C ADDS a single click as a SECOND way in, but only
4288
+ // when the cell's content is actually truncated (see `handleClick` below) --
4289
+ // double-click keeps working unconditionally, same as before this feature.
4290
+ const gated = column.editable && !alwaysLive;
4291
+ function handleDoubleClick(event) {
4292
+ if (!gated) return;
4293
+ setOverlayOpen(isCellTruncated(event.currentTarget));
4294
+ startEdit(row.id, column.key);
4295
+ }
4296
+ function handleClick(event) {
4297
+ if (!isCellTruncated(event.currentTarget)) return;
4298
+ if (gated) {
4299
+ setOverlayOpen(true);
4300
+ startEdit(row.id, column.key);
4301
+ } else if (alwaysLive) {
4302
+ // No separate open/edit state to start -- the always-live widget already
4303
+ // renders unconditionally; this only switches ITS OWN box to the anchored
4304
+ // overlay so a long chip list (etc.) gets room to show every chip.
4305
+ setOverlayOpen(true);
4306
+ } else {
4307
+ setOverlayOpen(true);
4308
+ openPeek(row.id, column.key);
4309
+ }
4310
+ }
4311
+ // Closes the overlay on Escape or an outside click/pointerdown -- a gated
4312
+ // editor's OWN Escape/blur handling already ends `isEditing` on its own (see
4313
+ // `TextEditor`/`SelectEditor`'s existing onCancel/onOpenChange wiring), which
4314
+ // alone is enough to hide a GATED cell's overlay (its render below is gated on
4315
+ // `isEditing` too); this listener is what the PEEK and ALWAYS-LIVE cases need,
4316
+ // since neither has any other mechanism that ever calls `closePeek`/resets
4317
+ // `overlayOpen` on its own.
4318
+ React.useEffect(()=>{
4319
+ if (!overlayOpen) return;
4320
+ function handlePointerDown(event) {
4321
+ if (cellRef.current && !cellRef.current.contains(event.target)) {
4322
+ setOverlayOpen(false);
4323
+ closePeek();
4324
+ }
4325
+ }
4326
+ function handleKeyDown(event) {
4327
+ if (event.key !== "Escape") return;
4328
+ setOverlayOpen(false);
4329
+ closePeek();
4330
+ }
4331
+ document.addEventListener("pointerdown", handlePointerDown);
4332
+ document.addEventListener("keydown", handleKeyDown);
4333
+ return ()=>{
4334
+ document.removeEventListener("pointerdown", handlePointerDown);
4335
+ document.removeEventListener("keydown", handleKeyDown);
4336
+ };
4337
+ }, [
4338
+ overlayOpen,
4339
+ closePeek
4340
+ ]);
4341
+ const overlayActive = overlayOpen && (alwaysLive || isEditing(row.id, column.key) || isPeeking(row.id, column.key));
4342
+ return /*#__PURE__*/ jsx(DataTableCell, {
4343
+ ref: cellRef,
4344
+ "data-numeric": dataAttr(column.type === "number" || column.type === "currency"),
4345
+ "data-editable": dataAttr(!!column.editable),
4346
+ "data-sticky": sticky["data-sticky"],
4347
+ "data-sticky-edge": sticky["data-sticky-edge"],
4348
+ "data-sticky-edge-end": sticky["data-sticky-edge-end"],
4349
+ style: sticky.style,
4350
+ onClick: handleClick,
4351
+ onDoubleClick: handleDoubleClick,
4352
+ children: /*#__PURE__*/ jsx(CellContent, {
4353
+ column: column,
4354
+ row: row,
4355
+ rowId: row.id,
4356
+ classNames: classNames,
4357
+ locale: locale,
4358
+ overlayActive: overlayActive
4359
+ })
4360
+ });
4361
+ }
3944
4362
  /**
3945
4363
  * Dispatches to whichever of cells.tsx's static renderers or editors.tsx's live
3946
4364
  * editors applies to THIS column/row right now -- always-live first (no rest state to
@@ -3948,7 +4366,7 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
3948
4366
  * display (a custom `render` wins there, same as before `editable` existed; it never
3949
4367
  * overrides an actual editing/always-live state, matching crisp's own split between
3950
4368
  * `renderTyped`'s rest-state branch and the editing branches above it).
3951
- */ function CellContent({ column, row, rowId, classNames, locale }) {
4369
+ */ function CellContent({ column, row, rowId, classNames, locale, overlayActive }) {
3952
4370
  const { isEditing, isPending, getEditError, commitEdit, cancelEdit } = useDataTableContext();
3953
4371
  const rawValue = getColumnValue(column, row);
3954
4372
  const pending = isPending(rowId, column.key);
@@ -3977,6 +4395,14 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
3977
4395
  pending: pending,
3978
4396
  onCommit: commit
3979
4397
  });
4398
+ } else if (column.editable && column.type === "chip-list") {
4399
+ node = /*#__PURE__*/ jsx(ChipListEditor, {
4400
+ column: column,
4401
+ value: resolveEditValue(column, row, rawValue),
4402
+ pending: pending,
4403
+ onCommit: commit,
4404
+ classNames: classNames
4405
+ });
3980
4406
  } else if (column.editable && isEditing(rowId, column.key)) {
3981
4407
  if (column.editor === "select") {
3982
4408
  node = /*#__PURE__*/ jsx(SelectEditor, {
@@ -4012,6 +4438,10 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4012
4438
  });
4013
4439
  }
4014
4440
  } else {
4441
+ // Feature C (#12), read-only case: `DataTableBodyCell`'s own `isPeeking` already
4442
+ // decided whether the overlay should be open (folded into `overlayActive`
4443
+ // below) -- this branch always renders the SAME `DisplayCellContent` either
4444
+ // way, just possibly relocated into the overlay box.
4015
4445
  node = /*#__PURE__*/ jsx(DisplayCellContent, {
4016
4446
  column: column,
4017
4447
  row: row,
@@ -4022,7 +4452,13 @@ function ColumnBodyCells({ columns, row, classNames, locale, selectable, stickyF
4022
4452
  }
4023
4453
  return /*#__PURE__*/ jsxs(Fragment, {
4024
4454
  children: [
4025
- node,
4455
+ overlayActive ? /*#__PURE__*/ jsx(CellExpandOverlay, {
4456
+ className: classNames.expandOverlay,
4457
+ children: column.editable ? node : /*#__PURE__*/ jsx("span", {
4458
+ className: classNames.expandOverlayText,
4459
+ children: node
4460
+ })
4461
+ }) : node,
4026
4462
  error && /*#__PURE__*/ jsx(EditError, {
4027
4463
  message: error
4028
4464
  })
@@ -4053,7 +4489,11 @@ function DisplayCellContent({ column, row, value, classNames, locale }) {
4053
4489
  return /*#__PURE__*/ jsx(LinkCell, {
4054
4490
  column: column,
4055
4491
  value: value,
4056
- className: classNames.link
4492
+ row: row,
4493
+ className: classNames.link,
4494
+ rowClassName: classNames.cellSuffixRow,
4495
+ textClassName: classNames.cellSuffixText,
4496
+ suffixClassName: classNames.cellSuffixIcon
4057
4497
  });
4058
4498
  case "tags":
4059
4499
  return /*#__PURE__*/ jsx(TagsCell, {
@@ -4064,6 +4504,17 @@ function DisplayCellContent({ column, row, value, classNames, locale }) {
4064
4504
  return /*#__PURE__*/ jsx(StatusCell, {
4065
4505
  value: value
4066
4506
  });
4507
+ case "chip":
4508
+ return /*#__PURE__*/ jsx(ChipCell, {
4509
+ value: value,
4510
+ className: classNames.chip
4511
+ });
4512
+ case "chip-list":
4513
+ return /*#__PURE__*/ jsx(ChipListCell, {
4514
+ value: value,
4515
+ className: classNames.chipList,
4516
+ chipClassName: classNames.chip
4517
+ });
4067
4518
  case "number":
4068
4519
  return /*#__PURE__*/ jsx(Fragment, {
4069
4520
  children: formatNumberCell(value, locale)
@@ -4090,13 +4541,27 @@ function DisplayCellContent({ column, row, value, classNames, locale }) {
4090
4541
  value: value
4091
4542
  });
4092
4543
  default:
4093
- return /*#__PURE__*/ jsx(Fragment, {
4094
- children: value == null ? null : String(value)
4095
- });
4544
+ {
4545
+ const text = value == null ? null : String(value);
4546
+ const suffix = column.suffixIcon?.(row);
4547
+ // Feature D (#13) -- a plain text column carrying a `suffixIcon` (e.g. a
4548
+ // location column's pin glyph) composes through the same `CellWithSuffix`
4549
+ // LinkCell already uses, so the icon never gets squeezed out by a long value.
4550
+ if (!suffix || text == null) return /*#__PURE__*/ jsx(Fragment, {
4551
+ children: text
4552
+ });
4553
+ return /*#__PURE__*/ jsx(CellWithSuffix, {
4554
+ text: text,
4555
+ suffix: suffix,
4556
+ rowClassName: classNames.cellSuffixRow,
4557
+ textClassName: classNames.cellSuffixText,
4558
+ suffixClassName: classNames.cellSuffixIcon
4559
+ });
4560
+ }
4096
4561
  }
4097
4562
  }
4098
4563
 
4099
- const { ClassNamesProvider, withContext, useClassNames } = createSlotRecipeContext(menu);
4564
+ const { ClassNamesProvider, withContext: withContext$1, useClassNames } = createSlotRecipeContext(menu);
4100
4565
  const { PropsProvider: ItemPropsProvider, useProps: useItemProps, withContext: withItemContext, ClassNamesProvider: ItemClassNamesProvider } = createSlotRecipeContext(menuItem);
4101
4566
  const withStateProps = createWithStateProps([
4102
4567
  useMenuContext
@@ -4130,10 +4595,10 @@ const MenuPositioner = /*#__PURE__*/ React.forwardRef(({ className, ...props },
4130
4595
  });
4131
4596
  });
4132
4597
  MenuPositioner.displayName = "MenuPositioner";
4133
- const MenuContent = withContext(Menu.Content, "content");
4134
- const MenuScrollArea = withContext(withStateProps(Primitive.div), "scrollArea");
4135
- withContext(Menu.Group, "group");
4136
- withContext(Menu.GroupLabel, "groupLabel");
4598
+ const MenuContent = withContext$1(Menu.Content, "content");
4599
+ const MenuScrollArea = withContext$1(withStateProps(Primitive.div), "scrollArea");
4600
+ withContext$1(Menu.Group, "group");
4601
+ withContext$1(Menu.GroupLabel, "groupLabel");
4137
4602
  const MenuItem = /*#__PURE__*/ React.forwardRef(({ className: propClassName, ...props }, ref)=>{
4138
4603
  const [variantProps, otherProps] = menuItem.splitVariantProps(props);
4139
4604
  const parentProps = useItemProps();
@@ -5898,8 +6363,8 @@ Object.assign(datePicker, {
5898
6363
  splitVariantProps: (props)=>splitVariantProps(props, datePickerVariantMap)
5899
6364
  }); // @recipe(seed): date-picker
5900
6365
 
5901
- var __defProp$1 = Object.defineProperty;
5902
- var __name$1 = (target, value)=>__defProp$1(target, "name", {
6366
+ var __defProp$3 = Object.defineProperty;
6367
+ var __name$3 = (target, value)=>__defProp$3(target, "name", {
5903
6368
  value,
5904
6369
  configurable: true
5905
6370
  });
@@ -5923,15 +6388,15 @@ function useEffectEvent(callback) {
5923
6388
  }
5924
6389
  return React.useMemo(()=>(...args)=>ref.current?.(...args), []);
5925
6390
  }
5926
- __name$1(useEffectEvent, "useEffectEvent");
6391
+ __name$3(useEffectEvent, "useEffectEvent");
5927
6392
 
5928
- var __defProp = Object.defineProperty;
5929
- var __name = (target, value)=>__defProp(target, "name", {
6393
+ var __defProp$2 = Object.defineProperty;
6394
+ var __name$2 = (target, value)=>__defProp$2(target, "name", {
5930
6395
  value,
5931
6396
  configurable: true
5932
6397
  });
5933
6398
  var useInsertionEffect = React[" useInsertionEffect ".trim().toString()] || useLayoutEffect2;
5934
- function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __name(()=>{}, "onChange"), caller }) {
6399
+ function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __name$2(()=>{}, "onChange"), caller }) {
5935
6400
  const [uncontrolledProp, setUncontrolledProp, onChangeRef] = useUncontrolledState({
5936
6401
  defaultProp,
5937
6402
  onChange
@@ -5958,7 +6423,7 @@ function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __
5958
6423
  setValue
5959
6424
  ];
5960
6425
  }
5961
- __name(useControllableState, "useControllableState");
6426
+ __name$2(useControllableState, "useControllableState");
5962
6427
  function useUncontrolledState({ defaultProp, onChange }) {
5963
6428
  const [value, setValue] = React.useState(defaultProp);
5964
6429
  const prevValueRef = React.useRef(value);
@@ -5983,11 +6448,11 @@ function useUncontrolledState({ defaultProp, onChange }) {
5983
6448
  onChangeRef
5984
6449
  ];
5985
6450
  }
5986
- __name(useUncontrolledState, "useUncontrolledState");
6451
+ __name$2(useUncontrolledState, "useUncontrolledState");
5987
6452
  function isFunction(value) {
5988
6453
  return typeof value === "function";
5989
6454
  }
5990
- __name(isFunction, "isFunction");
6455
+ __name$2(isFunction, "isFunction");
5991
6456
  var SYNC_STATE = Symbol("RADIX:SYNC_STATE");
5992
6457
  function useControllableStateReducer(reducer, userArgs, initialArg, init) {
5993
6458
  const { prop: controlledState, defaultProp, onChange: onChangeProp, caller } = userArgs;
@@ -6059,7 +6524,7 @@ function useControllableStateReducer(reducer, userArgs, initialArg, init) {
6059
6524
  dispatch
6060
6525
  ];
6061
6526
  }
6062
- __name(useControllableStateReducer, "useControllableStateReducer");
6527
+ __name$2(useControllableStateReducer, "useControllableStateReducer");
6063
6528
 
6064
6529
  /*
6065
6530
  * Copyright 2020 Adobe. All rights reserved.
@@ -10265,8 +10730,8 @@ const formatFilterDate = (iso)=>{
10265
10730
  });
10266
10731
  }
10267
10732
 
10268
- const DataTableToolbar = withContext$1(Primitive.div, "toolbar");
10269
- const DataTableCount = withContext$1(Primitive.span, "count");
10733
+ const DataTableToolbar = withContext$2(Primitive.div, "toolbar");
10734
+ const DataTableCount = withContext$2(Primitive.span, "count");
10270
10735
  /**
10271
10736
  * The toolbar's own right-hand group -- `count`'s `marginInlineStart: auto` moved
10272
10737
  * here (off the `<span>` itself) so a consumer's `toolbarActions` and the row count
@@ -10277,7 +10742,7 @@ const DataTableCount = withContext$1(Primitive.span, "count");
10277
10742
  * it. `DataTable.tsx` always renders this wrapper once either `toolbarActions` or
10278
10743
  * `showCount` is on, so it degrades to exactly the old single-count layout when no
10279
10744
  * `toolbarActions` is passed.
10280
- */ const DataTableToolbarEnd = withContext$1(Primitive.div, "toolbarEnd");
10745
+ */ const DataTableToolbarEnd = withContext$2(Primitive.div, "toolbarEnd");
10281
10746
  /**
10282
10747
  * A compact, borderless search box -- crisp's own measured `.crisp-datatable-search`
10283
10748
  * (32px, `bgSurface` background, a leading icon, a soft rest-state shadow, a 1px brand
@@ -10699,7 +11164,7 @@ DataTableInner.displayName = "DataTable";
10699
11164
  // is a SIBLING of the toolbar (both inside `DataTablePrimitiveRoot`'s card `<div>`)
10700
11165
  // rather than `DataTablePrimitiveRoot` itself: a `<table>` cannot host the toolbar as a
10701
11166
  // child without the HTML parser foster-parenting it back out.
10702
- const DataTableTable = withContext$1(DataTableTable$1, "table");
11167
+ const DataTableTable = withContext$2(DataTableTable$1, "table");
10703
11168
  /** One `<col>` per rendered column (plus the 40px selection column, if any) -- the
10704
11169
  * SINGLE place a column's width is ever set now. Every row's header/body/footer cell
10705
11170
  * reads its own width off this shared `<colgroup>` by construction (native table
@@ -10736,15 +11201,15 @@ const DataTableTable = withContext$1(DataTableTable$1, "table");
10736
11201
  ]
10737
11202
  });
10738
11203
  }
10739
- const DataTableHeader = withContext$1(DataTableHeader$1, "header");
10740
- const DataTableHeaderCell = withContext$1(DataTableHeaderCell$1, "headerCell");
11204
+ const DataTableHeader = withContext$2(DataTableHeader$1, "header");
11205
+ const DataTableHeaderCell = withContext$2(DataTableHeaderCell$1, "headerCell");
10741
11206
  // `.crisp-datatable-th-inner` -- the flex row INSIDE a `<th>` (label + filter trigger +
10742
11207
  // sort chevron + column menu). See qvism-preset's own `headerCellInner` slot comment
10743
11208
  // for why this lives one level below the cell itself rather than on it.
10744
- const DataTableHeaderCellInner = withContext$1(Primitive.div, "headerCellInner");
11209
+ const DataTableHeaderCellInner = withContext$2(Primitive.div, "headerCellInner");
10745
11210
  /** The label text specifically -- the one thing in `DataTableHeaderCellInner` that
10746
11211
  * truncates; the trailing filter/sort/menu icons stay fully visible regardless of how
10747
- * long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$1(Primitive.span, "headerCellLabel");
11212
+ * long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$2(Primitive.span, "headerCellLabel");
10748
11213
  /**
10749
11214
  * A selection `<th>`/`<td>`/footer `<td>` composes ITS base slot (`headerCell`/`cell`/
10750
11215
  * `footerCell`) together with the `selectionCell` MODIFIER, rather than replacing it --
@@ -10766,12 +11231,12 @@ const DataTableHeaderCellInner = withContext$1(Primitive.div, "headerCellInner")
10766
11231
  return StyledComponent;
10767
11232
  }
10768
11233
  const DataTableSelectionHeaderCell = withSelectionCell(DataTableHeaderCell$1, "headerCell");
10769
- const DataTableHeaderCellIcon = withContext$1(Primitive.span, "headerCellIcon");
11234
+ const DataTableHeaderCellIcon = withContext$2(Primitive.span, "headerCellIcon");
10770
11235
  // body has no CSS slot of its own in the rootage spec -- it is a plain structural
10771
11236
  // `<tbody>`, so it is not wrapped in `withContext`.
10772
11237
  const DataTableBody = DataTableBody$1;
10773
- const DataTableRow = withContext$1(DataTableRow$1, "row");
10774
- const DataTableCell = withContext$1(DataTableCell$1, "cell");
11238
+ const DataTableRow = withContext$2(DataTableRow$1, "row");
11239
+ const DataTableCell = withContext$2(DataTableCell$1, "cell");
10775
11240
  const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
10776
11241
  // Tranche 4 -- column menu + reorder + resize + sticky columns. A scroll boundary
10777
11242
  // wrapping the whole `<table>`: `overflowX: auto` is what makes a wide table (more
@@ -10781,7 +11246,7 @@ const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
10781
11246
  // stays out of this slot's own base css (nothing here has a bounded height of its own
10782
11247
  // to clip), so it never introduces a second vertical scrollbar next to whatever
10783
11248
  // ambient container the whole DataTable already sits in.
10784
- const DataTableScrollBodyBase = withContext$1(Primitive.div, "scrollArea");
11249
+ const DataTableScrollBodyBase = withContext$2(Primitive.div, "scrollArea");
10785
11250
  function DataTableScrollBody({ children, scrollProps }) {
10786
11251
  const { scrollAreaRef, scrolledStart, scrolledEnd, onScroll } = useDataTableContext();
10787
11252
  const { className: scrollClassName, style: scrollStyle, ...otherScrollProps } = scrollProps ?? {};
@@ -10796,7 +11261,7 @@ function DataTableScrollBody({ children, scrollProps }) {
10796
11261
  children: children
10797
11262
  });
10798
11263
  }
10799
- const DataTableFooterBase = withContext$1(Primitive.tfoot, "footer");
11264
+ const DataTableFooterBase = withContext$2(Primitive.tfoot, "footer");
10800
11265
  function DataTableFooter({ children, ...props }) {
10801
11266
  return /*#__PURE__*/ jsx(DataTableFooterBase, {
10802
11267
  ...props,
@@ -10805,7 +11270,7 @@ function DataTableFooter({ children, ...props }) {
10805
11270
  })
10806
11271
  });
10807
11272
  }
10808
- const DataTableFooterCell = withContext$1(Primitive.td, "footerCell");
11273
+ const DataTableFooterCell = withContext$2(Primitive.td, "footerCell");
10809
11274
  // The selection column's OWN sticky wrappers -- one each for header/body/footer,
10810
11275
  // reading the live pinned set via `useSelectionStickyFirst` (column-layout.ts) instead
10811
11276
  // of the `stickyFirst` PROP alone, so the checkbox column sticks the moment ANY column
@@ -11157,7 +11622,7 @@ DataTableViewsControl.displayName = "DataTableViewsControl";
11157
11622
  });
11158
11623
  }
11159
11624
 
11160
- __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}");
11625
+ __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}");
11161
11626
 
11162
11627
  const resizablePanelSlotNames = [
11163
11628
  [
@@ -11183,6 +11648,10 @@ const resizablePanelSlotNames = [
11183
11648
  [
11184
11649
  "handleInner",
11185
11650
  "seed-resizable-panel__handleInner"
11651
+ ],
11652
+ [
11653
+ "backdrop",
11654
+ "seed-resizable-panel__backdrop"
11186
11655
  ]
11187
11656
  ];
11188
11657
  const defaultVariant$1 = {};
@@ -11361,26 +11830,20 @@ Object.assign(resizablePanel, {
11361
11830
  // selector), so this component and that recipe stay in lockstep without a
11362
11831
  // second source of truth for the string.
11363
11832
  const COLUMN_HANDLE_CLASS = "crisp-seed-resizable-panel-handle--column";
11364
- // Kept in lock-step with `resizable-panel.ts`'s own `FLOATING_GUTTER` (a
11365
- // literal there too, for the same reason) -- the floating/inset-card variant
11366
- // needs an outward-facing gutter that DOCKED never reserves. Read on at
11367
- // `spacer`'s own render below for why this can't just be one more CSS rule.
11368
- const FLOATING_GUTTER_PX = 8;
11369
11833
  /**
11370
- * An adjustable-panel primitive: a spacer that reserves layout width plus a
11371
- * panel that overlays it, so it can resize (or animate open/closed) without
11372
- * a reflow glitch on the rest of the layout -- the spacer always reserves
11373
- * the panel's real width, in BOTH the docked and floating visual variants
11374
- * (floating changes only the panel's own look -- gutter, radius, shadow --
11375
- * never its `position`, so a consumer laying this out as a row-flex sibling
11376
- * of e.g. a table sees that table genuinely shrink to make room, matching
11377
- * Attio's own measured peek). Drag the edge handle to resize; constraints
11378
- * are CSS `min-width`/`max-width`, never re-clamped in JS. Ported from
11379
- * your-moon/crisp's `ResizablePanel` (`components/resizablepanel/`);
11380
- * state/drag behavior lives in `useResizablePanel`
11381
- * (`@seed-design/react-resizable-panel`), this component owns the DOM +
11382
- * recipe classes on top of it.
11383
- */ const ResizablePanel = /*#__PURE__*/ forwardRef(({ side = "left", width: sizeProp, defaultSize = 280, onWidthChange, min = 200, max, floating = false, handleVariant = "panel", resizable = true, storageKey, animationState, panelElementRef, className, children, ...rest }, forwardedRef)=>{
11834
+ * An adjustable-panel primitive with two layout models (`floating`'s own doc
11835
+ * comment above has the full contrast). Docked: a `spacer` reserves layout
11836
+ * width and the panel overlays exactly that reservation, so a resize drag
11837
+ * never causes a reflow glitch on the rest of the layout. Floating: no
11838
+ * spacer at all -- this component's own outer element becomes a
11839
+ * `position: absolute; inset: 0` overlay reserving zero width, with the
11840
+ * panel itself pinned to one edge and spanning the full height of that box.
11841
+ * Drag the edge handle to resize; constraints are CSS `min-width`/
11842
+ * `max-width`, never re-clamped in JS. Ported from your-moon/crisp's
11843
+ * `ResizablePanel` (`components/resizablepanel/`); state/drag behavior
11844
+ * lives in `useResizablePanel` (`@seed-design/react-resizable-panel`), this
11845
+ * component owns the DOM + recipe classes on top of it.
11846
+ */ const ResizablePanel = /*#__PURE__*/ 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)=>{
11384
11847
  const { width, dragging, containerRef, panelRef, handleHandlers } = useResizablePanel({
11385
11848
  side,
11386
11849
  size: sizeProp,
@@ -11390,27 +11853,12 @@ const FLOATING_GUTTER_PX = 8;
11390
11853
  });
11391
11854
  const composedPanelRef = useComposedRefs(panelRef, panelElementRef);
11392
11855
  const classNames = resizablePanel();
11393
- // The floating/inset-card variant sits INSET from `root`'s own outward
11394
- // edge by `FLOATING_GUTTER_PX`, flush against the inward edge (Attio's
11395
- // own peek: no gap on the side facing the table it shrinks). `width`
11396
- // (the prop a consumer sets, e.g. `RecordPreview`'s own 497px default)
11397
- // is the RESERVED FOOTPRINT -- what the table beside it actually gives
11398
- // up -- so the panel's own rendered card shrinks by the gutter INSIDE
11399
- // that footprint, rather than the footprint growing past it: reserving
11400
- // the panel's full width AND adding an outward gutter on top of it
11401
- // over-claims space the surrounding layout was never asked for --
11402
- // confirmed live, it pushed the whole page 8px into horizontal
11403
- // overflow. Docked (no gutter) keeps `width` as both the reservation
11404
- // and the render, as before.
11405
- const panelGutter = floating ? FLOATING_GUTTER_PX : 0;
11856
+ // Floating spans its full container edge to edge -- no gutter, no
11857
+ // shrink -- so the panel's own size style is the SAME reservation a
11858
+ // docked spacer would use; only whether `spacer` itself renders differs
11859
+ // below.
11406
11860
  const maxStyle = max == null ? undefined : typeof max === "number" ? `${max}px` : max;
11407
- const panelMaxStyle = panelGutter === 0 || maxStyle == null ? maxStyle : `calc(${maxStyle} - ${panelGutter}px)`;
11408
11861
  const panelSizeStyle = {
11409
- width: width - panelGutter,
11410
- minWidth: min - panelGutter,
11411
- maxWidth: panelMaxStyle
11412
- };
11413
- const spacerSizeStyle = {
11414
11862
  width,
11415
11863
  minWidth: min,
11416
11864
  maxWidth: maxStyle
@@ -11422,11 +11870,21 @@ const FLOATING_GUTTER_PX = 8;
11422
11870
  else if (forwardedRef) forwardedRef.current = node;
11423
11871
  },
11424
11872
  className: clsx(classNames.root, className),
11873
+ "data-floating": floating ? "true" : "false",
11425
11874
  ...rest,
11426
11875
  children: [
11427
- /*#__PURE__*/ jsx(Primitive.div, {
11876
+ !floating && /*#__PURE__*/ jsx(Primitive.div, {
11428
11877
  className: classNames.spacer,
11429
- style: spacerSizeStyle
11878
+ style: {
11879
+ width,
11880
+ minWidth: min,
11881
+ maxWidth: maxStyle
11882
+ }
11883
+ }),
11884
+ floating && backdrop && /*#__PURE__*/ jsx(Primitive.div, {
11885
+ className: classNames.backdrop,
11886
+ "data-state": animationState,
11887
+ onClick: onBackdropClick
11430
11888
  }),
11431
11889
  /*#__PURE__*/ jsxs(Primitive.div, {
11432
11890
  ref: composedPanelRef,
@@ -11458,7 +11916,7 @@ const FLOATING_GUTTER_PX = 8;
11458
11916
  });
11459
11917
  ResizablePanel.displayName = "ResizablePanel";
11460
11918
 
11461
- __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)}");
11919
+ __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}");
11462
11920
 
11463
11921
  const recordPreviewSlotNames = [
11464
11922
  [
@@ -11568,6 +12026,18 @@ const recordPreviewSlotNames = [
11568
12026
  [
11569
12027
  "footer",
11570
12028
  "seed-record-preview__footer"
12029
+ ],
12030
+ [
12031
+ "tabsRoot",
12032
+ "seed-record-preview__tabsRoot"
12033
+ ],
12034
+ [
12035
+ "tabPanel",
12036
+ "seed-record-preview__tabPanel"
12037
+ ],
12038
+ [
12039
+ "tabBadge",
12040
+ "seed-record-preview__tabBadge"
11571
12041
  ]
11572
12042
  ];
11573
12043
  const defaultVariant = {};
@@ -11729,30 +12199,2684 @@ function useStateMachine(initialState, machine) {
11729
12199
  const PresenceContext = /*#__PURE__*/ createContext(null);
11730
12200
  PresenceContext.Provider;
11731
12201
 
11732
- const RESIZE_MIN_WIDTH = 360;
11733
- const RESIZE_MAX_WIDTH = 720;
11734
- /**
11735
- * Right-side record peek panel -- Attio's own peek shows a Highlights card
11736
- * area and an Activity feed (measured live). Ported from your-moon/crisp's
11737
- * `RecordPanel` (`product/recordpanel/`), built ON `ResizablePanel`
11738
- * (`side="right" floating`) rather than crisp's own duplicate absolute-
11739
- * positioning/slide-in/resize-handle implementation -- the outer shell (the
11740
- * enter/exit keyframe animation, the floating elevation, the drag handle
11741
- * itself) all come from that primitive; this component only renders the
11742
- * CONTENT inside it.
11743
- *
11744
- * Enter/exit: closing this panel is a state change (`open` -> false) that
11745
- * typically clears `record` in the SAME update (see the DataTable story's
11746
- * own `onClose={() => setPreviewRow(null)}`), so by the time an exit
11747
- * animation should play, the values this component would render are
11748
- * already gone. `usePresence` (`@seed-design/react-presence`, the same
11749
- * idiom Dialog/Drawer already use for their own floating layers) keeps the
11750
- * tree mounted for exactly the exit animation's duration; the snapshot
11751
- * below keeps the LAST visible props around for that same window, since
11752
- * without it the panel would animate out empty.
11753
- */ function RecordPreview(props) {
11754
- const shouldRender = props.open && props.record != null;
11755
- const { isPresent, ref: panelElementRef } = usePresence(shouldRender);
12202
+ var __defProp$1 = Object.defineProperty;
12203
+ var __name$1 = (target, value)=>__defProp$1(target, "name", {
12204
+ value,
12205
+ configurable: true
12206
+ });
12207
+ function useSize(element) {
12208
+ const [size, setSize] = React.useState(void 0);
12209
+ useLayoutEffect2(()=>{
12210
+ if (element) {
12211
+ setSize({
12212
+ width: element.offsetWidth,
12213
+ height: element.offsetHeight
12214
+ });
12215
+ const resizeObserver = new ResizeObserver((entries)=>{
12216
+ if (!Array.isArray(entries)) {
12217
+ return;
12218
+ }
12219
+ if (!entries.length) {
12220
+ return;
12221
+ }
12222
+ const entry = entries[0];
12223
+ let width;
12224
+ let height;
12225
+ if ("borderBoxSize" in entry) {
12226
+ const borderSizeEntry = entry["borderBoxSize"];
12227
+ const borderSize = Array.isArray(borderSizeEntry) ? borderSizeEntry[0] : borderSizeEntry;
12228
+ width = borderSize["inlineSize"];
12229
+ height = borderSize["blockSize"];
12230
+ } else {
12231
+ width = element.offsetWidth;
12232
+ height = element.offsetHeight;
12233
+ }
12234
+ setSize({
12235
+ width,
12236
+ height
12237
+ });
12238
+ });
12239
+ resizeObserver.observe(element, {
12240
+ box: "border-box"
12241
+ });
12242
+ return ()=>resizeObserver.unobserve(element);
12243
+ } else {
12244
+ setSize(void 0);
12245
+ }
12246
+ }, [
12247
+ element
12248
+ ]);
12249
+ return size;
12250
+ }
12251
+ __name$1(useSize, "useSize");
12252
+
12253
+ function useSupports(selector) {
12254
+ const [isSupported, setIsSupported] = useState(null);
12255
+ useEffect(()=>{
12256
+ setIsSupported(CSS.supports(selector));
12257
+ }, [
12258
+ selector
12259
+ ]);
12260
+ return isSupported;
12261
+ }
12262
+
12263
+ var __defProp = Object.defineProperty;
12264
+ var __name = (target, value)=>__defProp(target, "name", {
12265
+ value,
12266
+ configurable: true
12267
+ });
12268
+ function useCallbackRef(callback) {
12269
+ const callbackRef = React.useRef(callback);
12270
+ React.useEffect(()=>{
12271
+ callbackRef.current = callback;
12272
+ });
12273
+ return React.useMemo(()=>(...args)=>callbackRef.current?.(...args), []);
12274
+ }
12275
+ __name(useCallbackRef, "useCallbackRef");
12276
+
12277
+ function AutoHeight(userOptions = {}) {
12278
+ let emblaApi;
12279
+ let slideHeights = [];
12280
+ const heightEvents = [
12281
+ 'select',
12282
+ 'slideFocus'
12283
+ ];
12284
+ function init(emblaApiInstance) {
12285
+ emblaApi = emblaApiInstance;
12286
+ const { options: { axis }, slideRects } = emblaApi.internalEngine();
12287
+ if (axis === 'y') return;
12288
+ slideHeights = slideRects.map((slideRect)=>slideRect.height);
12289
+ heightEvents.forEach((evt)=>emblaApi.on(evt, setContainerHeight));
12290
+ setContainerHeight();
12291
+ }
12292
+ function destroy() {
12293
+ heightEvents.forEach((evt)=>emblaApi.off(evt, setContainerHeight));
12294
+ const container = emblaApi.containerNode();
12295
+ container.style.height = '';
12296
+ if (!container.getAttribute('style')) container.removeAttribute('style');
12297
+ }
12298
+ function highestInView() {
12299
+ const { slideRegistry } = emblaApi.internalEngine();
12300
+ const selectedIndexes = slideRegistry[emblaApi.selectedScrollSnap()];
12301
+ if (!selectedIndexes) return null;
12302
+ return selectedIndexes.map((index)=>slideHeights[index]).reduce((a, b)=>Math.max(a, b), 0);
12303
+ }
12304
+ function setContainerHeight() {
12305
+ const height = highestInView();
12306
+ if (height === null) return;
12307
+ emblaApi.containerNode().style.height = `${highestInView()}px`;
12308
+ }
12309
+ const self = {
12310
+ name: 'autoHeight',
12311
+ options: userOptions,
12312
+ init,
12313
+ destroy
12314
+ };
12315
+ return self;
12316
+ }
12317
+ AutoHeight.globalOptions = undefined;
12318
+
12319
+ function isObject$1(subject) {
12320
+ return Object.prototype.toString.call(subject) === '[object Object]';
12321
+ }
12322
+ function isRecord(subject) {
12323
+ return isObject$1(subject) || Array.isArray(subject);
12324
+ }
12325
+ function canUseDOM() {
12326
+ return !!(typeof window !== 'undefined' && window.document && window.document.createElement);
12327
+ }
12328
+ function areOptionsEqual(optionsA, optionsB) {
12329
+ const optionsAKeys = Object.keys(optionsA);
12330
+ const optionsBKeys = Object.keys(optionsB);
12331
+ if (optionsAKeys.length !== optionsBKeys.length) return false;
12332
+ const breakpointsA = JSON.stringify(Object.keys(optionsA.breakpoints || {}));
12333
+ const breakpointsB = JSON.stringify(Object.keys(optionsB.breakpoints || {}));
12334
+ if (breakpointsA !== breakpointsB) return false;
12335
+ return optionsAKeys.every((key)=>{
12336
+ const valueA = optionsA[key];
12337
+ const valueB = optionsB[key];
12338
+ if (typeof valueA === 'function') return `${valueA}` === `${valueB}`;
12339
+ if (!isRecord(valueA) || !isRecord(valueB)) return valueA === valueB;
12340
+ return areOptionsEqual(valueA, valueB);
12341
+ });
12342
+ }
12343
+ function sortAndMapPluginToOptions(plugins) {
12344
+ return plugins.concat().sort((a, b)=>a.name > b.name ? 1 : -1).map((plugin)=>plugin.options);
12345
+ }
12346
+ function arePluginsEqual(pluginsA, pluginsB) {
12347
+ if (pluginsA.length !== pluginsB.length) return false;
12348
+ const optionsA = sortAndMapPluginToOptions(pluginsA);
12349
+ const optionsB = sortAndMapPluginToOptions(pluginsB);
12350
+ return optionsA.every((optionA, index)=>{
12351
+ const optionB = optionsB[index];
12352
+ return areOptionsEqual(optionA, optionB);
12353
+ });
12354
+ }
12355
+
12356
+ function isNumber(subject) {
12357
+ return typeof subject === 'number';
12358
+ }
12359
+ function isString(subject) {
12360
+ return typeof subject === 'string';
12361
+ }
12362
+ function isBoolean(subject) {
12363
+ return typeof subject === 'boolean';
12364
+ }
12365
+ function isObject(subject) {
12366
+ return Object.prototype.toString.call(subject) === '[object Object]';
12367
+ }
12368
+ function mathAbs(n) {
12369
+ return Math.abs(n);
12370
+ }
12371
+ function mathSign(n) {
12372
+ return Math.sign(n);
12373
+ }
12374
+ function deltaAbs(valueB, valueA) {
12375
+ return mathAbs(valueB - valueA);
12376
+ }
12377
+ function factorAbs(valueB, valueA) {
12378
+ if (valueB === 0 || valueA === 0) return 0;
12379
+ if (mathAbs(valueB) <= mathAbs(valueA)) return 0;
12380
+ const diff = deltaAbs(mathAbs(valueB), mathAbs(valueA));
12381
+ return mathAbs(diff / valueB);
12382
+ }
12383
+ function roundToTwoDecimals(num) {
12384
+ return Math.round(num * 100) / 100;
12385
+ }
12386
+ function arrayKeys(array) {
12387
+ return objectKeys(array).map(Number);
12388
+ }
12389
+ function arrayLast(array) {
12390
+ return array[arrayLastIndex(array)];
12391
+ }
12392
+ function arrayLastIndex(array) {
12393
+ return Math.max(0, array.length - 1);
12394
+ }
12395
+ function arrayIsLastIndex(array, index) {
12396
+ return index === arrayLastIndex(array);
12397
+ }
12398
+ function arrayFromNumber(n, startAt = 0) {
12399
+ return Array.from(Array(n), (_, i)=>startAt + i);
12400
+ }
12401
+ function objectKeys(object) {
12402
+ return Object.keys(object);
12403
+ }
12404
+ function objectsMergeDeep(objectA, objectB) {
12405
+ return [
12406
+ objectA,
12407
+ objectB
12408
+ ].reduce((mergedObjects, currentObject)=>{
12409
+ objectKeys(currentObject).forEach((key)=>{
12410
+ const valueA = mergedObjects[key];
12411
+ const valueB = currentObject[key];
12412
+ const areObjects = isObject(valueA) && isObject(valueB);
12413
+ mergedObjects[key] = areObjects ? objectsMergeDeep(valueA, valueB) : valueB;
12414
+ });
12415
+ return mergedObjects;
12416
+ }, {});
12417
+ }
12418
+ function isMouseEvent(evt, ownerWindow) {
12419
+ return typeof ownerWindow.MouseEvent !== 'undefined' && evt instanceof ownerWindow.MouseEvent;
12420
+ }
12421
+ function Alignment(align, viewSize) {
12422
+ const predefined = {
12423
+ start,
12424
+ center,
12425
+ end
12426
+ };
12427
+ function start() {
12428
+ return 0;
12429
+ }
12430
+ function center(n) {
12431
+ return end(n) / 2;
12432
+ }
12433
+ function end(n) {
12434
+ return viewSize - n;
12435
+ }
12436
+ function measure(n, index) {
12437
+ if (isString(align)) return predefined[align](n);
12438
+ return align(viewSize, n, index);
12439
+ }
12440
+ const self = {
12441
+ measure
12442
+ };
12443
+ return self;
12444
+ }
12445
+ function EventStore() {
12446
+ let listeners = [];
12447
+ function add(node, type, handler, options = {
12448
+ passive: true
12449
+ }) {
12450
+ let removeListener;
12451
+ if ('addEventListener' in node) {
12452
+ node.addEventListener(type, handler, options);
12453
+ removeListener = ()=>node.removeEventListener(type, handler, options);
12454
+ } else {
12455
+ const legacyMediaQueryList = node;
12456
+ legacyMediaQueryList.addListener(handler);
12457
+ removeListener = ()=>legacyMediaQueryList.removeListener(handler);
12458
+ }
12459
+ listeners.push(removeListener);
12460
+ return self;
12461
+ }
12462
+ function clear() {
12463
+ listeners = listeners.filter((remove)=>remove());
12464
+ }
12465
+ const self = {
12466
+ add,
12467
+ clear
12468
+ };
12469
+ return self;
12470
+ }
12471
+ function Animations(ownerDocument, ownerWindow, update, render) {
12472
+ const documentVisibleHandler = EventStore();
12473
+ const fixedTimeStep = 1000 / 60;
12474
+ let lastTimeStamp = null;
12475
+ let accumulatedTime = 0;
12476
+ let animationId = 0;
12477
+ function init() {
12478
+ documentVisibleHandler.add(ownerDocument, 'visibilitychange', ()=>{
12479
+ if (ownerDocument.hidden) reset();
12480
+ });
12481
+ }
12482
+ function destroy() {
12483
+ stop();
12484
+ documentVisibleHandler.clear();
12485
+ }
12486
+ function animate(timeStamp) {
12487
+ if (!animationId) return;
12488
+ if (!lastTimeStamp) {
12489
+ lastTimeStamp = timeStamp;
12490
+ update();
12491
+ update();
12492
+ }
12493
+ const timeElapsed = timeStamp - lastTimeStamp;
12494
+ lastTimeStamp = timeStamp;
12495
+ accumulatedTime += timeElapsed;
12496
+ while(accumulatedTime >= fixedTimeStep){
12497
+ update();
12498
+ accumulatedTime -= fixedTimeStep;
12499
+ }
12500
+ const alpha = accumulatedTime / fixedTimeStep;
12501
+ render(alpha);
12502
+ if (animationId) {
12503
+ animationId = ownerWindow.requestAnimationFrame(animate);
12504
+ }
12505
+ }
12506
+ function start() {
12507
+ if (animationId) return;
12508
+ animationId = ownerWindow.requestAnimationFrame(animate);
12509
+ }
12510
+ function stop() {
12511
+ ownerWindow.cancelAnimationFrame(animationId);
12512
+ lastTimeStamp = null;
12513
+ accumulatedTime = 0;
12514
+ animationId = 0;
12515
+ }
12516
+ function reset() {
12517
+ lastTimeStamp = null;
12518
+ accumulatedTime = 0;
12519
+ }
12520
+ const self = {
12521
+ init,
12522
+ destroy,
12523
+ start,
12524
+ stop,
12525
+ update,
12526
+ render
12527
+ };
12528
+ return self;
12529
+ }
12530
+ function Axis(axis, contentDirection) {
12531
+ const isRightToLeft = contentDirection === 'rtl';
12532
+ const isVertical = axis === 'y';
12533
+ const scroll = isVertical ? 'y' : 'x';
12534
+ const cross = isVertical ? 'x' : 'y';
12535
+ const sign = !isVertical && isRightToLeft ? -1 : 1;
12536
+ const startEdge = getStartEdge();
12537
+ const endEdge = getEndEdge();
12538
+ function measureSize(nodeRect) {
12539
+ const { height, width } = nodeRect;
12540
+ return isVertical ? height : width;
12541
+ }
12542
+ function getStartEdge() {
12543
+ if (isVertical) return 'top';
12544
+ return isRightToLeft ? 'right' : 'left';
12545
+ }
12546
+ function getEndEdge() {
12547
+ if (isVertical) return 'bottom';
12548
+ return isRightToLeft ? 'left' : 'right';
12549
+ }
12550
+ function direction(n) {
12551
+ return n * sign;
12552
+ }
12553
+ const self = {
12554
+ scroll,
12555
+ cross,
12556
+ startEdge,
12557
+ endEdge,
12558
+ measureSize,
12559
+ direction
12560
+ };
12561
+ return self;
12562
+ }
12563
+ function Limit(min = 0, max = 0) {
12564
+ const length = mathAbs(min - max);
12565
+ function reachedMin(n) {
12566
+ return n < min;
12567
+ }
12568
+ function reachedMax(n) {
12569
+ return n > max;
12570
+ }
12571
+ function reachedAny(n) {
12572
+ return reachedMin(n) || reachedMax(n);
12573
+ }
12574
+ function constrain(n) {
12575
+ if (!reachedAny(n)) return n;
12576
+ return reachedMin(n) ? min : max;
12577
+ }
12578
+ function removeOffset(n) {
12579
+ if (!length) return n;
12580
+ return n - length * Math.ceil((n - max) / length);
12581
+ }
12582
+ const self = {
12583
+ length,
12584
+ max,
12585
+ min,
12586
+ constrain,
12587
+ reachedAny,
12588
+ reachedMax,
12589
+ reachedMin,
12590
+ removeOffset
12591
+ };
12592
+ return self;
12593
+ }
12594
+ function Counter(max, start, loop) {
12595
+ const { constrain } = Limit(0, max);
12596
+ const loopEnd = max + 1;
12597
+ let counter = withinLimit(start);
12598
+ function withinLimit(n) {
12599
+ return !loop ? constrain(n) : mathAbs((loopEnd + n) % loopEnd);
12600
+ }
12601
+ function get() {
12602
+ return counter;
12603
+ }
12604
+ function set(n) {
12605
+ counter = withinLimit(n);
12606
+ return self;
12607
+ }
12608
+ function add(n) {
12609
+ return clone().set(get() + n);
12610
+ }
12611
+ function clone() {
12612
+ return Counter(max, get(), loop);
12613
+ }
12614
+ const self = {
12615
+ get,
12616
+ set,
12617
+ add,
12618
+ clone
12619
+ };
12620
+ return self;
12621
+ }
12622
+ function DragHandler(axis, rootNode, ownerDocument, ownerWindow, target, dragTracker, location, animation, scrollTo, scrollBody, scrollTarget, index, eventHandler, percentOfView, dragFree, dragThreshold, skipSnaps, baseFriction, watchDrag) {
12623
+ const { cross: crossAxis, direction } = axis;
12624
+ const focusNodes = [
12625
+ 'INPUT',
12626
+ 'SELECT',
12627
+ 'TEXTAREA'
12628
+ ];
12629
+ const nonPassiveEvent = {
12630
+ passive: false
12631
+ };
12632
+ const initEvents = EventStore();
12633
+ const dragEvents = EventStore();
12634
+ const goToNextThreshold = Limit(50, 225).constrain(percentOfView.measure(20));
12635
+ const snapForceBoost = {
12636
+ mouse: 300,
12637
+ touch: 400
12638
+ };
12639
+ const freeForceBoost = {
12640
+ mouse: 500,
12641
+ touch: 600
12642
+ };
12643
+ const baseSpeed = dragFree ? 43 : 25;
12644
+ let isMoving = false;
12645
+ let startScroll = 0;
12646
+ let startCross = 0;
12647
+ let pointerIsDown = false;
12648
+ let preventScroll = false;
12649
+ let preventClick = false;
12650
+ let isMouse = false;
12651
+ function init(emblaApi) {
12652
+ if (!watchDrag) return;
12653
+ function downIfAllowed(evt) {
12654
+ if (isBoolean(watchDrag) || watchDrag(emblaApi, evt)) down(evt);
12655
+ }
12656
+ const node = rootNode;
12657
+ 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);
12658
+ }
12659
+ function destroy() {
12660
+ initEvents.clear();
12661
+ dragEvents.clear();
12662
+ }
12663
+ function addDragEvents() {
12664
+ const node = isMouse ? ownerDocument : rootNode;
12665
+ dragEvents.add(node, 'touchmove', move, nonPassiveEvent).add(node, 'touchend', up).add(node, 'mousemove', move, nonPassiveEvent).add(node, 'mouseup', up);
12666
+ }
12667
+ function isFocusNode(node) {
12668
+ const nodeName = node.nodeName || '';
12669
+ return focusNodes.includes(nodeName);
12670
+ }
12671
+ function forceBoost() {
12672
+ const boost = dragFree ? freeForceBoost : snapForceBoost;
12673
+ const type = isMouse ? 'mouse' : 'touch';
12674
+ return boost[type];
12675
+ }
12676
+ function allowedForce(force, targetChanged) {
12677
+ const next = index.add(mathSign(force) * -1);
12678
+ const baseForce = scrollTarget.byDistance(force, !dragFree).distance;
12679
+ if (dragFree || mathAbs(force) < goToNextThreshold) return baseForce;
12680
+ if (skipSnaps && targetChanged) return baseForce * 0.5;
12681
+ return scrollTarget.byIndex(next.get(), 0).distance;
12682
+ }
12683
+ function down(evt) {
12684
+ const isMouseEvt = isMouseEvent(evt, ownerWindow);
12685
+ isMouse = isMouseEvt;
12686
+ preventClick = dragFree && isMouseEvt && !evt.buttons && isMoving;
12687
+ isMoving = deltaAbs(target.get(), location.get()) >= 2;
12688
+ if (isMouseEvt && evt.button !== 0) return;
12689
+ if (isFocusNode(evt.target)) return;
12690
+ pointerIsDown = true;
12691
+ dragTracker.pointerDown(evt);
12692
+ scrollBody.useFriction(0).useDuration(0);
12693
+ target.set(location);
12694
+ addDragEvents();
12695
+ startScroll = dragTracker.readPoint(evt);
12696
+ startCross = dragTracker.readPoint(evt, crossAxis);
12697
+ eventHandler.emit('pointerDown');
12698
+ }
12699
+ function move(evt) {
12700
+ const isTouchEvt = !isMouseEvent(evt, ownerWindow);
12701
+ if (isTouchEvt && evt.touches.length >= 2) return up(evt);
12702
+ const lastScroll = dragTracker.readPoint(evt);
12703
+ const lastCross = dragTracker.readPoint(evt, crossAxis);
12704
+ const diffScroll = deltaAbs(lastScroll, startScroll);
12705
+ const diffCross = deltaAbs(lastCross, startCross);
12706
+ if (!preventScroll && !isMouse) {
12707
+ if (!evt.cancelable) return up(evt);
12708
+ preventScroll = diffScroll > diffCross;
12709
+ if (!preventScroll) return up(evt);
12710
+ }
12711
+ const diff = dragTracker.pointerMove(evt);
12712
+ if (diffScroll > dragThreshold) preventClick = true;
12713
+ scrollBody.useFriction(0.3).useDuration(0.75);
12714
+ animation.start();
12715
+ target.add(direction(diff));
12716
+ evt.preventDefault();
12717
+ }
12718
+ function up(evt) {
12719
+ const currentLocation = scrollTarget.byDistance(0, false);
12720
+ const targetChanged = currentLocation.index !== index.get();
12721
+ const rawForce = dragTracker.pointerUp(evt) * forceBoost();
12722
+ const force = allowedForce(direction(rawForce), targetChanged);
12723
+ const forceFactor = factorAbs(rawForce, force);
12724
+ const speed = baseSpeed - 10 * forceFactor;
12725
+ const friction = baseFriction + forceFactor / 50;
12726
+ preventScroll = false;
12727
+ pointerIsDown = false;
12728
+ dragEvents.clear();
12729
+ scrollBody.useDuration(speed).useFriction(friction);
12730
+ scrollTo.distance(force, !dragFree);
12731
+ isMouse = false;
12732
+ eventHandler.emit('pointerUp');
12733
+ }
12734
+ function click(evt) {
12735
+ if (preventClick) {
12736
+ evt.stopPropagation();
12737
+ evt.preventDefault();
12738
+ preventClick = false;
12739
+ }
12740
+ }
12741
+ function pointerDown() {
12742
+ return pointerIsDown;
12743
+ }
12744
+ const self = {
12745
+ init,
12746
+ destroy,
12747
+ pointerDown
12748
+ };
12749
+ return self;
12750
+ }
12751
+ function DragTracker(axis, ownerWindow) {
12752
+ const logInterval = 170;
12753
+ let startEvent;
12754
+ let lastEvent;
12755
+ function readTime(evt) {
12756
+ return evt.timeStamp;
12757
+ }
12758
+ function readPoint(evt, evtAxis) {
12759
+ const property = evtAxis || axis.scroll;
12760
+ const coord = `client${property === 'x' ? 'X' : 'Y'}`;
12761
+ return (isMouseEvent(evt, ownerWindow) ? evt : evt.touches[0])[coord];
12762
+ }
12763
+ function pointerDown(evt) {
12764
+ startEvent = evt;
12765
+ lastEvent = evt;
12766
+ return readPoint(evt);
12767
+ }
12768
+ function pointerMove(evt) {
12769
+ const diff = readPoint(evt) - readPoint(lastEvent);
12770
+ const expired = readTime(evt) - readTime(startEvent) > logInterval;
12771
+ lastEvent = evt;
12772
+ if (expired) startEvent = evt;
12773
+ return diff;
12774
+ }
12775
+ function pointerUp(evt) {
12776
+ if (!startEvent || !lastEvent) return 0;
12777
+ const diffDrag = readPoint(lastEvent) - readPoint(startEvent);
12778
+ const diffTime = readTime(evt) - readTime(startEvent);
12779
+ const expired = readTime(evt) - readTime(lastEvent) > logInterval;
12780
+ const force = diffDrag / diffTime;
12781
+ const isFlick = diffTime && !expired && mathAbs(force) > 0.1;
12782
+ return isFlick ? force : 0;
12783
+ }
12784
+ const self = {
12785
+ pointerDown,
12786
+ pointerMove,
12787
+ pointerUp,
12788
+ readPoint
12789
+ };
12790
+ return self;
12791
+ }
12792
+ function NodeRects() {
12793
+ function measure(node) {
12794
+ const { offsetTop, offsetLeft, offsetWidth, offsetHeight } = node;
12795
+ const offset = {
12796
+ top: offsetTop,
12797
+ right: offsetLeft + offsetWidth,
12798
+ bottom: offsetTop + offsetHeight,
12799
+ left: offsetLeft,
12800
+ width: offsetWidth,
12801
+ height: offsetHeight
12802
+ };
12803
+ return offset;
12804
+ }
12805
+ const self = {
12806
+ measure
12807
+ };
12808
+ return self;
12809
+ }
12810
+ function PercentOfView(viewSize) {
12811
+ function measure(n) {
12812
+ return viewSize * (n / 100);
12813
+ }
12814
+ const self = {
12815
+ measure
12816
+ };
12817
+ return self;
12818
+ }
12819
+ function ResizeHandler(container, eventHandler, ownerWindow, slides, axis, watchResize, nodeRects) {
12820
+ const observeNodes = [
12821
+ container
12822
+ ].concat(slides);
12823
+ let resizeObserver;
12824
+ let containerSize;
12825
+ let slideSizes = [];
12826
+ let destroyed = false;
12827
+ function readSize(node) {
12828
+ return axis.measureSize(nodeRects.measure(node));
12829
+ }
12830
+ function init(emblaApi) {
12831
+ if (!watchResize) return;
12832
+ containerSize = readSize(container);
12833
+ slideSizes = slides.map(readSize);
12834
+ function defaultCallback(entries) {
12835
+ for (const entry of entries){
12836
+ if (destroyed) return;
12837
+ const isContainer = entry.target === container;
12838
+ const slideIndex = slides.indexOf(entry.target);
12839
+ const lastSize = isContainer ? containerSize : slideSizes[slideIndex];
12840
+ const newSize = readSize(isContainer ? container : slides[slideIndex]);
12841
+ const diffSize = mathAbs(newSize - lastSize);
12842
+ if (diffSize >= 0.5) {
12843
+ emblaApi.reInit();
12844
+ eventHandler.emit('resize');
12845
+ break;
12846
+ }
12847
+ }
12848
+ }
12849
+ resizeObserver = new ResizeObserver((entries)=>{
12850
+ if (isBoolean(watchResize) || watchResize(emblaApi, entries)) {
12851
+ defaultCallback(entries);
12852
+ }
12853
+ });
12854
+ ownerWindow.requestAnimationFrame(()=>{
12855
+ observeNodes.forEach((node)=>resizeObserver.observe(node));
12856
+ });
12857
+ }
12858
+ function destroy() {
12859
+ destroyed = true;
12860
+ if (resizeObserver) resizeObserver.disconnect();
12861
+ }
12862
+ const self = {
12863
+ init,
12864
+ destroy
12865
+ };
12866
+ return self;
12867
+ }
12868
+ function ScrollBody(location, offsetLocation, previousLocation, target, baseDuration, baseFriction) {
12869
+ let scrollVelocity = 0;
12870
+ let scrollDirection = 0;
12871
+ let scrollDuration = baseDuration;
12872
+ let scrollFriction = baseFriction;
12873
+ let rawLocation = location.get();
12874
+ let rawLocationPrevious = 0;
12875
+ function seek() {
12876
+ const displacement = target.get() - location.get();
12877
+ const isInstant = !scrollDuration;
12878
+ let scrollDistance = 0;
12879
+ if (isInstant) {
12880
+ scrollVelocity = 0;
12881
+ previousLocation.set(target);
12882
+ location.set(target);
12883
+ scrollDistance = displacement;
12884
+ } else {
12885
+ previousLocation.set(location);
12886
+ scrollVelocity += displacement / scrollDuration;
12887
+ scrollVelocity *= scrollFriction;
12888
+ rawLocation += scrollVelocity;
12889
+ location.add(scrollVelocity);
12890
+ scrollDistance = rawLocation - rawLocationPrevious;
12891
+ }
12892
+ scrollDirection = mathSign(scrollDistance);
12893
+ rawLocationPrevious = rawLocation;
12894
+ return self;
12895
+ }
12896
+ function settled() {
12897
+ const diff = target.get() - offsetLocation.get();
12898
+ return mathAbs(diff) < 0.001;
12899
+ }
12900
+ function duration() {
12901
+ return scrollDuration;
12902
+ }
12903
+ function direction() {
12904
+ return scrollDirection;
12905
+ }
12906
+ function velocity() {
12907
+ return scrollVelocity;
12908
+ }
12909
+ function useBaseDuration() {
12910
+ return useDuration(baseDuration);
12911
+ }
12912
+ function useBaseFriction() {
12913
+ return useFriction(baseFriction);
12914
+ }
12915
+ function useDuration(n) {
12916
+ scrollDuration = n;
12917
+ return self;
12918
+ }
12919
+ function useFriction(n) {
12920
+ scrollFriction = n;
12921
+ return self;
12922
+ }
12923
+ const self = {
12924
+ direction,
12925
+ duration,
12926
+ velocity,
12927
+ seek,
12928
+ settled,
12929
+ useBaseFriction,
12930
+ useBaseDuration,
12931
+ useFriction,
12932
+ useDuration
12933
+ };
12934
+ return self;
12935
+ }
12936
+ function ScrollBounds(limit, location, target, scrollBody, percentOfView) {
12937
+ const pullBackThreshold = percentOfView.measure(10);
12938
+ const edgeOffsetTolerance = percentOfView.measure(50);
12939
+ const frictionLimit = Limit(0.1, 0.99);
12940
+ let disabled = false;
12941
+ function shouldConstrain() {
12942
+ if (disabled) return false;
12943
+ if (!limit.reachedAny(target.get())) return false;
12944
+ if (!limit.reachedAny(location.get())) return false;
12945
+ return true;
12946
+ }
12947
+ function constrain(pointerDown) {
12948
+ if (!shouldConstrain()) return;
12949
+ const edge = limit.reachedMin(location.get()) ? 'min' : 'max';
12950
+ const diffToEdge = mathAbs(limit[edge] - location.get());
12951
+ const diffToTarget = target.get() - location.get();
12952
+ const friction = frictionLimit.constrain(diffToEdge / edgeOffsetTolerance);
12953
+ target.subtract(diffToTarget * friction);
12954
+ if (!pointerDown && mathAbs(diffToTarget) < pullBackThreshold) {
12955
+ target.set(limit.constrain(target.get()));
12956
+ scrollBody.useDuration(25).useBaseFriction();
12957
+ }
12958
+ }
12959
+ function toggleActive(active) {
12960
+ disabled = !active;
12961
+ }
12962
+ const self = {
12963
+ shouldConstrain,
12964
+ constrain,
12965
+ toggleActive
12966
+ };
12967
+ return self;
12968
+ }
12969
+ function ScrollContain(viewSize, contentSize, snapsAligned, containScroll, pixelTolerance) {
12970
+ const scrollBounds = Limit(-contentSize + viewSize, 0);
12971
+ const snapsBounded = measureBounded();
12972
+ const scrollContainLimit = findScrollContainLimit();
12973
+ const snapsContained = measureContained();
12974
+ function usePixelTolerance(bound, snap) {
12975
+ return deltaAbs(bound, snap) <= 1;
12976
+ }
12977
+ function findScrollContainLimit() {
12978
+ const startSnap = snapsBounded[0];
12979
+ const endSnap = arrayLast(snapsBounded);
12980
+ const min = snapsBounded.lastIndexOf(startSnap);
12981
+ const max = snapsBounded.indexOf(endSnap) + 1;
12982
+ return Limit(min, max);
12983
+ }
12984
+ function measureBounded() {
12985
+ return snapsAligned.map((snapAligned, index)=>{
12986
+ const { min, max } = scrollBounds;
12987
+ const snap = scrollBounds.constrain(snapAligned);
12988
+ const isFirst = !index;
12989
+ const isLast = arrayIsLastIndex(snapsAligned, index);
12990
+ if (isFirst) return max;
12991
+ if (isLast) return min;
12992
+ if (usePixelTolerance(min, snap)) return min;
12993
+ if (usePixelTolerance(max, snap)) return max;
12994
+ return snap;
12995
+ }).map((scrollBound)=>parseFloat(scrollBound.toFixed(3)));
12996
+ }
12997
+ function measureContained() {
12998
+ if (contentSize <= viewSize + pixelTolerance) return [
12999
+ scrollBounds.max
13000
+ ];
13001
+ if (containScroll === 'keepSnaps') return snapsBounded;
13002
+ const { min, max } = scrollContainLimit;
13003
+ return snapsBounded.slice(min, max);
13004
+ }
13005
+ const self = {
13006
+ snapsContained,
13007
+ scrollContainLimit
13008
+ };
13009
+ return self;
13010
+ }
13011
+ function ScrollLimit(contentSize, scrollSnaps, loop) {
13012
+ const max = scrollSnaps[0];
13013
+ const min = loop ? max - contentSize : arrayLast(scrollSnaps);
13014
+ const limit = Limit(min, max);
13015
+ const self = {
13016
+ limit
13017
+ };
13018
+ return self;
13019
+ }
13020
+ function ScrollLooper(contentSize, limit, location, vectors) {
13021
+ const jointSafety = 0.1;
13022
+ const min = limit.min + jointSafety;
13023
+ const max = limit.max + jointSafety;
13024
+ const { reachedMin, reachedMax } = Limit(min, max);
13025
+ function shouldLoop(direction) {
13026
+ if (direction === 1) return reachedMax(location.get());
13027
+ if (direction === -1) return reachedMin(location.get());
13028
+ return false;
13029
+ }
13030
+ function loop(direction) {
13031
+ if (!shouldLoop(direction)) return;
13032
+ const loopDistance = contentSize * (direction * -1);
13033
+ vectors.forEach((v)=>v.add(loopDistance));
13034
+ }
13035
+ const self = {
13036
+ loop
13037
+ };
13038
+ return self;
13039
+ }
13040
+ function ScrollProgress(limit) {
13041
+ const { max, length } = limit;
13042
+ function get(n) {
13043
+ const currentLocation = n - max;
13044
+ return length ? currentLocation / -length : 0;
13045
+ }
13046
+ const self = {
13047
+ get
13048
+ };
13049
+ return self;
13050
+ }
13051
+ function ScrollSnaps(axis, alignment, containerRect, slideRects, slidesToScroll) {
13052
+ const { startEdge, endEdge } = axis;
13053
+ const { groupSlides } = slidesToScroll;
13054
+ const alignments = measureSizes().map(alignment.measure);
13055
+ const snaps = measureUnaligned();
13056
+ const snapsAligned = measureAligned();
13057
+ function measureSizes() {
13058
+ return groupSlides(slideRects).map((rects)=>arrayLast(rects)[endEdge] - rects[0][startEdge]).map(mathAbs);
13059
+ }
13060
+ function measureUnaligned() {
13061
+ return slideRects.map((rect)=>containerRect[startEdge] - rect[startEdge]).map((snap)=>-mathAbs(snap));
13062
+ }
13063
+ function measureAligned() {
13064
+ return groupSlides(snaps).map((g)=>g[0]).map((snap, index)=>snap + alignments[index]);
13065
+ }
13066
+ const self = {
13067
+ snaps,
13068
+ snapsAligned
13069
+ };
13070
+ return self;
13071
+ }
13072
+ function SlideRegistry(containSnaps, containScroll, scrollSnaps, scrollContainLimit, slidesToScroll, slideIndexes) {
13073
+ const { groupSlides } = slidesToScroll;
13074
+ const { min, max } = scrollContainLimit;
13075
+ const slideRegistry = createSlideRegistry();
13076
+ function createSlideRegistry() {
13077
+ const groupedSlideIndexes = groupSlides(slideIndexes);
13078
+ const doNotContain = !containSnaps || containScroll === 'keepSnaps';
13079
+ if (scrollSnaps.length === 1) return [
13080
+ slideIndexes
13081
+ ];
13082
+ if (doNotContain) return groupedSlideIndexes;
13083
+ return groupedSlideIndexes.slice(min, max).map((group, index, groups)=>{
13084
+ const isFirst = !index;
13085
+ const isLast = arrayIsLastIndex(groups, index);
13086
+ if (isFirst) {
13087
+ const range = arrayLast(groups[0]) + 1;
13088
+ return arrayFromNumber(range);
13089
+ }
13090
+ if (isLast) {
13091
+ const range = arrayLastIndex(slideIndexes) - arrayLast(groups)[0] + 1;
13092
+ return arrayFromNumber(range, arrayLast(groups)[0]);
13093
+ }
13094
+ return group;
13095
+ });
13096
+ }
13097
+ const self = {
13098
+ slideRegistry
13099
+ };
13100
+ return self;
13101
+ }
13102
+ function ScrollTarget(loop, scrollSnaps, contentSize, limit, targetVector) {
13103
+ const { reachedAny, removeOffset, constrain } = limit;
13104
+ function minDistance(distances) {
13105
+ return distances.concat().sort((a, b)=>mathAbs(a) - mathAbs(b))[0];
13106
+ }
13107
+ function findTargetSnap(target) {
13108
+ const distance = loop ? removeOffset(target) : constrain(target);
13109
+ const ascDiffsToSnaps = scrollSnaps.map((snap, index)=>({
13110
+ diff: shortcut(snap - distance, 0),
13111
+ index
13112
+ })).sort((d1, d2)=>mathAbs(d1.diff) - mathAbs(d2.diff));
13113
+ const { index } = ascDiffsToSnaps[0];
13114
+ return {
13115
+ index,
13116
+ distance
13117
+ };
13118
+ }
13119
+ function shortcut(target, direction) {
13120
+ const targets = [
13121
+ target,
13122
+ target + contentSize,
13123
+ target - contentSize
13124
+ ];
13125
+ if (!loop) return target;
13126
+ if (!direction) return minDistance(targets);
13127
+ const matchingTargets = targets.filter((t)=>mathSign(t) === direction);
13128
+ if (matchingTargets.length) return minDistance(matchingTargets);
13129
+ return arrayLast(targets) - contentSize;
13130
+ }
13131
+ function byIndex(index, direction) {
13132
+ const diffToSnap = scrollSnaps[index] - targetVector.get();
13133
+ const distance = shortcut(diffToSnap, direction);
13134
+ return {
13135
+ index,
13136
+ distance
13137
+ };
13138
+ }
13139
+ function byDistance(distance, snap) {
13140
+ const target = targetVector.get() + distance;
13141
+ const { index, distance: targetSnapDistance } = findTargetSnap(target);
13142
+ const reachedBound = !loop && reachedAny(target);
13143
+ if (!snap || reachedBound) return {
13144
+ index,
13145
+ distance
13146
+ };
13147
+ const diffToSnap = scrollSnaps[index] - targetSnapDistance;
13148
+ const snapDistance = distance + shortcut(diffToSnap, 0);
13149
+ return {
13150
+ index,
13151
+ distance: snapDistance
13152
+ };
13153
+ }
13154
+ const self = {
13155
+ byDistance,
13156
+ byIndex,
13157
+ shortcut
13158
+ };
13159
+ return self;
13160
+ }
13161
+ function ScrollTo(animation, indexCurrent, indexPrevious, scrollBody, scrollTarget, targetVector, eventHandler) {
13162
+ function scrollTo(target) {
13163
+ const distanceDiff = target.distance;
13164
+ const indexDiff = target.index !== indexCurrent.get();
13165
+ targetVector.add(distanceDiff);
13166
+ if (distanceDiff) {
13167
+ if (scrollBody.duration()) {
13168
+ animation.start();
13169
+ } else {
13170
+ animation.update();
13171
+ animation.render(1);
13172
+ animation.update();
13173
+ }
13174
+ }
13175
+ if (indexDiff) {
13176
+ indexPrevious.set(indexCurrent.get());
13177
+ indexCurrent.set(target.index);
13178
+ eventHandler.emit('select');
13179
+ }
13180
+ }
13181
+ function distance(n, snap) {
13182
+ const target = scrollTarget.byDistance(n, snap);
13183
+ scrollTo(target);
13184
+ }
13185
+ function index(n, direction) {
13186
+ const targetIndex = indexCurrent.clone().set(n);
13187
+ const target = scrollTarget.byIndex(targetIndex.get(), direction);
13188
+ scrollTo(target);
13189
+ }
13190
+ const self = {
13191
+ distance,
13192
+ index
13193
+ };
13194
+ return self;
13195
+ }
13196
+ function SlideFocus(root, slides, slideRegistry, scrollTo, scrollBody, eventStore, eventHandler, watchFocus) {
13197
+ const focusListenerOptions = {
13198
+ passive: true,
13199
+ capture: true
13200
+ };
13201
+ let lastTabPressTime = 0;
13202
+ function init(emblaApi) {
13203
+ if (!watchFocus) return;
13204
+ function defaultCallback(index) {
13205
+ const nowTime = new Date().getTime();
13206
+ const diffTime = nowTime - lastTabPressTime;
13207
+ if (diffTime > 10) return;
13208
+ eventHandler.emit('slideFocusStart');
13209
+ root.scrollLeft = 0;
13210
+ const group = slideRegistry.findIndex((group)=>group.includes(index));
13211
+ if (!isNumber(group)) return;
13212
+ scrollBody.useDuration(0);
13213
+ scrollTo.index(group, 0);
13214
+ eventHandler.emit('slideFocus');
13215
+ }
13216
+ eventStore.add(document, 'keydown', registerTabPress, false);
13217
+ slides.forEach((slide, slideIndex)=>{
13218
+ eventStore.add(slide, 'focus', (evt)=>{
13219
+ if (isBoolean(watchFocus) || watchFocus(emblaApi, evt)) {
13220
+ defaultCallback(slideIndex);
13221
+ }
13222
+ }, focusListenerOptions);
13223
+ });
13224
+ }
13225
+ function registerTabPress(event) {
13226
+ if (event.code === 'Tab') lastTabPressTime = new Date().getTime();
13227
+ }
13228
+ const self = {
13229
+ init
13230
+ };
13231
+ return self;
13232
+ }
13233
+ function Vector1D(initialValue) {
13234
+ let value = initialValue;
13235
+ function get() {
13236
+ return value;
13237
+ }
13238
+ function set(n) {
13239
+ value = normalizeInput(n);
13240
+ }
13241
+ function add(n) {
13242
+ value += normalizeInput(n);
13243
+ }
13244
+ function subtract(n) {
13245
+ value -= normalizeInput(n);
13246
+ }
13247
+ function normalizeInput(n) {
13248
+ return isNumber(n) ? n : n.get();
13249
+ }
13250
+ const self = {
13251
+ get,
13252
+ set,
13253
+ add,
13254
+ subtract
13255
+ };
13256
+ return self;
13257
+ }
13258
+ function Translate(axis, container) {
13259
+ const translate = axis.scroll === 'x' ? x : y;
13260
+ const containerStyle = container.style;
13261
+ let previousTarget = null;
13262
+ let disabled = false;
13263
+ function x(n) {
13264
+ return `translate3d(${n}px,0px,0px)`;
13265
+ }
13266
+ function y(n) {
13267
+ return `translate3d(0px,${n}px,0px)`;
13268
+ }
13269
+ function to(target) {
13270
+ if (disabled) return;
13271
+ const newTarget = roundToTwoDecimals(axis.direction(target));
13272
+ if (newTarget === previousTarget) return;
13273
+ containerStyle.transform = translate(newTarget);
13274
+ previousTarget = newTarget;
13275
+ }
13276
+ function toggleActive(active) {
13277
+ disabled = !active;
13278
+ }
13279
+ function clear() {
13280
+ if (disabled) return;
13281
+ containerStyle.transform = '';
13282
+ if (!container.getAttribute('style')) container.removeAttribute('style');
13283
+ }
13284
+ const self = {
13285
+ clear,
13286
+ to,
13287
+ toggleActive
13288
+ };
13289
+ return self;
13290
+ }
13291
+ function SlideLooper(axis, viewSize, contentSize, slideSizes, slideSizesWithGaps, snaps, scrollSnaps, location, slides) {
13292
+ const roundingSafety = 0.5;
13293
+ const ascItems = arrayKeys(slideSizesWithGaps);
13294
+ const descItems = arrayKeys(slideSizesWithGaps).reverse();
13295
+ const loopPoints = startPoints().concat(endPoints());
13296
+ function removeSlideSizes(indexes, from) {
13297
+ return indexes.reduce((a, i)=>{
13298
+ return a - slideSizesWithGaps[i];
13299
+ }, from);
13300
+ }
13301
+ function slidesInGap(indexes, gap) {
13302
+ return indexes.reduce((a, i)=>{
13303
+ const remainingGap = removeSlideSizes(a, gap);
13304
+ return remainingGap > 0 ? a.concat([
13305
+ i
13306
+ ]) : a;
13307
+ }, []);
13308
+ }
13309
+ function findSlideBounds(offset) {
13310
+ return snaps.map((snap, index)=>({
13311
+ start: snap - slideSizes[index] + roundingSafety + offset,
13312
+ end: snap + viewSize - roundingSafety + offset
13313
+ }));
13314
+ }
13315
+ function findLoopPoints(indexes, offset, isEndEdge) {
13316
+ const slideBounds = findSlideBounds(offset);
13317
+ return indexes.map((index)=>{
13318
+ const initial = isEndEdge ? 0 : -contentSize;
13319
+ const altered = isEndEdge ? contentSize : 0;
13320
+ const boundEdge = isEndEdge ? 'end' : 'start';
13321
+ const loopPoint = slideBounds[index][boundEdge];
13322
+ return {
13323
+ index,
13324
+ loopPoint,
13325
+ slideLocation: Vector1D(-1),
13326
+ translate: Translate(axis, slides[index]),
13327
+ target: ()=>location.get() > loopPoint ? initial : altered
13328
+ };
13329
+ });
13330
+ }
13331
+ function startPoints() {
13332
+ const gap = scrollSnaps[0];
13333
+ const indexes = slidesInGap(descItems, gap);
13334
+ return findLoopPoints(indexes, contentSize, false);
13335
+ }
13336
+ function endPoints() {
13337
+ const gap = viewSize - scrollSnaps[0] - 1;
13338
+ const indexes = slidesInGap(ascItems, gap);
13339
+ return findLoopPoints(indexes, -contentSize, true);
13340
+ }
13341
+ function canLoop() {
13342
+ return loopPoints.every(({ index })=>{
13343
+ const otherIndexes = ascItems.filter((i)=>i !== index);
13344
+ return removeSlideSizes(otherIndexes, viewSize) <= 0.1;
13345
+ });
13346
+ }
13347
+ function loop() {
13348
+ loopPoints.forEach((loopPoint)=>{
13349
+ const { target, translate, slideLocation } = loopPoint;
13350
+ const shiftLocation = target();
13351
+ if (shiftLocation === slideLocation.get()) return;
13352
+ translate.to(shiftLocation);
13353
+ slideLocation.set(shiftLocation);
13354
+ });
13355
+ }
13356
+ function clear() {
13357
+ loopPoints.forEach((loopPoint)=>loopPoint.translate.clear());
13358
+ }
13359
+ const self = {
13360
+ canLoop,
13361
+ clear,
13362
+ loop,
13363
+ loopPoints
13364
+ };
13365
+ return self;
13366
+ }
13367
+ function SlidesHandler(container, eventHandler, watchSlides) {
13368
+ let mutationObserver;
13369
+ let destroyed = false;
13370
+ function init(emblaApi) {
13371
+ if (!watchSlides) return;
13372
+ function defaultCallback(mutations) {
13373
+ for (const mutation of mutations){
13374
+ if (mutation.type === 'childList') {
13375
+ emblaApi.reInit();
13376
+ eventHandler.emit('slidesChanged');
13377
+ break;
13378
+ }
13379
+ }
13380
+ }
13381
+ mutationObserver = new MutationObserver((mutations)=>{
13382
+ if (destroyed) return;
13383
+ if (isBoolean(watchSlides) || watchSlides(emblaApi, mutations)) {
13384
+ defaultCallback(mutations);
13385
+ }
13386
+ });
13387
+ mutationObserver.observe(container, {
13388
+ childList: true
13389
+ });
13390
+ }
13391
+ function destroy() {
13392
+ if (mutationObserver) mutationObserver.disconnect();
13393
+ destroyed = true;
13394
+ }
13395
+ const self = {
13396
+ init,
13397
+ destroy
13398
+ };
13399
+ return self;
13400
+ }
13401
+ function SlidesInView(container, slides, eventHandler, threshold) {
13402
+ const intersectionEntryMap = {};
13403
+ let inViewCache = null;
13404
+ let notInViewCache = null;
13405
+ let intersectionObserver;
13406
+ let destroyed = false;
13407
+ function init() {
13408
+ intersectionObserver = new IntersectionObserver((entries)=>{
13409
+ if (destroyed) return;
13410
+ entries.forEach((entry)=>{
13411
+ const index = slides.indexOf(entry.target);
13412
+ intersectionEntryMap[index] = entry;
13413
+ });
13414
+ inViewCache = null;
13415
+ notInViewCache = null;
13416
+ eventHandler.emit('slidesInView');
13417
+ }, {
13418
+ root: container.parentElement,
13419
+ threshold
13420
+ });
13421
+ slides.forEach((slide)=>intersectionObserver.observe(slide));
13422
+ }
13423
+ function destroy() {
13424
+ if (intersectionObserver) intersectionObserver.disconnect();
13425
+ destroyed = true;
13426
+ }
13427
+ function createInViewList(inView) {
13428
+ return objectKeys(intersectionEntryMap).reduce((list, slideIndex)=>{
13429
+ const index = parseInt(slideIndex);
13430
+ const { isIntersecting } = intersectionEntryMap[index];
13431
+ const inViewMatch = inView && isIntersecting;
13432
+ const notInViewMatch = !inView && !isIntersecting;
13433
+ if (inViewMatch || notInViewMatch) list.push(index);
13434
+ return list;
13435
+ }, []);
13436
+ }
13437
+ function get(inView = true) {
13438
+ if (inView && inViewCache) return inViewCache;
13439
+ if (!inView && notInViewCache) return notInViewCache;
13440
+ const slideIndexes = createInViewList(inView);
13441
+ if (inView) inViewCache = slideIndexes;
13442
+ if (!inView) notInViewCache = slideIndexes;
13443
+ return slideIndexes;
13444
+ }
13445
+ const self = {
13446
+ init,
13447
+ destroy,
13448
+ get
13449
+ };
13450
+ return self;
13451
+ }
13452
+ function SlideSizes(axis, containerRect, slideRects, slides, readEdgeGap, ownerWindow) {
13453
+ const { measureSize, startEdge, endEdge } = axis;
13454
+ const withEdgeGap = slideRects[0] && readEdgeGap;
13455
+ const startGap = measureStartGap();
13456
+ const endGap = measureEndGap();
13457
+ const slideSizes = slideRects.map(measureSize);
13458
+ const slideSizesWithGaps = measureWithGaps();
13459
+ function measureStartGap() {
13460
+ if (!withEdgeGap) return 0;
13461
+ const slideRect = slideRects[0];
13462
+ return mathAbs(containerRect[startEdge] - slideRect[startEdge]);
13463
+ }
13464
+ function measureEndGap() {
13465
+ if (!withEdgeGap) return 0;
13466
+ const style = ownerWindow.getComputedStyle(arrayLast(slides));
13467
+ return parseFloat(style.getPropertyValue(`margin-${endEdge}`));
13468
+ }
13469
+ function measureWithGaps() {
13470
+ return slideRects.map((rect, index, rects)=>{
13471
+ const isFirst = !index;
13472
+ const isLast = arrayIsLastIndex(rects, index);
13473
+ if (isFirst) return slideSizes[index] + startGap;
13474
+ if (isLast) return slideSizes[index] + endGap;
13475
+ return rects[index + 1][startEdge] - rect[startEdge];
13476
+ }).map(mathAbs);
13477
+ }
13478
+ const self = {
13479
+ slideSizes,
13480
+ slideSizesWithGaps,
13481
+ startGap,
13482
+ endGap
13483
+ };
13484
+ return self;
13485
+ }
13486
+ function SlidesToScroll(axis, viewSize, slidesToScroll, loop, containerRect, slideRects, startGap, endGap, pixelTolerance) {
13487
+ const { startEdge, endEdge, direction } = axis;
13488
+ const groupByNumber = isNumber(slidesToScroll);
13489
+ function byNumber(array, groupSize) {
13490
+ return arrayKeys(array).filter((i)=>i % groupSize === 0).map((i)=>array.slice(i, i + groupSize));
13491
+ }
13492
+ function bySize(array) {
13493
+ if (!array.length) return [];
13494
+ return arrayKeys(array).reduce((groups, rectB, index)=>{
13495
+ const rectA = arrayLast(groups) || 0;
13496
+ const isFirst = rectA === 0;
13497
+ const isLast = rectB === arrayLastIndex(array);
13498
+ const edgeA = containerRect[startEdge] - slideRects[rectA][startEdge];
13499
+ const edgeB = containerRect[startEdge] - slideRects[rectB][endEdge];
13500
+ const gapA = !loop && isFirst ? direction(startGap) : 0;
13501
+ const gapB = !loop && isLast ? direction(endGap) : 0;
13502
+ const chunkSize = mathAbs(edgeB - gapB - (edgeA + gapA));
13503
+ if (index && chunkSize > viewSize + pixelTolerance) groups.push(rectB);
13504
+ if (isLast) groups.push(array.length);
13505
+ return groups;
13506
+ }, []).map((currentSize, index, groups)=>{
13507
+ const previousSize = Math.max(groups[index - 1] || 0);
13508
+ return array.slice(previousSize, currentSize);
13509
+ });
13510
+ }
13511
+ function groupSlides(array) {
13512
+ return groupByNumber ? byNumber(array, slidesToScroll) : bySize(array);
13513
+ }
13514
+ const self = {
13515
+ groupSlides
13516
+ };
13517
+ return self;
13518
+ }
13519
+ function Engine(root, container, slides, ownerDocument, ownerWindow, options, eventHandler) {
13520
+ // Options
13521
+ const { align, axis: scrollAxis, direction, startIndex, loop, duration, dragFree, dragThreshold, inViewThreshold, slidesToScroll: groupSlides, skipSnaps, containScroll, watchResize, watchSlides, watchDrag, watchFocus } = options;
13522
+ // Measurements
13523
+ const pixelTolerance = 2;
13524
+ const nodeRects = NodeRects();
13525
+ const containerRect = nodeRects.measure(container);
13526
+ const slideRects = slides.map(nodeRects.measure);
13527
+ const axis = Axis(scrollAxis, direction);
13528
+ const viewSize = axis.measureSize(containerRect);
13529
+ const percentOfView = PercentOfView(viewSize);
13530
+ const alignment = Alignment(align, viewSize);
13531
+ const containSnaps = !loop && !!containScroll;
13532
+ const readEdgeGap = loop || !!containScroll;
13533
+ const { slideSizes, slideSizesWithGaps, startGap, endGap } = SlideSizes(axis, containerRect, slideRects, slides, readEdgeGap, ownerWindow);
13534
+ const slidesToScroll = SlidesToScroll(axis, viewSize, groupSlides, loop, containerRect, slideRects, startGap, endGap, pixelTolerance);
13535
+ const { snaps, snapsAligned } = ScrollSnaps(axis, alignment, containerRect, slideRects, slidesToScroll);
13536
+ const contentSize = -arrayLast(snaps) + arrayLast(slideSizesWithGaps);
13537
+ const { snapsContained, scrollContainLimit } = ScrollContain(viewSize, contentSize, snapsAligned, containScroll, pixelTolerance);
13538
+ const scrollSnaps = containSnaps ? snapsContained : snapsAligned;
13539
+ const { limit } = ScrollLimit(contentSize, scrollSnaps, loop);
13540
+ // Indexes
13541
+ const index = Counter(arrayLastIndex(scrollSnaps), startIndex, loop);
13542
+ const indexPrevious = index.clone();
13543
+ const slideIndexes = arrayKeys(slides);
13544
+ // Animation
13545
+ const update = ({ dragHandler, scrollBody, scrollBounds, options: { loop } })=>{
13546
+ if (!loop) scrollBounds.constrain(dragHandler.pointerDown());
13547
+ scrollBody.seek();
13548
+ };
13549
+ const render = ({ scrollBody, translate, location, offsetLocation, previousLocation, scrollLooper, slideLooper, dragHandler, animation, eventHandler, scrollBounds, options: { loop } }, alpha)=>{
13550
+ const shouldSettle = scrollBody.settled();
13551
+ const withinBounds = !scrollBounds.shouldConstrain();
13552
+ const hasSettled = loop ? shouldSettle : shouldSettle && withinBounds;
13553
+ const hasSettledAndIdle = hasSettled && !dragHandler.pointerDown();
13554
+ if (hasSettledAndIdle) animation.stop();
13555
+ const interpolatedLocation = location.get() * alpha + previousLocation.get() * (1 - alpha);
13556
+ offsetLocation.set(interpolatedLocation);
13557
+ if (loop) {
13558
+ scrollLooper.loop(scrollBody.direction());
13559
+ slideLooper.loop();
13560
+ }
13561
+ translate.to(offsetLocation.get());
13562
+ if (hasSettledAndIdle) eventHandler.emit('settle');
13563
+ if (!hasSettled) eventHandler.emit('scroll');
13564
+ };
13565
+ const animation = Animations(ownerDocument, ownerWindow, ()=>update(engine), (alpha)=>render(engine, alpha));
13566
+ // Shared
13567
+ const friction = 0.68;
13568
+ const startLocation = scrollSnaps[index.get()];
13569
+ const location = Vector1D(startLocation);
13570
+ const previousLocation = Vector1D(startLocation);
13571
+ const offsetLocation = Vector1D(startLocation);
13572
+ const target = Vector1D(startLocation);
13573
+ const scrollBody = ScrollBody(location, offsetLocation, previousLocation, target, duration, friction);
13574
+ const scrollTarget = ScrollTarget(loop, scrollSnaps, contentSize, limit, target);
13575
+ const scrollTo = ScrollTo(animation, index, indexPrevious, scrollBody, scrollTarget, target, eventHandler);
13576
+ const scrollProgress = ScrollProgress(limit);
13577
+ const eventStore = EventStore();
13578
+ const slidesInView = SlidesInView(container, slides, eventHandler, inViewThreshold);
13579
+ const { slideRegistry } = SlideRegistry(containSnaps, containScroll, scrollSnaps, scrollContainLimit, slidesToScroll, slideIndexes);
13580
+ const slideFocus = SlideFocus(root, slides, slideRegistry, scrollTo, scrollBody, eventStore, eventHandler, watchFocus);
13581
+ // Engine
13582
+ const engine = {
13583
+ ownerDocument,
13584
+ ownerWindow,
13585
+ eventHandler,
13586
+ containerRect,
13587
+ slideRects,
13588
+ animation,
13589
+ axis,
13590
+ dragHandler: DragHandler(axis, root, ownerDocument, ownerWindow, target, DragTracker(axis, ownerWindow), location, animation, scrollTo, scrollBody, scrollTarget, index, eventHandler, percentOfView, dragFree, dragThreshold, skipSnaps, friction, watchDrag),
13591
+ eventStore,
13592
+ percentOfView,
13593
+ index,
13594
+ indexPrevious,
13595
+ limit,
13596
+ location,
13597
+ offsetLocation,
13598
+ previousLocation,
13599
+ options,
13600
+ resizeHandler: ResizeHandler(container, eventHandler, ownerWindow, slides, axis, watchResize, nodeRects),
13601
+ scrollBody,
13602
+ scrollBounds: ScrollBounds(limit, offsetLocation, target, scrollBody, percentOfView),
13603
+ scrollLooper: ScrollLooper(contentSize, limit, offsetLocation, [
13604
+ location,
13605
+ offsetLocation,
13606
+ previousLocation,
13607
+ target
13608
+ ]),
13609
+ scrollProgress,
13610
+ scrollSnapList: scrollSnaps.map(scrollProgress.get),
13611
+ scrollSnaps,
13612
+ scrollTarget,
13613
+ scrollTo,
13614
+ slideLooper: SlideLooper(axis, viewSize, contentSize, slideSizes, slideSizesWithGaps, snaps, scrollSnaps, offsetLocation, slides),
13615
+ slideFocus,
13616
+ slidesHandler: SlidesHandler(container, eventHandler, watchSlides),
13617
+ slidesInView,
13618
+ slideIndexes,
13619
+ slideRegistry,
13620
+ slidesToScroll,
13621
+ target,
13622
+ translate: Translate(axis, container)
13623
+ };
13624
+ return engine;
13625
+ }
13626
+ function EventHandler() {
13627
+ let listeners = {};
13628
+ let api;
13629
+ function init(emblaApi) {
13630
+ api = emblaApi;
13631
+ }
13632
+ function getListeners(evt) {
13633
+ return listeners[evt] || [];
13634
+ }
13635
+ function emit(evt) {
13636
+ getListeners(evt).forEach((e)=>e(api, evt));
13637
+ return self;
13638
+ }
13639
+ function on(evt, cb) {
13640
+ listeners[evt] = getListeners(evt).concat([
13641
+ cb
13642
+ ]);
13643
+ return self;
13644
+ }
13645
+ function off(evt, cb) {
13646
+ listeners[evt] = getListeners(evt).filter((e)=>e !== cb);
13647
+ return self;
13648
+ }
13649
+ function clear() {
13650
+ listeners = {};
13651
+ }
13652
+ const self = {
13653
+ init,
13654
+ emit,
13655
+ off,
13656
+ on,
13657
+ clear
13658
+ };
13659
+ return self;
13660
+ }
13661
+ const defaultOptions = {
13662
+ align: 'center',
13663
+ axis: 'x',
13664
+ container: null,
13665
+ slides: null,
13666
+ containScroll: 'trimSnaps',
13667
+ direction: 'ltr',
13668
+ slidesToScroll: 1,
13669
+ inViewThreshold: 0,
13670
+ breakpoints: {},
13671
+ dragFree: false,
13672
+ dragThreshold: 10,
13673
+ loop: false,
13674
+ skipSnaps: false,
13675
+ duration: 25,
13676
+ startIndex: 0,
13677
+ active: true,
13678
+ watchDrag: true,
13679
+ watchResize: true,
13680
+ watchSlides: true,
13681
+ watchFocus: true
13682
+ };
13683
+ function OptionsHandler(ownerWindow) {
13684
+ function mergeOptions(optionsA, optionsB) {
13685
+ return objectsMergeDeep(optionsA, optionsB || {});
13686
+ }
13687
+ function optionsAtMedia(options) {
13688
+ const optionsAtMedia = options.breakpoints || {};
13689
+ const matchedMediaOptions = objectKeys(optionsAtMedia).filter((media)=>ownerWindow.matchMedia(media).matches).map((media)=>optionsAtMedia[media]).reduce((a, mediaOption)=>mergeOptions(a, mediaOption), {});
13690
+ return mergeOptions(options, matchedMediaOptions);
13691
+ }
13692
+ function optionsMediaQueries(optionsList) {
13693
+ return optionsList.map((options)=>objectKeys(options.breakpoints || {})).reduce((acc, mediaQueries)=>acc.concat(mediaQueries), []).map(ownerWindow.matchMedia);
13694
+ }
13695
+ const self = {
13696
+ mergeOptions,
13697
+ optionsAtMedia,
13698
+ optionsMediaQueries
13699
+ };
13700
+ return self;
13701
+ }
13702
+ function PluginsHandler(optionsHandler) {
13703
+ let activePlugins = [];
13704
+ function init(emblaApi, plugins) {
13705
+ activePlugins = plugins.filter(({ options })=>optionsHandler.optionsAtMedia(options).active !== false);
13706
+ activePlugins.forEach((plugin)=>plugin.init(emblaApi, optionsHandler));
13707
+ return plugins.reduce((map, plugin)=>Object.assign(map, {
13708
+ [plugin.name]: plugin
13709
+ }), {});
13710
+ }
13711
+ function destroy() {
13712
+ activePlugins = activePlugins.filter((plugin)=>plugin.destroy());
13713
+ }
13714
+ const self = {
13715
+ init,
13716
+ destroy
13717
+ };
13718
+ return self;
13719
+ }
13720
+ function EmblaCarousel(root, userOptions, userPlugins) {
13721
+ const ownerDocument = root.ownerDocument;
13722
+ const ownerWindow = ownerDocument.defaultView;
13723
+ const optionsHandler = OptionsHandler(ownerWindow);
13724
+ const pluginsHandler = PluginsHandler(optionsHandler);
13725
+ const mediaHandlers = EventStore();
13726
+ const eventHandler = EventHandler();
13727
+ const { mergeOptions, optionsAtMedia, optionsMediaQueries } = optionsHandler;
13728
+ const { on, off, emit } = eventHandler;
13729
+ const reInit = reActivate;
13730
+ let destroyed = false;
13731
+ let engine;
13732
+ let optionsBase = mergeOptions(defaultOptions, EmblaCarousel.globalOptions);
13733
+ let options = mergeOptions(optionsBase);
13734
+ let pluginList = [];
13735
+ let pluginApis;
13736
+ let container;
13737
+ let slides;
13738
+ function storeElements() {
13739
+ const { container: userContainer, slides: userSlides } = options;
13740
+ const customContainer = isString(userContainer) ? root.querySelector(userContainer) : userContainer;
13741
+ container = customContainer || root.children[0];
13742
+ const customSlides = isString(userSlides) ? container.querySelectorAll(userSlides) : userSlides;
13743
+ slides = [].slice.call(customSlides || container.children);
13744
+ }
13745
+ function createEngine(options) {
13746
+ const engine = Engine(root, container, slides, ownerDocument, ownerWindow, options, eventHandler);
13747
+ if (options.loop && !engine.slideLooper.canLoop()) {
13748
+ const optionsWithoutLoop = Object.assign({}, options, {
13749
+ loop: false
13750
+ });
13751
+ return createEngine(optionsWithoutLoop);
13752
+ }
13753
+ return engine;
13754
+ }
13755
+ function activate(withOptions, withPlugins) {
13756
+ if (destroyed) return;
13757
+ optionsBase = mergeOptions(optionsBase, withOptions);
13758
+ options = optionsAtMedia(optionsBase);
13759
+ pluginList = withPlugins || pluginList;
13760
+ storeElements();
13761
+ engine = createEngine(options);
13762
+ optionsMediaQueries([
13763
+ optionsBase,
13764
+ ...pluginList.map(({ options })=>options)
13765
+ ]).forEach((query)=>mediaHandlers.add(query, 'change', reActivate));
13766
+ if (!options.active) return;
13767
+ engine.translate.to(engine.location.get());
13768
+ engine.animation.init();
13769
+ engine.slidesInView.init();
13770
+ engine.slideFocus.init(self);
13771
+ engine.eventHandler.init(self);
13772
+ engine.resizeHandler.init(self);
13773
+ engine.slidesHandler.init(self);
13774
+ if (engine.options.loop) engine.slideLooper.loop();
13775
+ if (container.offsetParent && slides.length) engine.dragHandler.init(self);
13776
+ pluginApis = pluginsHandler.init(self, pluginList);
13777
+ }
13778
+ function reActivate(withOptions, withPlugins) {
13779
+ const startIndex = selectedScrollSnap();
13780
+ deActivate();
13781
+ activate(mergeOptions({
13782
+ startIndex
13783
+ }, withOptions), withPlugins);
13784
+ eventHandler.emit('reInit');
13785
+ }
13786
+ function deActivate() {
13787
+ engine.dragHandler.destroy();
13788
+ engine.eventStore.clear();
13789
+ engine.translate.clear();
13790
+ engine.slideLooper.clear();
13791
+ engine.resizeHandler.destroy();
13792
+ engine.slidesHandler.destroy();
13793
+ engine.slidesInView.destroy();
13794
+ engine.animation.destroy();
13795
+ pluginsHandler.destroy();
13796
+ mediaHandlers.clear();
13797
+ }
13798
+ function destroy() {
13799
+ if (destroyed) return;
13800
+ destroyed = true;
13801
+ mediaHandlers.clear();
13802
+ deActivate();
13803
+ eventHandler.emit('destroy');
13804
+ eventHandler.clear();
13805
+ }
13806
+ function scrollTo(index, jump, direction) {
13807
+ if (!options.active || destroyed) return;
13808
+ engine.scrollBody.useBaseFriction().useDuration(jump === true ? 0 : options.duration);
13809
+ engine.scrollTo.index(index, direction || 0);
13810
+ }
13811
+ function scrollNext(jump) {
13812
+ const next = engine.index.add(1).get();
13813
+ scrollTo(next, jump, -1);
13814
+ }
13815
+ function scrollPrev(jump) {
13816
+ const prev = engine.index.add(-1).get();
13817
+ scrollTo(prev, jump, 1);
13818
+ }
13819
+ function canScrollNext() {
13820
+ const next = engine.index.add(1).get();
13821
+ return next !== selectedScrollSnap();
13822
+ }
13823
+ function canScrollPrev() {
13824
+ const prev = engine.index.add(-1).get();
13825
+ return prev !== selectedScrollSnap();
13826
+ }
13827
+ function scrollSnapList() {
13828
+ return engine.scrollSnapList;
13829
+ }
13830
+ function scrollProgress() {
13831
+ return engine.scrollProgress.get(engine.offsetLocation.get());
13832
+ }
13833
+ function selectedScrollSnap() {
13834
+ return engine.index.get();
13835
+ }
13836
+ function previousScrollSnap() {
13837
+ return engine.indexPrevious.get();
13838
+ }
13839
+ function slidesInView() {
13840
+ return engine.slidesInView.get();
13841
+ }
13842
+ function slidesNotInView() {
13843
+ return engine.slidesInView.get(false);
13844
+ }
13845
+ function plugins() {
13846
+ return pluginApis;
13847
+ }
13848
+ function internalEngine() {
13849
+ return engine;
13850
+ }
13851
+ function rootNode() {
13852
+ return root;
13853
+ }
13854
+ function containerNode() {
13855
+ return container;
13856
+ }
13857
+ function slideNodes() {
13858
+ return slides;
13859
+ }
13860
+ const self = {
13861
+ canScrollNext,
13862
+ canScrollPrev,
13863
+ containerNode,
13864
+ internalEngine,
13865
+ destroy,
13866
+ off,
13867
+ on,
13868
+ emit,
13869
+ plugins,
13870
+ previousScrollSnap,
13871
+ reInit,
13872
+ rootNode,
13873
+ scrollNext,
13874
+ scrollPrev,
13875
+ scrollProgress,
13876
+ scrollSnapList,
13877
+ scrollTo,
13878
+ selectedScrollSnap,
13879
+ slideNodes,
13880
+ slidesInView,
13881
+ slidesNotInView
13882
+ };
13883
+ activate(userOptions, userPlugins);
13884
+ setTimeout(()=>eventHandler.emit('init'), 0);
13885
+ return self;
13886
+ }
13887
+ EmblaCarousel.globalOptions = undefined;
13888
+
13889
+ function useEmblaCarousel(options = {}, plugins = []) {
13890
+ const storedOptions = useRef(options);
13891
+ const storedPlugins = useRef(plugins);
13892
+ const [emblaApi, setEmblaApi] = useState();
13893
+ const [viewport, setViewport] = useState();
13894
+ const reInit = useCallback(()=>{
13895
+ if (emblaApi) emblaApi.reInit(storedOptions.current, storedPlugins.current);
13896
+ }, [
13897
+ emblaApi
13898
+ ]);
13899
+ useEffect(()=>{
13900
+ if (areOptionsEqual(storedOptions.current, options)) return;
13901
+ storedOptions.current = options;
13902
+ reInit();
13903
+ }, [
13904
+ options,
13905
+ reInit
13906
+ ]);
13907
+ useEffect(()=>{
13908
+ if (arePluginsEqual(storedPlugins.current, plugins)) return;
13909
+ storedPlugins.current = plugins;
13910
+ reInit();
13911
+ }, [
13912
+ plugins,
13913
+ reInit
13914
+ ]);
13915
+ useEffect(()=>{
13916
+ if (canUseDOM() && viewport) {
13917
+ EmblaCarousel.globalOptions = useEmblaCarousel.globalOptions;
13918
+ const newEmblaApi = EmblaCarousel(viewport, storedOptions.current, storedPlugins.current);
13919
+ setEmblaApi(newEmblaApi);
13920
+ return ()=>newEmblaApi.destroy();
13921
+ } else {
13922
+ setEmblaApi(undefined);
13923
+ }
13924
+ }, [
13925
+ viewport,
13926
+ setEmblaApi
13927
+ ]);
13928
+ return [
13929
+ setViewport,
13930
+ emblaApi
13931
+ ];
13932
+ }
13933
+ useEmblaCarousel.globalOptions = undefined;
13934
+
13935
+ function useRenderStrategy(props) {
13936
+ const wasEverPresent = useRef(false);
13937
+ if (props.present) {
13938
+ wasEverPresent.current = true;
13939
+ }
13940
+ return {
13941
+ unmounted: !props.present && !wasEverPresent.current && props.lazyMount || props.unmountOnExit && !props.present && wasEverPresent.current
13942
+ };
13943
+ }
13944
+ const RenderStrategyPropsContext = /*#__PURE__*/ createContext(null);
13945
+ const RenderStrategyPropsProvider = RenderStrategyPropsContext.Provider;
13946
+ function useRenderStrategyPropsContext({ strict = true } = {}) {
13947
+ const context = useContext(RenderStrategyPropsContext);
13948
+ if (!context && strict) {
13949
+ throw new Error("useRenderStrategyPropsContext must be used within a RenderStrategyPropsProvider");
13950
+ }
13951
+ return context;
13952
+ }
13953
+ /* ID -----------------------------------------------------------------------
13954
+ -------------------------------------------------------------------------------- */ const getTriggerId = (value, id)=>`tabs:${value}:${id}:trigger-root`;
13955
+ const getListId = (id)=>`tabs:${id}:list`;
13956
+ const getContentId = (value, id)=>`tabs:${value}:${id}:content`;
13957
+ /* Query -----------------------------------------------------------------------
13958
+ -------------------------------------------------------------------------------- */ const getTriggerByValue = (listEl, value)=>{
13959
+ return listEl.querySelector(`[data-value="${value}"]`);
13960
+ };
13961
+ const getEnabledValues = (el)=>{
13962
+ if (!el) return [];
13963
+ return Array.from(el.children).filter((child)=>!child.hasAttribute("aria-disabled")).map((child)=>child.getAttribute("data-value")).filter(Boolean);
13964
+ };
13965
+ /* Prevent Drag -----------------------------------------------------------------------
13966
+ -------------------------------------------------------------------------------- */ const PREVENT_DRAG_ATTRIBUTE = "data-embla-prevent-drag";
13967
+ const isDragPrevented = (el)=>{
13968
+ return el.closest(`[${PREVENT_DRAG_ATTRIBUTE}]`) != null;
13969
+ };
13970
+ function getPrevIndex(index, length) {
13971
+ if (length <= 0) {
13972
+ throw new Error("Length must be a positive number.");
13973
+ }
13974
+ return (index - 1 + length) % length;
13975
+ }
13976
+ function getNextIndex(index, length) {
13977
+ if (length <= 0) {
13978
+ throw new Error("Length must be a positive number.");
13979
+ }
13980
+ return (index + 1) % length;
13981
+ }
13982
+ // Helper function to scroll tab into view
13983
+ function scrollTabIntoView(tabElement, listElement, options) {
13984
+ const isHorizontal = listElement.getAttribute("aria-orientation") !== "vertical";
13985
+ // Default padding is 16px if not specified
13986
+ const scrollPadding = options?.scrollPadding;
13987
+ if (isHorizontal) {
13988
+ const tabLeft = tabElement.offsetLeft;
13989
+ const tabRight = tabLeft + tabElement.offsetWidth;
13990
+ const listScrollLeft = listElement.scrollLeft;
13991
+ const listWidth = listElement.clientWidth;
13992
+ const listRight = listScrollLeft + listWidth;
13993
+ if (tabLeft < listScrollLeft) {
13994
+ // Tab is to the left of the visible area - scroll a bit more to the left
13995
+ listElement.scrollTo({
13996
+ left: Math.max(0, tabLeft - scrollPadding),
13997
+ behavior: "smooth"
13998
+ });
13999
+ } else if (tabRight > listRight) {
14000
+ // Tab is to the right of the visible area - scroll a bit more to the right
14001
+ listElement.scrollTo({
14002
+ left: tabRight - listWidth + scrollPadding,
14003
+ behavior: "smooth"
14004
+ });
14005
+ }
14006
+ } else {
14007
+ const tabTop = tabElement.offsetTop;
14008
+ const tabBottom = tabTop + tabElement.offsetHeight;
14009
+ const listScrollTop = listElement.scrollTop;
14010
+ const listHeight = listElement.clientHeight;
14011
+ const listBottom = listScrollTop + listHeight;
14012
+ if (tabTop < listScrollTop) {
14013
+ // Tab is above the visible area - scroll a bit more up
14014
+ listElement.scrollTo({
14015
+ top: Math.max(0, tabTop - scrollPadding),
14016
+ behavior: "smooth"
14017
+ });
14018
+ } else if (tabBottom > listBottom) {
14019
+ // Tab is below the visible area - scroll a bit more down
14020
+ listElement.scrollTo({
14021
+ top: tabBottom - listHeight + scrollPadding,
14022
+ behavior: "smooth"
14023
+ });
14024
+ }
14025
+ }
14026
+ }
14027
+ function useIsSSR() {
14028
+ const [isSSR, setIsSSR] = useState(true);
14029
+ useEffect(()=>{
14030
+ setIsSSR(false);
14031
+ }, []);
14032
+ return isSSR;
14033
+ }
14034
+ function useTabsState(props) {
14035
+ const [interactionState, setInteractionState] = useState("idle");
14036
+ const [value, setValue] = useControllableState$1({
14037
+ prop: props.value,
14038
+ defaultProp: props.defaultValue ?? undefined,
14039
+ onChange: props.onValueChange
14040
+ });
14041
+ const [focusedValue, setFocusedValue] = useState(null);
14042
+ const [isFocusVisible, setIsFocusVisible] = useState(false);
14043
+ const isFocusVisibleSupported = useSupports("selector(:focus-visible)");
14044
+ const [listEl, listRef] = useState(null);
14045
+ const [selectedTriggerEl, setSelectedTriggerEl] = useState(null);
14046
+ const selectedTriggerSize = useSize(selectedTriggerEl);
14047
+ const enabledValues = useMemo(()=>listEl ? getEnabledValues(listEl) : [], [
14048
+ listEl
14049
+ ]);
14050
+ const contentIndex = value ? enabledValues.indexOf(value) : -1;
14051
+ const prevIndex = contentIndex >= 0 ? getPrevIndex(contentIndex, enabledValues.length) : -1;
14052
+ const nextIndex = contentIndex >= 0 ? getNextIndex(contentIndex, enabledValues.length) : -1;
14053
+ // Scroll selected tab into view when it changes
14054
+ // TODO: this implementation is temporary, we should create some sort of hook or plugin system
14055
+ // to allow users to customize the scroll behavior.
14056
+ useEffect(()=>{
14057
+ if (selectedTriggerEl && listEl) {
14058
+ scrollTabIntoView(selectedTriggerEl, listEl, {
14059
+ scrollPadding: 16
14060
+ });
14061
+ }
14062
+ }, [
14063
+ selectedTriggerEl,
14064
+ listEl
14065
+ ]);
14066
+ const focusTriggerByValue = useCallback((value)=>{
14067
+ if (listEl) {
14068
+ getTriggerByValue(listEl, value)?.focus();
14069
+ }
14070
+ }, [
14071
+ listEl
14072
+ ]);
14073
+ const selectPrevAction = useCallback(()=>{
14074
+ const prevValue = enabledValues[prevIndex];
14075
+ if (!prevValue) return;
14076
+ setValue(prevValue);
14077
+ }, [
14078
+ enabledValues,
14079
+ prevIndex,
14080
+ setValue
14081
+ ]);
14082
+ const selectNextAction = useCallback(()=>{
14083
+ const nextValue = enabledValues[nextIndex];
14084
+ if (!nextValue) return;
14085
+ setValue(nextValue);
14086
+ }, [
14087
+ enabledValues,
14088
+ nextIndex,
14089
+ setValue
14090
+ ]);
14091
+ const selectFirstAction = useCallback(()=>{
14092
+ const firstValue = enabledValues[0];
14093
+ if (!firstValue) return;
14094
+ setValue(firstValue);
14095
+ }, [
14096
+ enabledValues,
14097
+ setValue
14098
+ ]);
14099
+ const selectLastAction = useCallback(()=>{
14100
+ const lastValue = enabledValues[enabledValues.length - 1];
14101
+ if (!lastValue) return;
14102
+ setValue(lastValue);
14103
+ }, [
14104
+ enabledValues,
14105
+ setValue
14106
+ ]);
14107
+ const setFocusedValueAction = useCallback((value)=>{
14108
+ setFocusedValue(value);
14109
+ }, []);
14110
+ const clearFocusedValueAction = useCallback(()=>{
14111
+ setFocusedValue(null);
14112
+ }, []);
14113
+ const setValueAction = useCallback((value)=>{
14114
+ setValue(value);
14115
+ }, [
14116
+ setValue
14117
+ ]);
14118
+ const actions = {
14119
+ selectPrev: selectPrevAction,
14120
+ selectNext: selectNextAction,
14121
+ selectFirst: selectFirstAction,
14122
+ selectLast: selectLastAction,
14123
+ setFocusedValue: setFocusedValueAction,
14124
+ clearFocusedValue: clearFocusedValueAction,
14125
+ setValue: setValueAction
14126
+ };
14127
+ const arrowPrevEvent = useCallback(()=>{
14128
+ if (interactionState === "focused") {
14129
+ actions.selectPrev();
14130
+ focusTriggerByValue(enabledValues[prevIndex] ?? "");
14131
+ if (isFocusVisibleSupported) {
14132
+ setIsFocusVisible(true);
14133
+ }
14134
+ }
14135
+ }, [
14136
+ interactionState,
14137
+ actions.selectPrev,
14138
+ focusTriggerByValue,
14139
+ enabledValues,
14140
+ prevIndex,
14141
+ isFocusVisibleSupported
14142
+ ]);
14143
+ const arrowNextEvent = useCallback(()=>{
14144
+ if (interactionState === "focused") {
14145
+ actions.selectNext();
14146
+ focusTriggerByValue(enabledValues[nextIndex] ?? "");
14147
+ if (isFocusVisibleSupported) {
14148
+ setIsFocusVisible(true);
14149
+ }
14150
+ }
14151
+ }, [
14152
+ interactionState,
14153
+ actions.selectNext,
14154
+ focusTriggerByValue,
14155
+ enabledValues,
14156
+ nextIndex,
14157
+ isFocusVisibleSupported
14158
+ ]);
14159
+ const arrowUpEvent = useCallback(()=>{
14160
+ if (interactionState === "focused") {
14161
+ actions.selectPrev();
14162
+ focusTriggerByValue(enabledValues[prevIndex] ?? "");
14163
+ if (isFocusVisibleSupported) {
14164
+ setIsFocusVisible(true);
14165
+ }
14166
+ }
14167
+ }, [
14168
+ interactionState,
14169
+ actions.selectPrev,
14170
+ focusTriggerByValue,
14171
+ enabledValues,
14172
+ prevIndex,
14173
+ isFocusVisibleSupported
14174
+ ]);
14175
+ const arrowDownEvent = useCallback(()=>{
14176
+ if (interactionState === "focused") {
14177
+ actions.selectNext();
14178
+ focusTriggerByValue(enabledValues[nextIndex] ?? "");
14179
+ if (isFocusVisibleSupported) {
14180
+ setIsFocusVisible(true);
14181
+ }
14182
+ }
14183
+ }, [
14184
+ interactionState,
14185
+ actions.selectNext,
14186
+ focusTriggerByValue,
14187
+ enabledValues,
14188
+ nextIndex,
14189
+ isFocusVisibleSupported
14190
+ ]);
14191
+ const homeEvent = useCallback(()=>{
14192
+ if (interactionState === "focused") {
14193
+ actions.selectFirst();
14194
+ focusTriggerByValue(enabledValues[0] ?? "");
14195
+ if (isFocusVisibleSupported) {
14196
+ setIsFocusVisible(true);
14197
+ }
14198
+ }
14199
+ }, [
14200
+ interactionState,
14201
+ actions.selectFirst,
14202
+ focusTriggerByValue,
14203
+ enabledValues,
14204
+ isFocusVisibleSupported
14205
+ ]);
14206
+ const endEvent = useCallback(()=>{
14207
+ if (interactionState === "focused") {
14208
+ actions.selectLast();
14209
+ focusTriggerByValue(enabledValues[enabledValues.length - 1] ?? "");
14210
+ if (isFocusVisibleSupported) {
14211
+ setIsFocusVisible(true);
14212
+ }
14213
+ }
14214
+ }, [
14215
+ interactionState,
14216
+ actions.selectLast,
14217
+ focusTriggerByValue,
14218
+ enabledValues,
14219
+ isFocusVisibleSupported
14220
+ ]);
14221
+ const tabFocusEvent = useCallback((value)=>{
14222
+ actions.setFocusedValue(value);
14223
+ if (interactionState === "idle") {
14224
+ setInteractionState("focused");
14225
+ }
14226
+ }, [
14227
+ interactionState,
14228
+ actions.setFocusedValue
14229
+ ]);
14230
+ const tabBlurEvent = useCallback(()=>{
14231
+ if (interactionState === "focused") {
14232
+ actions.clearFocusedValue();
14233
+ setInteractionState("idle");
14234
+ }
14235
+ }, [
14236
+ interactionState,
14237
+ actions.clearFocusedValue
14238
+ ]);
14239
+ const tabClickEvent = useCallback((value)=>{
14240
+ actions.setValue(value);
14241
+ if (interactionState === "idle") {
14242
+ setInteractionState("focused");
14243
+ }
14244
+ }, [
14245
+ interactionState,
14246
+ actions.setValue
14247
+ ]);
14248
+ const setValueEvent = useCallback((value)=>{
14249
+ actions.setValue(value);
14250
+ }, [
14251
+ actions.setValue
14252
+ ]);
14253
+ const selectNextEvent = useCallback(()=>{
14254
+ if (interactionState === "focused") {
14255
+ actions.selectNext();
14256
+ if (isFocusVisibleSupported) {
14257
+ setIsFocusVisible(true);
14258
+ }
14259
+ }
14260
+ }, [
14261
+ interactionState,
14262
+ actions.selectNext,
14263
+ isFocusVisibleSupported
14264
+ ]);
14265
+ const selectPrevEvent = useCallback(()=>{
14266
+ if (interactionState === "focused") {
14267
+ actions.selectPrev();
14268
+ if (isFocusVisibleSupported) {
14269
+ setIsFocusVisible(true);
14270
+ }
14271
+ }
14272
+ }, [
14273
+ interactionState,
14274
+ actions.selectPrev,
14275
+ isFocusVisibleSupported
14276
+ ]);
14277
+ const setContentIndexEvent = useCallback((index)=>{
14278
+ const valueFromIndex = enabledValues[index];
14279
+ if (!valueFromIndex) return;
14280
+ actions.setValue(valueFromIndex);
14281
+ }, [
14282
+ actions.setValue,
14283
+ enabledValues
14284
+ ]);
14285
+ const setIsFocusVisibleEvent = useCallback((isFocusVisible)=>{
14286
+ setIsFocusVisible(isFocusVisible);
14287
+ }, []);
14288
+ const setSelectedTriggerElEvent = useCallback((element)=>{
14289
+ setSelectedTriggerEl(element);
14290
+ }, []);
14291
+ const events = {
14292
+ arrowPrev: arrowPrevEvent,
14293
+ arrowNext: arrowNextEvent,
14294
+ arrowUp: arrowUpEvent,
14295
+ arrowDown: arrowDownEvent,
14296
+ home: homeEvent,
14297
+ end: endEvent,
14298
+ tabFocus: tabFocusEvent,
14299
+ tabBlur: tabBlurEvent,
14300
+ tabClick: tabClickEvent,
14301
+ setValue: setValueEvent,
14302
+ selectNext: selectNextEvent,
14303
+ selectPrev: selectPrevEvent,
14304
+ setContentIndex: setContentIndexEvent,
14305
+ setIsFocusVisible: setIsFocusVisibleEvent,
14306
+ setSelectedTriggerEl: setSelectedTriggerElEvent
14307
+ };
14308
+ const refs = useMemo(()=>({
14309
+ list: listRef
14310
+ }), []);
14311
+ const triggerRect = useMemo(()=>({
14312
+ width: selectedTriggerSize?.width ?? selectedTriggerEl?.offsetWidth ?? 0,
14313
+ left: selectedTriggerEl?.offsetLeft ?? 0
14314
+ }), [
14315
+ selectedTriggerSize,
14316
+ selectedTriggerEl
14317
+ ]);
14318
+ const isSSR = useIsSSR();
14319
+ return {
14320
+ refs,
14321
+ interactionState,
14322
+ value,
14323
+ isSSR,
14324
+ triggerRect,
14325
+ focusedValue,
14326
+ isFocusVisible,
14327
+ contentIndex,
14328
+ events,
14329
+ isFocusVisibleSupported
14330
+ };
14331
+ }
14332
+ function useTabs(props) {
14333
+ const autoId = useId();
14334
+ const { refs, interactionState, value, isSSR, events, triggerRect, focusedValue, isFocusVisible, contentIndex, isFocusVisibleSupported } = useTabsState(props);
14335
+ const { orientation = "horizontal" } = props;
14336
+ const focused = interactionState === "focused";
14337
+ const stateProps = useMemo(()=>elementProps({
14338
+ "data-orientation": orientation,
14339
+ "data-focus": dataAttr(focused),
14340
+ "data-ssr": dataAttr(isSSR)
14341
+ }), [
14342
+ orientation,
14343
+ focused,
14344
+ isSSR
14345
+ ]);
14346
+ return {
14347
+ refs,
14348
+ value,
14349
+ contentIndex,
14350
+ triggerRect,
14351
+ selectNext: events.selectNext,
14352
+ selectPrev: events.selectPrev,
14353
+ setValue: events.setValue,
14354
+ setContentIndex: events.setContentIndex,
14355
+ stateProps,
14356
+ rootProps: elementProps({
14357
+ ...stateProps,
14358
+ style: {
14359
+ "--indicator-left": `${triggerRect.left}px`,
14360
+ "--indicator-width": `${triggerRect.width}px`
14361
+ }
14362
+ }),
14363
+ listProps: elementProps({
14364
+ id: getListId(autoId),
14365
+ role: "tablist",
14366
+ "aria-orientation": orientation,
14367
+ ...stateProps,
14368
+ onKeyDown (event) {
14369
+ if (event.defaultPrevented) return;
14370
+ if (event.nativeEvent.isComposing) return;
14371
+ // TODO: support activationMode="manual"
14372
+ switch(event.key){
14373
+ case "ArrowLeft":
14374
+ if (orientation !== "horizontal") return;
14375
+ event.preventDefault();
14376
+ events.arrowPrev();
14377
+ break;
14378
+ case "ArrowRight":
14379
+ if (orientation !== "horizontal") return;
14380
+ event.preventDefault();
14381
+ events.arrowNext();
14382
+ break;
14383
+ case "ArrowUp":
14384
+ if (orientation !== "vertical") return;
14385
+ event.preventDefault();
14386
+ events.arrowPrev();
14387
+ break;
14388
+ case "ArrowDown":
14389
+ if (orientation !== "vertical") return;
14390
+ event.preventDefault();
14391
+ events.arrowNext();
14392
+ break;
14393
+ case "Home":
14394
+ {
14395
+ event.preventDefault();
14396
+ events.home();
14397
+ break;
14398
+ }
14399
+ case "End":
14400
+ {
14401
+ event.preventDefault();
14402
+ events.end();
14403
+ break;
14404
+ }
14405
+ }
14406
+ }
14407
+ }),
14408
+ getTriggerProps: (props)=>{
14409
+ const { disabled: isDisabled, value: triggerValue } = props;
14410
+ const itemState = {
14411
+ isDisabled,
14412
+ isSelected: value === triggerValue,
14413
+ isFocused: focusedValue === triggerValue
14414
+ };
14415
+ const itemStateProps = {
14416
+ "data-focus": dataAttr(itemState.isFocused),
14417
+ "data-focus-visible": dataAttr(itemState.isFocused && isFocusVisible),
14418
+ "data-selected": dataAttr(itemState.isSelected),
14419
+ "data-disabled": dataAttr(itemState.isDisabled),
14420
+ "data-ssr": dataAttr(isSSR),
14421
+ "aria-disabled": ariaAttr(itemState.isDisabled),
14422
+ "aria-selected": ariaAttr(itemState.isSelected)
14423
+ };
14424
+ const ref = (element)=>{
14425
+ if (element && triggerValue === value) {
14426
+ events.setSelectedTriggerEl(element);
14427
+ }
14428
+ };
14429
+ return {
14430
+ ...itemState,
14431
+ refs: {
14432
+ root: ref
14433
+ },
14434
+ stateProps: itemStateProps,
14435
+ rootProps: buttonProps({
14436
+ id: getTriggerId(triggerValue, autoId),
14437
+ role: "tab",
14438
+ type: "button",
14439
+ disabled: isDisabled,
14440
+ tabIndex: itemState.isSelected ? 0 : -1,
14441
+ ...itemStateProps,
14442
+ "data-value": triggerValue,
14443
+ "data-orientation": orientation,
14444
+ "data-ownedby": getListId(autoId),
14445
+ "aria-controls": getContentId(triggerValue, autoId),
14446
+ onClick (event) {
14447
+ if (itemState.isDisabled) return;
14448
+ if (event.defaultPrevented) return;
14449
+ events.tabClick(triggerValue);
14450
+ },
14451
+ onFocus (event) {
14452
+ events.tabFocus(props.value);
14453
+ if (isFocusVisibleSupported) {
14454
+ events.setIsFocusVisible(event.target.matches(":focus-visible"));
14455
+ }
14456
+ },
14457
+ onBlur (event) {
14458
+ const target = event.relatedTarget;
14459
+ if (target?.getAttribute("role") !== "tab") {
14460
+ events.tabBlur();
14461
+ }
14462
+ if (isFocusVisibleSupported) {
14463
+ events.setIsFocusVisible(false);
14464
+ }
14465
+ }
14466
+ })
14467
+ };
14468
+ },
14469
+ getContentProps: (props)=>{
14470
+ const { value: contentValue } = props;
14471
+ const triggerId = getTriggerId(contentValue, autoId);
14472
+ const isSelected = value === contentValue;
14473
+ return elementProps({
14474
+ id: getContentId(contentValue, autoId),
14475
+ tabIndex: -1,
14476
+ role: "tabpanel",
14477
+ "aria-labelledby": triggerId,
14478
+ "aria-selected": ariaAttr(isSelected),
14479
+ "aria-hidden": !isSelected,
14480
+ "data-selected": dataAttr(isSelected),
14481
+ "data-orientation": orientation,
14482
+ "data-ownedby": getListId(autoId),
14483
+ "data-ssr": dataAttr(isSSR)
14484
+ });
14485
+ },
14486
+ indicatorProps: elementProps({
14487
+ ...stateProps
14488
+ })
14489
+ };
14490
+ }
14491
+ const TabsContext = /*#__PURE__*/ createContext(null);
14492
+ const TabsProvider = TabsContext.Provider;
14493
+ function useTabsContext({ strict = true } = {}) {
14494
+ const context = useContext(TabsContext);
14495
+ if (!context && strict) {
14496
+ throw new Error("useTabsContext must be used within a Tabs");
14497
+ }
14498
+ return context;
14499
+ }
14500
+ const autoHeight = AutoHeight();
14501
+ const plugins = [
14502
+ autoHeight
14503
+ ];
14504
+ const useTabsCarouselState = (props)=>{
14505
+ const api = useTabsContext();
14506
+ const slideHeights = useRef(new WeakMap());
14507
+ const [emblaRef, emblaApi] = useEmblaCarousel({
14508
+ loop: props.loop,
14509
+ dragThreshold: props.dragThreshold,
14510
+ duration: 20,
14511
+ watchDrag: (_, event)=>{
14512
+ if (event.target instanceof HTMLElement) {
14513
+ if (isDragPrevented(event.target)) {
14514
+ return false;
14515
+ }
14516
+ }
14517
+ return true;
14518
+ },
14519
+ // Workaround for Embla v8 AutoHeight plugin not detecting lazy-loaded content height changes.
14520
+ // ref: https://github.com/davidjerleke/embla-carousel/discussions/1142
14521
+ // Embla's default resize handler only reInits on axis-size (width, for a horizontal carousel)
14522
+ // changes, so a slide growing taller (e.g. after lazy mount) never triggers a recalculation.
14523
+ // We reInit manually, but ONLY when a slide's *height* actually changes — reInit-ing on every
14524
+ // slide resize (including width fluctuations during horizontal scroll) re-applies Embla's
14525
+ // translate3d mid-scroll and breaks momentum scrolling in WKWebView/Safari.
14526
+ watchResize: (emblaApi, entries)=>{
14527
+ const heights = slideHeights.current;
14528
+ const slides = emblaApi.slideNodes();
14529
+ for (const entry of entries){
14530
+ const slide = slides.find((node)=>node === entry.target);
14531
+ if (!slide) continue;
14532
+ const newHeight = slide.offsetHeight;
14533
+ const prevHeight = heights.get(slide);
14534
+ heights.set(slide, newHeight);
14535
+ if (prevHeight !== undefined && newHeight !== prevHeight) {
14536
+ emblaApi.reInit();
14537
+ return false;
14538
+ }
14539
+ }
14540
+ return true;
14541
+ }
14542
+ }, plugins);
14543
+ const onSettle = useCallbackRef(props.onSettle);
14544
+ const onSwipeStart = useCallbackRef(props.onSwipeStart);
14545
+ const onSwipeEnd = useCallbackRef(props.onSwipeEnd);
14546
+ useEffect(()=>{
14547
+ const select = emblaApi?.on("select", ()=>{
14548
+ const contentIndex = emblaApi.selectedScrollSnap();
14549
+ api.setContentIndex(contentIndex);
14550
+ });
14551
+ const settle = emblaApi?.on("settle", ()=>{
14552
+ onSettle?.();
14553
+ });
14554
+ const pointerDown = emblaApi?.on("pointerDown", ()=>{
14555
+ onSwipeStart?.();
14556
+ });
14557
+ const pointerUp = emblaApi?.on("pointerUp", ()=>{
14558
+ onSwipeEnd?.();
14559
+ });
14560
+ return ()=>{
14561
+ select?.clear();
14562
+ settle?.clear();
14563
+ pointerDown?.clear();
14564
+ pointerUp?.clear();
14565
+ };
14566
+ }, [
14567
+ emblaApi,
14568
+ api.setContentIndex,
14569
+ onSettle,
14570
+ onSwipeStart,
14571
+ onSwipeEnd
14572
+ ]);
14573
+ const getContentIndex = useCallbackRef(()=>api.contentIndex);
14574
+ useEffect(()=>{
14575
+ const reInit = emblaApi?.on("reInit", ()=>{
14576
+ emblaApi?.scrollTo(getContentIndex(), true);
14577
+ });
14578
+ return ()=>{
14579
+ reInit?.clear();
14580
+ };
14581
+ }, [
14582
+ getContentIndex,
14583
+ emblaApi
14584
+ ]);
14585
+ useEffect(()=>{
14586
+ if (!emblaApi) return;
14587
+ const { dragHandler } = emblaApi.internalEngine();
14588
+ if (props.swipeable) {
14589
+ dragHandler.init(emblaApi);
14590
+ } else {
14591
+ dragHandler.destroy();
14592
+ }
14593
+ }, [
14594
+ emblaApi,
14595
+ props.swipeable
14596
+ ]);
14597
+ const isInitialScroll = useRef(true);
14598
+ useEffect(()=>{
14599
+ if (emblaApi && api.contentIndex !== emblaApi.selectedScrollSnap()) {
14600
+ const engine = emblaApi.internalEngine();
14601
+ /**
14602
+ * We want the content to quickly snap into place when a tab changes.
14603
+ * However, using a very small duration can result in an underdamped spring, which produces a rubber-band (oscillatory) effect.
14604
+ *
14605
+ * To avoid that, we intentionally use a low duration and friction so that the motion feels fast (snappy)
14606
+ * without overshooting. The underlying idea is to map our parameters to a physical spring model—a damped harmonic oscillator—
14607
+ * so we can control the dynamics in a physically intuitive way.
14608
+ *
14609
+ * In embla, the acceleration is modeled as:
14610
+ * a = (displacement * friction) / duration - velocity * (1 - friction)
14611
+ *
14612
+ * In a classical damped harmonic oscillator (assuming unit mass, m = 1), the acceleration is:
14613
+ * a = (stiffness * displacement - damping * velocity)
14614
+ *
14615
+ * By comparing the two equations, we can equate the coefficients for displacement and velocity:
14616
+ *
14617
+ * For the displacement term:
14618
+ * stiffness = friction / duration
14619
+ *
14620
+ * For the velocity term:
14621
+ * damping = 1 - friction
14622
+ *
14623
+ * Additionally, the damping ratio (zeta) for a damped oscillator is defined as:
14624
+ * zeta = damping / (2 * sqrt(stiffness))
14625
+ *
14626
+ * Substituting the derived expressions for stiffness and damping gives:
14627
+ * zeta = (1 - friction) / (2 * sqrt(friction / duration))
14628
+ *
14629
+ * This damping ratio (zeta) tells us the nature of the response:
14630
+ * - zeta < 1: Underdamped (oscillatory, rubber-band effect)
14631
+ * - zeta = 1: Critically damped (fast snap without overshoot)
14632
+ * - zeta > 1: Overdamped (slow response)
14633
+ *
14634
+ * For our current configuration, we use:
14635
+ *
14636
+ * duration = 4
14637
+ * friction = 0.4
14638
+ *
14639
+ * Which gives:
14640
+ *
14641
+ * stiffness = 0.4 / 4 = 0.1
14642
+ * damping = 1 - 0.4 = 0.6
14643
+ *
14644
+ * And the damping ratio becomes:
14645
+ *
14646
+ * zeta = 0.6 / (2 * sqrt(0.1))
14647
+ * = 0.6 / (2 * 0.31623...)
14648
+ * = 0.6 / 0.63246...
14649
+ * = 0.94868...
14650
+ *
14651
+ * Since zeta is close to 1, this spring is nearly critically damped. This is enough for our usage,
14652
+ * ensuring that the animation snaps quickly without overshooting (avoiding the rubber-band effect)
14653
+ * when switching tabs.
14654
+ */ if (isInitialScroll.current) {
14655
+ engine.scrollBody.useDuration(0);
14656
+ isInitialScroll.current = false;
14657
+ } else {
14658
+ engine.scrollBody.useDuration(4).useFriction(0.4);
14659
+ }
14660
+ engine.scrollTo.index(api.contentIndex, 0);
14661
+ }
14662
+ }, [
14663
+ emblaApi,
14664
+ api.contentIndex
14665
+ ]);
14666
+ return {
14667
+ refs: {
14668
+ root: emblaRef
14669
+ },
14670
+ autoHeight: props.autoHeight
14671
+ };
14672
+ };
14673
+ const useTabsCarousel = (props)=>{
14674
+ const { refs, autoHeight } = useTabsCarouselState(props);
14675
+ const stateProps = elementProps({
14676
+ "data-carousel": "",
14677
+ "data-auto-height": dataAttr(autoHeight)
14678
+ });
14679
+ return {
14680
+ refs,
14681
+ stateProps,
14682
+ rootProps: elementProps({
14683
+ ...stateProps
14684
+ }),
14685
+ cameraProps: elementProps({
14686
+ ...stateProps
14687
+ })
14688
+ };
14689
+ };
14690
+ const TabsCarouselContext = /*#__PURE__*/ createContext(null);
14691
+ const TabsCarouselProvider = TabsCarouselContext.Provider;
14692
+ function useTabsCarouselContext({ strict = true } = {}) {
14693
+ const context = useContext(TabsCarouselContext);
14694
+ if (!context && strict) {
14695
+ throw new Error("useTabsCarouselContext must be used within a TabsCarousel");
14696
+ }
14697
+ return context;
14698
+ }
14699
+ const TabsTriggerContext = /*#__PURE__*/ createContext(null);
14700
+ const TabsTriggerProvider = TabsTriggerContext.Provider;
14701
+ const TabsRoot$1 = /*#__PURE__*/ forwardRef((props, ref)=>{
14702
+ const { defaultValue, value, onValueChange, orientation, lazyMount, unmountOnExit, ...otherProps } = props;
14703
+ const api = useTabs({
14704
+ defaultValue,
14705
+ value,
14706
+ onValueChange,
14707
+ orientation
14708
+ });
14709
+ return /*#__PURE__*/ jsx(TabsProvider, {
14710
+ value: api,
14711
+ children: /*#__PURE__*/ jsx(RenderStrategyPropsProvider, {
14712
+ value: useMemo(()=>({
14713
+ lazyMount,
14714
+ unmountOnExit
14715
+ }), [
14716
+ lazyMount,
14717
+ unmountOnExit
14718
+ ]),
14719
+ children: /*#__PURE__*/ jsx(Primitive.div, {
14720
+ ref: ref,
14721
+ ...mergeProps$1(api.rootProps, otherProps)
14722
+ })
14723
+ })
14724
+ });
14725
+ });
14726
+ TabsRoot$1.displayName = "TabsRoot";
14727
+ const TabsList$1 = /*#__PURE__*/ forwardRef((props, ref)=>{
14728
+ const api = useTabsContext();
14729
+ return /*#__PURE__*/ jsx(Primitive.div, {
14730
+ ref: composeRefs(api.refs.list, ref),
14731
+ ...mergeProps$1(api.listProps, props)
14732
+ });
14733
+ });
14734
+ TabsList$1.displayName = "TabsList";
14735
+ const TabsTrigger$1 = /*#__PURE__*/ forwardRef((props, ref)=>{
14736
+ const { value, disabled, ...otherProps } = props;
14737
+ const api = useTabsContext();
14738
+ const triggerApi = api.getTriggerProps({
14739
+ value,
14740
+ disabled
14741
+ });
14742
+ return /*#__PURE__*/ jsx(TabsTriggerProvider, {
14743
+ value: triggerApi,
14744
+ children: /*#__PURE__*/ jsx(Primitive.button, {
14745
+ ref: composeRefs(triggerApi.refs.root, ref),
14746
+ ...mergeProps$1(triggerApi.rootProps, otherProps)
14747
+ })
14748
+ });
14749
+ });
14750
+ TabsTrigger$1.displayName = "TabsTrigger";
14751
+ const TabsContent$1 = /*#__PURE__*/ forwardRef((props, ref)=>{
14752
+ const { value, children, ...otherProps } = props;
14753
+ const api = useTabsContext();
14754
+ const carouselApi = useTabsCarouselContext({
14755
+ strict: false
14756
+ });
14757
+ const inCarousel = carouselApi != null;
14758
+ const renderStrategyProps = useRenderStrategyPropsContext();
14759
+ const { unmounted } = useRenderStrategy({
14760
+ ...renderStrategyProps,
14761
+ present: api.value === value
14762
+ });
14763
+ // Outside a carousel, an unmounted tab renders nothing at all.
14764
+ // Inside a carousel, every tab must keep its slide slot even while unmounted:
14765
+ // the carousel syncs by numeric index, so collapsing the slide set (returning
14766
+ // null for lazy/exited tabs) makes embla's snap index drift from the tab's
14767
+ // logical index, and clicking/swiping to a far tab lands on the wrong one.
14768
+ // We keep the slot but still honor lazyMount/unmountOnExit for the children,
14769
+ // so expensive content stays lazy — only the empty slide wrapper persists.
14770
+ if (unmounted && !inCarousel) return null;
14771
+ return /*#__PURE__*/ jsx(Primitive.div, {
14772
+ ref: ref,
14773
+ ...mergeProps$1(api.getContentProps({
14774
+ value
14775
+ }), carouselApi?.stateProps ?? {}, otherProps),
14776
+ children: unmounted ? null : children
14777
+ });
14778
+ });
14779
+ TabsContent$1.displayName = "TabsContent";
14780
+ const TabsIndicator$1 = /*#__PURE__*/ forwardRef((props, ref)=>{
14781
+ const api = useTabsContext();
14782
+ return /*#__PURE__*/ jsx(Primitive.div, {
14783
+ ref: ref,
14784
+ ...mergeProps$1(api.indicatorProps, props)
14785
+ });
14786
+ });
14787
+ const TabsCarousel = /*#__PURE__*/ forwardRef((props, ref)=>{
14788
+ const { dragThreshold, loop, swipeable, autoHeight, onSettle, onSwipeStart, onSwipeEnd, ...otherProps } = props;
14789
+ const api = useTabsCarousel({
14790
+ dragThreshold,
14791
+ loop,
14792
+ swipeable,
14793
+ onSettle,
14794
+ autoHeight,
14795
+ onSwipeStart,
14796
+ onSwipeEnd
14797
+ });
14798
+ return /*#__PURE__*/ jsx(TabsCarouselProvider, {
14799
+ value: api,
14800
+ children: /*#__PURE__*/ jsx(Primitive.div, {
14801
+ ref: composeRefs(api.refs.root, ref),
14802
+ ...mergeProps$1(api.rootProps, otherProps),
14803
+ children: props.children
14804
+ })
14805
+ });
14806
+ });
14807
+ TabsCarousel.displayName = "TabsCarousel";
14808
+ const TabsCarouselCamera = /*#__PURE__*/ forwardRef((props, ref)=>{
14809
+ const api = useTabsCarouselContext();
14810
+ return /*#__PURE__*/ jsx(Primitive.div, {
14811
+ ref: ref,
14812
+ ...mergeProps$1(api.cameraProps, props)
14813
+ });
14814
+ });
14815
+ TabsCarouselCamera.displayName = "TabsCarouselCamera";
14816
+
14817
+ var Tabs_namespace = {
14818
+ __proto__: null,
14819
+ Carousel: TabsCarousel,
14820
+ CarouselCamera: TabsCarouselCamera,
14821
+ Content: TabsContent$1,
14822
+ Indicator: TabsIndicator$1,
14823
+ List: TabsList$1,
14824
+ Root: TabsRoot$1,
14825
+ Trigger: TabsTrigger$1};
14826
+
14827
+ const { withProvider, withContext } = createSlotRecipeContext(tabs);
14828
+ const TabsRoot = withProvider(Tabs_namespace.Root, "root");
14829
+ const TabsList = withContext(Tabs_namespace.List, "list");
14830
+ const TabsTrigger = withScaleFeedback(withContext(Tabs_namespace.Trigger, "trigger"));
14831
+ const TabsIndicator = withContext(Tabs_namespace.Indicator, "indicator");
14832
+ const TabsContent = withContext(Tabs_namespace.Content, "content");
14833
+ withContext(Tabs_namespace.Carousel, "carousel");
14834
+ withContext(Tabs_namespace.CarouselCamera, "carouselCamera");
14835
+
14836
+ const RESIZE_MIN_WIDTH = 360;
14837
+ const RESIZE_MAX_WIDTH = 720;
14838
+ /**
14839
+ * Right-side record peek panel -- Attio's own peek shows a Highlights card
14840
+ * area and an Activity feed (measured live). Ported from your-moon/crisp's
14841
+ * `RecordPanel` (`product/recordpanel/`), built ON `ResizablePanel`
14842
+ * (`side="right" floating`) rather than crisp's own duplicate absolute-
14843
+ * positioning/slide-in/resize-handle implementation -- the outer shell (the
14844
+ * enter/exit keyframe animation, the full-height overlay, the drag handle
14845
+ * itself) all come from that primitive; this component only renders the
14846
+ * CONTENT inside it.
14847
+ *
14848
+ * OVERLAY, not docked: `floating` (on the `ResizablePanel` this is built on)
14849
+ * now means a real `position: absolute` overlay that reserves ZERO layout
14850
+ * width -- opening this panel never shrinks or reflows a table beside it
14851
+ * (your-moon/crisp#448 reversed an earlier in-flow/docked attempt at this
14852
+ * exact spot, reported live as "table's action buttons moving"). Because of
14853
+ * that, THIS COMPONENT OWNS ITS OWN POSITIONING -- a consumer does not wrap
14854
+ * it in a flex row with the table; it renders `<RecordPreview>` as an
14855
+ * ordinary sibling of the table. The one requirement: the shared parent
14856
+ * (the table region's own wrapper) must itself be a positioned element
14857
+ * (typically `position: relative`) with a real height, since that's the
14858
+ * containing block this panel's `inset: 0` resolves against. See
14859
+ * `DataTable.stories.tsx`'s own Showcase for the pattern:
14860
+ *
14861
+ * ```tsx
14862
+ * <div style={{ position: "relative", flex: 1, minHeight: 0 }}>
14863
+ * <DataTable style={{ width: "100%", height: "100%" }} ... />
14864
+ * <RecordPreview open={open} record={record} onClose={onClose} ... />
14865
+ * </div>
14866
+ * ```
14867
+ *
14868
+ * Enter/exit: closing this panel is a state change (`open` -> false) that
14869
+ * typically clears `record` in the SAME update (see the DataTable story's
14870
+ * own `onClose={() => setPreviewRow(null)}`), so by the time an exit
14871
+ * animation should play, the values this component would render are
14872
+ * already gone. `usePresence` (`@seed-design/react-presence`, the same
14873
+ * idiom Dialog/Drawer already use for their own floating layers) keeps the
14874
+ * tree mounted for exactly the exit animation's duration; the snapshot
14875
+ * below keeps the LAST visible props around for that same window, since
14876
+ * without it the panel would animate out empty.
14877
+ */ function RecordPreview(props) {
14878
+ const shouldRender = props.open && props.record != null;
14879
+ const { isPresent, ref: panelElementRef } = usePresence(shouldRender);
11756
14880
  const lastVisiblePropsRef = useRef(props);
11757
14881
  if (shouldRender) lastVisiblePropsRef.current = props;
11758
14882
  if (!isPresent) return null;
@@ -11763,7 +14887,7 @@ const RESIZE_MAX_WIDTH = 720;
11763
14887
  });
11764
14888
  }
11765
14889
  RecordPreview.displayName = "RecordPreview";
11766
- 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 }) {
14890
+ 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 }) {
11767
14891
  const titleId = useId();
11768
14892
  const classNames = recordPreview();
11769
14893
  // Which sections (your-moon/crisp#413) are expanded -- uncontrolled and
@@ -11803,9 +14927,143 @@ function RecordPreviewPanel({ open: _open, record, onClose, className, animation
11803
14927
  // Defensive only -- `RecordPreview` never mounts this without a record.
11804
14928
  if (!record) return null;
11805
14929
  const heading = title ?? record.name ?? "Record";
14930
+ // The plain (non-tabs) layout -- unchanged from before `tabs` existed.
14931
+ // Extracted so it can ALSO become the synthesized "Overview" tab's own
14932
+ // content below, rather than being re-authored as JSX twice.
14933
+ const overviewContent = /*#__PURE__*/ jsxs(Fragment, {
14934
+ children: [
14935
+ highlights && highlights.length > 0 && /*#__PURE__*/ jsxs(Primitive.div, {
14936
+ className: classNames.section,
14937
+ children: [
14938
+ /*#__PURE__*/ jsx(Primitive.div, {
14939
+ className: classNames.sectionHead,
14940
+ children: /*#__PURE__*/ jsx(Primitive.span, {
14941
+ className: classNames.sectionLabel,
14942
+ children: highlightsLabel
14943
+ })
14944
+ }),
14945
+ /*#__PURE__*/ jsx(Primitive.div, {
14946
+ className: classNames.highlights,
14947
+ children: highlights.map((h)=>/*#__PURE__*/ jsxs(Primitive.div, {
14948
+ className: classNames.highlight,
14949
+ children: [
14950
+ /*#__PURE__*/ jsxs(Primitive.span, {
14951
+ className: classNames.highlightLabel,
14952
+ children: [
14953
+ /*#__PURE__*/ jsx("span", {
14954
+ children: h.label
14955
+ }),
14956
+ h.icon ? /*#__PURE__*/ jsx(h.icon, {
14957
+ size: 16
14958
+ }) : null
14959
+ ]
14960
+ }),
14961
+ /*#__PURE__*/ jsx(Primitive.span, {
14962
+ className: classNames.highlightValue,
14963
+ children: h.value
14964
+ })
14965
+ ]
14966
+ }, h.key))
14967
+ })
14968
+ ]
14969
+ }),
14970
+ activity != null && /*#__PURE__*/ jsxs(Primitive.div, {
14971
+ className: classNames.section,
14972
+ children: [
14973
+ /*#__PURE__*/ jsxs(Primitive.div, {
14974
+ className: classNames.sectionHead,
14975
+ children: [
14976
+ /*#__PURE__*/ jsx(Primitive.span, {
14977
+ className: classNames.sectionLabel,
14978
+ children: activityLabel
14979
+ }),
14980
+ onViewAllActivity && /*#__PURE__*/ jsx(Primitive.button, {
14981
+ type: "button",
14982
+ className: classNames.sectionAction,
14983
+ onClick: onViewAllActivity,
14984
+ children: viewAllActivityLabel
14985
+ })
14986
+ ]
14987
+ }),
14988
+ /*#__PURE__*/ jsx(Primitive.div, {
14989
+ className: classNames.activity,
14990
+ children: activity
14991
+ })
14992
+ ]
14993
+ }),
14994
+ sections && sections.length > 0 && /*#__PURE__*/ jsx(Primitive.div, {
14995
+ className: classNames.sections,
14996
+ children: sections.map((s)=>{
14997
+ const isOpen = openSections.has(s.key);
14998
+ const panelId = `${titleId}-section-${s.key}`;
14999
+ return /*#__PURE__*/ jsxs("div", {
15000
+ children: [
15001
+ s.content ? /*#__PURE__*/ jsxs(Primitive.button, {
15002
+ type: "button",
15003
+ className: classNames.sectionTrigger,
15004
+ "aria-expanded": isOpen,
15005
+ "aria-controls": panelId,
15006
+ onClick: ()=>toggleSection(s.key),
15007
+ children: [
15008
+ /*#__PURE__*/ jsx(Primitive.span, {
15009
+ className: classNames.sectionRowLabel,
15010
+ children: s.label
15011
+ }),
15012
+ /*#__PURE__*/ jsx(Primitive.span, {
15013
+ className: classNames.sectionRowCount,
15014
+ children: s.count
15015
+ }),
15016
+ /*#__PURE__*/ jsx(IconChevronDownLine_default, {
15017
+ className: classNames.sectionChevron,
15018
+ size: 14,
15019
+ "data-open": isOpen ? "" : undefined,
15020
+ "aria-hidden": "true"
15021
+ })
15022
+ ]
15023
+ }) : /*#__PURE__*/ jsxs(Primitive.div, {
15024
+ className: classNames.sectionTrigger,
15025
+ "data-inert": "",
15026
+ children: [
15027
+ /*#__PURE__*/ jsx(Primitive.span, {
15028
+ className: classNames.sectionRowLabel,
15029
+ children: s.label
15030
+ }),
15031
+ /*#__PURE__*/ jsx(Primitive.span, {
15032
+ className: classNames.sectionRowCount,
15033
+ children: s.count
15034
+ })
15035
+ ]
15036
+ }),
15037
+ s.content && isOpen && /*#__PURE__*/ jsx(Primitive.div, {
15038
+ id: panelId,
15039
+ className: classNames.sectionContent,
15040
+ children: s.content
15041
+ })
15042
+ ]
15043
+ }, s.key);
15044
+ })
15045
+ })
15046
+ ]
15047
+ });
15048
+ // Combine (`tabs`'s own doc comment): when there's ALSO plain overview
15049
+ // content given alongside `tabs`, prepend a synthesized tab carrying it,
15050
+ // ahead of the consumer's own tabs -- so `tabs` and the simpler Highlights/
15051
+ // Activity/sections props stay usable TOGETHER rather than one silently
15052
+ // replacing the other.
15053
+ const hasOverviewContent = highlights && highlights.length > 0 || activity != null || sections && sections.length > 0;
15054
+ const allTabs = tabs && hasOverviewContent ? [
15055
+ {
15056
+ key: "__overview",
15057
+ label: overviewTabLabel,
15058
+ content: overviewContent
15059
+ },
15060
+ ...tabs
15061
+ ] : tabs ?? [];
11806
15062
  return /*#__PURE__*/ jsx(ResizablePanel, {
11807
15063
  side: "right",
11808
15064
  floating: true,
15065
+ backdrop: backdrop,
15066
+ onBackdropClick: onClose,
11809
15067
  animationState: animationState,
11810
15068
  panelElementRef: panelElementRef,
11811
15069
  resizable: resizable,
@@ -11895,121 +15153,49 @@ function RecordPreviewPanel({ open: _open, record, onClose, className, animation
11895
15153
  })
11896
15154
  ]
11897
15155
  }),
11898
- /*#__PURE__*/ jsxs(Primitive.div, {
11899
- className: classNames.body,
11900
- children: [
11901
- highlights && highlights.length > 0 && /*#__PURE__*/ jsxs(Primitive.div, {
11902
- className: classNames.section,
11903
- children: [
11904
- /*#__PURE__*/ jsx(Primitive.div, {
11905
- className: classNames.sectionHead,
11906
- children: /*#__PURE__*/ jsx(Primitive.span, {
11907
- className: classNames.sectionLabel,
11908
- children: highlightsLabel
11909
- })
11910
- }),
11911
- /*#__PURE__*/ jsx(Primitive.div, {
11912
- className: classNames.highlights,
11913
- children: highlights.map((h)=>/*#__PURE__*/ jsxs(Primitive.div, {
11914
- className: classNames.highlight,
11915
- children: [
11916
- /*#__PURE__*/ jsxs(Primitive.span, {
11917
- className: classNames.highlightLabel,
11918
- children: [
11919
- /*#__PURE__*/ jsx("span", {
11920
- children: h.label
11921
- }),
11922
- h.icon ? /*#__PURE__*/ jsx(h.icon, {
11923
- size: 16
11924
- }) : null
11925
- ]
11926
- }),
11927
- /*#__PURE__*/ jsx(Primitive.span, {
11928
- className: classNames.highlightValue,
11929
- children: h.value
11930
- })
11931
- ]
11932
- }, h.key))
11933
- })
11934
- ]
11935
- }),
11936
- activity != null && /*#__PURE__*/ jsxs(Primitive.div, {
11937
- className: classNames.section,
11938
- children: [
11939
- /*#__PURE__*/ jsxs(Primitive.div, {
11940
- className: classNames.sectionHead,
11941
- children: [
11942
- /*#__PURE__*/ jsx(Primitive.span, {
11943
- className: classNames.sectionLabel,
11944
- children: activityLabel
11945
- }),
11946
- onViewAllActivity && /*#__PURE__*/ jsx(Primitive.button, {
11947
- type: "button",
11948
- className: classNames.sectionAction,
11949
- onClick: onViewAllActivity,
11950
- children: viewAllActivityLabel
11951
- })
11952
- ]
11953
- }),
11954
- /*#__PURE__*/ jsx(Primitive.div, {
11955
- className: classNames.activity,
11956
- children: activity
11957
- })
11958
- ]
11959
- }),
11960
- sections && sections.length > 0 && /*#__PURE__*/ jsx(Primitive.div, {
11961
- className: classNames.sections,
11962
- children: sections.map((s)=>{
11963
- const isOpen = openSections.has(s.key);
11964
- const panelId = `${titleId}-section-${s.key}`;
11965
- return /*#__PURE__*/ jsxs("div", {
11966
- children: [
11967
- s.content ? /*#__PURE__*/ jsxs(Primitive.button, {
11968
- type: "button",
11969
- className: classNames.sectionTrigger,
11970
- "aria-expanded": isOpen,
11971
- "aria-controls": panelId,
11972
- onClick: ()=>toggleSection(s.key),
11973
- children: [
11974
- /*#__PURE__*/ jsx(Primitive.span, {
11975
- className: classNames.sectionRowLabel,
11976
- children: s.label
11977
- }),
11978
- /*#__PURE__*/ jsx(Primitive.span, {
11979
- className: classNames.sectionRowCount,
11980
- children: s.count
11981
- }),
11982
- /*#__PURE__*/ jsx(IconChevronDownLine_default, {
11983
- className: classNames.sectionChevron,
11984
- size: 14,
11985
- "data-open": isOpen ? "" : undefined,
11986
- "aria-hidden": "true"
11987
- })
11988
- ]
11989
- }) : /*#__PURE__*/ jsxs(Primitive.div, {
11990
- className: classNames.sectionTrigger,
11991
- "data-inert": "",
15156
+ tabs && tabs.length > 0 ? /*#__PURE__*/ jsx(Primitive.div, {
15157
+ className: classNames.tabsRoot,
15158
+ children: /*#__PURE__*/ jsxs(TabsRoot, {
15159
+ value: activeTab,
15160
+ defaultValue: defaultActiveTab ?? allTabs[0]?.key,
15161
+ onValueChange: onTabChange,
15162
+ style: {
15163
+ display: "flex",
15164
+ flexDirection: "column",
15165
+ flex: "1 1 auto",
15166
+ minHeight: 0,
15167
+ overflow: "hidden"
15168
+ },
15169
+ children: [
15170
+ /*#__PURE__*/ jsxs(TabsList, {
15171
+ children: [
15172
+ allTabs.map((tab)=>/*#__PURE__*/ jsxs(TabsTrigger, {
15173
+ value: tab.key,
11992
15174
  children: [
11993
- /*#__PURE__*/ jsx(Primitive.span, {
11994
- className: classNames.sectionRowLabel,
11995
- children: s.label
11996
- }),
11997
- /*#__PURE__*/ jsx(Primitive.span, {
11998
- className: classNames.sectionRowCount,
11999
- children: s.count
15175
+ tab.label,
15176
+ tab.badge != null && /*#__PURE__*/ jsx(Primitive.span, {
15177
+ className: classNames.tabBadge,
15178
+ children: tab.badge
12000
15179
  })
12001
15180
  ]
12002
- }),
12003
- s.content && isOpen && /*#__PURE__*/ jsx(Primitive.div, {
12004
- id: panelId,
12005
- className: classNames.sectionContent,
12006
- children: s.content
12007
- })
12008
- ]
12009
- }, s.key);
12010
- })
12011
- })
12012
- ]
15181
+ }, tab.key)),
15182
+ /*#__PURE__*/ jsx(TabsIndicator, {})
15183
+ ]
15184
+ }),
15185
+ allTabs.map((tab)=>/*#__PURE__*/ jsx(TabsContent, {
15186
+ value: tab.key,
15187
+ className: classNames.tabPanel,
15188
+ style: {
15189
+ flex: "1 1 auto",
15190
+ minHeight: 0
15191
+ },
15192
+ children: tab.content
15193
+ }, tab.key))
15194
+ ]
15195
+ })
15196
+ }) : /*#__PURE__*/ jsx(Primitive.div, {
15197
+ className: classNames.body,
15198
+ children: overviewContent
12013
15199
  }),
12014
15200
  footer != null && /*#__PURE__*/ jsx(Primitive.div, {
12015
15201
  className: classNames.footer,