@crispy-seed/data-table 0.7.0 → 0.9.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
@@ -11,7 +11,7 @@ function __insertCSS(code) {
11
11
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
12
12
  import * as React from 'react';
13
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';
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()];
@@ -750,21 +759,31 @@ function isFunction$1(value) {
750
759
 
751
760
  const DATA_TABLE_UNIVERSAL_CALC_OPS = [
752
761
  "count",
753
- "countFilled"
762
+ "empty",
763
+ "notEmpty"
754
764
  ];
755
765
  const DATA_TABLE_NUMERIC_CALC_OPS = [
756
766
  "sum",
757
767
  "average",
768
+ "median",
758
769
  "min",
759
- "max"
770
+ "max",
771
+ "range"
772
+ ];
773
+ /** Offered instead of `DATA_TABLE_NUMERIC_CALC_OPS` on a non-numeric column. */ const DATA_TABLE_NON_NUMERIC_CALC_OPS = [
774
+ "countUnique"
760
775
  ];
761
776
  const DATA_TABLE_CALC_LABEL = {
762
777
  count: "Count",
763
- countFilled: "Count filled",
778
+ empty: "Empty",
779
+ notEmpty: "Not empty",
764
780
  sum: "Sum",
765
781
  average: "Average",
782
+ median: "Median",
766
783
  min: "Min",
767
- max: "Max"
784
+ max: "Max",
785
+ range: "Range",
786
+ countUnique: "Count unique"
768
787
  };
769
788
  /** A value counts as empty when it is null/undefined, an empty string, or an empty
770
789
  * array -- crisp's own `isEmptyValue` (columncalc.tsx), reused verbatim. */ function isEmptyValueForCalc(value) {
@@ -773,29 +792,50 @@ const DATA_TABLE_CALC_LABEL = {
773
792
  if (Array.isArray(value)) return value.length === 0;
774
793
  return false;
775
794
  }
795
+ /** The middle value of a SORTED, non-empty numeric array -- the mean of the two
796
+ * middle entries on an even-length array, same convention as every stats library. */ function median(sortedNumbers) {
797
+ const mid = Math.floor(sortedNumbers.length / 2);
798
+ return sortedNumbers.length % 2 === 0 ? (sortedNumbers[mid - 1] + sortedNumbers[mid]) / 2 : sortedNumbers[mid];
799
+ }
776
800
  /**
777
801
  * Pure so the arithmetic is independently (and mutation-) testable without rendering a
778
- * table at all. `sum`/`average`/`min`/`max` skip empty values and non-numeric ones
779
- * (coerced via `Number`, dropping `NaN`); they return `null` -- render nothing, not a
780
- * misleading 0 -- when there is nothing numeric left to reduce over.
802
+ * table at all. The numeric ops (`sum`/`average`/`median`/`min`/`max`/`range`) skip
803
+ * empty values and non-numeric ones (coerced via `Number`, dropping `NaN`); they
804
+ * return `null` -- render nothing, not a misleading 0 -- when there is nothing
805
+ * numeric left to reduce over. `countUnique` counts distinct NON-EMPTY values
806
+ * (`JSON.stringify`-normalized, so two arrays/objects with the same shape count
807
+ * once) -- empties are already their own op (`empty`), so folding them into "one
808
+ * more unique value" here would double-count the same information two different
809
+ * ways in the same menu.
781
810
  */ function computeColumnCalc(values, op) {
782
811
  switch(op){
783
812
  case "count":
784
813
  return values.length;
785
- case "countFilled":
814
+ case "empty":
815
+ return values.filter(isEmptyValueForCalc).length;
816
+ case "notEmpty":
786
817
  return values.filter((value)=>!isEmptyValueForCalc(value)).length;
818
+ case "countUnique":
819
+ {
820
+ const seen = new Set(values.filter((value)=>!isEmptyValueForCalc(value)).map((value)=>JSON.stringify(value)));
821
+ return seen.size;
822
+ }
787
823
  }
788
- const numbers = values.filter((value)=>!isEmptyValueForCalc(value)).map((value)=>typeof value === "number" ? value : Number(value)).filter((value)=>Number.isFinite(value));
824
+ const numbers = values.filter((value)=>!isEmptyValueForCalc(value)).map((value)=>typeof value === "number" ? value : Number(value)).filter((value)=>Number.isFinite(value)).sort((a, b)=>a - b);
789
825
  if (numbers.length === 0) return null;
790
826
  switch(op){
791
827
  case "sum":
792
828
  return numbers.reduce((total, value)=>total + value, 0);
793
829
  case "average":
794
830
  return numbers.reduce((total, value)=>total + value, 0) / numbers.length;
831
+ case "median":
832
+ return median(numbers);
795
833
  case "min":
796
- return Math.min(...numbers);
834
+ return numbers[0];
797
835
  case "max":
798
- return Math.max(...numbers);
836
+ return numbers[numbers.length - 1];
837
+ case "range":
838
+ return numbers[numbers.length - 1] - numbers[0];
799
839
  default:
800
840
  return null;
801
841
  }
@@ -1200,31 +1240,48 @@ const EMPTY_RECORD = {};
1200
1240
  ]);
1201
1241
  const endColumnDrag = useCallback(()=>setDraggingColumnKey(null), []);
1202
1242
  // -- drag-to-resize ------------------------------------------------------------------
1203
- // `resizeStartRef` holds the ONE thing that must survive across pointermove events
1204
- // without triggering a re-render on every pixel (the drag's origin) -- state
1205
- // (`resizingColumnKey`) is only for what the render actually needs: which column's
1206
- // handle should show its active/dragging look.
1243
+ // `resizeStartRef` holds everything that must survive across pointermove events
1244
+ // WITHOUT triggering a re-render on every pixel: the drag's origin, AND (as of the
1245
+ // live-review fix below) the latest computed width, so `endColumnResize` has
1246
+ // something to commit. State (`resizingColumnKey`) is only for what the render
1247
+ // actually needs: which column's handle should show its active/dragging look.
1248
+ //
1249
+ // `updateColumnResize` used to call `setColumnWidth` on every single pointermove --
1250
+ // a `setColumnWidths` state update (and therefore a full DataTable re-render) per
1251
+ // PIXEL of drag, confirmed live as the stutter a column-border drag produced. It now
1252
+ // only computes+returns the clamped width; the styled layer (`resize.tsx`) writes
1253
+ // that directly to the dragged column's own `<col>` element (`col.style.width`,
1254
+ // outside React), which is all a resize needs frame-to-frame -- table layout already
1255
+ // recalculates from a `<col>`'s own inline style with no re-render involved. React
1256
+ // state (and this hook's `onColumnWidthsChange` contract) is only touched ONCE, in
1257
+ // `endColumnResize`, committing the final width exactly as a controlled consumer
1258
+ // already expects.
1207
1259
  const resizeStartRef = useRef(null);
1208
1260
  const [resizingColumnKey, setResizingColumnKey] = useState(null);
1209
1261
  const beginColumnResize = useCallback((key, clientX, currentWidth)=>{
1210
1262
  resizeStartRef.current = {
1211
1263
  key,
1212
1264
  startX: clientX,
1213
- startWidth: currentWidth
1265
+ startWidth: currentWidth,
1266
+ currentWidth
1214
1267
  };
1215
1268
  setResizingColumnKey(key);
1216
1269
  }, []);
1217
1270
  const updateColumnResize = useCallback((clientX)=>{
1218
1271
  const start = resizeStartRef.current;
1219
- if (!start) return;
1220
- setColumnWidth(start.key, start.startWidth + (clientX - start.startX));
1221
- }, [
1222
- setColumnWidth
1223
- ]);
1272
+ if (!start) return undefined;
1273
+ const width = clampColumnWidth(start.startWidth + (clientX - start.startX));
1274
+ start.currentWidth = width;
1275
+ return width;
1276
+ }, []);
1224
1277
  const endColumnResize = useCallback(()=>{
1278
+ const start = resizeStartRef.current;
1279
+ if (start) setColumnWidth(start.key, start.currentWidth);
1225
1280
  resizeStartRef.current = null;
1226
1281
  setResizingColumnKey(null);
1227
- }, []);
1282
+ }, [
1283
+ setColumnWidth
1284
+ ]);
1228
1285
  return {
1229
1286
  columnOrder,
1230
1287
  visibleColumnOrder,
@@ -1574,11 +1631,16 @@ function useDataTableSort(props) {
1574
1631
  const [sort, setSort] = useControllableState$1({
1575
1632
  prop: sortProp,
1576
1633
  defaultProp: defaultSort,
1577
- onChange: (nextSort)=>{
1578
- // `toggleSort` never sets `sort` back to `undefined`, but the underlying state is
1579
- // typed as possibly-undefined so an uncontrolled table can start with no sort at all.
1580
- if (nextSort) onSortChange?.(nextSort);
1581
- }
1634
+ // Live-review fix: this used to only fire for a DEFINED next sort ("`toggleSort`
1635
+ // never clears it, so why would this ever see `undefined`") -- true until
1636
+ // `clearSort` (below) existed. Guarding the callback on truthiness meant a
1637
+ // consumer controlling `sort` had no way to ever learn the column sort was
1638
+ // cleared: `setSort(undefined)` would run, but the controlled VALUE never moves
1639
+ // without the consumer's own state updating from this callback, and the guard
1640
+ // swallowed exactly the call that would have told it to. Every other value this
1641
+ // hook contract carries (`onColumnWidthsChange`, `onColumnCalcOpsChange`, ...)
1642
+ // already passes its "cleared" shape through unconditionally; this now matches.
1643
+ onChange: onSortChange
1582
1644
  });
1583
1645
  const toggleSort = useCallback((key)=>{
1584
1646
  setSort((current)=>{
@@ -1594,6 +1656,12 @@ function useDataTableSort(props) {
1594
1656
  }, [
1595
1657
  setSort
1596
1658
  ]);
1659
+ /** Attio/GRC's "Remove sort" -- the header click cycle (above) only ever toggles
1660
+ * asc/desc once a column has been clicked once; this is the only way back to no
1661
+ * sort at all. Exposed for a column's own header menu (`column-menu.tsx`'s
1662
+ * `ColumnMenu`) to offer as a "Clear sort" item when that column is the sorted one. */ const clearSort = useCallback(()=>setSort(undefined), [
1663
+ setSort
1664
+ ]);
1597
1665
  const getHeaderCellProps = useCallback((columnKey, sortable)=>{
1598
1666
  const isSorted = Boolean(columnKey) && sort?.key === columnKey;
1599
1667
  const canSort = Boolean(sortable && columnKey);
@@ -1623,6 +1691,7 @@ function useDataTableSort(props) {
1623
1691
  return {
1624
1692
  sort,
1625
1693
  toggleSort,
1694
+ clearSort,
1626
1695
  getHeaderCellProps
1627
1696
  };
1628
1697
  }
@@ -2372,7 +2441,7 @@ function createRecipeContext(recipe) {
2372
2441
  }
2373
2442
 
2374
2443
  const { withContext: withGroupContext } = createRecipeContext(checkboxGroup);
2375
- const { ClassNamesProvider: ClassNamesProvider$3, withContext: withContext$5 } = createSlotRecipeContext(checkbox);
2444
+ const { ClassNamesProvider: ClassNamesProvider$3, withContext: withContext$6 } = createSlotRecipeContext(checkbox);
2376
2445
  const { withProvider: withCheckmarkProvider, useClassNames: useCheckmarkClassNames, PropsProvider: CheckmarkPropsProvider } = createSlotRecipeContext(checkmark);
2377
2446
  const withStateProps$4 = createWithStateProps([
2378
2447
  useCheckboxContext
@@ -2426,14 +2495,14 @@ const CheckboxIndicator = /*#__PURE__*/ forwardRef(({ unchecked: uncheckedSvg, c
2426
2495
  return null;
2427
2496
  });
2428
2497
  CheckboxIndicator.displayName = "CheckboxIndicator";
2429
- const CheckboxLabel = withContext$5(withStateProps$4(Primitive.span), "label");
2498
+ const CheckboxLabel = withContext$6(withStateProps$4(Primitive.span), "label");
2430
2499
  const CheckboxHiddenInput = Checkbox.HiddenInput;
2431
2500
 
2432
- const { withProvider: withProvider$1, withContext: withContext$4 } = createSlotRecipeContext(avatar);
2433
- const AvatarRoot = withProvider$1(Image.Root, "root");
2434
- const AvatarImage = withContext$4(Image.Content, "image");
2435
- const AvatarFallback = withContext$4(Image.Fallback, "fallback");
2436
- withContext$4(Primitive.div, "badge");
2501
+ const { withProvider: withProvider$2, withContext: withContext$5 } = createSlotRecipeContext(avatar);
2502
+ const AvatarRoot = withProvider$2(Image.Root, "root");
2503
+ const AvatarImage = withContext$5(Image.Content, "image");
2504
+ const AvatarFallback = withContext$5(Image.Fallback, "fallback");
2505
+ withContext$5(Primitive.div, "badge");
2437
2506
 
2438
2507
  const Badge = /*#__PURE__*/ forwardRef(({ className, children, ...props }, ref)=>{
2439
2508
  const [variantProps, restProps] = badge.splitVariantProps(props);
@@ -2568,7 +2637,7 @@ function EntityCell({ value, className, nameClassName, actionsClassName, actionC
2568
2637
  event.stopPropagation();
2569
2638
  column.onOpenPreview?.(row);
2570
2639
  },
2571
- children: /*#__PURE__*/ jsx(IconEyeLine_default, {
2640
+ children: /*#__PURE__*/ jsx(IconCorner4OutwardLine_default, {
2572
2641
  size: 16
2573
2642
  })
2574
2643
  })
@@ -3003,7 +3072,7 @@ const SelectHiddenSelect = /*#__PURE__*/ React.forwardRef((props, ref)=>{
3003
3072
  });
3004
3073
  SelectHiddenSelect.displayName = "SelectHiddenSelect";
3005
3074
 
3006
- const { withContext: withContext$3, ClassNamesProvider: ClassNamesProvider$2 } = createSlotRecipeContext(switchStyle);
3075
+ const { withContext: withContext$4, ClassNamesProvider: ClassNamesProvider$2 } = createSlotRecipeContext(switchStyle);
3007
3076
  const { withContext: withControlContext, PropsProvider: ControlPropsProvider, withProvider: withControlProvider } = createSlotRecipeContext(switchmark);
3008
3077
  const withStateProps$2 = createWithStateProps([
3009
3078
  useSwitchContext
@@ -3029,13 +3098,13 @@ const SwitchRoot = /*#__PURE__*/ React__default.forwardRef(({ className, ...prop
3029
3098
  SwitchRoot.displayName = "SwitchRoot";
3030
3099
  const SwitchControl = withScaleFeedback(withControlProvider(Switch.Control, "root"));
3031
3100
  const SwitchThumb = withControlContext(Switch.Thumb, "thumb");
3032
- withContext$3(withStateProps$2(Primitive.span), "label");
3101
+ withContext$4(withStateProps$2(Primitive.span), "label");
3033
3102
  const SwitchHiddenInput = Switch.HiddenInput;
3034
3103
 
3035
3104
  // src/use-layout-effect.tsx
3036
3105
  var useLayoutEffect2 = globalThis?.document ? React.useLayoutEffect : ()=>{};
3037
3106
 
3038
- const { withProvider, withContext: withContext$2, useClassNames: useClassNames$2 } = createSlotRecipeContext(textInput);
3107
+ const { withProvider: withProvider$1, withContext: withContext$3, useClassNames: useClassNames$2 } = createSlotRecipeContext(textInput);
3039
3108
  const withFieldStateProps = createWithStateProps([
3040
3109
  {
3041
3110
  useContext: useFieldContext,
@@ -3049,11 +3118,11 @@ const withStateProps$1 = createWithStateProps([
3049
3118
  strict: false
3050
3119
  }
3051
3120
  ]);
3052
- const TextFieldRoot = withProvider(withFieldStateProps(TextField.Root), "root");
3053
- withContext$2(withStateProps$1(InternalIcon), "prefixIcon");
3054
- withContext$2(withStateProps$1(Primitive.span), "prefixText");
3055
- withContext$2(withStateProps$1(InternalIcon), "suffixIcon");
3056
- withContext$2(withStateProps$1(Primitive.span), "suffixText");
3121
+ const TextFieldRoot = withProvider$1(withFieldStateProps(TextField.Root), "root");
3122
+ withContext$3(withStateProps$1(InternalIcon), "prefixIcon");
3123
+ withContext$3(withStateProps$1(Primitive.span), "prefixText");
3124
+ withContext$3(withStateProps$1(InternalIcon), "suffixIcon");
3125
+ withContext$3(withStateProps$1(Primitive.span), "suffixText");
3057
3126
  const TextFieldInput = /*#__PURE__*/ forwardRef(({ className, ...otherProps }, ref)=>{
3058
3127
  const classNames = useClassNames$2();
3059
3128
  const textFieldContext = useTextFieldContext();
@@ -3130,7 +3199,7 @@ const TextFieldTextarea = /*#__PURE__*/ forwardRef(({ className, autoresize = tr
3130
3199
  });
3131
3200
  TextFieldTextarea.displayName = "TextFieldTextarea";
3132
3201
 
3133
- __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))}");
3202
+ __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__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))}");
3134
3203
 
3135
3204
  const createClassName = (className, variants, compoundVariants = [])=>{
3136
3205
  const variantKeys = Object.keys(variants);
@@ -3317,7 +3386,7 @@ Object.assign(dataTable, {
3317
3386
  // other file (`DataTable.tsx`, `column-menu.tsx`, `resize.tsx`, `column-calc.tsx`,
3318
3387
  // `toolbar.tsx`, `body-cells.tsx`, `editors.tsx`) imports its exports from here rather
3319
3388
  // than re-deriving its own.
3320
- const { ClassNamesProvider: ClassNamesProvider$1, withContext: withContext$1, useClassNames: useClassNames$1 } = createSlotRecipeContext(dataTable);
3389
+ const { ClassNamesProvider: ClassNamesProvider$1, withContext: withContext$2, useClassNames: useClassNames$1 } = createSlotRecipeContext(dataTable);
3321
3390
 
3322
3391
  /**
3323
3392
  * The pure filter itself, split out of `useSelectSearch` below so it can be
@@ -4036,7 +4105,7 @@ function DisplayCellContent({ column, row, value, classNames, locale }) {
4036
4105
  }
4037
4106
  }
4038
4107
 
4039
- const { ClassNamesProvider, withContext, useClassNames } = createSlotRecipeContext(menu);
4108
+ const { ClassNamesProvider, withContext: withContext$1, useClassNames } = createSlotRecipeContext(menu);
4040
4109
  const { PropsProvider: ItemPropsProvider, useProps: useItemProps, withContext: withItemContext, ClassNamesProvider: ItemClassNamesProvider } = createSlotRecipeContext(menuItem);
4041
4110
  const withStateProps = createWithStateProps([
4042
4111
  useMenuContext
@@ -4070,10 +4139,10 @@ const MenuPositioner = /*#__PURE__*/ React.forwardRef(({ className, ...props },
4070
4139
  });
4071
4140
  });
4072
4141
  MenuPositioner.displayName = "MenuPositioner";
4073
- const MenuContent = withContext(Menu.Content, "content");
4074
- const MenuScrollArea = withContext(withStateProps(Primitive.div), "scrollArea");
4075
- withContext(Menu.Group, "group");
4076
- withContext(Menu.GroupLabel, "groupLabel");
4142
+ const MenuContent = withContext$1(Menu.Content, "content");
4143
+ const MenuScrollArea = withContext$1(withStateProps(Primitive.div), "scrollArea");
4144
+ withContext$1(Menu.Group, "group");
4145
+ withContext$1(Menu.GroupLabel, "groupLabel");
4077
4146
  const MenuItem = /*#__PURE__*/ React.forwardRef(({ className: propClassName, ...props }, ref)=>{
4078
4147
  const [variantProps, otherProps] = menuItem.splitVariantProps(props);
4079
4148
  const parentProps = useItemProps();
@@ -4144,7 +4213,7 @@ function formatCalcValue(value) {
4144
4213
  ]);
4145
4214
  const ops = [
4146
4215
  ...DATA_TABLE_UNIVERSAL_CALC_OPS,
4147
- ...numeric ? DATA_TABLE_NUMERIC_CALC_OPS : []
4216
+ ...numeric ? DATA_TABLE_NUMERIC_CALC_OPS : DATA_TABLE_NON_NUMERIC_CALC_OPS
4148
4217
  ];
4149
4218
  return /*#__PURE__*/ jsxs(MenuRoot, {
4150
4219
  children: [
@@ -4210,8 +4279,32 @@ function formatCalcValue(value) {
4210
4279
  * once captured, this SAME element keeps receiving `pointermove`/`pointerup` even
4211
4280
  * after the cursor leaves it, so the drag math can live entirely in this one
4212
4281
  * handler set with no effect/cleanup to get wrong.
4282
+ *
4283
+ * Live-review fix (stutter): `updateColumnResize` used to also commit the new width
4284
+ * to React state on every `pointermove` -- a full DataTable re-render per pixel of
4285
+ * drag, confirmed as the stutter. During the drag this component now writes the
4286
+ * computed width straight to the dragged column's own `<col>` element (found once,
4287
+ * at pointerdown, via its `data-column-key`, per `column-layout`'s `<colgroup>`
4288
+ * comment: a `<col>`'s own inline `width` is the SINGLE place table layout reads a
4289
+ * column's width from, so writing it directly reflows the table with no React
4290
+ * render at all), coalesced to at most one write per animation frame so a fast
4291
+ * mouse doesn't queue more layout work than the screen can show. React state (and
4292
+ * a controlled consumer's `onColumnWidthsChange`) is only touched ONCE more, in
4293
+ * `endColumnResize`, which is unchanged: it commits whatever `updateColumnResize`
4294
+ * last computed.
4213
4295
  */ function ColumnResizeHandle({ columnKey, width }) {
4214
4296
  const { beginColumnResize, updateColumnResize, endColumnResize, isColumnResizing } = useDataTableContext();
4297
+ // Survive across pointermove events without a re-render: the `<col>` this drag
4298
+ // writes to (resolved once, at pointerdown) and the rAF coalescing the writes.
4299
+ const colElementRef = useRef(null);
4300
+ const pendingWidthRef = useRef(null);
4301
+ const rafIdRef = useRef(null);
4302
+ const flushPendingWidth = ()=>{
4303
+ rafIdRef.current = null;
4304
+ const col = colElementRef.current;
4305
+ const pendingWidth = pendingWidthRef.current;
4306
+ if (col && pendingWidth != null) col.style.width = `${pendingWidth}px`;
4307
+ };
4215
4308
  return /*#__PURE__*/ jsx("span", {
4216
4309
  // Mouse/pointer-drag only for now (no keyboard equivalent yet) -- `aria-hidden`
4217
4310
  // rather than `role="separator"` + `aria-label`, which would otherwise fold
@@ -4227,16 +4320,32 @@ function formatCalcValue(value) {
4227
4320
  event.stopPropagation();
4228
4321
  event.preventDefault();
4229
4322
  event.currentTarget.setPointerCapture(event.pointerId);
4230
- const startWidth = width ?? event.currentTarget.closest('[role="columnheader"]')?.getBoundingClientRect().width ?? 150;
4323
+ const headerCell = event.currentTarget.closest('[role="columnheader"]');
4324
+ const startWidth = width ?? headerCell?.getBoundingClientRect().width ?? 150;
4325
+ colElementRef.current = event.currentTarget.closest("table")?.querySelector(`col[data-column-key="${CSS.escape(columnKey)}"]`) ?? null;
4231
4326
  beginColumnResize(columnKey, event.clientX, startWidth);
4232
4327
  },
4233
4328
  onPointerMove: (event)=>{
4234
- if (event.currentTarget.hasPointerCapture(event.pointerId)) {
4235
- updateColumnResize(event.clientX);
4329
+ if (!event.currentTarget.hasPointerCapture(event.pointerId)) return;
4330
+ const nextWidth = updateColumnResize(event.clientX);
4331
+ if (nextWidth == null) return;
4332
+ pendingWidthRef.current = nextWidth;
4333
+ if (rafIdRef.current == null) {
4334
+ rafIdRef.current = requestAnimationFrame(flushPendingWidth);
4236
4335
  }
4237
4336
  },
4238
4337
  onPointerUp: (event)=>{
4239
4338
  event.currentTarget.releasePointerCapture(event.pointerId);
4339
+ if (rafIdRef.current != null) {
4340
+ cancelAnimationFrame(rafIdRef.current);
4341
+ rafIdRef.current = null;
4342
+ }
4343
+ // Apply any not-yet-flushed width immediately so the `<col>` matches the
4344
+ // exact value `endColumnResize` is about to commit to React state -- with
4345
+ // no more pointermoves coming, nothing else would flush it.
4346
+ flushPendingWidth();
4347
+ pendingWidthRef.current = null;
4348
+ colElementRef.current = null;
4240
4349
  endColumnResize();
4241
4350
  },
4242
4351
  children: /*#__PURE__*/ jsx("span", {
@@ -4427,68 +4536,100 @@ function ColumnRenameInput({ column, initialLabel, onDone }) {
4427
4536
  * permanently, so this one carries its own marker attribute distinct from that
4428
4537
  * slot's OTHER occupants (the sort chevron, the filter trigger), which stay visible
4429
4538
  * under their own separate rules regardless of hover. */ function ColumnMenu({ column, onRename }) {
4430
- const { moveColumn, hideColumn, isColumnPinned, togglePinColumn } = useDataTableContext();
4539
+ const { moveColumn, hideColumn, isColumnPinned, togglePinColumn, sort, clearSort } = useDataTableContext();
4431
4540
  const columnLabel = typeof column.header === "string" ? column.header : column.key;
4432
4541
  const pinned = isColumnPinned(column.key);
4542
+ // Attio/GRC's "Remove sort" (live-review fix D) -- the header's own click-to-sort
4543
+ // (`SortIndicator`/`toggleSort`, above) only cycles asc/desc once a column has been
4544
+ // clicked; this is the only affordance back to no sort at all, so it only makes
4545
+ // sense to show on the column that IS currently sorted.
4546
+ const isSorted = sort?.key === column.key;
4433
4547
  const stopPropagation = (event)=>event.stopPropagation();
4434
- return /*#__PURE__*/ jsxs(MenuRoot, {
4435
- children: [
4436
- /*#__PURE__*/ jsx(DataTableHeaderCellIcon, {
4437
- asChild: true,
4438
- "data-column-menu": "",
4439
- onClick: stopPropagation,
4440
- onKeyDown: stopPropagation,
4441
- draggable: false,
4442
- children: /*#__PURE__*/ jsx(MenuTrigger, {
4443
- "aria-label": `${columnLabel} column menu`,
4444
- children: /*#__PURE__*/ jsx(IconDot3HorizontalLine_default, {
4445
- size: 16
4548
+ return(// Live-review fix (D): a `MenuItem` inside `MenuPositioner` renders through a
4549
+ // `FloatingPortal` -- a REAL React portal (`ReactDOM.createPortal`), which
4550
+ // bubbles a click/keydown up through the REACT TREE (this component, then the
4551
+ // columnheader that renders it), NOT the DOM tree the portal actually appends
4552
+ // to. Confirmed live: clicking ANY item here (not just "Clear sort" -- "Move
4553
+ // left" on a column with no sort at all reproduced it too) also fired the
4554
+ // COLUMNHEADER's own `toggleSort`, because nothing stopped that bubble before
4555
+ // it reached the header's `onClick`/`onKeyDown` (`getHeaderCellProps`). The
4556
+ // trigger's own `stopPropagation` (on `DataTableHeaderCellIcon` below) only
4557
+ // covers clicks that start on the trigger itself -- a SIBLING of
4558
+ // `MenuPositioner` in this tree, not an ancestor of it, so it never sees a
4559
+ // menu-item event at all. This wrapper is a genuine DOM node sitting between
4560
+ // BOTH of them and the header, so it catches the bubble from either one;
4561
+ // `display: contents` keeps it invisible to `DataTableHeaderCellInner`'s own
4562
+ // flex layout.
4563
+ // 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 trigger button, the portaled menu items). No role fits it because it isn't a control.
4564
+ /*#__PURE__*/ jsx("span", {
4565
+ onClick: stopPropagation,
4566
+ onKeyDown: stopPropagation,
4567
+ style: {
4568
+ display: "contents"
4569
+ },
4570
+ children: /*#__PURE__*/ jsxs(MenuRoot, {
4571
+ children: [
4572
+ /*#__PURE__*/ jsx(DataTableHeaderCellIcon, {
4573
+ asChild: true,
4574
+ "data-column-menu": "",
4575
+ draggable: false,
4576
+ children: /*#__PURE__*/ jsx(MenuTrigger, {
4577
+ "aria-label": `${columnLabel} column menu`,
4578
+ children: /*#__PURE__*/ jsx(IconDot3HorizontalLine_default, {
4579
+ size: 16
4580
+ })
4446
4581
  })
4447
- })
4448
- }),
4449
- /*#__PURE__*/ jsx(MenuPositioner, {
4450
- children: /*#__PURE__*/ jsx(MenuContent, {
4451
- style: {
4452
- minWidth: 160
4453
- },
4454
- children: /*#__PURE__*/ jsxs(MenuScrollArea, {
4455
- children: [
4456
- /*#__PURE__*/ jsx(MenuItem, {
4457
- onClick: onRename,
4458
- children: /*#__PURE__*/ jsx(MenuItemLabel, {
4459
- children: "Rename"
4460
- })
4461
- }),
4462
- /*#__PURE__*/ jsx(MenuItem, {
4463
- onClick: ()=>moveColumn(column.key, "left"),
4464
- children: /*#__PURE__*/ jsx(MenuItemLabel, {
4465
- children: "Move left"
4466
- })
4467
- }),
4468
- /*#__PURE__*/ jsx(MenuItem, {
4469
- onClick: ()=>moveColumn(column.key, "right"),
4470
- children: /*#__PURE__*/ jsx(MenuItemLabel, {
4471
- children: "Move right"
4472
- })
4473
- }),
4474
- /*#__PURE__*/ jsx(MenuItem, {
4475
- onClick: ()=>togglePinColumn(column.key),
4476
- children: /*#__PURE__*/ jsx(MenuItemLabel, {
4477
- children: pinned ? "Unpin column" : "Pin column"
4478
- })
4479
- }),
4480
- /*#__PURE__*/ jsx(MenuItem, {
4481
- onClick: ()=>hideColumn(column.key),
4482
- children: /*#__PURE__*/ jsx(MenuItemLabel, {
4483
- children: "Hide column"
4582
+ }),
4583
+ /*#__PURE__*/ jsx(MenuPositioner, {
4584
+ children: /*#__PURE__*/ jsx(MenuContent, {
4585
+ style: {
4586
+ minWidth: 160
4587
+ },
4588
+ children: /*#__PURE__*/ jsxs(MenuScrollArea, {
4589
+ children: [
4590
+ /*#__PURE__*/ jsx(MenuItem, {
4591
+ onClick: onRename,
4592
+ children: /*#__PURE__*/ jsx(MenuItemLabel, {
4593
+ children: "Rename"
4594
+ })
4595
+ }),
4596
+ /*#__PURE__*/ jsx(MenuItem, {
4597
+ onClick: ()=>moveColumn(column.key, "left"),
4598
+ children: /*#__PURE__*/ jsx(MenuItemLabel, {
4599
+ children: "Move left"
4600
+ })
4601
+ }),
4602
+ /*#__PURE__*/ jsx(MenuItem, {
4603
+ onClick: ()=>moveColumn(column.key, "right"),
4604
+ children: /*#__PURE__*/ jsx(MenuItemLabel, {
4605
+ children: "Move right"
4606
+ })
4607
+ }),
4608
+ isSorted && /*#__PURE__*/ jsx(MenuItem, {
4609
+ onClick: clearSort,
4610
+ children: /*#__PURE__*/ jsx(MenuItemLabel, {
4611
+ children: "Clear sort"
4612
+ })
4613
+ }),
4614
+ /*#__PURE__*/ jsx(MenuItem, {
4615
+ onClick: ()=>togglePinColumn(column.key),
4616
+ children: /*#__PURE__*/ jsx(MenuItemLabel, {
4617
+ children: pinned ? "Unpin column" : "Pin column"
4618
+ })
4619
+ }),
4620
+ /*#__PURE__*/ jsx(MenuItem, {
4621
+ onClick: ()=>hideColumn(column.key),
4622
+ children: /*#__PURE__*/ jsx(MenuItemLabel, {
4623
+ children: "Hide column"
4624
+ })
4484
4625
  })
4485
- })
4486
- ]
4626
+ ]
4627
+ })
4487
4628
  })
4488
4629
  })
4489
- })
4490
- ]
4491
- });
4630
+ ]
4631
+ })
4632
+ }));
4492
4633
  }
4493
4634
 
4494
4635
  /**
@@ -5766,8 +5907,8 @@ Object.assign(datePicker, {
5766
5907
  splitVariantProps: (props)=>splitVariantProps(props, datePickerVariantMap)
5767
5908
  }); // @recipe(seed): date-picker
5768
5909
 
5769
- var __defProp$1 = Object.defineProperty;
5770
- var __name$1 = (target, value)=>__defProp$1(target, "name", {
5910
+ var __defProp$3 = Object.defineProperty;
5911
+ var __name$3 = (target, value)=>__defProp$3(target, "name", {
5771
5912
  value,
5772
5913
  configurable: true
5773
5914
  });
@@ -5791,15 +5932,15 @@ function useEffectEvent(callback) {
5791
5932
  }
5792
5933
  return React.useMemo(()=>(...args)=>ref.current?.(...args), []);
5793
5934
  }
5794
- __name$1(useEffectEvent, "useEffectEvent");
5935
+ __name$3(useEffectEvent, "useEffectEvent");
5795
5936
 
5796
- var __defProp = Object.defineProperty;
5797
- var __name = (target, value)=>__defProp(target, "name", {
5937
+ var __defProp$2 = Object.defineProperty;
5938
+ var __name$2 = (target, value)=>__defProp$2(target, "name", {
5798
5939
  value,
5799
5940
  configurable: true
5800
5941
  });
5801
5942
  var useInsertionEffect = React[" useInsertionEffect ".trim().toString()] || useLayoutEffect2;
5802
- function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __name(()=>{}, "onChange"), caller }) {
5943
+ function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __name$2(()=>{}, "onChange"), caller }) {
5803
5944
  const [uncontrolledProp, setUncontrolledProp, onChangeRef] = useUncontrolledState({
5804
5945
  defaultProp,
5805
5946
  onChange
@@ -5826,7 +5967,7 @@ function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __
5826
5967
  setValue
5827
5968
  ];
5828
5969
  }
5829
- __name(useControllableState, "useControllableState");
5970
+ __name$2(useControllableState, "useControllableState");
5830
5971
  function useUncontrolledState({ defaultProp, onChange }) {
5831
5972
  const [value, setValue] = React.useState(defaultProp);
5832
5973
  const prevValueRef = React.useRef(value);
@@ -5851,11 +5992,11 @@ function useUncontrolledState({ defaultProp, onChange }) {
5851
5992
  onChangeRef
5852
5993
  ];
5853
5994
  }
5854
- __name(useUncontrolledState, "useUncontrolledState");
5995
+ __name$2(useUncontrolledState, "useUncontrolledState");
5855
5996
  function isFunction(value) {
5856
5997
  return typeof value === "function";
5857
5998
  }
5858
- __name(isFunction, "isFunction");
5999
+ __name$2(isFunction, "isFunction");
5859
6000
  var SYNC_STATE = Symbol("RADIX:SYNC_STATE");
5860
6001
  function useControllableStateReducer(reducer, userArgs, initialArg, init) {
5861
6002
  const { prop: controlledState, defaultProp, onChange: onChangeProp, caller } = userArgs;
@@ -5927,7 +6068,7 @@ function useControllableStateReducer(reducer, userArgs, initialArg, init) {
5927
6068
  dispatch
5928
6069
  ];
5929
6070
  }
5930
- __name(useControllableStateReducer, "useControllableStateReducer");
6071
+ __name$2(useControllableStateReducer, "useControllableStateReducer");
5931
6072
 
5932
6073
  /*
5933
6074
  * Copyright 2020 Adobe. All rights reserved.
@@ -10133,8 +10274,8 @@ const formatFilterDate = (iso)=>{
10133
10274
  });
10134
10275
  }
10135
10276
 
10136
- const DataTableToolbar = withContext$1(Primitive.div, "toolbar");
10137
- const DataTableCount = withContext$1(Primitive.span, "count");
10277
+ const DataTableToolbar = withContext$2(Primitive.div, "toolbar");
10278
+ const DataTableCount = withContext$2(Primitive.span, "count");
10138
10279
  /**
10139
10280
  * The toolbar's own right-hand group -- `count`'s `marginInlineStart: auto` moved
10140
10281
  * here (off the `<span>` itself) so a consumer's `toolbarActions` and the row count
@@ -10145,7 +10286,7 @@ const DataTableCount = withContext$1(Primitive.span, "count");
10145
10286
  * it. `DataTable.tsx` always renders this wrapper once either `toolbarActions` or
10146
10287
  * `showCount` is on, so it degrades to exactly the old single-count layout when no
10147
10288
  * `toolbarActions` is passed.
10148
- */ const DataTableToolbarEnd = withContext$1(Primitive.div, "toolbarEnd");
10289
+ */ const DataTableToolbarEnd = withContext$2(Primitive.div, "toolbarEnd");
10149
10290
  /**
10150
10291
  * A compact, borderless search box -- crisp's own measured `.crisp-datatable-search`
10151
10292
  * (32px, `bgSurface` background, a leading icon, a soft rest-state shadow, a 1px brand
@@ -10205,6 +10346,25 @@ function DataTableInner(props, ref) {
10205
10346
  // column/direction is active. A consumer that pre-sorts server-side can pass
10206
10347
  // a controlled `sort`; this still sorts the rows it's given, which for
10207
10348
  // already-sorted data is a no-op.
10349
+ //
10350
+ // Live-review fix (D, "Clear sort"): the JSX below passes the RAW `sort` prop
10351
+ // (not `resolvedSort`) on to `DataTablePrimitiveRoot`, plus `defaultSort` --
10352
+ // passing `resolvedSort` used to feed the headless `useDataTableSort`'s own
10353
+ // `useControllableState` a value that starts `undefined` (uncontrolled) and
10354
+ // BECOMES defined the instant a header is first clicked (once `internalSort`
10355
+ // picks it up), flipping that inner hook from uncontrolled to controlled
10356
+ // mid-lifecycle -- `@radix-ui/react-use-controllable-state` warns about
10357
+ // exactly this and, worse, silently misbehaves on it: its own internal
10358
+ // uncontrolled state stops updating the moment it's controlled, so when
10359
+ // `clearSort` later drove `resolvedSort` back to `undefined` (flipping BACK
10360
+ // to uncontrolled), the hook fell through to that stale, never-updated
10361
+ // internal state instead -- still the pre-clear value, confirmed live
10362
+ // (`aria-sort` stayed "ascending" after "Clear sort"). The RAW `sort` prop
10363
+ // never has this problem: for the common uncontrolled case it's simply
10364
+ // always `undefined`, so the headless hook is uncontrolled for its entire
10365
+ // lifetime and owns its state correctly; a consumer that genuinely controls
10366
+ // `sort` behaves exactly as before (`resolvedSort === sort` whenever `sort`
10367
+ // is defined, so nothing changes for that path).
10208
10368
  const [internalSort, setInternalSort] = React.useState(defaultSort);
10209
10369
  const resolvedSort = sort !== undefined ? sort : internalSort;
10210
10370
  const handleSortChange = React.useCallback((next)=>{
@@ -10310,7 +10470,8 @@ function DataTableInner(props, ref) {
10310
10470
  selectedIds: selectedIds,
10311
10471
  defaultSelectedIds: defaultSelectedIds,
10312
10472
  onSelectionChange: onSelectionChange,
10313
- sort: resolvedSort,
10473
+ sort: sort,
10474
+ defaultSort: defaultSort,
10314
10475
  onSortChange: handleSortChange,
10315
10476
  query: resolvedQuery,
10316
10477
  onQueryChange: handleQueryChange,
@@ -10547,7 +10708,7 @@ DataTableInner.displayName = "DataTable";
10547
10708
  // is a SIBLING of the toolbar (both inside `DataTablePrimitiveRoot`'s card `<div>`)
10548
10709
  // rather than `DataTablePrimitiveRoot` itself: a `<table>` cannot host the toolbar as a
10549
10710
  // child without the HTML parser foster-parenting it back out.
10550
- const DataTableTable = withContext$1(DataTableTable$1, "table");
10711
+ const DataTableTable = withContext$2(DataTableTable$1, "table");
10551
10712
  /** One `<col>` per rendered column (plus the 40px selection column, if any) -- the
10552
10713
  * SINGLE place a column's width is ever set now. Every row's header/body/footer cell
10553
10714
  * reads its own width off this shared `<colgroup>` by construction (native table
@@ -10573,6 +10734,10 @@ const DataTableTable = withContext$1(DataTableTable$1, "table");
10573
10734
  }
10574
10735
  }),
10575
10736
  effective.map(({ column, width })=>/*#__PURE__*/ jsx("col", {
10737
+ // Read by `resize.tsx`'s own pointermove handler (`data-column-key`,
10738
+ // not a ref) to write the dragged width straight to this element
10739
+ // during a drag -- see that file's own comment for why.
10740
+ "data-column-key": column.key,
10576
10741
  style: width === undefined ? undefined : {
10577
10742
  width
10578
10743
  }
@@ -10580,15 +10745,15 @@ const DataTableTable = withContext$1(DataTableTable$1, "table");
10580
10745
  ]
10581
10746
  });
10582
10747
  }
10583
- const DataTableHeader = withContext$1(DataTableHeader$1, "header");
10584
- const DataTableHeaderCell = withContext$1(DataTableHeaderCell$1, "headerCell");
10748
+ const DataTableHeader = withContext$2(DataTableHeader$1, "header");
10749
+ const DataTableHeaderCell = withContext$2(DataTableHeaderCell$1, "headerCell");
10585
10750
  // `.crisp-datatable-th-inner` -- the flex row INSIDE a `<th>` (label + filter trigger +
10586
10751
  // sort chevron + column menu). See qvism-preset's own `headerCellInner` slot comment
10587
10752
  // for why this lives one level below the cell itself rather than on it.
10588
- const DataTableHeaderCellInner = withContext$1(Primitive.div, "headerCellInner");
10753
+ const DataTableHeaderCellInner = withContext$2(Primitive.div, "headerCellInner");
10589
10754
  /** The label text specifically -- the one thing in `DataTableHeaderCellInner` that
10590
10755
  * truncates; the trailing filter/sort/menu icons stay fully visible regardless of how
10591
- * long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$1(Primitive.span, "headerCellLabel");
10756
+ * long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$2(Primitive.span, "headerCellLabel");
10592
10757
  /**
10593
10758
  * A selection `<th>`/`<td>`/footer `<td>` composes ITS base slot (`headerCell`/`cell`/
10594
10759
  * `footerCell`) together with the `selectionCell` MODIFIER, rather than replacing it --
@@ -10610,12 +10775,12 @@ const DataTableHeaderCellInner = withContext$1(Primitive.div, "headerCellInner")
10610
10775
  return StyledComponent;
10611
10776
  }
10612
10777
  const DataTableSelectionHeaderCell = withSelectionCell(DataTableHeaderCell$1, "headerCell");
10613
- const DataTableHeaderCellIcon = withContext$1(Primitive.span, "headerCellIcon");
10778
+ const DataTableHeaderCellIcon = withContext$2(Primitive.span, "headerCellIcon");
10614
10779
  // body has no CSS slot of its own in the rootage spec -- it is a plain structural
10615
10780
  // `<tbody>`, so it is not wrapped in `withContext`.
10616
10781
  const DataTableBody = DataTableBody$1;
10617
- const DataTableRow = withContext$1(DataTableRow$1, "row");
10618
- const DataTableCell = withContext$1(DataTableCell$1, "cell");
10782
+ const DataTableRow = withContext$2(DataTableRow$1, "row");
10783
+ const DataTableCell = withContext$2(DataTableCell$1, "cell");
10619
10784
  const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
10620
10785
  // Tranche 4 -- column menu + reorder + resize + sticky columns. A scroll boundary
10621
10786
  // wrapping the whole `<table>`: `overflowX: auto` is what makes a wide table (more
@@ -10625,7 +10790,7 @@ const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
10625
10790
  // stays out of this slot's own base css (nothing here has a bounded height of its own
10626
10791
  // to clip), so it never introduces a second vertical scrollbar next to whatever
10627
10792
  // ambient container the whole DataTable already sits in.
10628
- const DataTableScrollBodyBase = withContext$1(Primitive.div, "scrollArea");
10793
+ const DataTableScrollBodyBase = withContext$2(Primitive.div, "scrollArea");
10629
10794
  function DataTableScrollBody({ children, scrollProps }) {
10630
10795
  const { scrollAreaRef, scrolledStart, scrolledEnd, onScroll } = useDataTableContext();
10631
10796
  const { className: scrollClassName, style: scrollStyle, ...otherScrollProps } = scrollProps ?? {};
@@ -10640,7 +10805,7 @@ function DataTableScrollBody({ children, scrollProps }) {
10640
10805
  children: children
10641
10806
  });
10642
10807
  }
10643
- const DataTableFooterBase = withContext$1(Primitive.tfoot, "footer");
10808
+ const DataTableFooterBase = withContext$2(Primitive.tfoot, "footer");
10644
10809
  function DataTableFooter({ children, ...props }) {
10645
10810
  return /*#__PURE__*/ jsx(DataTableFooterBase, {
10646
10811
  ...props,
@@ -10649,7 +10814,7 @@ function DataTableFooter({ children, ...props }) {
10649
10814
  })
10650
10815
  });
10651
10816
  }
10652
- const DataTableFooterCell = withContext$1(Primitive.td, "footerCell");
10817
+ const DataTableFooterCell = withContext$2(Primitive.td, "footerCell");
10653
10818
  // The selection column's OWN sticky wrappers -- one each for header/body/footer,
10654
10819
  // reading the live pinned set via `useSelectionStickyFirst` (column-layout.ts) instead
10655
10820
  // of the `stickyFirst` PROP alone, so the checkbox column sticks the moment ANY column
@@ -11001,7 +11166,7 @@ DataTableViewsControl.displayName = "DataTableViewsControl";
11001
11166
  });
11002
11167
  }
11003
11168
 
11004
- __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:fixed;top:8px;bottom:8px;border-inline-end:none;border-inline-start:none;border-radius:10px;box-shadow:inset 0 0 0 1px transparent,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;transition:transform .14s,padding .14s}.seed-resizable-panel__panel[data-floating=true][data-side=left]{padding:8px 0 8px 8px}[data-floating=true]>.seed-resizable-panel__inner{transition:opacity .14s}.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}");
11169
+ __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}");
11005
11170
 
11006
11171
  const resizablePanelSlotNames = [
11007
11172
  [
@@ -11027,6 +11192,10 @@ const resizablePanelSlotNames = [
11027
11192
  [
11028
11193
  "handleInner",
11029
11194
  "seed-resizable-panel__handleInner"
11195
+ ],
11196
+ [
11197
+ "backdrop",
11198
+ "seed-resizable-panel__backdrop"
11030
11199
  ]
11031
11200
  ];
11032
11201
  const defaultVariant$1 = {};
@@ -11206,15 +11375,19 @@ Object.assign(resizablePanel, {
11206
11375
  // second source of truth for the string.
11207
11376
  const COLUMN_HANDLE_CLASS = "crisp-seed-resizable-panel-handle--column";
11208
11377
  /**
11209
- * An adjustable-panel primitive: a spacer that reserves layout width plus a
11210
- * panel that overlays it, so the panel can move between docked (in-flow) and
11211
- * floating (`position: fixed`, an overlay) without a reflow glitch. Drag the
11212
- * edge handle to resize; constraints are CSS `min-width`/`max-width`, never
11213
- * re-clamped in JS. Ported from your-moon/crisp's `ResizablePanel`
11214
- * (`components/resizablepanel/`); state/drag behavior lives in
11215
- * `useResizablePanel` (`@seed-design/react-resizable-panel`), this component
11216
- * owns the DOM + recipe classes on top of it.
11217
- */ const ResizablePanel = /*#__PURE__*/ forwardRef(({ side = "left", width: sizeProp, defaultSize = 280, onWidthChange, min = 200, max, floating = false, handleVariant = "panel", resizable = true, storageKey, className, children, ...rest }, forwardedRef)=>{
11378
+ * An adjustable-panel primitive with two layout models (`floating`'s own doc
11379
+ * comment above has the full contrast). Docked: a `spacer` reserves layout
11380
+ * width and the panel overlays exactly that reservation, so a resize drag
11381
+ * never causes a reflow glitch on the rest of the layout. Floating: no
11382
+ * spacer at all -- this component's own outer element becomes a
11383
+ * `position: absolute; inset: 0` overlay reserving zero width, with the
11384
+ * panel itself pinned to one edge and spanning the full height of that box.
11385
+ * Drag the edge handle to resize; constraints are CSS `min-width`/
11386
+ * `max-width`, never re-clamped in JS. Ported from your-moon/crisp's
11387
+ * `ResizablePanel` (`components/resizablepanel/`); state/drag behavior
11388
+ * lives in `useResizablePanel` (`@seed-design/react-resizable-panel`), this
11389
+ * component owns the DOM + recipe classes on top of it.
11390
+ */ 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)=>{
11218
11391
  const { width, dragging, containerRef, panelRef, handleHandlers } = useResizablePanel({
11219
11392
  side,
11220
11393
  size: sizeProp,
@@ -11222,7 +11395,12 @@ const COLUMN_HANDLE_CLASS = "crisp-seed-resizable-panel-handle--column";
11222
11395
  onSizeChange: onWidthChange,
11223
11396
  storageKey
11224
11397
  });
11398
+ const composedPanelRef = useComposedRefs(panelRef, panelElementRef);
11225
11399
  const classNames = resizablePanel();
11400
+ // Floating spans its full container edge to edge -- no gutter, no
11401
+ // shrink -- so the panel's own size style is the SAME reservation a
11402
+ // docked spacer would use; only whether `spacer` itself renders differs
11403
+ // below.
11226
11404
  const maxStyle = max == null ? undefined : typeof max === "number" ? `${max}px` : max;
11227
11405
  const panelSizeStyle = {
11228
11406
  width,
@@ -11236,23 +11414,28 @@ const COLUMN_HANDLE_CLASS = "crisp-seed-resizable-panel-handle--column";
11236
11414
  else if (forwardedRef) forwardedRef.current = node;
11237
11415
  },
11238
11416
  className: clsx(classNames.root, className),
11417
+ "data-floating": floating ? "true" : "false",
11239
11418
  ...rest,
11240
11419
  children: [
11241
- /*#__PURE__*/ jsx(Primitive.div, {
11420
+ !floating && /*#__PURE__*/ jsx(Primitive.div, {
11242
11421
  className: classNames.spacer,
11243
- style: floating ? {
11244
- width: 0
11245
- } : {
11422
+ style: {
11246
11423
  width,
11247
11424
  minWidth: min,
11248
11425
  maxWidth: maxStyle
11249
11426
  }
11250
11427
  }),
11428
+ floating && backdrop && /*#__PURE__*/ jsx(Primitive.div, {
11429
+ className: classNames.backdrop,
11430
+ "data-state": animationState,
11431
+ onClick: onBackdropClick
11432
+ }),
11251
11433
  /*#__PURE__*/ jsxs(Primitive.div, {
11252
- ref: panelRef,
11434
+ ref: composedPanelRef,
11253
11435
  className: classNames.panel,
11254
11436
  "data-side": side,
11255
11437
  "data-floating": floating ? "true" : "false",
11438
+ "data-state": animationState,
11256
11439
  style: panelSizeStyle,
11257
11440
  children: [
11258
11441
  /*#__PURE__*/ jsx(Primitive.div, {
@@ -11277,7 +11460,7 @@ const COLUMN_HANDLE_CLASS = "crisp-seed-resizable-panel-handle--column";
11277
11460
  });
11278
11461
  ResizablePanel.displayName = "ResizablePanel";
11279
11462
 
11280
- __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)}");
11463
+ __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}");
11281
11464
 
11282
11465
  const recordPreviewSlotNames = [
11283
11466
  [
@@ -11387,6 +11570,18 @@ const recordPreviewSlotNames = [
11387
11570
  [
11388
11571
  "footer",
11389
11572
  "seed-record-preview__footer"
11573
+ ],
11574
+ [
11575
+ "tabsRoot",
11576
+ "seed-record-preview__tabsRoot"
11577
+ ],
11578
+ [
11579
+ "tabPanel",
11580
+ "seed-record-preview__tabPanel"
11581
+ ],
11582
+ [
11583
+ "tabBadge",
11584
+ "seed-record-preview__tabBadge"
11390
11585
  ]
11391
11586
  ];
11392
11587
  const defaultVariant = {};
@@ -11404,96 +11599,3054 @@ Object.assign(recordPreview, {
11404
11599
  splitVariantProps: (props)=>splitVariantProps(props, recordPreviewVariantMap)
11405
11600
  }); // @recipe(seed): record-preview
11406
11601
 
11407
- const RESIZE_MIN_WIDTH = 360;
11408
- const RESIZE_MAX_WIDTH = 720;
11409
- /**
11410
- * Right-side record peek panel -- Attio's own peek shows a Highlights card
11411
- * area and an Activity feed (measured live). Ported from your-moon/crisp's
11412
- * `RecordPanel` (`product/recordpanel/`), built ON `ResizablePanel`
11413
- * (`side="right" floating`) rather than crisp's own duplicate absolute-
11414
- * positioning/slide-in/resize-handle implementation -- the outer shell (the
11415
- * `@starting-style` slide-in, the floating elevation, the drag handle
11416
- * itself) all come from that primitive; this component only renders the
11417
- * CONTENT inside it.
11418
- */ function RecordPreview({ open, record, onClose, className, 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 = 420, onWidthChange, minWidth = RESIZE_MIN_WIDTH, maxWidth = RESIZE_MAX_WIDTH, activityLabel = "Activity", viewAllActivityLabel = "View all", onViewAllActivity, activity, sections, footer, ...rest }) {
11419
- const titleId = useId();
11420
- const classNames = recordPreview();
11421
- // Which sections (your-moon/crisp#413) are expanded -- uncontrolled and
11422
- // independent (Attio's own Emails/Notes/Tasks open without closing one
11423
- // another): a plain `Set`, reset whenever the record itself changes so a
11424
- // stale open section doesn't carry over between two different records.
11425
- const [openSections, setOpenSections] = useState(()=>new Set());
11426
- // biome-ignore lint/correctness/useExhaustiveDependencies: `record` is read as a change TRIGGER, not a value the effect body needs -- reset whenever it changes identity, regardless of what it now points to.
11427
- useEffect(()=>{
11428
- setOpenSections(new Set());
11602
+ // This code includes portions derived from radix-ui/primitives (https://github.com/radix-ui/primitives)
11603
+ // Used under the MIT License: https://opensource.org/licenses/MIT
11604
+ function usePresence(present) {
11605
+ const [node, setNode] = React.useState();
11606
+ const stylesRef = React.useRef(null);
11607
+ const prevPresentRef = React.useRef(present);
11608
+ const prevAnimationNameRef = React.useRef("none");
11609
+ const initialState = present ? "mounted" : "unmounted";
11610
+ const [state, send] = useStateMachine(initialState, {
11611
+ mounted: {
11612
+ UNMOUNT: "unmounted",
11613
+ ANIMATION_OUT: "unmountSuspended"
11614
+ },
11615
+ unmountSuspended: {
11616
+ MOUNT: "mounted",
11617
+ ANIMATION_END: "unmounted"
11618
+ },
11619
+ unmounted: {
11620
+ MOUNT: "mounted"
11621
+ }
11622
+ });
11623
+ React.useEffect(()=>{
11624
+ const currentAnimationName = getAnimationName(stylesRef.current);
11625
+ prevAnimationNameRef.current = state === "mounted" ? currentAnimationName : "none";
11429
11626
  }, [
11430
- record
11627
+ state
11431
11628
  ]);
11432
- const toggleSection = (key)=>setOpenSections((prev)=>{
11433
- const next = new Set(prev);
11434
- if (next.has(key)) next.delete(key);
11435
- else next.add(key);
11436
- return next;
11437
- });
11438
- // Close on Escape while open. Simpler than crisp's own version (crisp
11439
- // additionally checks `hasOpenFloatingLayer()` so an Escape meant for a
11440
- // nested Menu/Select/Popover doesn't also close this panel) -- crispy-seed
11441
- // has no equivalent floating-stack registry to read, so a consumer nesting
11442
- // its own floating layers inside `footer`/`sections` content should stop
11443
- // propagation on its own Escape handler.
11444
- useEffect(()=>{
11445
- if (!open) return;
11446
- const onKey = (event)=>{
11447
- if (event.key === "Escape") onClose();
11448
- };
11449
- document.addEventListener("keydown", onKey);
11450
- return ()=>document.removeEventListener("keydown", onKey);
11629
+ useLayoutEffect2(()=>{
11630
+ const styles = stylesRef.current;
11631
+ const wasPresent = prevPresentRef.current;
11632
+ const hasPresentChanged = wasPresent !== present;
11633
+ if (hasPresentChanged) {
11634
+ const prevAnimationName = prevAnimationNameRef.current;
11635
+ const currentAnimationName = getAnimationName(styles);
11636
+ if (present) {
11637
+ send("MOUNT");
11638
+ } else if (currentAnimationName === "none" || styles?.display === "none") {
11639
+ // If there is no exit animation or the element is hidden, animations won't run
11640
+ // so we unmount instantly
11641
+ send("UNMOUNT");
11642
+ } else {
11643
+ /**
11644
+ * When `present` changes to `false`, we check changes to animation-name to
11645
+ * determine whether an animation has started. We chose this approach (reading
11646
+ * computed styles) because there is no `animationrun` event and `animationstart`
11647
+ * fires after `animation-delay` has expired which would be too late.
11648
+ */ const isAnimating = prevAnimationName !== currentAnimationName;
11649
+ if (wasPresent && isAnimating) {
11650
+ send("ANIMATION_OUT");
11651
+ } else {
11652
+ send("UNMOUNT");
11653
+ }
11654
+ }
11655
+ prevPresentRef.current = present;
11656
+ }
11451
11657
  }, [
11452
- open,
11453
- onClose
11658
+ present,
11659
+ send
11454
11660
  ]);
11455
- if (!open || !record) return null;
11456
- const heading = title ?? record.name ?? "Record";
11457
- return /*#__PURE__*/ jsx(ResizablePanel, {
11458
- side: "right",
11459
- floating: true,
11460
- resizable: resizable,
11461
- width: width,
11462
- defaultSize: defaultWidth,
11463
- onWidthChange: onWidthChange,
11464
- min: minWidth,
11465
- max: maxWidth,
11466
- children: /*#__PURE__*/ jsxs(Primitive.div, {
11467
- className: clsx(classNames.root, className),
11468
- ...rest,
11469
- role: "dialog",
11470
- "aria-labelledby": titleId,
11471
- children: [
11472
- /*#__PURE__*/ jsxs(Primitive.div, {
11473
- className: classNames.toolbar,
11474
- children: [
11475
- /*#__PURE__*/ jsx(Primitive.button, {
11476
- type: "button",
11477
- className: classNames.iconbtn,
11478
- "aria-label": closeLabel,
11479
- onClick: onClose,
11480
- children: /*#__PURE__*/ jsx(IconXmarkLine_default, {
11481
- size: 16
11482
- })
11483
- }),
11484
- (onPrevious || onNext) && /*#__PURE__*/ jsxs(Primitive.span, {
11485
- className: classNames.pager,
11486
- children: [
11487
- /*#__PURE__*/ jsx(Primitive.button, {
11488
- type: "button",
11489
- className: classNames.navbtn,
11490
- "aria-label": previousRecordLabel,
11491
- disabled: !onPrevious,
11492
- onClick: onPrevious,
11493
- children: /*#__PURE__*/ jsx(IconChevronUpLine_default, {
11494
- size: 16
11495
- })
11496
- }),
11661
+ useLayoutEffect2(()=>{
11662
+ if (node) {
11663
+ let timeoutId;
11664
+ const ownerWindow = node.ownerDocument.defaultView ?? window;
11665
+ /**
11666
+ * Triggering an ANIMATION_OUT during an ANIMATION_IN will fire an `animationcancel`
11667
+ * event for ANIMATION_IN after we have entered `unmountSuspended` state. So, we
11668
+ * make sure we only trigger ANIMATION_END for the currently active animation.
11669
+ */ const handleAnimationEnd = (event)=>{
11670
+ const currentAnimationName = getAnimationName(stylesRef.current);
11671
+ const isCurrentAnimation = currentAnimationName.includes(event.animationName);
11672
+ if (event.target === node && isCurrentAnimation) {
11673
+ // With React 18 concurrency this update is applied a frame after the
11674
+ // animation ends, creating a flash of visible content. By setting the
11675
+ // animation fill mode to "forwards", we force the node to keep the
11676
+ // styles of the last keyframe, removing the flash.
11677
+ //
11678
+ // Previously we flushed the update via ReactDom.flushSync, but with
11679
+ // exit animations this resulted in the node being removed from the
11680
+ // DOM before the synthetic animationEnd event was dispatched, meaning
11681
+ // user-provided event handlers would not be called.
11682
+ // https://github.com/radix-ui/primitives/pull/1849
11683
+ send("ANIMATION_END");
11684
+ if (!prevPresentRef.current) {
11685
+ const currentFillMode = node.style.animationFillMode;
11686
+ node.style.animationFillMode = "forwards";
11687
+ // Reset the style after the node had time to unmount (for cases
11688
+ // where the component chooses not to unmount). Doing this any
11689
+ // sooner than `setTimeout` (e.g. with `requestAnimationFrame`)
11690
+ // still causes a flash.
11691
+ timeoutId = ownerWindow.setTimeout(()=>{
11692
+ if (node.style.animationFillMode === "forwards") {
11693
+ node.style.animationFillMode = currentFillMode;
11694
+ }
11695
+ });
11696
+ }
11697
+ }
11698
+ };
11699
+ const handleAnimationStart = (event)=>{
11700
+ if (event.target === node) {
11701
+ // if animation occurred, store its name as the previous animation.
11702
+ prevAnimationNameRef.current = getAnimationName(stylesRef.current);
11703
+ }
11704
+ };
11705
+ node.addEventListener("animationstart", handleAnimationStart);
11706
+ node.addEventListener("animationcancel", handleAnimationEnd);
11707
+ node.addEventListener("animationend", handleAnimationEnd);
11708
+ return ()=>{
11709
+ ownerWindow.clearTimeout(timeoutId);
11710
+ node.removeEventListener("animationstart", handleAnimationStart);
11711
+ node.removeEventListener("animationcancel", handleAnimationEnd);
11712
+ node.removeEventListener("animationend", handleAnimationEnd);
11713
+ };
11714
+ } else {
11715
+ // Transition to the unmounted state if the node is removed prematurely.
11716
+ // We avoid doing so during cleanup as the node may change but still exist.
11717
+ send("ANIMATION_END");
11718
+ }
11719
+ }, [
11720
+ node,
11721
+ send
11722
+ ]);
11723
+ return {
11724
+ isPresent: [
11725
+ "mounted",
11726
+ "unmountSuspended"
11727
+ ].includes(state),
11728
+ ref: React.useCallback((node)=>{
11729
+ if (node) stylesRef.current = getComputedStyle(node);
11730
+ setNode(node ?? undefined);
11731
+ }, [])
11732
+ };
11733
+ }
11734
+ /* -----------------------------------------------------------------------------------------------*/ function getAnimationName(styles) {
11735
+ return styles?.animationName || "none";
11736
+ }
11737
+ function useStateMachine(initialState, machine) {
11738
+ return React.useReducer((state, event)=>{
11739
+ const nextState = machine[state][event];
11740
+ return nextState ?? state;
11741
+ }, initialState);
11742
+ }
11743
+ const PresenceContext = /*#__PURE__*/ createContext(null);
11744
+ PresenceContext.Provider;
11745
+
11746
+ var __defProp$1 = Object.defineProperty;
11747
+ var __name$1 = (target, value)=>__defProp$1(target, "name", {
11748
+ value,
11749
+ configurable: true
11750
+ });
11751
+ function useSize(element) {
11752
+ const [size, setSize] = React.useState(void 0);
11753
+ useLayoutEffect2(()=>{
11754
+ if (element) {
11755
+ setSize({
11756
+ width: element.offsetWidth,
11757
+ height: element.offsetHeight
11758
+ });
11759
+ const resizeObserver = new ResizeObserver((entries)=>{
11760
+ if (!Array.isArray(entries)) {
11761
+ return;
11762
+ }
11763
+ if (!entries.length) {
11764
+ return;
11765
+ }
11766
+ const entry = entries[0];
11767
+ let width;
11768
+ let height;
11769
+ if ("borderBoxSize" in entry) {
11770
+ const borderSizeEntry = entry["borderBoxSize"];
11771
+ const borderSize = Array.isArray(borderSizeEntry) ? borderSizeEntry[0] : borderSizeEntry;
11772
+ width = borderSize["inlineSize"];
11773
+ height = borderSize["blockSize"];
11774
+ } else {
11775
+ width = element.offsetWidth;
11776
+ height = element.offsetHeight;
11777
+ }
11778
+ setSize({
11779
+ width,
11780
+ height
11781
+ });
11782
+ });
11783
+ resizeObserver.observe(element, {
11784
+ box: "border-box"
11785
+ });
11786
+ return ()=>resizeObserver.unobserve(element);
11787
+ } else {
11788
+ setSize(void 0);
11789
+ }
11790
+ }, [
11791
+ element
11792
+ ]);
11793
+ return size;
11794
+ }
11795
+ __name$1(useSize, "useSize");
11796
+
11797
+ function useSupports(selector) {
11798
+ const [isSupported, setIsSupported] = useState(null);
11799
+ useEffect(()=>{
11800
+ setIsSupported(CSS.supports(selector));
11801
+ }, [
11802
+ selector
11803
+ ]);
11804
+ return isSupported;
11805
+ }
11806
+
11807
+ var __defProp = Object.defineProperty;
11808
+ var __name = (target, value)=>__defProp(target, "name", {
11809
+ value,
11810
+ configurable: true
11811
+ });
11812
+ function useCallbackRef(callback) {
11813
+ const callbackRef = React.useRef(callback);
11814
+ React.useEffect(()=>{
11815
+ callbackRef.current = callback;
11816
+ });
11817
+ return React.useMemo(()=>(...args)=>callbackRef.current?.(...args), []);
11818
+ }
11819
+ __name(useCallbackRef, "useCallbackRef");
11820
+
11821
+ function AutoHeight(userOptions = {}) {
11822
+ let emblaApi;
11823
+ let slideHeights = [];
11824
+ const heightEvents = [
11825
+ 'select',
11826
+ 'slideFocus'
11827
+ ];
11828
+ function init(emblaApiInstance) {
11829
+ emblaApi = emblaApiInstance;
11830
+ const { options: { axis }, slideRects } = emblaApi.internalEngine();
11831
+ if (axis === 'y') return;
11832
+ slideHeights = slideRects.map((slideRect)=>slideRect.height);
11833
+ heightEvents.forEach((evt)=>emblaApi.on(evt, setContainerHeight));
11834
+ setContainerHeight();
11835
+ }
11836
+ function destroy() {
11837
+ heightEvents.forEach((evt)=>emblaApi.off(evt, setContainerHeight));
11838
+ const container = emblaApi.containerNode();
11839
+ container.style.height = '';
11840
+ if (!container.getAttribute('style')) container.removeAttribute('style');
11841
+ }
11842
+ function highestInView() {
11843
+ const { slideRegistry } = emblaApi.internalEngine();
11844
+ const selectedIndexes = slideRegistry[emblaApi.selectedScrollSnap()];
11845
+ if (!selectedIndexes) return null;
11846
+ return selectedIndexes.map((index)=>slideHeights[index]).reduce((a, b)=>Math.max(a, b), 0);
11847
+ }
11848
+ function setContainerHeight() {
11849
+ const height = highestInView();
11850
+ if (height === null) return;
11851
+ emblaApi.containerNode().style.height = `${highestInView()}px`;
11852
+ }
11853
+ const self = {
11854
+ name: 'autoHeight',
11855
+ options: userOptions,
11856
+ init,
11857
+ destroy
11858
+ };
11859
+ return self;
11860
+ }
11861
+ AutoHeight.globalOptions = undefined;
11862
+
11863
+ function isObject$1(subject) {
11864
+ return Object.prototype.toString.call(subject) === '[object Object]';
11865
+ }
11866
+ function isRecord(subject) {
11867
+ return isObject$1(subject) || Array.isArray(subject);
11868
+ }
11869
+ function canUseDOM() {
11870
+ return !!(typeof window !== 'undefined' && window.document && window.document.createElement);
11871
+ }
11872
+ function areOptionsEqual(optionsA, optionsB) {
11873
+ const optionsAKeys = Object.keys(optionsA);
11874
+ const optionsBKeys = Object.keys(optionsB);
11875
+ if (optionsAKeys.length !== optionsBKeys.length) return false;
11876
+ const breakpointsA = JSON.stringify(Object.keys(optionsA.breakpoints || {}));
11877
+ const breakpointsB = JSON.stringify(Object.keys(optionsB.breakpoints || {}));
11878
+ if (breakpointsA !== breakpointsB) return false;
11879
+ return optionsAKeys.every((key)=>{
11880
+ const valueA = optionsA[key];
11881
+ const valueB = optionsB[key];
11882
+ if (typeof valueA === 'function') return `${valueA}` === `${valueB}`;
11883
+ if (!isRecord(valueA) || !isRecord(valueB)) return valueA === valueB;
11884
+ return areOptionsEqual(valueA, valueB);
11885
+ });
11886
+ }
11887
+ function sortAndMapPluginToOptions(plugins) {
11888
+ return plugins.concat().sort((a, b)=>a.name > b.name ? 1 : -1).map((plugin)=>plugin.options);
11889
+ }
11890
+ function arePluginsEqual(pluginsA, pluginsB) {
11891
+ if (pluginsA.length !== pluginsB.length) return false;
11892
+ const optionsA = sortAndMapPluginToOptions(pluginsA);
11893
+ const optionsB = sortAndMapPluginToOptions(pluginsB);
11894
+ return optionsA.every((optionA, index)=>{
11895
+ const optionB = optionsB[index];
11896
+ return areOptionsEqual(optionA, optionB);
11897
+ });
11898
+ }
11899
+
11900
+ function isNumber(subject) {
11901
+ return typeof subject === 'number';
11902
+ }
11903
+ function isString(subject) {
11904
+ return typeof subject === 'string';
11905
+ }
11906
+ function isBoolean(subject) {
11907
+ return typeof subject === 'boolean';
11908
+ }
11909
+ function isObject(subject) {
11910
+ return Object.prototype.toString.call(subject) === '[object Object]';
11911
+ }
11912
+ function mathAbs(n) {
11913
+ return Math.abs(n);
11914
+ }
11915
+ function mathSign(n) {
11916
+ return Math.sign(n);
11917
+ }
11918
+ function deltaAbs(valueB, valueA) {
11919
+ return mathAbs(valueB - valueA);
11920
+ }
11921
+ function factorAbs(valueB, valueA) {
11922
+ if (valueB === 0 || valueA === 0) return 0;
11923
+ if (mathAbs(valueB) <= mathAbs(valueA)) return 0;
11924
+ const diff = deltaAbs(mathAbs(valueB), mathAbs(valueA));
11925
+ return mathAbs(diff / valueB);
11926
+ }
11927
+ function roundToTwoDecimals(num) {
11928
+ return Math.round(num * 100) / 100;
11929
+ }
11930
+ function arrayKeys(array) {
11931
+ return objectKeys(array).map(Number);
11932
+ }
11933
+ function arrayLast(array) {
11934
+ return array[arrayLastIndex(array)];
11935
+ }
11936
+ function arrayLastIndex(array) {
11937
+ return Math.max(0, array.length - 1);
11938
+ }
11939
+ function arrayIsLastIndex(array, index) {
11940
+ return index === arrayLastIndex(array);
11941
+ }
11942
+ function arrayFromNumber(n, startAt = 0) {
11943
+ return Array.from(Array(n), (_, i)=>startAt + i);
11944
+ }
11945
+ function objectKeys(object) {
11946
+ return Object.keys(object);
11947
+ }
11948
+ function objectsMergeDeep(objectA, objectB) {
11949
+ return [
11950
+ objectA,
11951
+ objectB
11952
+ ].reduce((mergedObjects, currentObject)=>{
11953
+ objectKeys(currentObject).forEach((key)=>{
11954
+ const valueA = mergedObjects[key];
11955
+ const valueB = currentObject[key];
11956
+ const areObjects = isObject(valueA) && isObject(valueB);
11957
+ mergedObjects[key] = areObjects ? objectsMergeDeep(valueA, valueB) : valueB;
11958
+ });
11959
+ return mergedObjects;
11960
+ }, {});
11961
+ }
11962
+ function isMouseEvent(evt, ownerWindow) {
11963
+ return typeof ownerWindow.MouseEvent !== 'undefined' && evt instanceof ownerWindow.MouseEvent;
11964
+ }
11965
+ function Alignment(align, viewSize) {
11966
+ const predefined = {
11967
+ start,
11968
+ center,
11969
+ end
11970
+ };
11971
+ function start() {
11972
+ return 0;
11973
+ }
11974
+ function center(n) {
11975
+ return end(n) / 2;
11976
+ }
11977
+ function end(n) {
11978
+ return viewSize - n;
11979
+ }
11980
+ function measure(n, index) {
11981
+ if (isString(align)) return predefined[align](n);
11982
+ return align(viewSize, n, index);
11983
+ }
11984
+ const self = {
11985
+ measure
11986
+ };
11987
+ return self;
11988
+ }
11989
+ function EventStore() {
11990
+ let listeners = [];
11991
+ function add(node, type, handler, options = {
11992
+ passive: true
11993
+ }) {
11994
+ let removeListener;
11995
+ if ('addEventListener' in node) {
11996
+ node.addEventListener(type, handler, options);
11997
+ removeListener = ()=>node.removeEventListener(type, handler, options);
11998
+ } else {
11999
+ const legacyMediaQueryList = node;
12000
+ legacyMediaQueryList.addListener(handler);
12001
+ removeListener = ()=>legacyMediaQueryList.removeListener(handler);
12002
+ }
12003
+ listeners.push(removeListener);
12004
+ return self;
12005
+ }
12006
+ function clear() {
12007
+ listeners = listeners.filter((remove)=>remove());
12008
+ }
12009
+ const self = {
12010
+ add,
12011
+ clear
12012
+ };
12013
+ return self;
12014
+ }
12015
+ function Animations(ownerDocument, ownerWindow, update, render) {
12016
+ const documentVisibleHandler = EventStore();
12017
+ const fixedTimeStep = 1000 / 60;
12018
+ let lastTimeStamp = null;
12019
+ let accumulatedTime = 0;
12020
+ let animationId = 0;
12021
+ function init() {
12022
+ documentVisibleHandler.add(ownerDocument, 'visibilitychange', ()=>{
12023
+ if (ownerDocument.hidden) reset();
12024
+ });
12025
+ }
12026
+ function destroy() {
12027
+ stop();
12028
+ documentVisibleHandler.clear();
12029
+ }
12030
+ function animate(timeStamp) {
12031
+ if (!animationId) return;
12032
+ if (!lastTimeStamp) {
12033
+ lastTimeStamp = timeStamp;
12034
+ update();
12035
+ update();
12036
+ }
12037
+ const timeElapsed = timeStamp - lastTimeStamp;
12038
+ lastTimeStamp = timeStamp;
12039
+ accumulatedTime += timeElapsed;
12040
+ while(accumulatedTime >= fixedTimeStep){
12041
+ update();
12042
+ accumulatedTime -= fixedTimeStep;
12043
+ }
12044
+ const alpha = accumulatedTime / fixedTimeStep;
12045
+ render(alpha);
12046
+ if (animationId) {
12047
+ animationId = ownerWindow.requestAnimationFrame(animate);
12048
+ }
12049
+ }
12050
+ function start() {
12051
+ if (animationId) return;
12052
+ animationId = ownerWindow.requestAnimationFrame(animate);
12053
+ }
12054
+ function stop() {
12055
+ ownerWindow.cancelAnimationFrame(animationId);
12056
+ lastTimeStamp = null;
12057
+ accumulatedTime = 0;
12058
+ animationId = 0;
12059
+ }
12060
+ function reset() {
12061
+ lastTimeStamp = null;
12062
+ accumulatedTime = 0;
12063
+ }
12064
+ const self = {
12065
+ init,
12066
+ destroy,
12067
+ start,
12068
+ stop,
12069
+ update,
12070
+ render
12071
+ };
12072
+ return self;
12073
+ }
12074
+ function Axis(axis, contentDirection) {
12075
+ const isRightToLeft = contentDirection === 'rtl';
12076
+ const isVertical = axis === 'y';
12077
+ const scroll = isVertical ? 'y' : 'x';
12078
+ const cross = isVertical ? 'x' : 'y';
12079
+ const sign = !isVertical && isRightToLeft ? -1 : 1;
12080
+ const startEdge = getStartEdge();
12081
+ const endEdge = getEndEdge();
12082
+ function measureSize(nodeRect) {
12083
+ const { height, width } = nodeRect;
12084
+ return isVertical ? height : width;
12085
+ }
12086
+ function getStartEdge() {
12087
+ if (isVertical) return 'top';
12088
+ return isRightToLeft ? 'right' : 'left';
12089
+ }
12090
+ function getEndEdge() {
12091
+ if (isVertical) return 'bottom';
12092
+ return isRightToLeft ? 'left' : 'right';
12093
+ }
12094
+ function direction(n) {
12095
+ return n * sign;
12096
+ }
12097
+ const self = {
12098
+ scroll,
12099
+ cross,
12100
+ startEdge,
12101
+ endEdge,
12102
+ measureSize,
12103
+ direction
12104
+ };
12105
+ return self;
12106
+ }
12107
+ function Limit(min = 0, max = 0) {
12108
+ const length = mathAbs(min - max);
12109
+ function reachedMin(n) {
12110
+ return n < min;
12111
+ }
12112
+ function reachedMax(n) {
12113
+ return n > max;
12114
+ }
12115
+ function reachedAny(n) {
12116
+ return reachedMin(n) || reachedMax(n);
12117
+ }
12118
+ function constrain(n) {
12119
+ if (!reachedAny(n)) return n;
12120
+ return reachedMin(n) ? min : max;
12121
+ }
12122
+ function removeOffset(n) {
12123
+ if (!length) return n;
12124
+ return n - length * Math.ceil((n - max) / length);
12125
+ }
12126
+ const self = {
12127
+ length,
12128
+ max,
12129
+ min,
12130
+ constrain,
12131
+ reachedAny,
12132
+ reachedMax,
12133
+ reachedMin,
12134
+ removeOffset
12135
+ };
12136
+ return self;
12137
+ }
12138
+ function Counter(max, start, loop) {
12139
+ const { constrain } = Limit(0, max);
12140
+ const loopEnd = max + 1;
12141
+ let counter = withinLimit(start);
12142
+ function withinLimit(n) {
12143
+ return !loop ? constrain(n) : mathAbs((loopEnd + n) % loopEnd);
12144
+ }
12145
+ function get() {
12146
+ return counter;
12147
+ }
12148
+ function set(n) {
12149
+ counter = withinLimit(n);
12150
+ return self;
12151
+ }
12152
+ function add(n) {
12153
+ return clone().set(get() + n);
12154
+ }
12155
+ function clone() {
12156
+ return Counter(max, get(), loop);
12157
+ }
12158
+ const self = {
12159
+ get,
12160
+ set,
12161
+ add,
12162
+ clone
12163
+ };
12164
+ return self;
12165
+ }
12166
+ function DragHandler(axis, rootNode, ownerDocument, ownerWindow, target, dragTracker, location, animation, scrollTo, scrollBody, scrollTarget, index, eventHandler, percentOfView, dragFree, dragThreshold, skipSnaps, baseFriction, watchDrag) {
12167
+ const { cross: crossAxis, direction } = axis;
12168
+ const focusNodes = [
12169
+ 'INPUT',
12170
+ 'SELECT',
12171
+ 'TEXTAREA'
12172
+ ];
12173
+ const nonPassiveEvent = {
12174
+ passive: false
12175
+ };
12176
+ const initEvents = EventStore();
12177
+ const dragEvents = EventStore();
12178
+ const goToNextThreshold = Limit(50, 225).constrain(percentOfView.measure(20));
12179
+ const snapForceBoost = {
12180
+ mouse: 300,
12181
+ touch: 400
12182
+ };
12183
+ const freeForceBoost = {
12184
+ mouse: 500,
12185
+ touch: 600
12186
+ };
12187
+ const baseSpeed = dragFree ? 43 : 25;
12188
+ let isMoving = false;
12189
+ let startScroll = 0;
12190
+ let startCross = 0;
12191
+ let pointerIsDown = false;
12192
+ let preventScroll = false;
12193
+ let preventClick = false;
12194
+ let isMouse = false;
12195
+ function init(emblaApi) {
12196
+ if (!watchDrag) return;
12197
+ function downIfAllowed(evt) {
12198
+ if (isBoolean(watchDrag) || watchDrag(emblaApi, evt)) down(evt);
12199
+ }
12200
+ const node = rootNode;
12201
+ 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);
12202
+ }
12203
+ function destroy() {
12204
+ initEvents.clear();
12205
+ dragEvents.clear();
12206
+ }
12207
+ function addDragEvents() {
12208
+ const node = isMouse ? ownerDocument : rootNode;
12209
+ dragEvents.add(node, 'touchmove', move, nonPassiveEvent).add(node, 'touchend', up).add(node, 'mousemove', move, nonPassiveEvent).add(node, 'mouseup', up);
12210
+ }
12211
+ function isFocusNode(node) {
12212
+ const nodeName = node.nodeName || '';
12213
+ return focusNodes.includes(nodeName);
12214
+ }
12215
+ function forceBoost() {
12216
+ const boost = dragFree ? freeForceBoost : snapForceBoost;
12217
+ const type = isMouse ? 'mouse' : 'touch';
12218
+ return boost[type];
12219
+ }
12220
+ function allowedForce(force, targetChanged) {
12221
+ const next = index.add(mathSign(force) * -1);
12222
+ const baseForce = scrollTarget.byDistance(force, !dragFree).distance;
12223
+ if (dragFree || mathAbs(force) < goToNextThreshold) return baseForce;
12224
+ if (skipSnaps && targetChanged) return baseForce * 0.5;
12225
+ return scrollTarget.byIndex(next.get(), 0).distance;
12226
+ }
12227
+ function down(evt) {
12228
+ const isMouseEvt = isMouseEvent(evt, ownerWindow);
12229
+ isMouse = isMouseEvt;
12230
+ preventClick = dragFree && isMouseEvt && !evt.buttons && isMoving;
12231
+ isMoving = deltaAbs(target.get(), location.get()) >= 2;
12232
+ if (isMouseEvt && evt.button !== 0) return;
12233
+ if (isFocusNode(evt.target)) return;
12234
+ pointerIsDown = true;
12235
+ dragTracker.pointerDown(evt);
12236
+ scrollBody.useFriction(0).useDuration(0);
12237
+ target.set(location);
12238
+ addDragEvents();
12239
+ startScroll = dragTracker.readPoint(evt);
12240
+ startCross = dragTracker.readPoint(evt, crossAxis);
12241
+ eventHandler.emit('pointerDown');
12242
+ }
12243
+ function move(evt) {
12244
+ const isTouchEvt = !isMouseEvent(evt, ownerWindow);
12245
+ if (isTouchEvt && evt.touches.length >= 2) return up(evt);
12246
+ const lastScroll = dragTracker.readPoint(evt);
12247
+ const lastCross = dragTracker.readPoint(evt, crossAxis);
12248
+ const diffScroll = deltaAbs(lastScroll, startScroll);
12249
+ const diffCross = deltaAbs(lastCross, startCross);
12250
+ if (!preventScroll && !isMouse) {
12251
+ if (!evt.cancelable) return up(evt);
12252
+ preventScroll = diffScroll > diffCross;
12253
+ if (!preventScroll) return up(evt);
12254
+ }
12255
+ const diff = dragTracker.pointerMove(evt);
12256
+ if (diffScroll > dragThreshold) preventClick = true;
12257
+ scrollBody.useFriction(0.3).useDuration(0.75);
12258
+ animation.start();
12259
+ target.add(direction(diff));
12260
+ evt.preventDefault();
12261
+ }
12262
+ function up(evt) {
12263
+ const currentLocation = scrollTarget.byDistance(0, false);
12264
+ const targetChanged = currentLocation.index !== index.get();
12265
+ const rawForce = dragTracker.pointerUp(evt) * forceBoost();
12266
+ const force = allowedForce(direction(rawForce), targetChanged);
12267
+ const forceFactor = factorAbs(rawForce, force);
12268
+ const speed = baseSpeed - 10 * forceFactor;
12269
+ const friction = baseFriction + forceFactor / 50;
12270
+ preventScroll = false;
12271
+ pointerIsDown = false;
12272
+ dragEvents.clear();
12273
+ scrollBody.useDuration(speed).useFriction(friction);
12274
+ scrollTo.distance(force, !dragFree);
12275
+ isMouse = false;
12276
+ eventHandler.emit('pointerUp');
12277
+ }
12278
+ function click(evt) {
12279
+ if (preventClick) {
12280
+ evt.stopPropagation();
12281
+ evt.preventDefault();
12282
+ preventClick = false;
12283
+ }
12284
+ }
12285
+ function pointerDown() {
12286
+ return pointerIsDown;
12287
+ }
12288
+ const self = {
12289
+ init,
12290
+ destroy,
12291
+ pointerDown
12292
+ };
12293
+ return self;
12294
+ }
12295
+ function DragTracker(axis, ownerWindow) {
12296
+ const logInterval = 170;
12297
+ let startEvent;
12298
+ let lastEvent;
12299
+ function readTime(evt) {
12300
+ return evt.timeStamp;
12301
+ }
12302
+ function readPoint(evt, evtAxis) {
12303
+ const property = evtAxis || axis.scroll;
12304
+ const coord = `client${property === 'x' ? 'X' : 'Y'}`;
12305
+ return (isMouseEvent(evt, ownerWindow) ? evt : evt.touches[0])[coord];
12306
+ }
12307
+ function pointerDown(evt) {
12308
+ startEvent = evt;
12309
+ lastEvent = evt;
12310
+ return readPoint(evt);
12311
+ }
12312
+ function pointerMove(evt) {
12313
+ const diff = readPoint(evt) - readPoint(lastEvent);
12314
+ const expired = readTime(evt) - readTime(startEvent) > logInterval;
12315
+ lastEvent = evt;
12316
+ if (expired) startEvent = evt;
12317
+ return diff;
12318
+ }
12319
+ function pointerUp(evt) {
12320
+ if (!startEvent || !lastEvent) return 0;
12321
+ const diffDrag = readPoint(lastEvent) - readPoint(startEvent);
12322
+ const diffTime = readTime(evt) - readTime(startEvent);
12323
+ const expired = readTime(evt) - readTime(lastEvent) > logInterval;
12324
+ const force = diffDrag / diffTime;
12325
+ const isFlick = diffTime && !expired && mathAbs(force) > 0.1;
12326
+ return isFlick ? force : 0;
12327
+ }
12328
+ const self = {
12329
+ pointerDown,
12330
+ pointerMove,
12331
+ pointerUp,
12332
+ readPoint
12333
+ };
12334
+ return self;
12335
+ }
12336
+ function NodeRects() {
12337
+ function measure(node) {
12338
+ const { offsetTop, offsetLeft, offsetWidth, offsetHeight } = node;
12339
+ const offset = {
12340
+ top: offsetTop,
12341
+ right: offsetLeft + offsetWidth,
12342
+ bottom: offsetTop + offsetHeight,
12343
+ left: offsetLeft,
12344
+ width: offsetWidth,
12345
+ height: offsetHeight
12346
+ };
12347
+ return offset;
12348
+ }
12349
+ const self = {
12350
+ measure
12351
+ };
12352
+ return self;
12353
+ }
12354
+ function PercentOfView(viewSize) {
12355
+ function measure(n) {
12356
+ return viewSize * (n / 100);
12357
+ }
12358
+ const self = {
12359
+ measure
12360
+ };
12361
+ return self;
12362
+ }
12363
+ function ResizeHandler(container, eventHandler, ownerWindow, slides, axis, watchResize, nodeRects) {
12364
+ const observeNodes = [
12365
+ container
12366
+ ].concat(slides);
12367
+ let resizeObserver;
12368
+ let containerSize;
12369
+ let slideSizes = [];
12370
+ let destroyed = false;
12371
+ function readSize(node) {
12372
+ return axis.measureSize(nodeRects.measure(node));
12373
+ }
12374
+ function init(emblaApi) {
12375
+ if (!watchResize) return;
12376
+ containerSize = readSize(container);
12377
+ slideSizes = slides.map(readSize);
12378
+ function defaultCallback(entries) {
12379
+ for (const entry of entries){
12380
+ if (destroyed) return;
12381
+ const isContainer = entry.target === container;
12382
+ const slideIndex = slides.indexOf(entry.target);
12383
+ const lastSize = isContainer ? containerSize : slideSizes[slideIndex];
12384
+ const newSize = readSize(isContainer ? container : slides[slideIndex]);
12385
+ const diffSize = mathAbs(newSize - lastSize);
12386
+ if (diffSize >= 0.5) {
12387
+ emblaApi.reInit();
12388
+ eventHandler.emit('resize');
12389
+ break;
12390
+ }
12391
+ }
12392
+ }
12393
+ resizeObserver = new ResizeObserver((entries)=>{
12394
+ if (isBoolean(watchResize) || watchResize(emblaApi, entries)) {
12395
+ defaultCallback(entries);
12396
+ }
12397
+ });
12398
+ ownerWindow.requestAnimationFrame(()=>{
12399
+ observeNodes.forEach((node)=>resizeObserver.observe(node));
12400
+ });
12401
+ }
12402
+ function destroy() {
12403
+ destroyed = true;
12404
+ if (resizeObserver) resizeObserver.disconnect();
12405
+ }
12406
+ const self = {
12407
+ init,
12408
+ destroy
12409
+ };
12410
+ return self;
12411
+ }
12412
+ function ScrollBody(location, offsetLocation, previousLocation, target, baseDuration, baseFriction) {
12413
+ let scrollVelocity = 0;
12414
+ let scrollDirection = 0;
12415
+ let scrollDuration = baseDuration;
12416
+ let scrollFriction = baseFriction;
12417
+ let rawLocation = location.get();
12418
+ let rawLocationPrevious = 0;
12419
+ function seek() {
12420
+ const displacement = target.get() - location.get();
12421
+ const isInstant = !scrollDuration;
12422
+ let scrollDistance = 0;
12423
+ if (isInstant) {
12424
+ scrollVelocity = 0;
12425
+ previousLocation.set(target);
12426
+ location.set(target);
12427
+ scrollDistance = displacement;
12428
+ } else {
12429
+ previousLocation.set(location);
12430
+ scrollVelocity += displacement / scrollDuration;
12431
+ scrollVelocity *= scrollFriction;
12432
+ rawLocation += scrollVelocity;
12433
+ location.add(scrollVelocity);
12434
+ scrollDistance = rawLocation - rawLocationPrevious;
12435
+ }
12436
+ scrollDirection = mathSign(scrollDistance);
12437
+ rawLocationPrevious = rawLocation;
12438
+ return self;
12439
+ }
12440
+ function settled() {
12441
+ const diff = target.get() - offsetLocation.get();
12442
+ return mathAbs(diff) < 0.001;
12443
+ }
12444
+ function duration() {
12445
+ return scrollDuration;
12446
+ }
12447
+ function direction() {
12448
+ return scrollDirection;
12449
+ }
12450
+ function velocity() {
12451
+ return scrollVelocity;
12452
+ }
12453
+ function useBaseDuration() {
12454
+ return useDuration(baseDuration);
12455
+ }
12456
+ function useBaseFriction() {
12457
+ return useFriction(baseFriction);
12458
+ }
12459
+ function useDuration(n) {
12460
+ scrollDuration = n;
12461
+ return self;
12462
+ }
12463
+ function useFriction(n) {
12464
+ scrollFriction = n;
12465
+ return self;
12466
+ }
12467
+ const self = {
12468
+ direction,
12469
+ duration,
12470
+ velocity,
12471
+ seek,
12472
+ settled,
12473
+ useBaseFriction,
12474
+ useBaseDuration,
12475
+ useFriction,
12476
+ useDuration
12477
+ };
12478
+ return self;
12479
+ }
12480
+ function ScrollBounds(limit, location, target, scrollBody, percentOfView) {
12481
+ const pullBackThreshold = percentOfView.measure(10);
12482
+ const edgeOffsetTolerance = percentOfView.measure(50);
12483
+ const frictionLimit = Limit(0.1, 0.99);
12484
+ let disabled = false;
12485
+ function shouldConstrain() {
12486
+ if (disabled) return false;
12487
+ if (!limit.reachedAny(target.get())) return false;
12488
+ if (!limit.reachedAny(location.get())) return false;
12489
+ return true;
12490
+ }
12491
+ function constrain(pointerDown) {
12492
+ if (!shouldConstrain()) return;
12493
+ const edge = limit.reachedMin(location.get()) ? 'min' : 'max';
12494
+ const diffToEdge = mathAbs(limit[edge] - location.get());
12495
+ const diffToTarget = target.get() - location.get();
12496
+ const friction = frictionLimit.constrain(diffToEdge / edgeOffsetTolerance);
12497
+ target.subtract(diffToTarget * friction);
12498
+ if (!pointerDown && mathAbs(diffToTarget) < pullBackThreshold) {
12499
+ target.set(limit.constrain(target.get()));
12500
+ scrollBody.useDuration(25).useBaseFriction();
12501
+ }
12502
+ }
12503
+ function toggleActive(active) {
12504
+ disabled = !active;
12505
+ }
12506
+ const self = {
12507
+ shouldConstrain,
12508
+ constrain,
12509
+ toggleActive
12510
+ };
12511
+ return self;
12512
+ }
12513
+ function ScrollContain(viewSize, contentSize, snapsAligned, containScroll, pixelTolerance) {
12514
+ const scrollBounds = Limit(-contentSize + viewSize, 0);
12515
+ const snapsBounded = measureBounded();
12516
+ const scrollContainLimit = findScrollContainLimit();
12517
+ const snapsContained = measureContained();
12518
+ function usePixelTolerance(bound, snap) {
12519
+ return deltaAbs(bound, snap) <= 1;
12520
+ }
12521
+ function findScrollContainLimit() {
12522
+ const startSnap = snapsBounded[0];
12523
+ const endSnap = arrayLast(snapsBounded);
12524
+ const min = snapsBounded.lastIndexOf(startSnap);
12525
+ const max = snapsBounded.indexOf(endSnap) + 1;
12526
+ return Limit(min, max);
12527
+ }
12528
+ function measureBounded() {
12529
+ return snapsAligned.map((snapAligned, index)=>{
12530
+ const { min, max } = scrollBounds;
12531
+ const snap = scrollBounds.constrain(snapAligned);
12532
+ const isFirst = !index;
12533
+ const isLast = arrayIsLastIndex(snapsAligned, index);
12534
+ if (isFirst) return max;
12535
+ if (isLast) return min;
12536
+ if (usePixelTolerance(min, snap)) return min;
12537
+ if (usePixelTolerance(max, snap)) return max;
12538
+ return snap;
12539
+ }).map((scrollBound)=>parseFloat(scrollBound.toFixed(3)));
12540
+ }
12541
+ function measureContained() {
12542
+ if (contentSize <= viewSize + pixelTolerance) return [
12543
+ scrollBounds.max
12544
+ ];
12545
+ if (containScroll === 'keepSnaps') return snapsBounded;
12546
+ const { min, max } = scrollContainLimit;
12547
+ return snapsBounded.slice(min, max);
12548
+ }
12549
+ const self = {
12550
+ snapsContained,
12551
+ scrollContainLimit
12552
+ };
12553
+ return self;
12554
+ }
12555
+ function ScrollLimit(contentSize, scrollSnaps, loop) {
12556
+ const max = scrollSnaps[0];
12557
+ const min = loop ? max - contentSize : arrayLast(scrollSnaps);
12558
+ const limit = Limit(min, max);
12559
+ const self = {
12560
+ limit
12561
+ };
12562
+ return self;
12563
+ }
12564
+ function ScrollLooper(contentSize, limit, location, vectors) {
12565
+ const jointSafety = 0.1;
12566
+ const min = limit.min + jointSafety;
12567
+ const max = limit.max + jointSafety;
12568
+ const { reachedMin, reachedMax } = Limit(min, max);
12569
+ function shouldLoop(direction) {
12570
+ if (direction === 1) return reachedMax(location.get());
12571
+ if (direction === -1) return reachedMin(location.get());
12572
+ return false;
12573
+ }
12574
+ function loop(direction) {
12575
+ if (!shouldLoop(direction)) return;
12576
+ const loopDistance = contentSize * (direction * -1);
12577
+ vectors.forEach((v)=>v.add(loopDistance));
12578
+ }
12579
+ const self = {
12580
+ loop
12581
+ };
12582
+ return self;
12583
+ }
12584
+ function ScrollProgress(limit) {
12585
+ const { max, length } = limit;
12586
+ function get(n) {
12587
+ const currentLocation = n - max;
12588
+ return length ? currentLocation / -length : 0;
12589
+ }
12590
+ const self = {
12591
+ get
12592
+ };
12593
+ return self;
12594
+ }
12595
+ function ScrollSnaps(axis, alignment, containerRect, slideRects, slidesToScroll) {
12596
+ const { startEdge, endEdge } = axis;
12597
+ const { groupSlides } = slidesToScroll;
12598
+ const alignments = measureSizes().map(alignment.measure);
12599
+ const snaps = measureUnaligned();
12600
+ const snapsAligned = measureAligned();
12601
+ function measureSizes() {
12602
+ return groupSlides(slideRects).map((rects)=>arrayLast(rects)[endEdge] - rects[0][startEdge]).map(mathAbs);
12603
+ }
12604
+ function measureUnaligned() {
12605
+ return slideRects.map((rect)=>containerRect[startEdge] - rect[startEdge]).map((snap)=>-mathAbs(snap));
12606
+ }
12607
+ function measureAligned() {
12608
+ return groupSlides(snaps).map((g)=>g[0]).map((snap, index)=>snap + alignments[index]);
12609
+ }
12610
+ const self = {
12611
+ snaps,
12612
+ snapsAligned
12613
+ };
12614
+ return self;
12615
+ }
12616
+ function SlideRegistry(containSnaps, containScroll, scrollSnaps, scrollContainLimit, slidesToScroll, slideIndexes) {
12617
+ const { groupSlides } = slidesToScroll;
12618
+ const { min, max } = scrollContainLimit;
12619
+ const slideRegistry = createSlideRegistry();
12620
+ function createSlideRegistry() {
12621
+ const groupedSlideIndexes = groupSlides(slideIndexes);
12622
+ const doNotContain = !containSnaps || containScroll === 'keepSnaps';
12623
+ if (scrollSnaps.length === 1) return [
12624
+ slideIndexes
12625
+ ];
12626
+ if (doNotContain) return groupedSlideIndexes;
12627
+ return groupedSlideIndexes.slice(min, max).map((group, index, groups)=>{
12628
+ const isFirst = !index;
12629
+ const isLast = arrayIsLastIndex(groups, index);
12630
+ if (isFirst) {
12631
+ const range = arrayLast(groups[0]) + 1;
12632
+ return arrayFromNumber(range);
12633
+ }
12634
+ if (isLast) {
12635
+ const range = arrayLastIndex(slideIndexes) - arrayLast(groups)[0] + 1;
12636
+ return arrayFromNumber(range, arrayLast(groups)[0]);
12637
+ }
12638
+ return group;
12639
+ });
12640
+ }
12641
+ const self = {
12642
+ slideRegistry
12643
+ };
12644
+ return self;
12645
+ }
12646
+ function ScrollTarget(loop, scrollSnaps, contentSize, limit, targetVector) {
12647
+ const { reachedAny, removeOffset, constrain } = limit;
12648
+ function minDistance(distances) {
12649
+ return distances.concat().sort((a, b)=>mathAbs(a) - mathAbs(b))[0];
12650
+ }
12651
+ function findTargetSnap(target) {
12652
+ const distance = loop ? removeOffset(target) : constrain(target);
12653
+ const ascDiffsToSnaps = scrollSnaps.map((snap, index)=>({
12654
+ diff: shortcut(snap - distance, 0),
12655
+ index
12656
+ })).sort((d1, d2)=>mathAbs(d1.diff) - mathAbs(d2.diff));
12657
+ const { index } = ascDiffsToSnaps[0];
12658
+ return {
12659
+ index,
12660
+ distance
12661
+ };
12662
+ }
12663
+ function shortcut(target, direction) {
12664
+ const targets = [
12665
+ target,
12666
+ target + contentSize,
12667
+ target - contentSize
12668
+ ];
12669
+ if (!loop) return target;
12670
+ if (!direction) return minDistance(targets);
12671
+ const matchingTargets = targets.filter((t)=>mathSign(t) === direction);
12672
+ if (matchingTargets.length) return minDistance(matchingTargets);
12673
+ return arrayLast(targets) - contentSize;
12674
+ }
12675
+ function byIndex(index, direction) {
12676
+ const diffToSnap = scrollSnaps[index] - targetVector.get();
12677
+ const distance = shortcut(diffToSnap, direction);
12678
+ return {
12679
+ index,
12680
+ distance
12681
+ };
12682
+ }
12683
+ function byDistance(distance, snap) {
12684
+ const target = targetVector.get() + distance;
12685
+ const { index, distance: targetSnapDistance } = findTargetSnap(target);
12686
+ const reachedBound = !loop && reachedAny(target);
12687
+ if (!snap || reachedBound) return {
12688
+ index,
12689
+ distance
12690
+ };
12691
+ const diffToSnap = scrollSnaps[index] - targetSnapDistance;
12692
+ const snapDistance = distance + shortcut(diffToSnap, 0);
12693
+ return {
12694
+ index,
12695
+ distance: snapDistance
12696
+ };
12697
+ }
12698
+ const self = {
12699
+ byDistance,
12700
+ byIndex,
12701
+ shortcut
12702
+ };
12703
+ return self;
12704
+ }
12705
+ function ScrollTo(animation, indexCurrent, indexPrevious, scrollBody, scrollTarget, targetVector, eventHandler) {
12706
+ function scrollTo(target) {
12707
+ const distanceDiff = target.distance;
12708
+ const indexDiff = target.index !== indexCurrent.get();
12709
+ targetVector.add(distanceDiff);
12710
+ if (distanceDiff) {
12711
+ if (scrollBody.duration()) {
12712
+ animation.start();
12713
+ } else {
12714
+ animation.update();
12715
+ animation.render(1);
12716
+ animation.update();
12717
+ }
12718
+ }
12719
+ if (indexDiff) {
12720
+ indexPrevious.set(indexCurrent.get());
12721
+ indexCurrent.set(target.index);
12722
+ eventHandler.emit('select');
12723
+ }
12724
+ }
12725
+ function distance(n, snap) {
12726
+ const target = scrollTarget.byDistance(n, snap);
12727
+ scrollTo(target);
12728
+ }
12729
+ function index(n, direction) {
12730
+ const targetIndex = indexCurrent.clone().set(n);
12731
+ const target = scrollTarget.byIndex(targetIndex.get(), direction);
12732
+ scrollTo(target);
12733
+ }
12734
+ const self = {
12735
+ distance,
12736
+ index
12737
+ };
12738
+ return self;
12739
+ }
12740
+ function SlideFocus(root, slides, slideRegistry, scrollTo, scrollBody, eventStore, eventHandler, watchFocus) {
12741
+ const focusListenerOptions = {
12742
+ passive: true,
12743
+ capture: true
12744
+ };
12745
+ let lastTabPressTime = 0;
12746
+ function init(emblaApi) {
12747
+ if (!watchFocus) return;
12748
+ function defaultCallback(index) {
12749
+ const nowTime = new Date().getTime();
12750
+ const diffTime = nowTime - lastTabPressTime;
12751
+ if (diffTime > 10) return;
12752
+ eventHandler.emit('slideFocusStart');
12753
+ root.scrollLeft = 0;
12754
+ const group = slideRegistry.findIndex((group)=>group.includes(index));
12755
+ if (!isNumber(group)) return;
12756
+ scrollBody.useDuration(0);
12757
+ scrollTo.index(group, 0);
12758
+ eventHandler.emit('slideFocus');
12759
+ }
12760
+ eventStore.add(document, 'keydown', registerTabPress, false);
12761
+ slides.forEach((slide, slideIndex)=>{
12762
+ eventStore.add(slide, 'focus', (evt)=>{
12763
+ if (isBoolean(watchFocus) || watchFocus(emblaApi, evt)) {
12764
+ defaultCallback(slideIndex);
12765
+ }
12766
+ }, focusListenerOptions);
12767
+ });
12768
+ }
12769
+ function registerTabPress(event) {
12770
+ if (event.code === 'Tab') lastTabPressTime = new Date().getTime();
12771
+ }
12772
+ const self = {
12773
+ init
12774
+ };
12775
+ return self;
12776
+ }
12777
+ function Vector1D(initialValue) {
12778
+ let value = initialValue;
12779
+ function get() {
12780
+ return value;
12781
+ }
12782
+ function set(n) {
12783
+ value = normalizeInput(n);
12784
+ }
12785
+ function add(n) {
12786
+ value += normalizeInput(n);
12787
+ }
12788
+ function subtract(n) {
12789
+ value -= normalizeInput(n);
12790
+ }
12791
+ function normalizeInput(n) {
12792
+ return isNumber(n) ? n : n.get();
12793
+ }
12794
+ const self = {
12795
+ get,
12796
+ set,
12797
+ add,
12798
+ subtract
12799
+ };
12800
+ return self;
12801
+ }
12802
+ function Translate(axis, container) {
12803
+ const translate = axis.scroll === 'x' ? x : y;
12804
+ const containerStyle = container.style;
12805
+ let previousTarget = null;
12806
+ let disabled = false;
12807
+ function x(n) {
12808
+ return `translate3d(${n}px,0px,0px)`;
12809
+ }
12810
+ function y(n) {
12811
+ return `translate3d(0px,${n}px,0px)`;
12812
+ }
12813
+ function to(target) {
12814
+ if (disabled) return;
12815
+ const newTarget = roundToTwoDecimals(axis.direction(target));
12816
+ if (newTarget === previousTarget) return;
12817
+ containerStyle.transform = translate(newTarget);
12818
+ previousTarget = newTarget;
12819
+ }
12820
+ function toggleActive(active) {
12821
+ disabled = !active;
12822
+ }
12823
+ function clear() {
12824
+ if (disabled) return;
12825
+ containerStyle.transform = '';
12826
+ if (!container.getAttribute('style')) container.removeAttribute('style');
12827
+ }
12828
+ const self = {
12829
+ clear,
12830
+ to,
12831
+ toggleActive
12832
+ };
12833
+ return self;
12834
+ }
12835
+ function SlideLooper(axis, viewSize, contentSize, slideSizes, slideSizesWithGaps, snaps, scrollSnaps, location, slides) {
12836
+ const roundingSafety = 0.5;
12837
+ const ascItems = arrayKeys(slideSizesWithGaps);
12838
+ const descItems = arrayKeys(slideSizesWithGaps).reverse();
12839
+ const loopPoints = startPoints().concat(endPoints());
12840
+ function removeSlideSizes(indexes, from) {
12841
+ return indexes.reduce((a, i)=>{
12842
+ return a - slideSizesWithGaps[i];
12843
+ }, from);
12844
+ }
12845
+ function slidesInGap(indexes, gap) {
12846
+ return indexes.reduce((a, i)=>{
12847
+ const remainingGap = removeSlideSizes(a, gap);
12848
+ return remainingGap > 0 ? a.concat([
12849
+ i
12850
+ ]) : a;
12851
+ }, []);
12852
+ }
12853
+ function findSlideBounds(offset) {
12854
+ return snaps.map((snap, index)=>({
12855
+ start: snap - slideSizes[index] + roundingSafety + offset,
12856
+ end: snap + viewSize - roundingSafety + offset
12857
+ }));
12858
+ }
12859
+ function findLoopPoints(indexes, offset, isEndEdge) {
12860
+ const slideBounds = findSlideBounds(offset);
12861
+ return indexes.map((index)=>{
12862
+ const initial = isEndEdge ? 0 : -contentSize;
12863
+ const altered = isEndEdge ? contentSize : 0;
12864
+ const boundEdge = isEndEdge ? 'end' : 'start';
12865
+ const loopPoint = slideBounds[index][boundEdge];
12866
+ return {
12867
+ index,
12868
+ loopPoint,
12869
+ slideLocation: Vector1D(-1),
12870
+ translate: Translate(axis, slides[index]),
12871
+ target: ()=>location.get() > loopPoint ? initial : altered
12872
+ };
12873
+ });
12874
+ }
12875
+ function startPoints() {
12876
+ const gap = scrollSnaps[0];
12877
+ const indexes = slidesInGap(descItems, gap);
12878
+ return findLoopPoints(indexes, contentSize, false);
12879
+ }
12880
+ function endPoints() {
12881
+ const gap = viewSize - scrollSnaps[0] - 1;
12882
+ const indexes = slidesInGap(ascItems, gap);
12883
+ return findLoopPoints(indexes, -contentSize, true);
12884
+ }
12885
+ function canLoop() {
12886
+ return loopPoints.every(({ index })=>{
12887
+ const otherIndexes = ascItems.filter((i)=>i !== index);
12888
+ return removeSlideSizes(otherIndexes, viewSize) <= 0.1;
12889
+ });
12890
+ }
12891
+ function loop() {
12892
+ loopPoints.forEach((loopPoint)=>{
12893
+ const { target, translate, slideLocation } = loopPoint;
12894
+ const shiftLocation = target();
12895
+ if (shiftLocation === slideLocation.get()) return;
12896
+ translate.to(shiftLocation);
12897
+ slideLocation.set(shiftLocation);
12898
+ });
12899
+ }
12900
+ function clear() {
12901
+ loopPoints.forEach((loopPoint)=>loopPoint.translate.clear());
12902
+ }
12903
+ const self = {
12904
+ canLoop,
12905
+ clear,
12906
+ loop,
12907
+ loopPoints
12908
+ };
12909
+ return self;
12910
+ }
12911
+ function SlidesHandler(container, eventHandler, watchSlides) {
12912
+ let mutationObserver;
12913
+ let destroyed = false;
12914
+ function init(emblaApi) {
12915
+ if (!watchSlides) return;
12916
+ function defaultCallback(mutations) {
12917
+ for (const mutation of mutations){
12918
+ if (mutation.type === 'childList') {
12919
+ emblaApi.reInit();
12920
+ eventHandler.emit('slidesChanged');
12921
+ break;
12922
+ }
12923
+ }
12924
+ }
12925
+ mutationObserver = new MutationObserver((mutations)=>{
12926
+ if (destroyed) return;
12927
+ if (isBoolean(watchSlides) || watchSlides(emblaApi, mutations)) {
12928
+ defaultCallback(mutations);
12929
+ }
12930
+ });
12931
+ mutationObserver.observe(container, {
12932
+ childList: true
12933
+ });
12934
+ }
12935
+ function destroy() {
12936
+ if (mutationObserver) mutationObserver.disconnect();
12937
+ destroyed = true;
12938
+ }
12939
+ const self = {
12940
+ init,
12941
+ destroy
12942
+ };
12943
+ return self;
12944
+ }
12945
+ function SlidesInView(container, slides, eventHandler, threshold) {
12946
+ const intersectionEntryMap = {};
12947
+ let inViewCache = null;
12948
+ let notInViewCache = null;
12949
+ let intersectionObserver;
12950
+ let destroyed = false;
12951
+ function init() {
12952
+ intersectionObserver = new IntersectionObserver((entries)=>{
12953
+ if (destroyed) return;
12954
+ entries.forEach((entry)=>{
12955
+ const index = slides.indexOf(entry.target);
12956
+ intersectionEntryMap[index] = entry;
12957
+ });
12958
+ inViewCache = null;
12959
+ notInViewCache = null;
12960
+ eventHandler.emit('slidesInView');
12961
+ }, {
12962
+ root: container.parentElement,
12963
+ threshold
12964
+ });
12965
+ slides.forEach((slide)=>intersectionObserver.observe(slide));
12966
+ }
12967
+ function destroy() {
12968
+ if (intersectionObserver) intersectionObserver.disconnect();
12969
+ destroyed = true;
12970
+ }
12971
+ function createInViewList(inView) {
12972
+ return objectKeys(intersectionEntryMap).reduce((list, slideIndex)=>{
12973
+ const index = parseInt(slideIndex);
12974
+ const { isIntersecting } = intersectionEntryMap[index];
12975
+ const inViewMatch = inView && isIntersecting;
12976
+ const notInViewMatch = !inView && !isIntersecting;
12977
+ if (inViewMatch || notInViewMatch) list.push(index);
12978
+ return list;
12979
+ }, []);
12980
+ }
12981
+ function get(inView = true) {
12982
+ if (inView && inViewCache) return inViewCache;
12983
+ if (!inView && notInViewCache) return notInViewCache;
12984
+ const slideIndexes = createInViewList(inView);
12985
+ if (inView) inViewCache = slideIndexes;
12986
+ if (!inView) notInViewCache = slideIndexes;
12987
+ return slideIndexes;
12988
+ }
12989
+ const self = {
12990
+ init,
12991
+ destroy,
12992
+ get
12993
+ };
12994
+ return self;
12995
+ }
12996
+ function SlideSizes(axis, containerRect, slideRects, slides, readEdgeGap, ownerWindow) {
12997
+ const { measureSize, startEdge, endEdge } = axis;
12998
+ const withEdgeGap = slideRects[0] && readEdgeGap;
12999
+ const startGap = measureStartGap();
13000
+ const endGap = measureEndGap();
13001
+ const slideSizes = slideRects.map(measureSize);
13002
+ const slideSizesWithGaps = measureWithGaps();
13003
+ function measureStartGap() {
13004
+ if (!withEdgeGap) return 0;
13005
+ const slideRect = slideRects[0];
13006
+ return mathAbs(containerRect[startEdge] - slideRect[startEdge]);
13007
+ }
13008
+ function measureEndGap() {
13009
+ if (!withEdgeGap) return 0;
13010
+ const style = ownerWindow.getComputedStyle(arrayLast(slides));
13011
+ return parseFloat(style.getPropertyValue(`margin-${endEdge}`));
13012
+ }
13013
+ function measureWithGaps() {
13014
+ return slideRects.map((rect, index, rects)=>{
13015
+ const isFirst = !index;
13016
+ const isLast = arrayIsLastIndex(rects, index);
13017
+ if (isFirst) return slideSizes[index] + startGap;
13018
+ if (isLast) return slideSizes[index] + endGap;
13019
+ return rects[index + 1][startEdge] - rect[startEdge];
13020
+ }).map(mathAbs);
13021
+ }
13022
+ const self = {
13023
+ slideSizes,
13024
+ slideSizesWithGaps,
13025
+ startGap,
13026
+ endGap
13027
+ };
13028
+ return self;
13029
+ }
13030
+ function SlidesToScroll(axis, viewSize, slidesToScroll, loop, containerRect, slideRects, startGap, endGap, pixelTolerance) {
13031
+ const { startEdge, endEdge, direction } = axis;
13032
+ const groupByNumber = isNumber(slidesToScroll);
13033
+ function byNumber(array, groupSize) {
13034
+ return arrayKeys(array).filter((i)=>i % groupSize === 0).map((i)=>array.slice(i, i + groupSize));
13035
+ }
13036
+ function bySize(array) {
13037
+ if (!array.length) return [];
13038
+ return arrayKeys(array).reduce((groups, rectB, index)=>{
13039
+ const rectA = arrayLast(groups) || 0;
13040
+ const isFirst = rectA === 0;
13041
+ const isLast = rectB === arrayLastIndex(array);
13042
+ const edgeA = containerRect[startEdge] - slideRects[rectA][startEdge];
13043
+ const edgeB = containerRect[startEdge] - slideRects[rectB][endEdge];
13044
+ const gapA = !loop && isFirst ? direction(startGap) : 0;
13045
+ const gapB = !loop && isLast ? direction(endGap) : 0;
13046
+ const chunkSize = mathAbs(edgeB - gapB - (edgeA + gapA));
13047
+ if (index && chunkSize > viewSize + pixelTolerance) groups.push(rectB);
13048
+ if (isLast) groups.push(array.length);
13049
+ return groups;
13050
+ }, []).map((currentSize, index, groups)=>{
13051
+ const previousSize = Math.max(groups[index - 1] || 0);
13052
+ return array.slice(previousSize, currentSize);
13053
+ });
13054
+ }
13055
+ function groupSlides(array) {
13056
+ return groupByNumber ? byNumber(array, slidesToScroll) : bySize(array);
13057
+ }
13058
+ const self = {
13059
+ groupSlides
13060
+ };
13061
+ return self;
13062
+ }
13063
+ function Engine(root, container, slides, ownerDocument, ownerWindow, options, eventHandler) {
13064
+ // Options
13065
+ const { align, axis: scrollAxis, direction, startIndex, loop, duration, dragFree, dragThreshold, inViewThreshold, slidesToScroll: groupSlides, skipSnaps, containScroll, watchResize, watchSlides, watchDrag, watchFocus } = options;
13066
+ // Measurements
13067
+ const pixelTolerance = 2;
13068
+ const nodeRects = NodeRects();
13069
+ const containerRect = nodeRects.measure(container);
13070
+ const slideRects = slides.map(nodeRects.measure);
13071
+ const axis = Axis(scrollAxis, direction);
13072
+ const viewSize = axis.measureSize(containerRect);
13073
+ const percentOfView = PercentOfView(viewSize);
13074
+ const alignment = Alignment(align, viewSize);
13075
+ const containSnaps = !loop && !!containScroll;
13076
+ const readEdgeGap = loop || !!containScroll;
13077
+ const { slideSizes, slideSizesWithGaps, startGap, endGap } = SlideSizes(axis, containerRect, slideRects, slides, readEdgeGap, ownerWindow);
13078
+ const slidesToScroll = SlidesToScroll(axis, viewSize, groupSlides, loop, containerRect, slideRects, startGap, endGap, pixelTolerance);
13079
+ const { snaps, snapsAligned } = ScrollSnaps(axis, alignment, containerRect, slideRects, slidesToScroll);
13080
+ const contentSize = -arrayLast(snaps) + arrayLast(slideSizesWithGaps);
13081
+ const { snapsContained, scrollContainLimit } = ScrollContain(viewSize, contentSize, snapsAligned, containScroll, pixelTolerance);
13082
+ const scrollSnaps = containSnaps ? snapsContained : snapsAligned;
13083
+ const { limit } = ScrollLimit(contentSize, scrollSnaps, loop);
13084
+ // Indexes
13085
+ const index = Counter(arrayLastIndex(scrollSnaps), startIndex, loop);
13086
+ const indexPrevious = index.clone();
13087
+ const slideIndexes = arrayKeys(slides);
13088
+ // Animation
13089
+ const update = ({ dragHandler, scrollBody, scrollBounds, options: { loop } })=>{
13090
+ if (!loop) scrollBounds.constrain(dragHandler.pointerDown());
13091
+ scrollBody.seek();
13092
+ };
13093
+ const render = ({ scrollBody, translate, location, offsetLocation, previousLocation, scrollLooper, slideLooper, dragHandler, animation, eventHandler, scrollBounds, options: { loop } }, alpha)=>{
13094
+ const shouldSettle = scrollBody.settled();
13095
+ const withinBounds = !scrollBounds.shouldConstrain();
13096
+ const hasSettled = loop ? shouldSettle : shouldSettle && withinBounds;
13097
+ const hasSettledAndIdle = hasSettled && !dragHandler.pointerDown();
13098
+ if (hasSettledAndIdle) animation.stop();
13099
+ const interpolatedLocation = location.get() * alpha + previousLocation.get() * (1 - alpha);
13100
+ offsetLocation.set(interpolatedLocation);
13101
+ if (loop) {
13102
+ scrollLooper.loop(scrollBody.direction());
13103
+ slideLooper.loop();
13104
+ }
13105
+ translate.to(offsetLocation.get());
13106
+ if (hasSettledAndIdle) eventHandler.emit('settle');
13107
+ if (!hasSettled) eventHandler.emit('scroll');
13108
+ };
13109
+ const animation = Animations(ownerDocument, ownerWindow, ()=>update(engine), (alpha)=>render(engine, alpha));
13110
+ // Shared
13111
+ const friction = 0.68;
13112
+ const startLocation = scrollSnaps[index.get()];
13113
+ const location = Vector1D(startLocation);
13114
+ const previousLocation = Vector1D(startLocation);
13115
+ const offsetLocation = Vector1D(startLocation);
13116
+ const target = Vector1D(startLocation);
13117
+ const scrollBody = ScrollBody(location, offsetLocation, previousLocation, target, duration, friction);
13118
+ const scrollTarget = ScrollTarget(loop, scrollSnaps, contentSize, limit, target);
13119
+ const scrollTo = ScrollTo(animation, index, indexPrevious, scrollBody, scrollTarget, target, eventHandler);
13120
+ const scrollProgress = ScrollProgress(limit);
13121
+ const eventStore = EventStore();
13122
+ const slidesInView = SlidesInView(container, slides, eventHandler, inViewThreshold);
13123
+ const { slideRegistry } = SlideRegistry(containSnaps, containScroll, scrollSnaps, scrollContainLimit, slidesToScroll, slideIndexes);
13124
+ const slideFocus = SlideFocus(root, slides, slideRegistry, scrollTo, scrollBody, eventStore, eventHandler, watchFocus);
13125
+ // Engine
13126
+ const engine = {
13127
+ ownerDocument,
13128
+ ownerWindow,
13129
+ eventHandler,
13130
+ containerRect,
13131
+ slideRects,
13132
+ animation,
13133
+ axis,
13134
+ dragHandler: DragHandler(axis, root, ownerDocument, ownerWindow, target, DragTracker(axis, ownerWindow), location, animation, scrollTo, scrollBody, scrollTarget, index, eventHandler, percentOfView, dragFree, dragThreshold, skipSnaps, friction, watchDrag),
13135
+ eventStore,
13136
+ percentOfView,
13137
+ index,
13138
+ indexPrevious,
13139
+ limit,
13140
+ location,
13141
+ offsetLocation,
13142
+ previousLocation,
13143
+ options,
13144
+ resizeHandler: ResizeHandler(container, eventHandler, ownerWindow, slides, axis, watchResize, nodeRects),
13145
+ scrollBody,
13146
+ scrollBounds: ScrollBounds(limit, offsetLocation, target, scrollBody, percentOfView),
13147
+ scrollLooper: ScrollLooper(contentSize, limit, offsetLocation, [
13148
+ location,
13149
+ offsetLocation,
13150
+ previousLocation,
13151
+ target
13152
+ ]),
13153
+ scrollProgress,
13154
+ scrollSnapList: scrollSnaps.map(scrollProgress.get),
13155
+ scrollSnaps,
13156
+ scrollTarget,
13157
+ scrollTo,
13158
+ slideLooper: SlideLooper(axis, viewSize, contentSize, slideSizes, slideSizesWithGaps, snaps, scrollSnaps, offsetLocation, slides),
13159
+ slideFocus,
13160
+ slidesHandler: SlidesHandler(container, eventHandler, watchSlides),
13161
+ slidesInView,
13162
+ slideIndexes,
13163
+ slideRegistry,
13164
+ slidesToScroll,
13165
+ target,
13166
+ translate: Translate(axis, container)
13167
+ };
13168
+ return engine;
13169
+ }
13170
+ function EventHandler() {
13171
+ let listeners = {};
13172
+ let api;
13173
+ function init(emblaApi) {
13174
+ api = emblaApi;
13175
+ }
13176
+ function getListeners(evt) {
13177
+ return listeners[evt] || [];
13178
+ }
13179
+ function emit(evt) {
13180
+ getListeners(evt).forEach((e)=>e(api, evt));
13181
+ return self;
13182
+ }
13183
+ function on(evt, cb) {
13184
+ listeners[evt] = getListeners(evt).concat([
13185
+ cb
13186
+ ]);
13187
+ return self;
13188
+ }
13189
+ function off(evt, cb) {
13190
+ listeners[evt] = getListeners(evt).filter((e)=>e !== cb);
13191
+ return self;
13192
+ }
13193
+ function clear() {
13194
+ listeners = {};
13195
+ }
13196
+ const self = {
13197
+ init,
13198
+ emit,
13199
+ off,
13200
+ on,
13201
+ clear
13202
+ };
13203
+ return self;
13204
+ }
13205
+ const defaultOptions = {
13206
+ align: 'center',
13207
+ axis: 'x',
13208
+ container: null,
13209
+ slides: null,
13210
+ containScroll: 'trimSnaps',
13211
+ direction: 'ltr',
13212
+ slidesToScroll: 1,
13213
+ inViewThreshold: 0,
13214
+ breakpoints: {},
13215
+ dragFree: false,
13216
+ dragThreshold: 10,
13217
+ loop: false,
13218
+ skipSnaps: false,
13219
+ duration: 25,
13220
+ startIndex: 0,
13221
+ active: true,
13222
+ watchDrag: true,
13223
+ watchResize: true,
13224
+ watchSlides: true,
13225
+ watchFocus: true
13226
+ };
13227
+ function OptionsHandler(ownerWindow) {
13228
+ function mergeOptions(optionsA, optionsB) {
13229
+ return objectsMergeDeep(optionsA, optionsB || {});
13230
+ }
13231
+ function optionsAtMedia(options) {
13232
+ const optionsAtMedia = options.breakpoints || {};
13233
+ const matchedMediaOptions = objectKeys(optionsAtMedia).filter((media)=>ownerWindow.matchMedia(media).matches).map((media)=>optionsAtMedia[media]).reduce((a, mediaOption)=>mergeOptions(a, mediaOption), {});
13234
+ return mergeOptions(options, matchedMediaOptions);
13235
+ }
13236
+ function optionsMediaQueries(optionsList) {
13237
+ return optionsList.map((options)=>objectKeys(options.breakpoints || {})).reduce((acc, mediaQueries)=>acc.concat(mediaQueries), []).map(ownerWindow.matchMedia);
13238
+ }
13239
+ const self = {
13240
+ mergeOptions,
13241
+ optionsAtMedia,
13242
+ optionsMediaQueries
13243
+ };
13244
+ return self;
13245
+ }
13246
+ function PluginsHandler(optionsHandler) {
13247
+ let activePlugins = [];
13248
+ function init(emblaApi, plugins) {
13249
+ activePlugins = plugins.filter(({ options })=>optionsHandler.optionsAtMedia(options).active !== false);
13250
+ activePlugins.forEach((plugin)=>plugin.init(emblaApi, optionsHandler));
13251
+ return plugins.reduce((map, plugin)=>Object.assign(map, {
13252
+ [plugin.name]: plugin
13253
+ }), {});
13254
+ }
13255
+ function destroy() {
13256
+ activePlugins = activePlugins.filter((plugin)=>plugin.destroy());
13257
+ }
13258
+ const self = {
13259
+ init,
13260
+ destroy
13261
+ };
13262
+ return self;
13263
+ }
13264
+ function EmblaCarousel(root, userOptions, userPlugins) {
13265
+ const ownerDocument = root.ownerDocument;
13266
+ const ownerWindow = ownerDocument.defaultView;
13267
+ const optionsHandler = OptionsHandler(ownerWindow);
13268
+ const pluginsHandler = PluginsHandler(optionsHandler);
13269
+ const mediaHandlers = EventStore();
13270
+ const eventHandler = EventHandler();
13271
+ const { mergeOptions, optionsAtMedia, optionsMediaQueries } = optionsHandler;
13272
+ const { on, off, emit } = eventHandler;
13273
+ const reInit = reActivate;
13274
+ let destroyed = false;
13275
+ let engine;
13276
+ let optionsBase = mergeOptions(defaultOptions, EmblaCarousel.globalOptions);
13277
+ let options = mergeOptions(optionsBase);
13278
+ let pluginList = [];
13279
+ let pluginApis;
13280
+ let container;
13281
+ let slides;
13282
+ function storeElements() {
13283
+ const { container: userContainer, slides: userSlides } = options;
13284
+ const customContainer = isString(userContainer) ? root.querySelector(userContainer) : userContainer;
13285
+ container = customContainer || root.children[0];
13286
+ const customSlides = isString(userSlides) ? container.querySelectorAll(userSlides) : userSlides;
13287
+ slides = [].slice.call(customSlides || container.children);
13288
+ }
13289
+ function createEngine(options) {
13290
+ const engine = Engine(root, container, slides, ownerDocument, ownerWindow, options, eventHandler);
13291
+ if (options.loop && !engine.slideLooper.canLoop()) {
13292
+ const optionsWithoutLoop = Object.assign({}, options, {
13293
+ loop: false
13294
+ });
13295
+ return createEngine(optionsWithoutLoop);
13296
+ }
13297
+ return engine;
13298
+ }
13299
+ function activate(withOptions, withPlugins) {
13300
+ if (destroyed) return;
13301
+ optionsBase = mergeOptions(optionsBase, withOptions);
13302
+ options = optionsAtMedia(optionsBase);
13303
+ pluginList = withPlugins || pluginList;
13304
+ storeElements();
13305
+ engine = createEngine(options);
13306
+ optionsMediaQueries([
13307
+ optionsBase,
13308
+ ...pluginList.map(({ options })=>options)
13309
+ ]).forEach((query)=>mediaHandlers.add(query, 'change', reActivate));
13310
+ if (!options.active) return;
13311
+ engine.translate.to(engine.location.get());
13312
+ engine.animation.init();
13313
+ engine.slidesInView.init();
13314
+ engine.slideFocus.init(self);
13315
+ engine.eventHandler.init(self);
13316
+ engine.resizeHandler.init(self);
13317
+ engine.slidesHandler.init(self);
13318
+ if (engine.options.loop) engine.slideLooper.loop();
13319
+ if (container.offsetParent && slides.length) engine.dragHandler.init(self);
13320
+ pluginApis = pluginsHandler.init(self, pluginList);
13321
+ }
13322
+ function reActivate(withOptions, withPlugins) {
13323
+ const startIndex = selectedScrollSnap();
13324
+ deActivate();
13325
+ activate(mergeOptions({
13326
+ startIndex
13327
+ }, withOptions), withPlugins);
13328
+ eventHandler.emit('reInit');
13329
+ }
13330
+ function deActivate() {
13331
+ engine.dragHandler.destroy();
13332
+ engine.eventStore.clear();
13333
+ engine.translate.clear();
13334
+ engine.slideLooper.clear();
13335
+ engine.resizeHandler.destroy();
13336
+ engine.slidesHandler.destroy();
13337
+ engine.slidesInView.destroy();
13338
+ engine.animation.destroy();
13339
+ pluginsHandler.destroy();
13340
+ mediaHandlers.clear();
13341
+ }
13342
+ function destroy() {
13343
+ if (destroyed) return;
13344
+ destroyed = true;
13345
+ mediaHandlers.clear();
13346
+ deActivate();
13347
+ eventHandler.emit('destroy');
13348
+ eventHandler.clear();
13349
+ }
13350
+ function scrollTo(index, jump, direction) {
13351
+ if (!options.active || destroyed) return;
13352
+ engine.scrollBody.useBaseFriction().useDuration(jump === true ? 0 : options.duration);
13353
+ engine.scrollTo.index(index, direction || 0);
13354
+ }
13355
+ function scrollNext(jump) {
13356
+ const next = engine.index.add(1).get();
13357
+ scrollTo(next, jump, -1);
13358
+ }
13359
+ function scrollPrev(jump) {
13360
+ const prev = engine.index.add(-1).get();
13361
+ scrollTo(prev, jump, 1);
13362
+ }
13363
+ function canScrollNext() {
13364
+ const next = engine.index.add(1).get();
13365
+ return next !== selectedScrollSnap();
13366
+ }
13367
+ function canScrollPrev() {
13368
+ const prev = engine.index.add(-1).get();
13369
+ return prev !== selectedScrollSnap();
13370
+ }
13371
+ function scrollSnapList() {
13372
+ return engine.scrollSnapList;
13373
+ }
13374
+ function scrollProgress() {
13375
+ return engine.scrollProgress.get(engine.offsetLocation.get());
13376
+ }
13377
+ function selectedScrollSnap() {
13378
+ return engine.index.get();
13379
+ }
13380
+ function previousScrollSnap() {
13381
+ return engine.indexPrevious.get();
13382
+ }
13383
+ function slidesInView() {
13384
+ return engine.slidesInView.get();
13385
+ }
13386
+ function slidesNotInView() {
13387
+ return engine.slidesInView.get(false);
13388
+ }
13389
+ function plugins() {
13390
+ return pluginApis;
13391
+ }
13392
+ function internalEngine() {
13393
+ return engine;
13394
+ }
13395
+ function rootNode() {
13396
+ return root;
13397
+ }
13398
+ function containerNode() {
13399
+ return container;
13400
+ }
13401
+ function slideNodes() {
13402
+ return slides;
13403
+ }
13404
+ const self = {
13405
+ canScrollNext,
13406
+ canScrollPrev,
13407
+ containerNode,
13408
+ internalEngine,
13409
+ destroy,
13410
+ off,
13411
+ on,
13412
+ emit,
13413
+ plugins,
13414
+ previousScrollSnap,
13415
+ reInit,
13416
+ rootNode,
13417
+ scrollNext,
13418
+ scrollPrev,
13419
+ scrollProgress,
13420
+ scrollSnapList,
13421
+ scrollTo,
13422
+ selectedScrollSnap,
13423
+ slideNodes,
13424
+ slidesInView,
13425
+ slidesNotInView
13426
+ };
13427
+ activate(userOptions, userPlugins);
13428
+ setTimeout(()=>eventHandler.emit('init'), 0);
13429
+ return self;
13430
+ }
13431
+ EmblaCarousel.globalOptions = undefined;
13432
+
13433
+ function useEmblaCarousel(options = {}, plugins = []) {
13434
+ const storedOptions = useRef(options);
13435
+ const storedPlugins = useRef(plugins);
13436
+ const [emblaApi, setEmblaApi] = useState();
13437
+ const [viewport, setViewport] = useState();
13438
+ const reInit = useCallback(()=>{
13439
+ if (emblaApi) emblaApi.reInit(storedOptions.current, storedPlugins.current);
13440
+ }, [
13441
+ emblaApi
13442
+ ]);
13443
+ useEffect(()=>{
13444
+ if (areOptionsEqual(storedOptions.current, options)) return;
13445
+ storedOptions.current = options;
13446
+ reInit();
13447
+ }, [
13448
+ options,
13449
+ reInit
13450
+ ]);
13451
+ useEffect(()=>{
13452
+ if (arePluginsEqual(storedPlugins.current, plugins)) return;
13453
+ storedPlugins.current = plugins;
13454
+ reInit();
13455
+ }, [
13456
+ plugins,
13457
+ reInit
13458
+ ]);
13459
+ useEffect(()=>{
13460
+ if (canUseDOM() && viewport) {
13461
+ EmblaCarousel.globalOptions = useEmblaCarousel.globalOptions;
13462
+ const newEmblaApi = EmblaCarousel(viewport, storedOptions.current, storedPlugins.current);
13463
+ setEmblaApi(newEmblaApi);
13464
+ return ()=>newEmblaApi.destroy();
13465
+ } else {
13466
+ setEmblaApi(undefined);
13467
+ }
13468
+ }, [
13469
+ viewport,
13470
+ setEmblaApi
13471
+ ]);
13472
+ return [
13473
+ setViewport,
13474
+ emblaApi
13475
+ ];
13476
+ }
13477
+ useEmblaCarousel.globalOptions = undefined;
13478
+
13479
+ function useRenderStrategy(props) {
13480
+ const wasEverPresent = useRef(false);
13481
+ if (props.present) {
13482
+ wasEverPresent.current = true;
13483
+ }
13484
+ return {
13485
+ unmounted: !props.present && !wasEverPresent.current && props.lazyMount || props.unmountOnExit && !props.present && wasEverPresent.current
13486
+ };
13487
+ }
13488
+ const RenderStrategyPropsContext = /*#__PURE__*/ createContext(null);
13489
+ const RenderStrategyPropsProvider = RenderStrategyPropsContext.Provider;
13490
+ function useRenderStrategyPropsContext({ strict = true } = {}) {
13491
+ const context = useContext(RenderStrategyPropsContext);
13492
+ if (!context && strict) {
13493
+ throw new Error("useRenderStrategyPropsContext must be used within a RenderStrategyPropsProvider");
13494
+ }
13495
+ return context;
13496
+ }
13497
+ /* ID -----------------------------------------------------------------------
13498
+ -------------------------------------------------------------------------------- */ const getTriggerId = (value, id)=>`tabs:${value}:${id}:trigger-root`;
13499
+ const getListId = (id)=>`tabs:${id}:list`;
13500
+ const getContentId = (value, id)=>`tabs:${value}:${id}:content`;
13501
+ /* Query -----------------------------------------------------------------------
13502
+ -------------------------------------------------------------------------------- */ const getTriggerByValue = (listEl, value)=>{
13503
+ return listEl.querySelector(`[data-value="${value}"]`);
13504
+ };
13505
+ const getEnabledValues = (el)=>{
13506
+ if (!el) return [];
13507
+ return Array.from(el.children).filter((child)=>!child.hasAttribute("aria-disabled")).map((child)=>child.getAttribute("data-value")).filter(Boolean);
13508
+ };
13509
+ /* Prevent Drag -----------------------------------------------------------------------
13510
+ -------------------------------------------------------------------------------- */ const PREVENT_DRAG_ATTRIBUTE = "data-embla-prevent-drag";
13511
+ const isDragPrevented = (el)=>{
13512
+ return el.closest(`[${PREVENT_DRAG_ATTRIBUTE}]`) != null;
13513
+ };
13514
+ function getPrevIndex(index, length) {
13515
+ if (length <= 0) {
13516
+ throw new Error("Length must be a positive number.");
13517
+ }
13518
+ return (index - 1 + length) % length;
13519
+ }
13520
+ function getNextIndex(index, length) {
13521
+ if (length <= 0) {
13522
+ throw new Error("Length must be a positive number.");
13523
+ }
13524
+ return (index + 1) % length;
13525
+ }
13526
+ // Helper function to scroll tab into view
13527
+ function scrollTabIntoView(tabElement, listElement, options) {
13528
+ const isHorizontal = listElement.getAttribute("aria-orientation") !== "vertical";
13529
+ // Default padding is 16px if not specified
13530
+ const scrollPadding = options?.scrollPadding;
13531
+ if (isHorizontal) {
13532
+ const tabLeft = tabElement.offsetLeft;
13533
+ const tabRight = tabLeft + tabElement.offsetWidth;
13534
+ const listScrollLeft = listElement.scrollLeft;
13535
+ const listWidth = listElement.clientWidth;
13536
+ const listRight = listScrollLeft + listWidth;
13537
+ if (tabLeft < listScrollLeft) {
13538
+ // Tab is to the left of the visible area - scroll a bit more to the left
13539
+ listElement.scrollTo({
13540
+ left: Math.max(0, tabLeft - scrollPadding),
13541
+ behavior: "smooth"
13542
+ });
13543
+ } else if (tabRight > listRight) {
13544
+ // Tab is to the right of the visible area - scroll a bit more to the right
13545
+ listElement.scrollTo({
13546
+ left: tabRight - listWidth + scrollPadding,
13547
+ behavior: "smooth"
13548
+ });
13549
+ }
13550
+ } else {
13551
+ const tabTop = tabElement.offsetTop;
13552
+ const tabBottom = tabTop + tabElement.offsetHeight;
13553
+ const listScrollTop = listElement.scrollTop;
13554
+ const listHeight = listElement.clientHeight;
13555
+ const listBottom = listScrollTop + listHeight;
13556
+ if (tabTop < listScrollTop) {
13557
+ // Tab is above the visible area - scroll a bit more up
13558
+ listElement.scrollTo({
13559
+ top: Math.max(0, tabTop - scrollPadding),
13560
+ behavior: "smooth"
13561
+ });
13562
+ } else if (tabBottom > listBottom) {
13563
+ // Tab is below the visible area - scroll a bit more down
13564
+ listElement.scrollTo({
13565
+ top: tabBottom - listHeight + scrollPadding,
13566
+ behavior: "smooth"
13567
+ });
13568
+ }
13569
+ }
13570
+ }
13571
+ function useIsSSR() {
13572
+ const [isSSR, setIsSSR] = useState(true);
13573
+ useEffect(()=>{
13574
+ setIsSSR(false);
13575
+ }, []);
13576
+ return isSSR;
13577
+ }
13578
+ function useTabsState(props) {
13579
+ const [interactionState, setInteractionState] = useState("idle");
13580
+ const [value, setValue] = useControllableState$1({
13581
+ prop: props.value,
13582
+ defaultProp: props.defaultValue ?? undefined,
13583
+ onChange: props.onValueChange
13584
+ });
13585
+ const [focusedValue, setFocusedValue] = useState(null);
13586
+ const [isFocusVisible, setIsFocusVisible] = useState(false);
13587
+ const isFocusVisibleSupported = useSupports("selector(:focus-visible)");
13588
+ const [listEl, listRef] = useState(null);
13589
+ const [selectedTriggerEl, setSelectedTriggerEl] = useState(null);
13590
+ const selectedTriggerSize = useSize(selectedTriggerEl);
13591
+ const enabledValues = useMemo(()=>listEl ? getEnabledValues(listEl) : [], [
13592
+ listEl
13593
+ ]);
13594
+ const contentIndex = value ? enabledValues.indexOf(value) : -1;
13595
+ const prevIndex = contentIndex >= 0 ? getPrevIndex(contentIndex, enabledValues.length) : -1;
13596
+ const nextIndex = contentIndex >= 0 ? getNextIndex(contentIndex, enabledValues.length) : -1;
13597
+ // Scroll selected tab into view when it changes
13598
+ // TODO: this implementation is temporary, we should create some sort of hook or plugin system
13599
+ // to allow users to customize the scroll behavior.
13600
+ useEffect(()=>{
13601
+ if (selectedTriggerEl && listEl) {
13602
+ scrollTabIntoView(selectedTriggerEl, listEl, {
13603
+ scrollPadding: 16
13604
+ });
13605
+ }
13606
+ }, [
13607
+ selectedTriggerEl,
13608
+ listEl
13609
+ ]);
13610
+ const focusTriggerByValue = useCallback((value)=>{
13611
+ if (listEl) {
13612
+ getTriggerByValue(listEl, value)?.focus();
13613
+ }
13614
+ }, [
13615
+ listEl
13616
+ ]);
13617
+ const selectPrevAction = useCallback(()=>{
13618
+ const prevValue = enabledValues[prevIndex];
13619
+ if (!prevValue) return;
13620
+ setValue(prevValue);
13621
+ }, [
13622
+ enabledValues,
13623
+ prevIndex,
13624
+ setValue
13625
+ ]);
13626
+ const selectNextAction = useCallback(()=>{
13627
+ const nextValue = enabledValues[nextIndex];
13628
+ if (!nextValue) return;
13629
+ setValue(nextValue);
13630
+ }, [
13631
+ enabledValues,
13632
+ nextIndex,
13633
+ setValue
13634
+ ]);
13635
+ const selectFirstAction = useCallback(()=>{
13636
+ const firstValue = enabledValues[0];
13637
+ if (!firstValue) return;
13638
+ setValue(firstValue);
13639
+ }, [
13640
+ enabledValues,
13641
+ setValue
13642
+ ]);
13643
+ const selectLastAction = useCallback(()=>{
13644
+ const lastValue = enabledValues[enabledValues.length - 1];
13645
+ if (!lastValue) return;
13646
+ setValue(lastValue);
13647
+ }, [
13648
+ enabledValues,
13649
+ setValue
13650
+ ]);
13651
+ const setFocusedValueAction = useCallback((value)=>{
13652
+ setFocusedValue(value);
13653
+ }, []);
13654
+ const clearFocusedValueAction = useCallback(()=>{
13655
+ setFocusedValue(null);
13656
+ }, []);
13657
+ const setValueAction = useCallback((value)=>{
13658
+ setValue(value);
13659
+ }, [
13660
+ setValue
13661
+ ]);
13662
+ const actions = {
13663
+ selectPrev: selectPrevAction,
13664
+ selectNext: selectNextAction,
13665
+ selectFirst: selectFirstAction,
13666
+ selectLast: selectLastAction,
13667
+ setFocusedValue: setFocusedValueAction,
13668
+ clearFocusedValue: clearFocusedValueAction,
13669
+ setValue: setValueAction
13670
+ };
13671
+ const arrowPrevEvent = useCallback(()=>{
13672
+ if (interactionState === "focused") {
13673
+ actions.selectPrev();
13674
+ focusTriggerByValue(enabledValues[prevIndex] ?? "");
13675
+ if (isFocusVisibleSupported) {
13676
+ setIsFocusVisible(true);
13677
+ }
13678
+ }
13679
+ }, [
13680
+ interactionState,
13681
+ actions.selectPrev,
13682
+ focusTriggerByValue,
13683
+ enabledValues,
13684
+ prevIndex,
13685
+ isFocusVisibleSupported
13686
+ ]);
13687
+ const arrowNextEvent = useCallback(()=>{
13688
+ if (interactionState === "focused") {
13689
+ actions.selectNext();
13690
+ focusTriggerByValue(enabledValues[nextIndex] ?? "");
13691
+ if (isFocusVisibleSupported) {
13692
+ setIsFocusVisible(true);
13693
+ }
13694
+ }
13695
+ }, [
13696
+ interactionState,
13697
+ actions.selectNext,
13698
+ focusTriggerByValue,
13699
+ enabledValues,
13700
+ nextIndex,
13701
+ isFocusVisibleSupported
13702
+ ]);
13703
+ const arrowUpEvent = useCallback(()=>{
13704
+ if (interactionState === "focused") {
13705
+ actions.selectPrev();
13706
+ focusTriggerByValue(enabledValues[prevIndex] ?? "");
13707
+ if (isFocusVisibleSupported) {
13708
+ setIsFocusVisible(true);
13709
+ }
13710
+ }
13711
+ }, [
13712
+ interactionState,
13713
+ actions.selectPrev,
13714
+ focusTriggerByValue,
13715
+ enabledValues,
13716
+ prevIndex,
13717
+ isFocusVisibleSupported
13718
+ ]);
13719
+ const arrowDownEvent = useCallback(()=>{
13720
+ if (interactionState === "focused") {
13721
+ actions.selectNext();
13722
+ focusTriggerByValue(enabledValues[nextIndex] ?? "");
13723
+ if (isFocusVisibleSupported) {
13724
+ setIsFocusVisible(true);
13725
+ }
13726
+ }
13727
+ }, [
13728
+ interactionState,
13729
+ actions.selectNext,
13730
+ focusTriggerByValue,
13731
+ enabledValues,
13732
+ nextIndex,
13733
+ isFocusVisibleSupported
13734
+ ]);
13735
+ const homeEvent = useCallback(()=>{
13736
+ if (interactionState === "focused") {
13737
+ actions.selectFirst();
13738
+ focusTriggerByValue(enabledValues[0] ?? "");
13739
+ if (isFocusVisibleSupported) {
13740
+ setIsFocusVisible(true);
13741
+ }
13742
+ }
13743
+ }, [
13744
+ interactionState,
13745
+ actions.selectFirst,
13746
+ focusTriggerByValue,
13747
+ enabledValues,
13748
+ isFocusVisibleSupported
13749
+ ]);
13750
+ const endEvent = useCallback(()=>{
13751
+ if (interactionState === "focused") {
13752
+ actions.selectLast();
13753
+ focusTriggerByValue(enabledValues[enabledValues.length - 1] ?? "");
13754
+ if (isFocusVisibleSupported) {
13755
+ setIsFocusVisible(true);
13756
+ }
13757
+ }
13758
+ }, [
13759
+ interactionState,
13760
+ actions.selectLast,
13761
+ focusTriggerByValue,
13762
+ enabledValues,
13763
+ isFocusVisibleSupported
13764
+ ]);
13765
+ const tabFocusEvent = useCallback((value)=>{
13766
+ actions.setFocusedValue(value);
13767
+ if (interactionState === "idle") {
13768
+ setInteractionState("focused");
13769
+ }
13770
+ }, [
13771
+ interactionState,
13772
+ actions.setFocusedValue
13773
+ ]);
13774
+ const tabBlurEvent = useCallback(()=>{
13775
+ if (interactionState === "focused") {
13776
+ actions.clearFocusedValue();
13777
+ setInteractionState("idle");
13778
+ }
13779
+ }, [
13780
+ interactionState,
13781
+ actions.clearFocusedValue
13782
+ ]);
13783
+ const tabClickEvent = useCallback((value)=>{
13784
+ actions.setValue(value);
13785
+ if (interactionState === "idle") {
13786
+ setInteractionState("focused");
13787
+ }
13788
+ }, [
13789
+ interactionState,
13790
+ actions.setValue
13791
+ ]);
13792
+ const setValueEvent = useCallback((value)=>{
13793
+ actions.setValue(value);
13794
+ }, [
13795
+ actions.setValue
13796
+ ]);
13797
+ const selectNextEvent = useCallback(()=>{
13798
+ if (interactionState === "focused") {
13799
+ actions.selectNext();
13800
+ if (isFocusVisibleSupported) {
13801
+ setIsFocusVisible(true);
13802
+ }
13803
+ }
13804
+ }, [
13805
+ interactionState,
13806
+ actions.selectNext,
13807
+ isFocusVisibleSupported
13808
+ ]);
13809
+ const selectPrevEvent = useCallback(()=>{
13810
+ if (interactionState === "focused") {
13811
+ actions.selectPrev();
13812
+ if (isFocusVisibleSupported) {
13813
+ setIsFocusVisible(true);
13814
+ }
13815
+ }
13816
+ }, [
13817
+ interactionState,
13818
+ actions.selectPrev,
13819
+ isFocusVisibleSupported
13820
+ ]);
13821
+ const setContentIndexEvent = useCallback((index)=>{
13822
+ const valueFromIndex = enabledValues[index];
13823
+ if (!valueFromIndex) return;
13824
+ actions.setValue(valueFromIndex);
13825
+ }, [
13826
+ actions.setValue,
13827
+ enabledValues
13828
+ ]);
13829
+ const setIsFocusVisibleEvent = useCallback((isFocusVisible)=>{
13830
+ setIsFocusVisible(isFocusVisible);
13831
+ }, []);
13832
+ const setSelectedTriggerElEvent = useCallback((element)=>{
13833
+ setSelectedTriggerEl(element);
13834
+ }, []);
13835
+ const events = {
13836
+ arrowPrev: arrowPrevEvent,
13837
+ arrowNext: arrowNextEvent,
13838
+ arrowUp: arrowUpEvent,
13839
+ arrowDown: arrowDownEvent,
13840
+ home: homeEvent,
13841
+ end: endEvent,
13842
+ tabFocus: tabFocusEvent,
13843
+ tabBlur: tabBlurEvent,
13844
+ tabClick: tabClickEvent,
13845
+ setValue: setValueEvent,
13846
+ selectNext: selectNextEvent,
13847
+ selectPrev: selectPrevEvent,
13848
+ setContentIndex: setContentIndexEvent,
13849
+ setIsFocusVisible: setIsFocusVisibleEvent,
13850
+ setSelectedTriggerEl: setSelectedTriggerElEvent
13851
+ };
13852
+ const refs = useMemo(()=>({
13853
+ list: listRef
13854
+ }), []);
13855
+ const triggerRect = useMemo(()=>({
13856
+ width: selectedTriggerSize?.width ?? selectedTriggerEl?.offsetWidth ?? 0,
13857
+ left: selectedTriggerEl?.offsetLeft ?? 0
13858
+ }), [
13859
+ selectedTriggerSize,
13860
+ selectedTriggerEl
13861
+ ]);
13862
+ const isSSR = useIsSSR();
13863
+ return {
13864
+ refs,
13865
+ interactionState,
13866
+ value,
13867
+ isSSR,
13868
+ triggerRect,
13869
+ focusedValue,
13870
+ isFocusVisible,
13871
+ contentIndex,
13872
+ events,
13873
+ isFocusVisibleSupported
13874
+ };
13875
+ }
13876
+ function useTabs(props) {
13877
+ const autoId = useId();
13878
+ const { refs, interactionState, value, isSSR, events, triggerRect, focusedValue, isFocusVisible, contentIndex, isFocusVisibleSupported } = useTabsState(props);
13879
+ const { orientation = "horizontal" } = props;
13880
+ const focused = interactionState === "focused";
13881
+ const stateProps = useMemo(()=>elementProps({
13882
+ "data-orientation": orientation,
13883
+ "data-focus": dataAttr(focused),
13884
+ "data-ssr": dataAttr(isSSR)
13885
+ }), [
13886
+ orientation,
13887
+ focused,
13888
+ isSSR
13889
+ ]);
13890
+ return {
13891
+ refs,
13892
+ value,
13893
+ contentIndex,
13894
+ triggerRect,
13895
+ selectNext: events.selectNext,
13896
+ selectPrev: events.selectPrev,
13897
+ setValue: events.setValue,
13898
+ setContentIndex: events.setContentIndex,
13899
+ stateProps,
13900
+ rootProps: elementProps({
13901
+ ...stateProps,
13902
+ style: {
13903
+ "--indicator-left": `${triggerRect.left}px`,
13904
+ "--indicator-width": `${triggerRect.width}px`
13905
+ }
13906
+ }),
13907
+ listProps: elementProps({
13908
+ id: getListId(autoId),
13909
+ role: "tablist",
13910
+ "aria-orientation": orientation,
13911
+ ...stateProps,
13912
+ onKeyDown (event) {
13913
+ if (event.defaultPrevented) return;
13914
+ if (event.nativeEvent.isComposing) return;
13915
+ // TODO: support activationMode="manual"
13916
+ switch(event.key){
13917
+ case "ArrowLeft":
13918
+ if (orientation !== "horizontal") return;
13919
+ event.preventDefault();
13920
+ events.arrowPrev();
13921
+ break;
13922
+ case "ArrowRight":
13923
+ if (orientation !== "horizontal") return;
13924
+ event.preventDefault();
13925
+ events.arrowNext();
13926
+ break;
13927
+ case "ArrowUp":
13928
+ if (orientation !== "vertical") return;
13929
+ event.preventDefault();
13930
+ events.arrowPrev();
13931
+ break;
13932
+ case "ArrowDown":
13933
+ if (orientation !== "vertical") return;
13934
+ event.preventDefault();
13935
+ events.arrowNext();
13936
+ break;
13937
+ case "Home":
13938
+ {
13939
+ event.preventDefault();
13940
+ events.home();
13941
+ break;
13942
+ }
13943
+ case "End":
13944
+ {
13945
+ event.preventDefault();
13946
+ events.end();
13947
+ break;
13948
+ }
13949
+ }
13950
+ }
13951
+ }),
13952
+ getTriggerProps: (props)=>{
13953
+ const { disabled: isDisabled, value: triggerValue } = props;
13954
+ const itemState = {
13955
+ isDisabled,
13956
+ isSelected: value === triggerValue,
13957
+ isFocused: focusedValue === triggerValue
13958
+ };
13959
+ const itemStateProps = {
13960
+ "data-focus": dataAttr(itemState.isFocused),
13961
+ "data-focus-visible": dataAttr(itemState.isFocused && isFocusVisible),
13962
+ "data-selected": dataAttr(itemState.isSelected),
13963
+ "data-disabled": dataAttr(itemState.isDisabled),
13964
+ "data-ssr": dataAttr(isSSR),
13965
+ "aria-disabled": ariaAttr(itemState.isDisabled),
13966
+ "aria-selected": ariaAttr(itemState.isSelected)
13967
+ };
13968
+ const ref = (element)=>{
13969
+ if (element && triggerValue === value) {
13970
+ events.setSelectedTriggerEl(element);
13971
+ }
13972
+ };
13973
+ return {
13974
+ ...itemState,
13975
+ refs: {
13976
+ root: ref
13977
+ },
13978
+ stateProps: itemStateProps,
13979
+ rootProps: buttonProps({
13980
+ id: getTriggerId(triggerValue, autoId),
13981
+ role: "tab",
13982
+ type: "button",
13983
+ disabled: isDisabled,
13984
+ tabIndex: itemState.isSelected ? 0 : -1,
13985
+ ...itemStateProps,
13986
+ "data-value": triggerValue,
13987
+ "data-orientation": orientation,
13988
+ "data-ownedby": getListId(autoId),
13989
+ "aria-controls": getContentId(triggerValue, autoId),
13990
+ onClick (event) {
13991
+ if (itemState.isDisabled) return;
13992
+ if (event.defaultPrevented) return;
13993
+ events.tabClick(triggerValue);
13994
+ },
13995
+ onFocus (event) {
13996
+ events.tabFocus(props.value);
13997
+ if (isFocusVisibleSupported) {
13998
+ events.setIsFocusVisible(event.target.matches(":focus-visible"));
13999
+ }
14000
+ },
14001
+ onBlur (event) {
14002
+ const target = event.relatedTarget;
14003
+ if (target?.getAttribute("role") !== "tab") {
14004
+ events.tabBlur();
14005
+ }
14006
+ if (isFocusVisibleSupported) {
14007
+ events.setIsFocusVisible(false);
14008
+ }
14009
+ }
14010
+ })
14011
+ };
14012
+ },
14013
+ getContentProps: (props)=>{
14014
+ const { value: contentValue } = props;
14015
+ const triggerId = getTriggerId(contentValue, autoId);
14016
+ const isSelected = value === contentValue;
14017
+ return elementProps({
14018
+ id: getContentId(contentValue, autoId),
14019
+ tabIndex: -1,
14020
+ role: "tabpanel",
14021
+ "aria-labelledby": triggerId,
14022
+ "aria-selected": ariaAttr(isSelected),
14023
+ "aria-hidden": !isSelected,
14024
+ "data-selected": dataAttr(isSelected),
14025
+ "data-orientation": orientation,
14026
+ "data-ownedby": getListId(autoId),
14027
+ "data-ssr": dataAttr(isSSR)
14028
+ });
14029
+ },
14030
+ indicatorProps: elementProps({
14031
+ ...stateProps
14032
+ })
14033
+ };
14034
+ }
14035
+ const TabsContext = /*#__PURE__*/ createContext(null);
14036
+ const TabsProvider = TabsContext.Provider;
14037
+ function useTabsContext({ strict = true } = {}) {
14038
+ const context = useContext(TabsContext);
14039
+ if (!context && strict) {
14040
+ throw new Error("useTabsContext must be used within a Tabs");
14041
+ }
14042
+ return context;
14043
+ }
14044
+ const autoHeight = AutoHeight();
14045
+ const plugins = [
14046
+ autoHeight
14047
+ ];
14048
+ const useTabsCarouselState = (props)=>{
14049
+ const api = useTabsContext();
14050
+ const slideHeights = useRef(new WeakMap());
14051
+ const [emblaRef, emblaApi] = useEmblaCarousel({
14052
+ loop: props.loop,
14053
+ dragThreshold: props.dragThreshold,
14054
+ duration: 20,
14055
+ watchDrag: (_, event)=>{
14056
+ if (event.target instanceof HTMLElement) {
14057
+ if (isDragPrevented(event.target)) {
14058
+ return false;
14059
+ }
14060
+ }
14061
+ return true;
14062
+ },
14063
+ // Workaround for Embla v8 AutoHeight plugin not detecting lazy-loaded content height changes.
14064
+ // ref: https://github.com/davidjerleke/embla-carousel/discussions/1142
14065
+ // Embla's default resize handler only reInits on axis-size (width, for a horizontal carousel)
14066
+ // changes, so a slide growing taller (e.g. after lazy mount) never triggers a recalculation.
14067
+ // We reInit manually, but ONLY when a slide's *height* actually changes — reInit-ing on every
14068
+ // slide resize (including width fluctuations during horizontal scroll) re-applies Embla's
14069
+ // translate3d mid-scroll and breaks momentum scrolling in WKWebView/Safari.
14070
+ watchResize: (emblaApi, entries)=>{
14071
+ const heights = slideHeights.current;
14072
+ const slides = emblaApi.slideNodes();
14073
+ for (const entry of entries){
14074
+ const slide = slides.find((node)=>node === entry.target);
14075
+ if (!slide) continue;
14076
+ const newHeight = slide.offsetHeight;
14077
+ const prevHeight = heights.get(slide);
14078
+ heights.set(slide, newHeight);
14079
+ if (prevHeight !== undefined && newHeight !== prevHeight) {
14080
+ emblaApi.reInit();
14081
+ return false;
14082
+ }
14083
+ }
14084
+ return true;
14085
+ }
14086
+ }, plugins);
14087
+ const onSettle = useCallbackRef(props.onSettle);
14088
+ const onSwipeStart = useCallbackRef(props.onSwipeStart);
14089
+ const onSwipeEnd = useCallbackRef(props.onSwipeEnd);
14090
+ useEffect(()=>{
14091
+ const select = emblaApi?.on("select", ()=>{
14092
+ const contentIndex = emblaApi.selectedScrollSnap();
14093
+ api.setContentIndex(contentIndex);
14094
+ });
14095
+ const settle = emblaApi?.on("settle", ()=>{
14096
+ onSettle?.();
14097
+ });
14098
+ const pointerDown = emblaApi?.on("pointerDown", ()=>{
14099
+ onSwipeStart?.();
14100
+ });
14101
+ const pointerUp = emblaApi?.on("pointerUp", ()=>{
14102
+ onSwipeEnd?.();
14103
+ });
14104
+ return ()=>{
14105
+ select?.clear();
14106
+ settle?.clear();
14107
+ pointerDown?.clear();
14108
+ pointerUp?.clear();
14109
+ };
14110
+ }, [
14111
+ emblaApi,
14112
+ api.setContentIndex,
14113
+ onSettle,
14114
+ onSwipeStart,
14115
+ onSwipeEnd
14116
+ ]);
14117
+ const getContentIndex = useCallbackRef(()=>api.contentIndex);
14118
+ useEffect(()=>{
14119
+ const reInit = emblaApi?.on("reInit", ()=>{
14120
+ emblaApi?.scrollTo(getContentIndex(), true);
14121
+ });
14122
+ return ()=>{
14123
+ reInit?.clear();
14124
+ };
14125
+ }, [
14126
+ getContentIndex,
14127
+ emblaApi
14128
+ ]);
14129
+ useEffect(()=>{
14130
+ if (!emblaApi) return;
14131
+ const { dragHandler } = emblaApi.internalEngine();
14132
+ if (props.swipeable) {
14133
+ dragHandler.init(emblaApi);
14134
+ } else {
14135
+ dragHandler.destroy();
14136
+ }
14137
+ }, [
14138
+ emblaApi,
14139
+ props.swipeable
14140
+ ]);
14141
+ const isInitialScroll = useRef(true);
14142
+ useEffect(()=>{
14143
+ if (emblaApi && api.contentIndex !== emblaApi.selectedScrollSnap()) {
14144
+ const engine = emblaApi.internalEngine();
14145
+ /**
14146
+ * We want the content to quickly snap into place when a tab changes.
14147
+ * However, using a very small duration can result in an underdamped spring, which produces a rubber-band (oscillatory) effect.
14148
+ *
14149
+ * To avoid that, we intentionally use a low duration and friction so that the motion feels fast (snappy)
14150
+ * without overshooting. The underlying idea is to map our parameters to a physical spring model—a damped harmonic oscillator—
14151
+ * so we can control the dynamics in a physically intuitive way.
14152
+ *
14153
+ * In embla, the acceleration is modeled as:
14154
+ * a = (displacement * friction) / duration - velocity * (1 - friction)
14155
+ *
14156
+ * In a classical damped harmonic oscillator (assuming unit mass, m = 1), the acceleration is:
14157
+ * a = (stiffness * displacement - damping * velocity)
14158
+ *
14159
+ * By comparing the two equations, we can equate the coefficients for displacement and velocity:
14160
+ *
14161
+ * For the displacement term:
14162
+ * stiffness = friction / duration
14163
+ *
14164
+ * For the velocity term:
14165
+ * damping = 1 - friction
14166
+ *
14167
+ * Additionally, the damping ratio (zeta) for a damped oscillator is defined as:
14168
+ * zeta = damping / (2 * sqrt(stiffness))
14169
+ *
14170
+ * Substituting the derived expressions for stiffness and damping gives:
14171
+ * zeta = (1 - friction) / (2 * sqrt(friction / duration))
14172
+ *
14173
+ * This damping ratio (zeta) tells us the nature of the response:
14174
+ * - zeta < 1: Underdamped (oscillatory, rubber-band effect)
14175
+ * - zeta = 1: Critically damped (fast snap without overshoot)
14176
+ * - zeta > 1: Overdamped (slow response)
14177
+ *
14178
+ * For our current configuration, we use:
14179
+ *
14180
+ * duration = 4
14181
+ * friction = 0.4
14182
+ *
14183
+ * Which gives:
14184
+ *
14185
+ * stiffness = 0.4 / 4 = 0.1
14186
+ * damping = 1 - 0.4 = 0.6
14187
+ *
14188
+ * And the damping ratio becomes:
14189
+ *
14190
+ * zeta = 0.6 / (2 * sqrt(0.1))
14191
+ * = 0.6 / (2 * 0.31623...)
14192
+ * = 0.6 / 0.63246...
14193
+ * = 0.94868...
14194
+ *
14195
+ * Since zeta is close to 1, this spring is nearly critically damped. This is enough for our usage,
14196
+ * ensuring that the animation snaps quickly without overshooting (avoiding the rubber-band effect)
14197
+ * when switching tabs.
14198
+ */ if (isInitialScroll.current) {
14199
+ engine.scrollBody.useDuration(0);
14200
+ isInitialScroll.current = false;
14201
+ } else {
14202
+ engine.scrollBody.useDuration(4).useFriction(0.4);
14203
+ }
14204
+ engine.scrollTo.index(api.contentIndex, 0);
14205
+ }
14206
+ }, [
14207
+ emblaApi,
14208
+ api.contentIndex
14209
+ ]);
14210
+ return {
14211
+ refs: {
14212
+ root: emblaRef
14213
+ },
14214
+ autoHeight: props.autoHeight
14215
+ };
14216
+ };
14217
+ const useTabsCarousel = (props)=>{
14218
+ const { refs, autoHeight } = useTabsCarouselState(props);
14219
+ const stateProps = elementProps({
14220
+ "data-carousel": "",
14221
+ "data-auto-height": dataAttr(autoHeight)
14222
+ });
14223
+ return {
14224
+ refs,
14225
+ stateProps,
14226
+ rootProps: elementProps({
14227
+ ...stateProps
14228
+ }),
14229
+ cameraProps: elementProps({
14230
+ ...stateProps
14231
+ })
14232
+ };
14233
+ };
14234
+ const TabsCarouselContext = /*#__PURE__*/ createContext(null);
14235
+ const TabsCarouselProvider = TabsCarouselContext.Provider;
14236
+ function useTabsCarouselContext({ strict = true } = {}) {
14237
+ const context = useContext(TabsCarouselContext);
14238
+ if (!context && strict) {
14239
+ throw new Error("useTabsCarouselContext must be used within a TabsCarousel");
14240
+ }
14241
+ return context;
14242
+ }
14243
+ const TabsTriggerContext = /*#__PURE__*/ createContext(null);
14244
+ const TabsTriggerProvider = TabsTriggerContext.Provider;
14245
+ const TabsRoot$1 = /*#__PURE__*/ forwardRef((props, ref)=>{
14246
+ const { defaultValue, value, onValueChange, orientation, lazyMount, unmountOnExit, ...otherProps } = props;
14247
+ const api = useTabs({
14248
+ defaultValue,
14249
+ value,
14250
+ onValueChange,
14251
+ orientation
14252
+ });
14253
+ return /*#__PURE__*/ jsx(TabsProvider, {
14254
+ value: api,
14255
+ children: /*#__PURE__*/ jsx(RenderStrategyPropsProvider, {
14256
+ value: useMemo(()=>({
14257
+ lazyMount,
14258
+ unmountOnExit
14259
+ }), [
14260
+ lazyMount,
14261
+ unmountOnExit
14262
+ ]),
14263
+ children: /*#__PURE__*/ jsx(Primitive.div, {
14264
+ ref: ref,
14265
+ ...mergeProps$1(api.rootProps, otherProps)
14266
+ })
14267
+ })
14268
+ });
14269
+ });
14270
+ TabsRoot$1.displayName = "TabsRoot";
14271
+ const TabsList$1 = /*#__PURE__*/ forwardRef((props, ref)=>{
14272
+ const api = useTabsContext();
14273
+ return /*#__PURE__*/ jsx(Primitive.div, {
14274
+ ref: composeRefs(api.refs.list, ref),
14275
+ ...mergeProps$1(api.listProps, props)
14276
+ });
14277
+ });
14278
+ TabsList$1.displayName = "TabsList";
14279
+ const TabsTrigger$1 = /*#__PURE__*/ forwardRef((props, ref)=>{
14280
+ const { value, disabled, ...otherProps } = props;
14281
+ const api = useTabsContext();
14282
+ const triggerApi = api.getTriggerProps({
14283
+ value,
14284
+ disabled
14285
+ });
14286
+ return /*#__PURE__*/ jsx(TabsTriggerProvider, {
14287
+ value: triggerApi,
14288
+ children: /*#__PURE__*/ jsx(Primitive.button, {
14289
+ ref: composeRefs(triggerApi.refs.root, ref),
14290
+ ...mergeProps$1(triggerApi.rootProps, otherProps)
14291
+ })
14292
+ });
14293
+ });
14294
+ TabsTrigger$1.displayName = "TabsTrigger";
14295
+ const TabsContent$1 = /*#__PURE__*/ forwardRef((props, ref)=>{
14296
+ const { value, children, ...otherProps } = props;
14297
+ const api = useTabsContext();
14298
+ const carouselApi = useTabsCarouselContext({
14299
+ strict: false
14300
+ });
14301
+ const inCarousel = carouselApi != null;
14302
+ const renderStrategyProps = useRenderStrategyPropsContext();
14303
+ const { unmounted } = useRenderStrategy({
14304
+ ...renderStrategyProps,
14305
+ present: api.value === value
14306
+ });
14307
+ // Outside a carousel, an unmounted tab renders nothing at all.
14308
+ // Inside a carousel, every tab must keep its slide slot even while unmounted:
14309
+ // the carousel syncs by numeric index, so collapsing the slide set (returning
14310
+ // null for lazy/exited tabs) makes embla's snap index drift from the tab's
14311
+ // logical index, and clicking/swiping to a far tab lands on the wrong one.
14312
+ // We keep the slot but still honor lazyMount/unmountOnExit for the children,
14313
+ // so expensive content stays lazy — only the empty slide wrapper persists.
14314
+ if (unmounted && !inCarousel) return null;
14315
+ return /*#__PURE__*/ jsx(Primitive.div, {
14316
+ ref: ref,
14317
+ ...mergeProps$1(api.getContentProps({
14318
+ value
14319
+ }), carouselApi?.stateProps ?? {}, otherProps),
14320
+ children: unmounted ? null : children
14321
+ });
14322
+ });
14323
+ TabsContent$1.displayName = "TabsContent";
14324
+ const TabsIndicator$1 = /*#__PURE__*/ forwardRef((props, ref)=>{
14325
+ const api = useTabsContext();
14326
+ return /*#__PURE__*/ jsx(Primitive.div, {
14327
+ ref: ref,
14328
+ ...mergeProps$1(api.indicatorProps, props)
14329
+ });
14330
+ });
14331
+ const TabsCarousel = /*#__PURE__*/ forwardRef((props, ref)=>{
14332
+ const { dragThreshold, loop, swipeable, autoHeight, onSettle, onSwipeStart, onSwipeEnd, ...otherProps } = props;
14333
+ const api = useTabsCarousel({
14334
+ dragThreshold,
14335
+ loop,
14336
+ swipeable,
14337
+ onSettle,
14338
+ autoHeight,
14339
+ onSwipeStart,
14340
+ onSwipeEnd
14341
+ });
14342
+ return /*#__PURE__*/ jsx(TabsCarouselProvider, {
14343
+ value: api,
14344
+ children: /*#__PURE__*/ jsx(Primitive.div, {
14345
+ ref: composeRefs(api.refs.root, ref),
14346
+ ...mergeProps$1(api.rootProps, otherProps),
14347
+ children: props.children
14348
+ })
14349
+ });
14350
+ });
14351
+ TabsCarousel.displayName = "TabsCarousel";
14352
+ const TabsCarouselCamera = /*#__PURE__*/ forwardRef((props, ref)=>{
14353
+ const api = useTabsCarouselContext();
14354
+ return /*#__PURE__*/ jsx(Primitive.div, {
14355
+ ref: ref,
14356
+ ...mergeProps$1(api.cameraProps, props)
14357
+ });
14358
+ });
14359
+ TabsCarouselCamera.displayName = "TabsCarouselCamera";
14360
+
14361
+ var Tabs_namespace = {
14362
+ __proto__: null,
14363
+ Carousel: TabsCarousel,
14364
+ CarouselCamera: TabsCarouselCamera,
14365
+ Content: TabsContent$1,
14366
+ Indicator: TabsIndicator$1,
14367
+ List: TabsList$1,
14368
+ Root: TabsRoot$1,
14369
+ Trigger: TabsTrigger$1};
14370
+
14371
+ const { withProvider, withContext } = createSlotRecipeContext(tabs);
14372
+ const TabsRoot = withProvider(Tabs_namespace.Root, "root");
14373
+ const TabsList = withContext(Tabs_namespace.List, "list");
14374
+ const TabsTrigger = withScaleFeedback(withContext(Tabs_namespace.Trigger, "trigger"));
14375
+ const TabsIndicator = withContext(Tabs_namespace.Indicator, "indicator");
14376
+ const TabsContent = withContext(Tabs_namespace.Content, "content");
14377
+ withContext(Tabs_namespace.Carousel, "carousel");
14378
+ withContext(Tabs_namespace.CarouselCamera, "carouselCamera");
14379
+
14380
+ const RESIZE_MIN_WIDTH = 360;
14381
+ const RESIZE_MAX_WIDTH = 720;
14382
+ /**
14383
+ * Right-side record peek panel -- Attio's own peek shows a Highlights card
14384
+ * area and an Activity feed (measured live). Ported from your-moon/crisp's
14385
+ * `RecordPanel` (`product/recordpanel/`), built ON `ResizablePanel`
14386
+ * (`side="right" floating`) rather than crisp's own duplicate absolute-
14387
+ * positioning/slide-in/resize-handle implementation -- the outer shell (the
14388
+ * enter/exit keyframe animation, the full-height overlay, the drag handle
14389
+ * itself) all come from that primitive; this component only renders the
14390
+ * CONTENT inside it.
14391
+ *
14392
+ * OVERLAY, not docked: `floating` (on the `ResizablePanel` this is built on)
14393
+ * now means a real `position: absolute` overlay that reserves ZERO layout
14394
+ * width -- opening this panel never shrinks or reflows a table beside it
14395
+ * (your-moon/crisp#448 reversed an earlier in-flow/docked attempt at this
14396
+ * exact spot, reported live as "table's action buttons moving"). Because of
14397
+ * that, THIS COMPONENT OWNS ITS OWN POSITIONING -- a consumer does not wrap
14398
+ * it in a flex row with the table; it renders `<RecordPreview>` as an
14399
+ * ordinary sibling of the table. The one requirement: the shared parent
14400
+ * (the table region's own wrapper) must itself be a positioned element
14401
+ * (typically `position: relative`) with a real height, since that's the
14402
+ * containing block this panel's `inset: 0` resolves against. See
14403
+ * `DataTable.stories.tsx`'s own Showcase for the pattern:
14404
+ *
14405
+ * ```tsx
14406
+ * <div style={{ position: "relative", flex: 1, minHeight: 0 }}>
14407
+ * <DataTable style={{ width: "100%", height: "100%" }} ... />
14408
+ * <RecordPreview open={open} record={record} onClose={onClose} ... />
14409
+ * </div>
14410
+ * ```
14411
+ *
14412
+ * Enter/exit: closing this panel is a state change (`open` -> false) that
14413
+ * typically clears `record` in the SAME update (see the DataTable story's
14414
+ * own `onClose={() => setPreviewRow(null)}`), so by the time an exit
14415
+ * animation should play, the values this component would render are
14416
+ * already gone. `usePresence` (`@seed-design/react-presence`, the same
14417
+ * idiom Dialog/Drawer already use for their own floating layers) keeps the
14418
+ * tree mounted for exactly the exit animation's duration; the snapshot
14419
+ * below keeps the LAST visible props around for that same window, since
14420
+ * without it the panel would animate out empty.
14421
+ */ function RecordPreview(props) {
14422
+ const shouldRender = props.open && props.record != null;
14423
+ const { isPresent, ref: panelElementRef } = usePresence(shouldRender);
14424
+ const lastVisiblePropsRef = useRef(props);
14425
+ if (shouldRender) lastVisiblePropsRef.current = props;
14426
+ if (!isPresent) return null;
14427
+ return /*#__PURE__*/ jsx(RecordPreviewPanel, {
14428
+ ...shouldRender ? props : lastVisiblePropsRef.current,
14429
+ animationState: shouldRender ? "open" : "closed",
14430
+ panelElementRef: panelElementRef
14431
+ });
14432
+ }
14433
+ RecordPreview.displayName = "RecordPreview";
14434
+ 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 }) {
14435
+ const titleId = useId();
14436
+ const classNames = recordPreview();
14437
+ // Which sections (your-moon/crisp#413) are expanded -- uncontrolled and
14438
+ // independent (Attio's own Emails/Notes/Tasks open without closing one
14439
+ // another): a plain `Set`, reset whenever the record itself changes so a
14440
+ // stale open section doesn't carry over between two different records.
14441
+ const [openSections, setOpenSections] = useState(()=>new Set());
14442
+ // biome-ignore lint/correctness/useExhaustiveDependencies: `record` is read as a change TRIGGER, not a value the effect body needs -- reset whenever it changes identity, regardless of what it now points to.
14443
+ useEffect(()=>{
14444
+ setOpenSections(new Set());
14445
+ }, [
14446
+ record
14447
+ ]);
14448
+ const toggleSection = (key)=>setOpenSections((prev)=>{
14449
+ const next = new Set(prev);
14450
+ if (next.has(key)) next.delete(key);
14451
+ else next.add(key);
14452
+ return next;
14453
+ });
14454
+ // Close on Escape. Simpler than crisp's own version (crisp additionally
14455
+ // checks `hasOpenFloatingLayer()` so an Escape meant for a nested Menu/
14456
+ // Select/Popover doesn't also close this panel) -- crispy-seed has no
14457
+ // equivalent floating-stack registry to read, so a consumer nesting its
14458
+ // own floating layers inside `footer`/`sections` content should stop
14459
+ // propagation on its own Escape handler. This component is only ever
14460
+ // mounted (by `RecordPreview`, above) while genuinely open or animating
14461
+ // out, so there's no separate `open` flag to gate on here.
14462
+ useEffect(()=>{
14463
+ const onKey = (event)=>{
14464
+ if (event.key === "Escape") onClose();
14465
+ };
14466
+ document.addEventListener("keydown", onKey);
14467
+ return ()=>document.removeEventListener("keydown", onKey);
14468
+ }, [
14469
+ onClose
14470
+ ]);
14471
+ // Defensive only -- `RecordPreview` never mounts this without a record.
14472
+ if (!record) return null;
14473
+ const heading = title ?? record.name ?? "Record";
14474
+ // The plain (non-tabs) layout -- unchanged from before `tabs` existed.
14475
+ // Extracted so it can ALSO become the synthesized "Overview" tab's own
14476
+ // content below, rather than being re-authored as JSX twice.
14477
+ const overviewContent = /*#__PURE__*/ jsxs(Fragment, {
14478
+ children: [
14479
+ highlights && highlights.length > 0 && /*#__PURE__*/ jsxs(Primitive.div, {
14480
+ className: classNames.section,
14481
+ children: [
14482
+ /*#__PURE__*/ jsx(Primitive.div, {
14483
+ className: classNames.sectionHead,
14484
+ children: /*#__PURE__*/ jsx(Primitive.span, {
14485
+ className: classNames.sectionLabel,
14486
+ children: highlightsLabel
14487
+ })
14488
+ }),
14489
+ /*#__PURE__*/ jsx(Primitive.div, {
14490
+ className: classNames.highlights,
14491
+ children: highlights.map((h)=>/*#__PURE__*/ jsxs(Primitive.div, {
14492
+ className: classNames.highlight,
14493
+ children: [
14494
+ /*#__PURE__*/ jsxs(Primitive.span, {
14495
+ className: classNames.highlightLabel,
14496
+ children: [
14497
+ /*#__PURE__*/ jsx("span", {
14498
+ children: h.label
14499
+ }),
14500
+ h.icon ? /*#__PURE__*/ jsx(h.icon, {
14501
+ size: 16
14502
+ }) : null
14503
+ ]
14504
+ }),
14505
+ /*#__PURE__*/ jsx(Primitive.span, {
14506
+ className: classNames.highlightValue,
14507
+ children: h.value
14508
+ })
14509
+ ]
14510
+ }, h.key))
14511
+ })
14512
+ ]
14513
+ }),
14514
+ activity != null && /*#__PURE__*/ jsxs(Primitive.div, {
14515
+ className: classNames.section,
14516
+ children: [
14517
+ /*#__PURE__*/ jsxs(Primitive.div, {
14518
+ className: classNames.sectionHead,
14519
+ children: [
14520
+ /*#__PURE__*/ jsx(Primitive.span, {
14521
+ className: classNames.sectionLabel,
14522
+ children: activityLabel
14523
+ }),
14524
+ onViewAllActivity && /*#__PURE__*/ jsx(Primitive.button, {
14525
+ type: "button",
14526
+ className: classNames.sectionAction,
14527
+ onClick: onViewAllActivity,
14528
+ children: viewAllActivityLabel
14529
+ })
14530
+ ]
14531
+ }),
14532
+ /*#__PURE__*/ jsx(Primitive.div, {
14533
+ className: classNames.activity,
14534
+ children: activity
14535
+ })
14536
+ ]
14537
+ }),
14538
+ sections && sections.length > 0 && /*#__PURE__*/ jsx(Primitive.div, {
14539
+ className: classNames.sections,
14540
+ children: sections.map((s)=>{
14541
+ const isOpen = openSections.has(s.key);
14542
+ const panelId = `${titleId}-section-${s.key}`;
14543
+ return /*#__PURE__*/ jsxs("div", {
14544
+ children: [
14545
+ s.content ? /*#__PURE__*/ jsxs(Primitive.button, {
14546
+ type: "button",
14547
+ className: classNames.sectionTrigger,
14548
+ "aria-expanded": isOpen,
14549
+ "aria-controls": panelId,
14550
+ onClick: ()=>toggleSection(s.key),
14551
+ children: [
14552
+ /*#__PURE__*/ jsx(Primitive.span, {
14553
+ className: classNames.sectionRowLabel,
14554
+ children: s.label
14555
+ }),
14556
+ /*#__PURE__*/ jsx(Primitive.span, {
14557
+ className: classNames.sectionRowCount,
14558
+ children: s.count
14559
+ }),
14560
+ /*#__PURE__*/ jsx(IconChevronDownLine_default, {
14561
+ className: classNames.sectionChevron,
14562
+ size: 14,
14563
+ "data-open": isOpen ? "" : undefined,
14564
+ "aria-hidden": "true"
14565
+ })
14566
+ ]
14567
+ }) : /*#__PURE__*/ jsxs(Primitive.div, {
14568
+ className: classNames.sectionTrigger,
14569
+ "data-inert": "",
14570
+ children: [
14571
+ /*#__PURE__*/ jsx(Primitive.span, {
14572
+ className: classNames.sectionRowLabel,
14573
+ children: s.label
14574
+ }),
14575
+ /*#__PURE__*/ jsx(Primitive.span, {
14576
+ className: classNames.sectionRowCount,
14577
+ children: s.count
14578
+ })
14579
+ ]
14580
+ }),
14581
+ s.content && isOpen && /*#__PURE__*/ jsx(Primitive.div, {
14582
+ id: panelId,
14583
+ className: classNames.sectionContent,
14584
+ children: s.content
14585
+ })
14586
+ ]
14587
+ }, s.key);
14588
+ })
14589
+ })
14590
+ ]
14591
+ });
14592
+ // Combine (`tabs`'s own doc comment): when there's ALSO plain overview
14593
+ // content given alongside `tabs`, prepend a synthesized tab carrying it,
14594
+ // ahead of the consumer's own tabs -- so `tabs` and the simpler Highlights/
14595
+ // Activity/sections props stay usable TOGETHER rather than one silently
14596
+ // replacing the other.
14597
+ const hasOverviewContent = highlights && highlights.length > 0 || activity != null || sections && sections.length > 0;
14598
+ const allTabs = tabs && hasOverviewContent ? [
14599
+ {
14600
+ key: "__overview",
14601
+ label: overviewTabLabel,
14602
+ content: overviewContent
14603
+ },
14604
+ ...tabs
14605
+ ] : tabs ?? [];
14606
+ return /*#__PURE__*/ jsx(ResizablePanel, {
14607
+ side: "right",
14608
+ floating: true,
14609
+ backdrop: backdrop,
14610
+ onBackdropClick: onClose,
14611
+ animationState: animationState,
14612
+ panelElementRef: panelElementRef,
14613
+ resizable: resizable,
14614
+ width: width,
14615
+ defaultSize: defaultWidth,
14616
+ onWidthChange: onWidthChange,
14617
+ min: minWidth,
14618
+ max: maxWidth,
14619
+ children: /*#__PURE__*/ jsxs(Primitive.div, {
14620
+ className: clsx(classNames.root, className),
14621
+ ...rest,
14622
+ role: "dialog",
14623
+ "aria-labelledby": titleId,
14624
+ children: [
14625
+ /*#__PURE__*/ jsxs(Primitive.div, {
14626
+ className: classNames.toolbar,
14627
+ children: [
14628
+ /*#__PURE__*/ jsx(Primitive.button, {
14629
+ type: "button",
14630
+ className: classNames.iconbtn,
14631
+ "aria-label": closeLabel,
14632
+ onClick: onClose,
14633
+ children: /*#__PURE__*/ jsx(IconXmarkLine_default, {
14634
+ size: 16
14635
+ })
14636
+ }),
14637
+ (onPrevious || onNext) && /*#__PURE__*/ jsxs(Primitive.span, {
14638
+ className: classNames.pager,
14639
+ children: [
14640
+ /*#__PURE__*/ jsx(Primitive.button, {
14641
+ type: "button",
14642
+ className: classNames.navbtn,
14643
+ "aria-label": previousRecordLabel,
14644
+ disabled: !onPrevious,
14645
+ onClick: onPrevious,
14646
+ children: /*#__PURE__*/ jsx(IconChevronUpLine_default, {
14647
+ size: 16
14648
+ })
14649
+ }),
11497
14650
  /*#__PURE__*/ jsx(Primitive.button, {
11498
14651
  type: "button",
11499
14652
  className: classNames.navbtn,
@@ -11544,121 +14697,49 @@ const RESIZE_MAX_WIDTH = 720;
11544
14697
  })
11545
14698
  ]
11546
14699
  }),
11547
- /*#__PURE__*/ jsxs(Primitive.div, {
11548
- className: classNames.body,
11549
- children: [
11550
- highlights && highlights.length > 0 && /*#__PURE__*/ jsxs(Primitive.div, {
11551
- className: classNames.section,
11552
- children: [
11553
- /*#__PURE__*/ jsx(Primitive.div, {
11554
- className: classNames.sectionHead,
11555
- children: /*#__PURE__*/ jsx(Primitive.span, {
11556
- className: classNames.sectionLabel,
11557
- children: highlightsLabel
11558
- })
11559
- }),
11560
- /*#__PURE__*/ jsx(Primitive.div, {
11561
- className: classNames.highlights,
11562
- children: highlights.map((h)=>/*#__PURE__*/ jsxs(Primitive.div, {
11563
- className: classNames.highlight,
11564
- children: [
11565
- /*#__PURE__*/ jsxs(Primitive.span, {
11566
- className: classNames.highlightLabel,
11567
- children: [
11568
- /*#__PURE__*/ jsx("span", {
11569
- children: h.label
11570
- }),
11571
- h.icon ? /*#__PURE__*/ jsx(h.icon, {
11572
- size: 16
11573
- }) : null
11574
- ]
11575
- }),
11576
- /*#__PURE__*/ jsx(Primitive.span, {
11577
- className: classNames.highlightValue,
11578
- children: h.value
11579
- })
11580
- ]
11581
- }, h.key))
11582
- })
11583
- ]
11584
- }),
11585
- activity != null && /*#__PURE__*/ jsxs(Primitive.div, {
11586
- className: classNames.section,
11587
- children: [
11588
- /*#__PURE__*/ jsxs(Primitive.div, {
11589
- className: classNames.sectionHead,
11590
- children: [
11591
- /*#__PURE__*/ jsx(Primitive.span, {
11592
- className: classNames.sectionLabel,
11593
- children: activityLabel
11594
- }),
11595
- onViewAllActivity && /*#__PURE__*/ jsx(Primitive.button, {
11596
- type: "button",
11597
- className: classNames.sectionAction,
11598
- onClick: onViewAllActivity,
11599
- children: viewAllActivityLabel
11600
- })
11601
- ]
11602
- }),
11603
- /*#__PURE__*/ jsx(Primitive.div, {
11604
- className: classNames.activity,
11605
- children: activity
11606
- })
11607
- ]
11608
- }),
11609
- sections && sections.length > 0 && /*#__PURE__*/ jsx(Primitive.div, {
11610
- className: classNames.sections,
11611
- children: sections.map((s)=>{
11612
- const isOpen = openSections.has(s.key);
11613
- const panelId = `${titleId}-section-${s.key}`;
11614
- return /*#__PURE__*/ jsxs("div", {
11615
- children: [
11616
- s.content ? /*#__PURE__*/ jsxs(Primitive.button, {
11617
- type: "button",
11618
- className: classNames.sectionTrigger,
11619
- "aria-expanded": isOpen,
11620
- "aria-controls": panelId,
11621
- onClick: ()=>toggleSection(s.key),
11622
- children: [
11623
- /*#__PURE__*/ jsx(Primitive.span, {
11624
- className: classNames.sectionRowLabel,
11625
- children: s.label
11626
- }),
11627
- /*#__PURE__*/ jsx(Primitive.span, {
11628
- className: classNames.sectionRowCount,
11629
- children: s.count
11630
- }),
11631
- /*#__PURE__*/ jsx(IconChevronDownLine_default, {
11632
- className: classNames.sectionChevron,
11633
- size: 14,
11634
- "data-open": isOpen ? "" : undefined,
11635
- "aria-hidden": "true"
11636
- })
11637
- ]
11638
- }) : /*#__PURE__*/ jsxs(Primitive.div, {
11639
- className: classNames.sectionTrigger,
11640
- "data-inert": "",
14700
+ tabs && tabs.length > 0 ? /*#__PURE__*/ jsx(Primitive.div, {
14701
+ className: classNames.tabsRoot,
14702
+ children: /*#__PURE__*/ jsxs(TabsRoot, {
14703
+ value: activeTab,
14704
+ defaultValue: defaultActiveTab ?? allTabs[0]?.key,
14705
+ onValueChange: onTabChange,
14706
+ style: {
14707
+ display: "flex",
14708
+ flexDirection: "column",
14709
+ flex: "1 1 auto",
14710
+ minHeight: 0,
14711
+ overflow: "hidden"
14712
+ },
14713
+ children: [
14714
+ /*#__PURE__*/ jsxs(TabsList, {
14715
+ children: [
14716
+ allTabs.map((tab)=>/*#__PURE__*/ jsxs(TabsTrigger, {
14717
+ value: tab.key,
11641
14718
  children: [
11642
- /*#__PURE__*/ jsx(Primitive.span, {
11643
- className: classNames.sectionRowLabel,
11644
- children: s.label
11645
- }),
11646
- /*#__PURE__*/ jsx(Primitive.span, {
11647
- className: classNames.sectionRowCount,
11648
- children: s.count
14719
+ tab.label,
14720
+ tab.badge != null && /*#__PURE__*/ jsx(Primitive.span, {
14721
+ className: classNames.tabBadge,
14722
+ children: tab.badge
11649
14723
  })
11650
14724
  ]
11651
- }),
11652
- s.content && isOpen && /*#__PURE__*/ jsx(Primitive.div, {
11653
- id: panelId,
11654
- className: classNames.sectionContent,
11655
- children: s.content
11656
- })
11657
- ]
11658
- }, s.key);
11659
- })
11660
- })
11661
- ]
14725
+ }, tab.key)),
14726
+ /*#__PURE__*/ jsx(TabsIndicator, {})
14727
+ ]
14728
+ }),
14729
+ allTabs.map((tab)=>/*#__PURE__*/ jsx(TabsContent, {
14730
+ value: tab.key,
14731
+ className: classNames.tabPanel,
14732
+ style: {
14733
+ flex: "1 1 auto",
14734
+ minHeight: 0
14735
+ },
14736
+ children: tab.content
14737
+ }, tab.key))
14738
+ ]
14739
+ })
14740
+ }) : /*#__PURE__*/ jsx(Primitive.div, {
14741
+ className: classNames.body,
14742
+ children: overviewContent
11662
14743
  }),
11663
14744
  footer != null && /*#__PURE__*/ jsx(Primitive.div, {
11664
14745
  className: classNames.footer,
@@ -11668,6 +14749,6 @@ const RESIZE_MAX_WIDTH = 720;
11668
14749
  })
11669
14750
  });
11670
14751
  }
11671
- RecordPreview.displayName = "RecordPreview";
14752
+ RecordPreviewPanel.displayName = "RecordPreviewPanel";
11672
14753
 
11673
14754
  export { DataTable, DataTableViewsControl, RecordPreview, ResizablePanel };