@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.cjs CHANGED
@@ -33,6 +33,7 @@ var textInput = require('@seed-design/css/recipes/text-input');
33
33
  var reactMenu = require('@seed-design/react-menu');
34
34
  var menu = require('@seed-design/css/recipes/menu');
35
35
  var menuItem = require('@seed-design/css/recipes/menu-item');
36
+ var tabs = require('@seed-design/css/recipes/tabs');
36
37
 
37
38
  function _interopNamespace(e) {
38
39
  if (e && e.__esModule) return e;
@@ -224,36 +225,8 @@ var Icon$b = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
224
225
  var IconDot3HorizontalLine = /*#__PURE__*/ React.forwardRef(Icon$b);
225
226
  var IconDot3HorizontalLine_default = IconDot3HorizontalLine;
226
227
 
227
- // src/IconEyeLine.tsx
228
- var Icon$a = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
229
- viewBox: "0 0 24 24",
230
- fill: "none",
231
- xmlns: "http://www.w3.org/2000/svg",
232
- "data-seed-icon": "true",
233
- width: size,
234
- height: size,
235
- ref,
236
- ...props,
237
- children: /* @__PURE__ */ jsxRuntime.jsx("g", {
238
- children: /* @__PURE__ */ jsxRuntime.jsxs("g", {
239
- children: [
240
- /* @__PURE__ */ jsxRuntime.jsx("path", {
241
- d: "M1.68785 14.0027C2.21223 14.1751 2.77712 13.8901 2.94996 13.3659C3.07236 13.0089 3.23163 12.663 3.39915 12.3254C3.7229 11.6731 4.22663 10.8054 4.94433 9.94191C6.3729 8.22305 8.6137 6.55273 12.0003 6.55273C15.3868 6.55273 17.6276 8.22305 19.0562 9.94191C19.7739 10.8054 20.2776 11.6731 20.6014 12.3254C20.7627 12.6505 20.8778 12.9189 20.9513 13.1024C21.0036 13.233 21.0295 13.302 21.0509 13.3669C21.2241 13.8904 21.7886 14.175 22.3127 14.0027C22.8373 13.8301 23.1225 13.264 22.9499 12.7394C22.9158 12.6366 22.8793 12.5369 22.8078 12.3586C22.717 12.1319 22.5805 11.8144 22.3929 11.4363C22.0187 10.6824 21.435 9.67502 20.5943 8.66356C18.9062 6.63242 16.147 4.55273 12.0003 4.55273C7.8535 4.55273 5.09431 6.63242 3.40621 8.66356C2.56557 9.67502 1.9818 10.6824 1.60765 11.4363C1.42001 11.8144 1.28353 12.1319 1.1927 12.3586C1.12106 12.5374 1.08448 12.6372 1.05033 12.7403C0.877781 13.2649 1.16321 13.8301 1.68785 14.0027Z",
242
- fill: "currentColor"
243
- }),
244
- /* @__PURE__ */ jsxRuntime.jsx("path", {
245
- d: "M12.576 9.10187C12.7569 9.46282 12.7029 9.89712 12.4393 10.2029C12.0675 10.634 11.8429 11.1792 11.8429 11.7727C11.8429 13.0923 12.9123 14.1613 14.2263 14.1613C14.8331 14.1613 15.3926 13.9188 15.8177 13.522C16.1163 13.2434 16.5544 13.1743 16.9243 13.3476C17.2941 13.5209 17.5214 13.9017 17.4984 14.3095C17.3348 17.2106 15.0545 19.5498 12.0068 19.5498C8.94739 19.5498 6.5 16.9571 6.5 14.0363C6.5 11.1187 8.76722 8.75104 11.6123 8.55225C12.015 8.52411 12.3952 8.74091 12.576 9.10187Z",
246
- fill: "currentColor"
247
- })
248
- ]
249
- })
250
- })
251
- });
252
- var IconEyeLine = /*#__PURE__*/ React.forwardRef(Icon$a);
253
- var IconEyeLine_default = IconEyeLine;
254
-
255
228
  // src/IconMagnifyingglassLine.tsx
256
- var Icon$9 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
229
+ var Icon$a = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
257
230
  viewBox: "0 0 24 24",
258
231
  fill: "none",
259
232
  xmlns: "http://www.w3.org/2000/svg",
@@ -271,11 +244,11 @@ var Icon$9 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
271
244
  })
272
245
  })
273
246
  });
274
- var IconMagnifyingglassLine = /*#__PURE__*/ React.forwardRef(Icon$9);
247
+ var IconMagnifyingglassLine = /*#__PURE__*/ React.forwardRef(Icon$a);
275
248
  var IconMagnifyingglassLine_default = IconMagnifyingglassLine;
276
249
 
277
250
  // src/IconSlider2HorizontalLine.tsx
278
- var Icon$8 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
251
+ var Icon$9 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
279
252
  viewBox: "0 0 24 24",
280
253
  fill: "none",
281
254
  xmlns: "http://www.w3.org/2000/svg",
@@ -303,11 +276,11 @@ var Icon$8 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
303
276
  })
304
277
  })
305
278
  });
306
- var IconSlider2HorizontalLine = /*#__PURE__*/ React.forwardRef(Icon$8);
279
+ var IconSlider2HorizontalLine = /*#__PURE__*/ React.forwardRef(Icon$9);
307
280
  var IconSlider2HorizontalLine_default = IconSlider2HorizontalLine;
308
281
 
309
282
  // src/IconPersonCircleLine.tsx
310
- var Icon$7 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
283
+ var Icon$8 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
311
284
  viewBox: "0 0 24 24",
312
285
  fill: "none",
313
286
  xmlns: "http://www.w3.org/2000/svg",
@@ -335,11 +308,11 @@ var Icon$7 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
335
308
  })
336
309
  })
337
310
  });
338
- var IconPersonCircleLine = /*#__PURE__*/ React.forwardRef(Icon$7);
311
+ var IconPersonCircleLine = /*#__PURE__*/ React.forwardRef(Icon$8);
339
312
  var IconPersonCircleLine_default = IconPersonCircleLine;
340
313
 
341
314
  // src/IconPlusLine.tsx
342
- var Icon$6 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
315
+ var Icon$7 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
343
316
  viewBox: "0 0 24 24",
344
317
  fill: "none",
345
318
  xmlns: "http://www.w3.org/2000/svg",
@@ -355,11 +328,11 @@ var Icon$6 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
355
328
  })
356
329
  })
357
330
  });
358
- var IconPlusLine = /*#__PURE__*/ React.forwardRef(Icon$6);
331
+ var IconPlusLine = /*#__PURE__*/ React.forwardRef(Icon$7);
359
332
  var IconPlusLine_default = IconPlusLine;
360
333
 
361
334
  // src/IconMinusLine.tsx
362
- var Icon$5 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
335
+ var Icon$6 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
363
336
  viewBox: "0 0 24 24",
364
337
  fill: "none",
365
338
  xmlns: "http://www.w3.org/2000/svg",
@@ -375,11 +348,11 @@ var Icon$5 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
375
348
  })
376
349
  })
377
350
  });
378
- var IconMinusLine = /*#__PURE__*/ React.forwardRef(Icon$5);
351
+ var IconMinusLine = /*#__PURE__*/ React.forwardRef(Icon$6);
379
352
  var IconMinusLine_default = IconMinusLine;
380
353
 
381
354
  // src/IconXmarkLine.tsx
382
- var Icon$4 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
355
+ var Icon$5 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
383
356
  viewBox: "0 0 24 24",
384
357
  fill: "none",
385
358
  xmlns: "http://www.w3.org/2000/svg",
@@ -395,11 +368,11 @@ var Icon$4 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
395
368
  })
396
369
  })
397
370
  });
398
- var IconXmarkLine = /*#__PURE__*/ React.forwardRef(Icon$4);
371
+ var IconXmarkLine = /*#__PURE__*/ React.forwardRef(Icon$5);
399
372
  var IconXmarkLine_default = IconXmarkLine;
400
373
 
401
374
  // src/IconCheckmarkLine.tsx
402
- var Icon$3 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
375
+ var Icon$4 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
403
376
  viewBox: "0 0 24 24",
404
377
  fill: "none",
405
378
  xmlns: "http://www.w3.org/2000/svg",
@@ -417,9 +390,45 @@ var Icon$3 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
417
390
  })
418
391
  })
419
392
  });
420
- var IconCheckmarkLine = /*#__PURE__*/ React.forwardRef(Icon$3);
393
+ var IconCheckmarkLine = /*#__PURE__*/ React.forwardRef(Icon$4);
421
394
  var IconCheckmarkLine_default = IconCheckmarkLine;
422
395
 
396
+ // src/IconCorner4OutwardLine.tsx
397
+ var Icon$3 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
398
+ viewBox: "0 0 24 24",
399
+ fill: "none",
400
+ xmlns: "http://www.w3.org/2000/svg",
401
+ "data-seed-icon": "true",
402
+ width: size,
403
+ height: size,
404
+ ref,
405
+ ...props,
406
+ children: /* @__PURE__ */ jsxRuntime.jsx("g", {
407
+ children: /* @__PURE__ */ jsxRuntime.jsxs("g", {
408
+ children: [
409
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
410
+ d: "M4.99961 4.00001L8.5 4C9.05228 4 9.5 3.55228 9.5 3C9.5 2.44772 9.05228 2 8.5 2L4.99959 2.00001C3.3426 2.00003 2 3.34333 2 4.99993V8.5C2 9.05228 2.44772 9.5 3 9.5C3.55228 9.5 4 9.05228 4 8.5V4.99993C4 4.44751 4.44757 4.00002 4.99961 4.00001Z",
411
+ fill: "currentColor"
412
+ }),
413
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
414
+ d: "M15.5 2C14.9477 2 14.5 2.44772 14.5 3C14.5 3.55228 14.9477 4 15.5 4H19C19.5525 4 20 4.44762 20 4.99974V8.5C20 9.05228 20.4477 9.5 21 9.5C21.5523 9.5 22 9.05228 22 8.5V4.99974C22 3.34271 20.6567 2 19 2H15.5Z",
415
+ fill: "currentColor"
416
+ }),
417
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
418
+ d: "M3.00001 14.5C3.5523 14.5 4.00001 14.9477 4.00001 15.5L4 19.0002C3.99999 19.5523 4.44759 20 5.00007 20H8.49995C9.05223 20 9.49995 20.4477 9.49995 21C9.49995 21.5523 9.05223 22 8.49995 22H5.00007C3.34331 22 1.99998 20.6572 2 19.0001L2.00001 15.5C2.00001 14.9477 2.44773 14.5 3.00001 14.5Z",
419
+ fill: "currentColor"
420
+ }),
421
+ /* @__PURE__ */ jsxRuntime.jsx("path", {
422
+ d: "M22 15.5C22 14.9477 21.5523 14.5 21 14.5C20.4477 14.5 20 14.9477 20 15.5V19.0003C20 19.5524 19.5525 20 19 20H15.5C14.9477 20 14.5 20.4477 14.5 21C14.5 21.5523 14.9477 22 15.5 22H19C20.6567 22 22 20.6573 22 19.0003V15.5Z",
423
+ fill: "currentColor"
424
+ })
425
+ ]
426
+ })
427
+ })
428
+ });
429
+ var IconCorner4OutwardLine = /*#__PURE__*/ React.forwardRef(Icon$3);
430
+ var IconCorner4OutwardLine_default = IconCorner4OutwardLine;
431
+
423
432
  // src/IconChevronDownSmallLine.tsx
424
433
  var Icon$2 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg", {
425
434
  viewBox: "0 0 24 24",
@@ -462,8 +471,8 @@ var Icon$1 = ({ size = 24, ...props }, ref)=>/* @__PURE__ */ jsxRuntime.jsx("svg
462
471
  var IconCheckmarkFatFill = /*#__PURE__*/ React.forwardRef(Icon$1);
463
472
  var IconCheckmarkFatFill_default = IconCheckmarkFatFill;
464
473
 
465
- var __defProp$3 = Object.defineProperty;
466
- var __name$3 = (target, value)=>__defProp$3(target, "name", {
474
+ var __defProp$5 = Object.defineProperty;
475
+ var __name$5 = (target, value)=>__defProp$5(target, "name", {
467
476
  value,
468
477
  configurable: true
469
478
  });
@@ -474,7 +483,7 @@ function setRef(ref, value) {
474
483
  ref.current = value;
475
484
  }
476
485
  }
477
- __name$3(setRef, "setRef");
486
+ __name$5(setRef, "setRef");
478
487
  function composeRefs(...refs) {
479
488
  return (node)=>{
480
489
  let hasCleanup = false;
@@ -499,14 +508,14 @@ function composeRefs(...refs) {
499
508
  }
500
509
  };
501
510
  }
502
- __name$3(composeRefs, "composeRefs");
511
+ __name$5(composeRefs, "composeRefs");
503
512
  function useComposedRefs(...refs) {
504
513
  return React__namespace.useCallback(composeRefs(...refs), refs);
505
514
  }
506
- __name$3(useComposedRefs, "useComposedRefs");
515
+ __name$5(useComposedRefs, "useComposedRefs");
507
516
 
508
- var __defProp$2 = Object.defineProperty;
509
- var __name$2 = (target, value)=>__defProp$2(target, "name", {
517
+ var __defProp$4 = Object.defineProperty;
518
+ var __name$4 = (target, value)=>__defProp$4(target, "name", {
510
519
  value,
511
520
  configurable: true
512
521
  });
@@ -560,18 +569,18 @@ function createSlot(ownerName) {
560
569
  Slot2.displayName = `${ownerName}.Slot`;
561
570
  return Slot2;
562
571
  }
563
- __name$2(createSlot, "createSlot");
572
+ __name$4(createSlot, "createSlot");
564
573
  var Slot = /* @__PURE__ */ createSlot("Slot");
565
574
  var SLOTTABLE_IDENTIFIER = Symbol.for("radix.slottable");
566
575
  // @__NO_SIDE_EFFECTS__
567
576
  function createSlottable(ownerName) {
568
- const Slottable2 = /* @__PURE__ */ __name$2((props)=>"child" in props ? props.children(props.child) : props.children, "Slottable");
577
+ const Slottable2 = /* @__PURE__ */ __name$4((props)=>"child" in props ? props.children(props.child) : props.children, "Slottable");
569
578
  Slottable2.displayName = `${ownerName}.Slottable`;
570
579
  Slottable2.__radixId = SLOTTABLE_IDENTIFIER;
571
580
  return Slottable2;
572
581
  }
573
- __name$2(createSlottable, "createSlottable");
574
- var getSlottableElementFromSlottable = /* @__PURE__ */ __name$2((slottable, child)=>{
582
+ __name$4(createSlottable, "createSlottable");
583
+ var getSlottableElementFromSlottable = /* @__PURE__ */ __name$4((slottable, child)=>{
575
584
  if ("child" in slottable.props) {
576
585
  const child2 = slottable.props.child;
577
586
  if (!/*#__PURE__*/ React__namespace.isValidElement(child2)) return null;
@@ -614,7 +623,7 @@ function mergeProps(slotProps, childProps) {
614
623
  ...overrideProps
615
624
  };
616
625
  }
617
- __name$2(mergeProps, "mergeProps");
626
+ __name$4(mergeProps, "mergeProps");
618
627
  function getElementRef(element) {
619
628
  let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
620
629
  let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
@@ -628,24 +637,24 @@ function getElementRef(element) {
628
637
  }
629
638
  return element.props.ref || element.ref;
630
639
  }
631
- __name$2(getElementRef, "getElementRef");
640
+ __name$4(getElementRef, "getElementRef");
632
641
  function isSlottable(child) {
633
642
  return /*#__PURE__*/ React__namespace.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
634
643
  }
635
- __name$2(isSlottable, "isSlottable");
644
+ __name$4(isSlottable, "isSlottable");
636
645
  var REACT_LAZY_TYPE = Symbol.for("react.lazy");
637
646
  function isLazyComponent(element) {
638
647
  return element != null && typeof element === "object" && "$$typeof" in element && element.$$typeof === REACT_LAZY_TYPE && "_payload" in element && isPromiseLike(element._payload);
639
648
  }
640
- __name$2(isLazyComponent, "isLazyComponent");
649
+ __name$4(isLazyComponent, "isLazyComponent");
641
650
  function isPromiseLike(value) {
642
651
  return typeof value === "object" && value !== null && "then" in value;
643
652
  }
644
- __name$2(isPromiseLike, "isPromiseLike");
645
- var createSlotError = /* @__PURE__ */ __name$2((ownerName)=>{
653
+ __name$4(isPromiseLike, "isPromiseLike");
654
+ var createSlotError = /* @__PURE__ */ __name$4((ownerName)=>{
646
655
  return `${ownerName} failed to slot onto its children. Expected a single React element child or \`Slottable\`.`;
647
656
  }, "createSlotError");
648
- var createSlottableError = /* @__PURE__ */ __name$2((ownerName)=>{
657
+ var createSlottableError = /* @__PURE__ */ __name$4((ownerName)=>{
649
658
  return `${ownerName} failed to slot onto its \`Slottable\`. Expected \`Slottable\` to receive a single React element child.`;
650
659
  }, "createSlottableError");
651
660
  var use = React__namespace[" use ".trim().toString()];
@@ -769,21 +778,31 @@ function isFunction$1(value) {
769
778
 
770
779
  const DATA_TABLE_UNIVERSAL_CALC_OPS = [
771
780
  "count",
772
- "countFilled"
781
+ "empty",
782
+ "notEmpty"
773
783
  ];
774
784
  const DATA_TABLE_NUMERIC_CALC_OPS = [
775
785
  "sum",
776
786
  "average",
787
+ "median",
777
788
  "min",
778
- "max"
789
+ "max",
790
+ "range"
791
+ ];
792
+ /** Offered instead of `DATA_TABLE_NUMERIC_CALC_OPS` on a non-numeric column. */ const DATA_TABLE_NON_NUMERIC_CALC_OPS = [
793
+ "countUnique"
779
794
  ];
780
795
  const DATA_TABLE_CALC_LABEL = {
781
796
  count: "Count",
782
- countFilled: "Count filled",
797
+ empty: "Empty",
798
+ notEmpty: "Not empty",
783
799
  sum: "Sum",
784
800
  average: "Average",
801
+ median: "Median",
785
802
  min: "Min",
786
- max: "Max"
803
+ max: "Max",
804
+ range: "Range",
805
+ countUnique: "Count unique"
787
806
  };
788
807
  /** A value counts as empty when it is null/undefined, an empty string, or an empty
789
808
  * array -- crisp's own `isEmptyValue` (columncalc.tsx), reused verbatim. */ function isEmptyValueForCalc(value) {
@@ -792,29 +811,50 @@ const DATA_TABLE_CALC_LABEL = {
792
811
  if (Array.isArray(value)) return value.length === 0;
793
812
  return false;
794
813
  }
814
+ /** The middle value of a SORTED, non-empty numeric array -- the mean of the two
815
+ * middle entries on an even-length array, same convention as every stats library. */ function median(sortedNumbers) {
816
+ const mid = Math.floor(sortedNumbers.length / 2);
817
+ return sortedNumbers.length % 2 === 0 ? (sortedNumbers[mid - 1] + sortedNumbers[mid]) / 2 : sortedNumbers[mid];
818
+ }
795
819
  /**
796
820
  * Pure so the arithmetic is independently (and mutation-) testable without rendering a
797
- * table at all. `sum`/`average`/`min`/`max` skip empty values and non-numeric ones
798
- * (coerced via `Number`, dropping `NaN`); they return `null` -- render nothing, not a
799
- * misleading 0 -- when there is nothing numeric left to reduce over.
821
+ * table at all. The numeric ops (`sum`/`average`/`median`/`min`/`max`/`range`) skip
822
+ * empty values and non-numeric ones (coerced via `Number`, dropping `NaN`); they
823
+ * return `null` -- render nothing, not a misleading 0 -- when there is nothing
824
+ * numeric left to reduce over. `countUnique` counts distinct NON-EMPTY values
825
+ * (`JSON.stringify`-normalized, so two arrays/objects with the same shape count
826
+ * once) -- empties are already their own op (`empty`), so folding them into "one
827
+ * more unique value" here would double-count the same information two different
828
+ * ways in the same menu.
800
829
  */ function computeColumnCalc(values, op) {
801
830
  switch(op){
802
831
  case "count":
803
832
  return values.length;
804
- case "countFilled":
833
+ case "empty":
834
+ return values.filter(isEmptyValueForCalc).length;
835
+ case "notEmpty":
805
836
  return values.filter((value)=>!isEmptyValueForCalc(value)).length;
837
+ case "countUnique":
838
+ {
839
+ const seen = new Set(values.filter((value)=>!isEmptyValueForCalc(value)).map((value)=>JSON.stringify(value)));
840
+ return seen.size;
841
+ }
806
842
  }
807
- const numbers = values.filter((value)=>!isEmptyValueForCalc(value)).map((value)=>typeof value === "number" ? value : Number(value)).filter((value)=>Number.isFinite(value));
843
+ 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);
808
844
  if (numbers.length === 0) return null;
809
845
  switch(op){
810
846
  case "sum":
811
847
  return numbers.reduce((total, value)=>total + value, 0);
812
848
  case "average":
813
849
  return numbers.reduce((total, value)=>total + value, 0) / numbers.length;
850
+ case "median":
851
+ return median(numbers);
814
852
  case "min":
815
- return Math.min(...numbers);
853
+ return numbers[0];
816
854
  case "max":
817
- return Math.max(...numbers);
855
+ return numbers[numbers.length - 1];
856
+ case "range":
857
+ return numbers[numbers.length - 1] - numbers[0];
818
858
  default:
819
859
  return null;
820
860
  }
@@ -1219,31 +1259,48 @@ const EMPTY_RECORD = {};
1219
1259
  ]);
1220
1260
  const endColumnDrag = React.useCallback(()=>setDraggingColumnKey(null), []);
1221
1261
  // -- drag-to-resize ------------------------------------------------------------------
1222
- // `resizeStartRef` holds the ONE thing that must survive across pointermove events
1223
- // without triggering a re-render on every pixel (the drag's origin) -- state
1224
- // (`resizingColumnKey`) is only for what the render actually needs: which column's
1225
- // handle should show its active/dragging look.
1262
+ // `resizeStartRef` holds everything that must survive across pointermove events
1263
+ // WITHOUT triggering a re-render on every pixel: the drag's origin, AND (as of the
1264
+ // live-review fix below) the latest computed width, so `endColumnResize` has
1265
+ // something to commit. State (`resizingColumnKey`) is only for what the render
1266
+ // actually needs: which column's handle should show its active/dragging look.
1267
+ //
1268
+ // `updateColumnResize` used to call `setColumnWidth` on every single pointermove --
1269
+ // a `setColumnWidths` state update (and therefore a full DataTable re-render) per
1270
+ // PIXEL of drag, confirmed live as the stutter a column-border drag produced. It now
1271
+ // only computes+returns the clamped width; the styled layer (`resize.tsx`) writes
1272
+ // that directly to the dragged column's own `<col>` element (`col.style.width`,
1273
+ // outside React), which is all a resize needs frame-to-frame -- table layout already
1274
+ // recalculates from a `<col>`'s own inline style with no re-render involved. React
1275
+ // state (and this hook's `onColumnWidthsChange` contract) is only touched ONCE, in
1276
+ // `endColumnResize`, committing the final width exactly as a controlled consumer
1277
+ // already expects.
1226
1278
  const resizeStartRef = React.useRef(null);
1227
1279
  const [resizingColumnKey, setResizingColumnKey] = React.useState(null);
1228
1280
  const beginColumnResize = React.useCallback((key, clientX, currentWidth)=>{
1229
1281
  resizeStartRef.current = {
1230
1282
  key,
1231
1283
  startX: clientX,
1232
- startWidth: currentWidth
1284
+ startWidth: currentWidth,
1285
+ currentWidth
1233
1286
  };
1234
1287
  setResizingColumnKey(key);
1235
1288
  }, []);
1236
1289
  const updateColumnResize = React.useCallback((clientX)=>{
1237
1290
  const start = resizeStartRef.current;
1238
- if (!start) return;
1239
- setColumnWidth(start.key, start.startWidth + (clientX - start.startX));
1240
- }, [
1241
- setColumnWidth
1242
- ]);
1291
+ if (!start) return undefined;
1292
+ const width = clampColumnWidth(start.startWidth + (clientX - start.startX));
1293
+ start.currentWidth = width;
1294
+ return width;
1295
+ }, []);
1243
1296
  const endColumnResize = React.useCallback(()=>{
1297
+ const start = resizeStartRef.current;
1298
+ if (start) setColumnWidth(start.key, start.currentWidth);
1244
1299
  resizeStartRef.current = null;
1245
1300
  setResizingColumnKey(null);
1246
- }, []);
1301
+ }, [
1302
+ setColumnWidth
1303
+ ]);
1247
1304
  return {
1248
1305
  columnOrder,
1249
1306
  visibleColumnOrder,
@@ -1593,11 +1650,16 @@ function useDataTableSort(props) {
1593
1650
  const [sort, setSort] = useControllableState$1({
1594
1651
  prop: sortProp,
1595
1652
  defaultProp: defaultSort,
1596
- onChange: (nextSort)=>{
1597
- // `toggleSort` never sets `sort` back to `undefined`, but the underlying state is
1598
- // typed as possibly-undefined so an uncontrolled table can start with no sort at all.
1599
- if (nextSort) onSortChange?.(nextSort);
1600
- }
1653
+ // Live-review fix: this used to only fire for a DEFINED next sort ("`toggleSort`
1654
+ // never clears it, so why would this ever see `undefined`") -- true until
1655
+ // `clearSort` (below) existed. Guarding the callback on truthiness meant a
1656
+ // consumer controlling `sort` had no way to ever learn the column sort was
1657
+ // cleared: `setSort(undefined)` would run, but the controlled VALUE never moves
1658
+ // without the consumer's own state updating from this callback, and the guard
1659
+ // swallowed exactly the call that would have told it to. Every other value this
1660
+ // hook contract carries (`onColumnWidthsChange`, `onColumnCalcOpsChange`, ...)
1661
+ // already passes its "cleared" shape through unconditionally; this now matches.
1662
+ onChange: onSortChange
1601
1663
  });
1602
1664
  const toggleSort = React.useCallback((key)=>{
1603
1665
  setSort((current)=>{
@@ -1613,6 +1675,12 @@ function useDataTableSort(props) {
1613
1675
  }, [
1614
1676
  setSort
1615
1677
  ]);
1678
+ /** Attio/GRC's "Remove sort" -- the header click cycle (above) only ever toggles
1679
+ * asc/desc once a column has been clicked once; this is the only way back to no
1680
+ * sort at all. Exposed for a column's own header menu (`column-menu.tsx`'s
1681
+ * `ColumnMenu`) to offer as a "Clear sort" item when that column is the sorted one. */ const clearSort = React.useCallback(()=>setSort(undefined), [
1682
+ setSort
1683
+ ]);
1616
1684
  const getHeaderCellProps = React.useCallback((columnKey, sortable)=>{
1617
1685
  const isSorted = Boolean(columnKey) && sort?.key === columnKey;
1618
1686
  const canSort = Boolean(sortable && columnKey);
@@ -1642,6 +1710,7 @@ function useDataTableSort(props) {
1642
1710
  return {
1643
1711
  sort,
1644
1712
  toggleSort,
1713
+ clearSort,
1645
1714
  getHeaderCellProps
1646
1715
  };
1647
1716
  }
@@ -2391,7 +2460,7 @@ function createRecipeContext(recipe) {
2391
2460
  }
2392
2461
 
2393
2462
  const { withContext: withGroupContext } = createRecipeContext(checkboxGroup.checkboxGroup);
2394
- const { ClassNamesProvider: ClassNamesProvider$3, withContext: withContext$5 } = createSlotRecipeContext(checkbox.checkbox);
2463
+ const { ClassNamesProvider: ClassNamesProvider$3, withContext: withContext$6 } = createSlotRecipeContext(checkbox.checkbox);
2395
2464
  const { withProvider: withCheckmarkProvider, useClassNames: useCheckmarkClassNames, PropsProvider: CheckmarkPropsProvider } = createSlotRecipeContext(checkmark.checkmark);
2396
2465
  const withStateProps$4 = createWithStateProps([
2397
2466
  reactCheckbox.useCheckboxContext
@@ -2445,14 +2514,14 @@ const CheckboxIndicator = /*#__PURE__*/ React.forwardRef(({ unchecked: unchecked
2445
2514
  return null;
2446
2515
  });
2447
2516
  CheckboxIndicator.displayName = "CheckboxIndicator";
2448
- const CheckboxLabel = withContext$5(withStateProps$4(Primitive.span), "label");
2517
+ const CheckboxLabel = withContext$6(withStateProps$4(Primitive.span), "label");
2449
2518
  const CheckboxHiddenInput = reactCheckbox.Checkbox.HiddenInput;
2450
2519
 
2451
- const { withProvider: withProvider$1, withContext: withContext$4 } = createSlotRecipeContext(avatar.avatar);
2452
- const AvatarRoot = withProvider$1(reactImage.Image.Root, "root");
2453
- const AvatarImage = withContext$4(reactImage.Image.Content, "image");
2454
- const AvatarFallback = withContext$4(reactImage.Image.Fallback, "fallback");
2455
- withContext$4(Primitive.div, "badge");
2520
+ const { withProvider: withProvider$2, withContext: withContext$5 } = createSlotRecipeContext(avatar.avatar);
2521
+ const AvatarRoot = withProvider$2(reactImage.Image.Root, "root");
2522
+ const AvatarImage = withContext$5(reactImage.Image.Content, "image");
2523
+ const AvatarFallback = withContext$5(reactImage.Image.Fallback, "fallback");
2524
+ withContext$5(Primitive.div, "badge");
2456
2525
 
2457
2526
  const Badge = /*#__PURE__*/ React.forwardRef(({ className, children, ...props }, ref)=>{
2458
2527
  const [variantProps, restProps] = badge.badge.splitVariantProps(props);
@@ -2587,7 +2656,7 @@ function EntityCell({ value, className, nameClassName, actionsClassName, actionC
2587
2656
  event.stopPropagation();
2588
2657
  column.onOpenPreview?.(row);
2589
2658
  },
2590
- children: /*#__PURE__*/ jsxRuntime.jsx(IconEyeLine_default, {
2659
+ children: /*#__PURE__*/ jsxRuntime.jsx(IconCorner4OutwardLine_default, {
2591
2660
  size: 16
2592
2661
  })
2593
2662
  })
@@ -3022,7 +3091,7 @@ const SelectHiddenSelect = /*#__PURE__*/ React__namespace.forwardRef((props, ref
3022
3091
  });
3023
3092
  SelectHiddenSelect.displayName = "SelectHiddenSelect";
3024
3093
 
3025
- const { withContext: withContext$3, ClassNamesProvider: ClassNamesProvider$2 } = createSlotRecipeContext(_switch.switchStyle);
3094
+ const { withContext: withContext$4, ClassNamesProvider: ClassNamesProvider$2 } = createSlotRecipeContext(_switch.switchStyle);
3026
3095
  const { withContext: withControlContext, PropsProvider: ControlPropsProvider, withProvider: withControlProvider } = createSlotRecipeContext(switchmark.switchmark);
3027
3096
  const withStateProps$2 = createWithStateProps([
3028
3097
  reactSwitch.useSwitchContext
@@ -3048,13 +3117,13 @@ const SwitchRoot = /*#__PURE__*/ React__namespace.default.forwardRef(({ classNam
3048
3117
  SwitchRoot.displayName = "SwitchRoot";
3049
3118
  const SwitchControl = withScaleFeedback(withControlProvider(reactSwitch.Switch.Control, "root"));
3050
3119
  const SwitchThumb = withControlContext(reactSwitch.Switch.Thumb, "thumb");
3051
- withContext$3(withStateProps$2(Primitive.span), "label");
3120
+ withContext$4(withStateProps$2(Primitive.span), "label");
3052
3121
  const SwitchHiddenInput = reactSwitch.Switch.HiddenInput;
3053
3122
 
3054
3123
  // src/use-layout-effect.tsx
3055
3124
  var useLayoutEffect2 = globalThis?.document ? React__namespace.useLayoutEffect : ()=>{};
3056
3125
 
3057
- const { withProvider, withContext: withContext$2, useClassNames: useClassNames$2 } = createSlotRecipeContext(textInput.textInput);
3126
+ const { withProvider: withProvider$1, withContext: withContext$3, useClassNames: useClassNames$2 } = createSlotRecipeContext(textInput.textInput);
3058
3127
  const withFieldStateProps = createWithStateProps([
3059
3128
  {
3060
3129
  useContext: reactField.useFieldContext,
@@ -3068,11 +3137,11 @@ const withStateProps$1 = createWithStateProps([
3068
3137
  strict: false
3069
3138
  }
3070
3139
  ]);
3071
- const TextFieldRoot = withProvider(withFieldStateProps(reactTextField.TextField.Root), "root");
3072
- withContext$2(withStateProps$1(InternalIcon), "prefixIcon");
3073
- withContext$2(withStateProps$1(Primitive.span), "prefixText");
3074
- withContext$2(withStateProps$1(InternalIcon), "suffixIcon");
3075
- withContext$2(withStateProps$1(Primitive.span), "suffixText");
3140
+ const TextFieldRoot = withProvider$1(withFieldStateProps(reactTextField.TextField.Root), "root");
3141
+ withContext$3(withStateProps$1(InternalIcon), "prefixIcon");
3142
+ withContext$3(withStateProps$1(Primitive.span), "prefixText");
3143
+ withContext$3(withStateProps$1(InternalIcon), "suffixIcon");
3144
+ withContext$3(withStateProps$1(Primitive.span), "suffixText");
3076
3145
  const TextFieldInput = /*#__PURE__*/ React.forwardRef(({ className, ...otherProps }, ref)=>{
3077
3146
  const classNames = useClassNames$2();
3078
3147
  const textFieldContext = reactTextField.useTextFieldContext();
@@ -3149,7 +3218,7 @@ const TextFieldTextarea = /*#__PURE__*/ React.forwardRef(({ className, autoresiz
3149
3218
  });
3150
3219
  TextFieldTextarea.displayName = "TextFieldTextarea";
3151
3220
 
3152
- __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))}");
3221
+ __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))}");
3153
3222
 
3154
3223
  const createClassName = (className, variants, compoundVariants = [])=>{
3155
3224
  const variantKeys = Object.keys(variants);
@@ -3336,7 +3405,7 @@ Object.assign(dataTable, {
3336
3405
  // other file (`DataTable.tsx`, `column-menu.tsx`, `resize.tsx`, `column-calc.tsx`,
3337
3406
  // `toolbar.tsx`, `body-cells.tsx`, `editors.tsx`) imports its exports from here rather
3338
3407
  // than re-deriving its own.
3339
- const { ClassNamesProvider: ClassNamesProvider$1, withContext: withContext$1, useClassNames: useClassNames$1 } = createSlotRecipeContext(dataTable);
3408
+ const { ClassNamesProvider: ClassNamesProvider$1, withContext: withContext$2, useClassNames: useClassNames$1 } = createSlotRecipeContext(dataTable);
3340
3409
 
3341
3410
  /**
3342
3411
  * The pure filter itself, split out of `useSelectSearch` below so it can be
@@ -4055,7 +4124,7 @@ function DisplayCellContent({ column, row, value, classNames, locale }) {
4055
4124
  }
4056
4125
  }
4057
4126
 
4058
- const { ClassNamesProvider, withContext, useClassNames } = createSlotRecipeContext(menu.menu);
4127
+ const { ClassNamesProvider, withContext: withContext$1, useClassNames } = createSlotRecipeContext(menu.menu);
4059
4128
  const { PropsProvider: ItemPropsProvider, useProps: useItemProps, withContext: withItemContext, ClassNamesProvider: ItemClassNamesProvider } = createSlotRecipeContext(menuItem.menuItem);
4060
4129
  const withStateProps = createWithStateProps([
4061
4130
  reactMenu.useMenuContext
@@ -4089,10 +4158,10 @@ const MenuPositioner = /*#__PURE__*/ React__namespace.forwardRef(({ className, .
4089
4158
  });
4090
4159
  });
4091
4160
  MenuPositioner.displayName = "MenuPositioner";
4092
- const MenuContent = withContext(reactMenu.Menu.Content, "content");
4093
- const MenuScrollArea = withContext(withStateProps(Primitive.div), "scrollArea");
4094
- withContext(reactMenu.Menu.Group, "group");
4095
- withContext(reactMenu.Menu.GroupLabel, "groupLabel");
4161
+ const MenuContent = withContext$1(reactMenu.Menu.Content, "content");
4162
+ const MenuScrollArea = withContext$1(withStateProps(Primitive.div), "scrollArea");
4163
+ withContext$1(reactMenu.Menu.Group, "group");
4164
+ withContext$1(reactMenu.Menu.GroupLabel, "groupLabel");
4096
4165
  const MenuItem = /*#__PURE__*/ React__namespace.forwardRef(({ className: propClassName, ...props }, ref)=>{
4097
4166
  const [variantProps, otherProps] = menuItem.menuItem.splitVariantProps(props);
4098
4167
  const parentProps = useItemProps();
@@ -4163,7 +4232,7 @@ function formatCalcValue(value) {
4163
4232
  ]);
4164
4233
  const ops = [
4165
4234
  ...DATA_TABLE_UNIVERSAL_CALC_OPS,
4166
- ...numeric ? DATA_TABLE_NUMERIC_CALC_OPS : []
4235
+ ...numeric ? DATA_TABLE_NUMERIC_CALC_OPS : DATA_TABLE_NON_NUMERIC_CALC_OPS
4167
4236
  ];
4168
4237
  return /*#__PURE__*/ jsxRuntime.jsxs(MenuRoot, {
4169
4238
  children: [
@@ -4229,8 +4298,32 @@ function formatCalcValue(value) {
4229
4298
  * once captured, this SAME element keeps receiving `pointermove`/`pointerup` even
4230
4299
  * after the cursor leaves it, so the drag math can live entirely in this one
4231
4300
  * handler set with no effect/cleanup to get wrong.
4301
+ *
4302
+ * Live-review fix (stutter): `updateColumnResize` used to also commit the new width
4303
+ * to React state on every `pointermove` -- a full DataTable re-render per pixel of
4304
+ * drag, confirmed as the stutter. During the drag this component now writes the
4305
+ * computed width straight to the dragged column's own `<col>` element (found once,
4306
+ * at pointerdown, via its `data-column-key`, per `column-layout`'s `<colgroup>`
4307
+ * comment: a `<col>`'s own inline `width` is the SINGLE place table layout reads a
4308
+ * column's width from, so writing it directly reflows the table with no React
4309
+ * render at all), coalesced to at most one write per animation frame so a fast
4310
+ * mouse doesn't queue more layout work than the screen can show. React state (and
4311
+ * a controlled consumer's `onColumnWidthsChange`) is only touched ONCE more, in
4312
+ * `endColumnResize`, which is unchanged: it commits whatever `updateColumnResize`
4313
+ * last computed.
4232
4314
  */ function ColumnResizeHandle({ columnKey, width }) {
4233
4315
  const { beginColumnResize, updateColumnResize, endColumnResize, isColumnResizing } = useDataTableContext();
4316
+ // Survive across pointermove events without a re-render: the `<col>` this drag
4317
+ // writes to (resolved once, at pointerdown) and the rAF coalescing the writes.
4318
+ const colElementRef = React.useRef(null);
4319
+ const pendingWidthRef = React.useRef(null);
4320
+ const rafIdRef = React.useRef(null);
4321
+ const flushPendingWidth = ()=>{
4322
+ rafIdRef.current = null;
4323
+ const col = colElementRef.current;
4324
+ const pendingWidth = pendingWidthRef.current;
4325
+ if (col && pendingWidth != null) col.style.width = `${pendingWidth}px`;
4326
+ };
4234
4327
  return /*#__PURE__*/ jsxRuntime.jsx("span", {
4235
4328
  // Mouse/pointer-drag only for now (no keyboard equivalent yet) -- `aria-hidden`
4236
4329
  // rather than `role="separator"` + `aria-label`, which would otherwise fold
@@ -4246,16 +4339,32 @@ function formatCalcValue(value) {
4246
4339
  event.stopPropagation();
4247
4340
  event.preventDefault();
4248
4341
  event.currentTarget.setPointerCapture(event.pointerId);
4249
- const startWidth = width ?? event.currentTarget.closest('[role="columnheader"]')?.getBoundingClientRect().width ?? 150;
4342
+ const headerCell = event.currentTarget.closest('[role="columnheader"]');
4343
+ const startWidth = width ?? headerCell?.getBoundingClientRect().width ?? 150;
4344
+ colElementRef.current = event.currentTarget.closest("table")?.querySelector(`col[data-column-key="${CSS.escape(columnKey)}"]`) ?? null;
4250
4345
  beginColumnResize(columnKey, event.clientX, startWidth);
4251
4346
  },
4252
4347
  onPointerMove: (event)=>{
4253
- if (event.currentTarget.hasPointerCapture(event.pointerId)) {
4254
- updateColumnResize(event.clientX);
4348
+ if (!event.currentTarget.hasPointerCapture(event.pointerId)) return;
4349
+ const nextWidth = updateColumnResize(event.clientX);
4350
+ if (nextWidth == null) return;
4351
+ pendingWidthRef.current = nextWidth;
4352
+ if (rafIdRef.current == null) {
4353
+ rafIdRef.current = requestAnimationFrame(flushPendingWidth);
4255
4354
  }
4256
4355
  },
4257
4356
  onPointerUp: (event)=>{
4258
4357
  event.currentTarget.releasePointerCapture(event.pointerId);
4358
+ if (rafIdRef.current != null) {
4359
+ cancelAnimationFrame(rafIdRef.current);
4360
+ rafIdRef.current = null;
4361
+ }
4362
+ // Apply any not-yet-flushed width immediately so the `<col>` matches the
4363
+ // exact value `endColumnResize` is about to commit to React state -- with
4364
+ // no more pointermoves coming, nothing else would flush it.
4365
+ flushPendingWidth();
4366
+ pendingWidthRef.current = null;
4367
+ colElementRef.current = null;
4259
4368
  endColumnResize();
4260
4369
  },
4261
4370
  children: /*#__PURE__*/ jsxRuntime.jsx("span", {
@@ -4446,68 +4555,100 @@ function ColumnRenameInput({ column, initialLabel, onDone }) {
4446
4555
  * permanently, so this one carries its own marker attribute distinct from that
4447
4556
  * slot's OTHER occupants (the sort chevron, the filter trigger), which stay visible
4448
4557
  * under their own separate rules regardless of hover. */ function ColumnMenu({ column, onRename }) {
4449
- const { moveColumn, hideColumn, isColumnPinned, togglePinColumn } = useDataTableContext();
4558
+ const { moveColumn, hideColumn, isColumnPinned, togglePinColumn, sort, clearSort } = useDataTableContext();
4450
4559
  const columnLabel = typeof column.header === "string" ? column.header : column.key;
4451
4560
  const pinned = isColumnPinned(column.key);
4561
+ // Attio/GRC's "Remove sort" (live-review fix D) -- the header's own click-to-sort
4562
+ // (`SortIndicator`/`toggleSort`, above) only cycles asc/desc once a column has been
4563
+ // clicked; this is the only affordance back to no sort at all, so it only makes
4564
+ // sense to show on the column that IS currently sorted.
4565
+ const isSorted = sort?.key === column.key;
4452
4566
  const stopPropagation = (event)=>event.stopPropagation();
4453
- return /*#__PURE__*/ jsxRuntime.jsxs(MenuRoot, {
4454
- children: [
4455
- /*#__PURE__*/ jsxRuntime.jsx(DataTableHeaderCellIcon, {
4456
- asChild: true,
4457
- "data-column-menu": "",
4458
- onClick: stopPropagation,
4459
- onKeyDown: stopPropagation,
4460
- draggable: false,
4461
- children: /*#__PURE__*/ jsxRuntime.jsx(MenuTrigger, {
4462
- "aria-label": `${columnLabel} column menu`,
4463
- children: /*#__PURE__*/ jsxRuntime.jsx(IconDot3HorizontalLine_default, {
4464
- size: 16
4567
+ return(// Live-review fix (D): a `MenuItem` inside `MenuPositioner` renders through a
4568
+ // `FloatingPortal` -- a REAL React portal (`ReactDOM.createPortal`), which
4569
+ // bubbles a click/keydown up through the REACT TREE (this component, then the
4570
+ // columnheader that renders it), NOT the DOM tree the portal actually appends
4571
+ // to. Confirmed live: clicking ANY item here (not just "Clear sort" -- "Move
4572
+ // left" on a column with no sort at all reproduced it too) also fired the
4573
+ // COLUMNHEADER's own `toggleSort`, because nothing stopped that bubble before
4574
+ // it reached the header's `onClick`/`onKeyDown` (`getHeaderCellProps`). The
4575
+ // trigger's own `stopPropagation` (on `DataTableHeaderCellIcon` below) only
4576
+ // covers clicks that start on the trigger itself -- a SIBLING of
4577
+ // `MenuPositioner` in this tree, not an ancestor of it, so it never sees a
4578
+ // menu-item event at all. This wrapper is a genuine DOM node sitting between
4579
+ // BOTH of them and the header, so it catches the bubble from either one;
4580
+ // `display: contents` keeps it invisible to `DataTableHeaderCellInner`'s own
4581
+ // flex layout.
4582
+ // 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.
4583
+ /*#__PURE__*/ jsxRuntime.jsx("span", {
4584
+ onClick: stopPropagation,
4585
+ onKeyDown: stopPropagation,
4586
+ style: {
4587
+ display: "contents"
4588
+ },
4589
+ children: /*#__PURE__*/ jsxRuntime.jsxs(MenuRoot, {
4590
+ children: [
4591
+ /*#__PURE__*/ jsxRuntime.jsx(DataTableHeaderCellIcon, {
4592
+ asChild: true,
4593
+ "data-column-menu": "",
4594
+ draggable: false,
4595
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuTrigger, {
4596
+ "aria-label": `${columnLabel} column menu`,
4597
+ children: /*#__PURE__*/ jsxRuntime.jsx(IconDot3HorizontalLine_default, {
4598
+ size: 16
4599
+ })
4465
4600
  })
4466
- })
4467
- }),
4468
- /*#__PURE__*/ jsxRuntime.jsx(MenuPositioner, {
4469
- children: /*#__PURE__*/ jsxRuntime.jsx(MenuContent, {
4470
- style: {
4471
- minWidth: 160
4472
- },
4473
- children: /*#__PURE__*/ jsxRuntime.jsxs(MenuScrollArea, {
4474
- children: [
4475
- /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
4476
- onClick: onRename,
4477
- children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
4478
- children: "Rename"
4479
- })
4480
- }),
4481
- /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
4482
- onClick: ()=>moveColumn(column.key, "left"),
4483
- children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
4484
- children: "Move left"
4485
- })
4486
- }),
4487
- /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
4488
- onClick: ()=>moveColumn(column.key, "right"),
4489
- children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
4490
- children: "Move right"
4491
- })
4492
- }),
4493
- /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
4494
- onClick: ()=>togglePinColumn(column.key),
4495
- children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
4496
- children: pinned ? "Unpin column" : "Pin column"
4497
- })
4498
- }),
4499
- /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
4500
- onClick: ()=>hideColumn(column.key),
4501
- children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
4502
- children: "Hide column"
4601
+ }),
4602
+ /*#__PURE__*/ jsxRuntime.jsx(MenuPositioner, {
4603
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuContent, {
4604
+ style: {
4605
+ minWidth: 160
4606
+ },
4607
+ children: /*#__PURE__*/ jsxRuntime.jsxs(MenuScrollArea, {
4608
+ children: [
4609
+ /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
4610
+ onClick: onRename,
4611
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
4612
+ children: "Rename"
4613
+ })
4614
+ }),
4615
+ /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
4616
+ onClick: ()=>moveColumn(column.key, "left"),
4617
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
4618
+ children: "Move left"
4619
+ })
4620
+ }),
4621
+ /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
4622
+ onClick: ()=>moveColumn(column.key, "right"),
4623
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
4624
+ children: "Move right"
4625
+ })
4626
+ }),
4627
+ isSorted && /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
4628
+ onClick: clearSort,
4629
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
4630
+ children: "Clear sort"
4631
+ })
4632
+ }),
4633
+ /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
4634
+ onClick: ()=>togglePinColumn(column.key),
4635
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
4636
+ children: pinned ? "Unpin column" : "Pin column"
4637
+ })
4638
+ }),
4639
+ /*#__PURE__*/ jsxRuntime.jsx(MenuItem, {
4640
+ onClick: ()=>hideColumn(column.key),
4641
+ children: /*#__PURE__*/ jsxRuntime.jsx(MenuItemLabel, {
4642
+ children: "Hide column"
4643
+ })
4503
4644
  })
4504
- })
4505
- ]
4645
+ ]
4646
+ })
4506
4647
  })
4507
4648
  })
4508
- })
4509
- ]
4510
- });
4649
+ ]
4650
+ })
4651
+ }));
4511
4652
  }
4512
4653
 
4513
4654
  /**
@@ -5785,8 +5926,8 @@ Object.assign(datePicker, {
5785
5926
  splitVariantProps: (props)=>splitVariantProps(props, datePickerVariantMap)
5786
5927
  }); // @recipe(seed): date-picker
5787
5928
 
5788
- var __defProp$1 = Object.defineProperty;
5789
- var __name$1 = (target, value)=>__defProp$1(target, "name", {
5929
+ var __defProp$3 = Object.defineProperty;
5930
+ var __name$3 = (target, value)=>__defProp$3(target, "name", {
5790
5931
  value,
5791
5932
  configurable: true
5792
5933
  });
@@ -5810,15 +5951,15 @@ function useEffectEvent(callback) {
5810
5951
  }
5811
5952
  return React__namespace.useMemo(()=>(...args)=>ref.current?.(...args), []);
5812
5953
  }
5813
- __name$1(useEffectEvent, "useEffectEvent");
5954
+ __name$3(useEffectEvent, "useEffectEvent");
5814
5955
 
5815
- var __defProp = Object.defineProperty;
5816
- var __name = (target, value)=>__defProp(target, "name", {
5956
+ var __defProp$2 = Object.defineProperty;
5957
+ var __name$2 = (target, value)=>__defProp$2(target, "name", {
5817
5958
  value,
5818
5959
  configurable: true
5819
5960
  });
5820
5961
  var useInsertionEffect = React__namespace[" useInsertionEffect ".trim().toString()] || useLayoutEffect2;
5821
- function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __name(()=>{}, "onChange"), caller }) {
5962
+ function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __name$2(()=>{}, "onChange"), caller }) {
5822
5963
  const [uncontrolledProp, setUncontrolledProp, onChangeRef] = useUncontrolledState({
5823
5964
  defaultProp,
5824
5965
  onChange
@@ -5845,7 +5986,7 @@ function useControllableState({ prop, defaultProp, onChange = /* @__PURE__ */ __
5845
5986
  setValue
5846
5987
  ];
5847
5988
  }
5848
- __name(useControllableState, "useControllableState");
5989
+ __name$2(useControllableState, "useControllableState");
5849
5990
  function useUncontrolledState({ defaultProp, onChange }) {
5850
5991
  const [value, setValue] = React__namespace.useState(defaultProp);
5851
5992
  const prevValueRef = React__namespace.useRef(value);
@@ -5870,11 +6011,11 @@ function useUncontrolledState({ defaultProp, onChange }) {
5870
6011
  onChangeRef
5871
6012
  ];
5872
6013
  }
5873
- __name(useUncontrolledState, "useUncontrolledState");
6014
+ __name$2(useUncontrolledState, "useUncontrolledState");
5874
6015
  function isFunction(value) {
5875
6016
  return typeof value === "function";
5876
6017
  }
5877
- __name(isFunction, "isFunction");
6018
+ __name$2(isFunction, "isFunction");
5878
6019
  var SYNC_STATE = Symbol("RADIX:SYNC_STATE");
5879
6020
  function useControllableStateReducer(reducer, userArgs, initialArg, init) {
5880
6021
  const { prop: controlledState, defaultProp, onChange: onChangeProp, caller } = userArgs;
@@ -5946,7 +6087,7 @@ function useControllableStateReducer(reducer, userArgs, initialArg, init) {
5946
6087
  dispatch
5947
6088
  ];
5948
6089
  }
5949
- __name(useControllableStateReducer, "useControllableStateReducer");
6090
+ __name$2(useControllableStateReducer, "useControllableStateReducer");
5950
6091
 
5951
6092
  /*
5952
6093
  * Copyright 2020 Adobe. All rights reserved.
@@ -10152,8 +10293,8 @@ const formatFilterDate = (iso)=>{
10152
10293
  });
10153
10294
  }
10154
10295
 
10155
- const DataTableToolbar = withContext$1(Primitive.div, "toolbar");
10156
- const DataTableCount = withContext$1(Primitive.span, "count");
10296
+ const DataTableToolbar = withContext$2(Primitive.div, "toolbar");
10297
+ const DataTableCount = withContext$2(Primitive.span, "count");
10157
10298
  /**
10158
10299
  * The toolbar's own right-hand group -- `count`'s `marginInlineStart: auto` moved
10159
10300
  * here (off the `<span>` itself) so a consumer's `toolbarActions` and the row count
@@ -10164,7 +10305,7 @@ const DataTableCount = withContext$1(Primitive.span, "count");
10164
10305
  * it. `DataTable.tsx` always renders this wrapper once either `toolbarActions` or
10165
10306
  * `showCount` is on, so it degrades to exactly the old single-count layout when no
10166
10307
  * `toolbarActions` is passed.
10167
- */ const DataTableToolbarEnd = withContext$1(Primitive.div, "toolbarEnd");
10308
+ */ const DataTableToolbarEnd = withContext$2(Primitive.div, "toolbarEnd");
10168
10309
  /**
10169
10310
  * A compact, borderless search box -- crisp's own measured `.crisp-datatable-search`
10170
10311
  * (32px, `bgSurface` background, a leading icon, a soft rest-state shadow, a 1px brand
@@ -10224,6 +10365,25 @@ function DataTableInner(props, ref) {
10224
10365
  // column/direction is active. A consumer that pre-sorts server-side can pass
10225
10366
  // a controlled `sort`; this still sorts the rows it's given, which for
10226
10367
  // already-sorted data is a no-op.
10368
+ //
10369
+ // Live-review fix (D, "Clear sort"): the JSX below passes the RAW `sort` prop
10370
+ // (not `resolvedSort`) on to `DataTablePrimitiveRoot`, plus `defaultSort` --
10371
+ // passing `resolvedSort` used to feed the headless `useDataTableSort`'s own
10372
+ // `useControllableState` a value that starts `undefined` (uncontrolled) and
10373
+ // BECOMES defined the instant a header is first clicked (once `internalSort`
10374
+ // picks it up), flipping that inner hook from uncontrolled to controlled
10375
+ // mid-lifecycle -- `@radix-ui/react-use-controllable-state` warns about
10376
+ // exactly this and, worse, silently misbehaves on it: its own internal
10377
+ // uncontrolled state stops updating the moment it's controlled, so when
10378
+ // `clearSort` later drove `resolvedSort` back to `undefined` (flipping BACK
10379
+ // to uncontrolled), the hook fell through to that stale, never-updated
10380
+ // internal state instead -- still the pre-clear value, confirmed live
10381
+ // (`aria-sort` stayed "ascending" after "Clear sort"). The RAW `sort` prop
10382
+ // never has this problem: for the common uncontrolled case it's simply
10383
+ // always `undefined`, so the headless hook is uncontrolled for its entire
10384
+ // lifetime and owns its state correctly; a consumer that genuinely controls
10385
+ // `sort` behaves exactly as before (`resolvedSort === sort` whenever `sort`
10386
+ // is defined, so nothing changes for that path).
10227
10387
  const [internalSort, setInternalSort] = React__namespace.useState(defaultSort);
10228
10388
  const resolvedSort = sort !== undefined ? sort : internalSort;
10229
10389
  const handleSortChange = React__namespace.useCallback((next)=>{
@@ -10329,7 +10489,8 @@ function DataTableInner(props, ref) {
10329
10489
  selectedIds: selectedIds,
10330
10490
  defaultSelectedIds: defaultSelectedIds,
10331
10491
  onSelectionChange: onSelectionChange,
10332
- sort: resolvedSort,
10492
+ sort: sort,
10493
+ defaultSort: defaultSort,
10333
10494
  onSortChange: handleSortChange,
10334
10495
  query: resolvedQuery,
10335
10496
  onQueryChange: handleQueryChange,
@@ -10566,7 +10727,7 @@ DataTableInner.displayName = "DataTable";
10566
10727
  // is a SIBLING of the toolbar (both inside `DataTablePrimitiveRoot`'s card `<div>`)
10567
10728
  // rather than `DataTablePrimitiveRoot` itself: a `<table>` cannot host the toolbar as a
10568
10729
  // child without the HTML parser foster-parenting it back out.
10569
- const DataTableTable = withContext$1(DataTableTable$1, "table");
10730
+ const DataTableTable = withContext$2(DataTableTable$1, "table");
10570
10731
  /** One `<col>` per rendered column (plus the 40px selection column, if any) -- the
10571
10732
  * SINGLE place a column's width is ever set now. Every row's header/body/footer cell
10572
10733
  * reads its own width off this shared `<colgroup>` by construction (native table
@@ -10592,6 +10753,10 @@ const DataTableTable = withContext$1(DataTableTable$1, "table");
10592
10753
  }
10593
10754
  }),
10594
10755
  effective.map(({ column, width })=>/*#__PURE__*/ jsxRuntime.jsx("col", {
10756
+ // Read by `resize.tsx`'s own pointermove handler (`data-column-key`,
10757
+ // not a ref) to write the dragged width straight to this element
10758
+ // during a drag -- see that file's own comment for why.
10759
+ "data-column-key": column.key,
10595
10760
  style: width === undefined ? undefined : {
10596
10761
  width
10597
10762
  }
@@ -10599,15 +10764,15 @@ const DataTableTable = withContext$1(DataTableTable$1, "table");
10599
10764
  ]
10600
10765
  });
10601
10766
  }
10602
- const DataTableHeader = withContext$1(DataTableHeader$1, "header");
10603
- const DataTableHeaderCell = withContext$1(DataTableHeaderCell$1, "headerCell");
10767
+ const DataTableHeader = withContext$2(DataTableHeader$1, "header");
10768
+ const DataTableHeaderCell = withContext$2(DataTableHeaderCell$1, "headerCell");
10604
10769
  // `.crisp-datatable-th-inner` -- the flex row INSIDE a `<th>` (label + filter trigger +
10605
10770
  // sort chevron + column menu). See qvism-preset's own `headerCellInner` slot comment
10606
10771
  // for why this lives one level below the cell itself rather than on it.
10607
- const DataTableHeaderCellInner = withContext$1(Primitive.div, "headerCellInner");
10772
+ const DataTableHeaderCellInner = withContext$2(Primitive.div, "headerCellInner");
10608
10773
  /** The label text specifically -- the one thing in `DataTableHeaderCellInner` that
10609
10774
  * truncates; the trailing filter/sort/menu icons stay fully visible regardless of how
10610
- * long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$1(Primitive.span, "headerCellLabel");
10775
+ * long a column's own header text is. */ const DataTableHeaderCellLabel = withContext$2(Primitive.span, "headerCellLabel");
10611
10776
  /**
10612
10777
  * A selection `<th>`/`<td>`/footer `<td>` composes ITS base slot (`headerCell`/`cell`/
10613
10778
  * `footerCell`) together with the `selectionCell` MODIFIER, rather than replacing it --
@@ -10629,12 +10794,12 @@ const DataTableHeaderCellInner = withContext$1(Primitive.div, "headerCellInner")
10629
10794
  return StyledComponent;
10630
10795
  }
10631
10796
  const DataTableSelectionHeaderCell = withSelectionCell(DataTableHeaderCell$1, "headerCell");
10632
- const DataTableHeaderCellIcon = withContext$1(Primitive.span, "headerCellIcon");
10797
+ const DataTableHeaderCellIcon = withContext$2(Primitive.span, "headerCellIcon");
10633
10798
  // body has no CSS slot of its own in the rootage spec -- it is a plain structural
10634
10799
  // `<tbody>`, so it is not wrapped in `withContext`.
10635
10800
  const DataTableBody = DataTableBody$1;
10636
- const DataTableRow = withContext$1(DataTableRow$1, "row");
10637
- const DataTableCell = withContext$1(DataTableCell$1, "cell");
10801
+ const DataTableRow = withContext$2(DataTableRow$1, "row");
10802
+ const DataTableCell = withContext$2(DataTableCell$1, "cell");
10638
10803
  const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
10639
10804
  // Tranche 4 -- column menu + reorder + resize + sticky columns. A scroll boundary
10640
10805
  // wrapping the whole `<table>`: `overflowX: auto` is what makes a wide table (more
@@ -10644,7 +10809,7 @@ const DataTableSelectionCell = withSelectionCell(DataTableCell$1, "cell");
10644
10809
  // stays out of this slot's own base css (nothing here has a bounded height of its own
10645
10810
  // to clip), so it never introduces a second vertical scrollbar next to whatever
10646
10811
  // ambient container the whole DataTable already sits in.
10647
- const DataTableScrollBodyBase = withContext$1(Primitive.div, "scrollArea");
10812
+ const DataTableScrollBodyBase = withContext$2(Primitive.div, "scrollArea");
10648
10813
  function DataTableScrollBody({ children, scrollProps }) {
10649
10814
  const { scrollAreaRef, scrolledStart, scrolledEnd, onScroll } = useDataTableContext();
10650
10815
  const { className: scrollClassName, style: scrollStyle, ...otherScrollProps } = scrollProps ?? {};
@@ -10659,7 +10824,7 @@ function DataTableScrollBody({ children, scrollProps }) {
10659
10824
  children: children
10660
10825
  });
10661
10826
  }
10662
- const DataTableFooterBase = withContext$1(Primitive.tfoot, "footer");
10827
+ const DataTableFooterBase = withContext$2(Primitive.tfoot, "footer");
10663
10828
  function DataTableFooter({ children, ...props }) {
10664
10829
  return /*#__PURE__*/ jsxRuntime.jsx(DataTableFooterBase, {
10665
10830
  ...props,
@@ -10668,7 +10833,7 @@ function DataTableFooter({ children, ...props }) {
10668
10833
  })
10669
10834
  });
10670
10835
  }
10671
- const DataTableFooterCell = withContext$1(Primitive.td, "footerCell");
10836
+ const DataTableFooterCell = withContext$2(Primitive.td, "footerCell");
10672
10837
  // The selection column's OWN sticky wrappers -- one each for header/body/footer,
10673
10838
  // reading the live pinned set via `useSelectionStickyFirst` (column-layout.ts) instead
10674
10839
  // of the `stickyFirst` PROP alone, so the checkbox column sticks the moment ANY column
@@ -11020,7 +11185,7 @@ DataTableViewsControl.displayName = "DataTableViewsControl";
11020
11185
  });
11021
11186
  }
11022
11187
 
11023
- __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}");
11188
+ __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}");
11024
11189
 
11025
11190
  const resizablePanelSlotNames = [
11026
11191
  [
@@ -11046,6 +11211,10 @@ const resizablePanelSlotNames = [
11046
11211
  [
11047
11212
  "handleInner",
11048
11213
  "seed-resizable-panel__handleInner"
11214
+ ],
11215
+ [
11216
+ "backdrop",
11217
+ "seed-resizable-panel__backdrop"
11049
11218
  ]
11050
11219
  ];
11051
11220
  const defaultVariant$1 = {};
@@ -11225,15 +11394,19 @@ Object.assign(resizablePanel, {
11225
11394
  // second source of truth for the string.
11226
11395
  const COLUMN_HANDLE_CLASS = "crisp-seed-resizable-panel-handle--column";
11227
11396
  /**
11228
- * An adjustable-panel primitive: a spacer that reserves layout width plus a
11229
- * panel that overlays it, so the panel can move between docked (in-flow) and
11230
- * floating (`position: fixed`, an overlay) without a reflow glitch. Drag the
11231
- * edge handle to resize; constraints are CSS `min-width`/`max-width`, never
11232
- * re-clamped in JS. Ported from your-moon/crisp's `ResizablePanel`
11233
- * (`components/resizablepanel/`); state/drag behavior lives in
11234
- * `useResizablePanel` (`@seed-design/react-resizable-panel`), this component
11235
- * owns the DOM + recipe classes on top of it.
11236
- */ const ResizablePanel = /*#__PURE__*/ React.forwardRef(({ side = "left", width: sizeProp, defaultSize = 280, onWidthChange, min = 200, max, floating = false, handleVariant = "panel", resizable = true, storageKey, className, children, ...rest }, forwardedRef)=>{
11397
+ * An adjustable-panel primitive with two layout models (`floating`'s own doc
11398
+ * comment above has the full contrast). Docked: a `spacer` reserves layout
11399
+ * width and the panel overlays exactly that reservation, so a resize drag
11400
+ * never causes a reflow glitch on the rest of the layout. Floating: no
11401
+ * spacer at all -- this component's own outer element becomes a
11402
+ * `position: absolute; inset: 0` overlay reserving zero width, with the
11403
+ * panel itself pinned to one edge and spanning the full height of that box.
11404
+ * Drag the edge handle to resize; constraints are CSS `min-width`/
11405
+ * `max-width`, never re-clamped in JS. Ported from your-moon/crisp's
11406
+ * `ResizablePanel` (`components/resizablepanel/`); state/drag behavior
11407
+ * lives in `useResizablePanel` (`@seed-design/react-resizable-panel`), this
11408
+ * component owns the DOM + recipe classes on top of it.
11409
+ */ const ResizablePanel = /*#__PURE__*/ React.forwardRef(({ side = "left", width: sizeProp, defaultSize = 280, onWidthChange, min = 200, max, floating = false, backdrop = false, onBackdropClick, handleVariant = "panel", resizable = true, storageKey, animationState, panelElementRef, className, children, ...rest }, forwardedRef)=>{
11237
11410
  const { width, dragging, containerRef, panelRef, handleHandlers } = useResizablePanel({
11238
11411
  side,
11239
11412
  size: sizeProp,
@@ -11241,7 +11414,12 @@ const COLUMN_HANDLE_CLASS = "crisp-seed-resizable-panel-handle--column";
11241
11414
  onSizeChange: onWidthChange,
11242
11415
  storageKey
11243
11416
  });
11417
+ const composedPanelRef = useComposedRefs(panelRef, panelElementRef);
11244
11418
  const classNames = resizablePanel();
11419
+ // Floating spans its full container edge to edge -- no gutter, no
11420
+ // shrink -- so the panel's own size style is the SAME reservation a
11421
+ // docked spacer would use; only whether `spacer` itself renders differs
11422
+ // below.
11245
11423
  const maxStyle = max == null ? undefined : typeof max === "number" ? `${max}px` : max;
11246
11424
  const panelSizeStyle = {
11247
11425
  width,
@@ -11255,23 +11433,28 @@ const COLUMN_HANDLE_CLASS = "crisp-seed-resizable-panel-handle--column";
11255
11433
  else if (forwardedRef) forwardedRef.current = node;
11256
11434
  },
11257
11435
  className: clsx(classNames.root, className),
11436
+ "data-floating": floating ? "true" : "false",
11258
11437
  ...rest,
11259
11438
  children: [
11260
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11439
+ !floating && /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11261
11440
  className: classNames.spacer,
11262
- style: floating ? {
11263
- width: 0
11264
- } : {
11441
+ style: {
11265
11442
  width,
11266
11443
  minWidth: min,
11267
11444
  maxWidth: maxStyle
11268
11445
  }
11269
11446
  }),
11447
+ floating && backdrop && /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11448
+ className: classNames.backdrop,
11449
+ "data-state": animationState,
11450
+ onClick: onBackdropClick
11451
+ }),
11270
11452
  /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11271
- ref: panelRef,
11453
+ ref: composedPanelRef,
11272
11454
  className: classNames.panel,
11273
11455
  "data-side": side,
11274
11456
  "data-floating": floating ? "true" : "false",
11457
+ "data-state": animationState,
11275
11458
  style: panelSizeStyle,
11276
11459
  children: [
11277
11460
  /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
@@ -11296,7 +11479,7 @@ const COLUMN_HANDLE_CLASS = "crisp-seed-resizable-panel-handle--column";
11296
11479
  });
11297
11480
  ResizablePanel.displayName = "ResizablePanel";
11298
11481
 
11299
- __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)}");
11482
+ __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}");
11300
11483
 
11301
11484
  const recordPreviewSlotNames = [
11302
11485
  [
@@ -11406,6 +11589,18 @@ const recordPreviewSlotNames = [
11406
11589
  [
11407
11590
  "footer",
11408
11591
  "seed-record-preview__footer"
11592
+ ],
11593
+ [
11594
+ "tabsRoot",
11595
+ "seed-record-preview__tabsRoot"
11596
+ ],
11597
+ [
11598
+ "tabPanel",
11599
+ "seed-record-preview__tabPanel"
11600
+ ],
11601
+ [
11602
+ "tabBadge",
11603
+ "seed-record-preview__tabBadge"
11409
11604
  ]
11410
11605
  ];
11411
11606
  const defaultVariant = {};
@@ -11423,96 +11618,3054 @@ Object.assign(recordPreview, {
11423
11618
  splitVariantProps: (props)=>splitVariantProps(props, recordPreviewVariantMap)
11424
11619
  }); // @recipe(seed): record-preview
11425
11620
 
11426
- const RESIZE_MIN_WIDTH = 360;
11427
- const RESIZE_MAX_WIDTH = 720;
11428
- /**
11429
- * Right-side record peek panel -- Attio's own peek shows a Highlights card
11430
- * area and an Activity feed (measured live). Ported from your-moon/crisp's
11431
- * `RecordPanel` (`product/recordpanel/`), built ON `ResizablePanel`
11432
- * (`side="right" floating`) rather than crisp's own duplicate absolute-
11433
- * positioning/slide-in/resize-handle implementation -- the outer shell (the
11434
- * `@starting-style` slide-in, the floating elevation, the drag handle
11435
- * itself) all come from that primitive; this component only renders the
11436
- * CONTENT inside it.
11437
- */ 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 }) {
11438
- const titleId = React.useId();
11439
- const classNames = recordPreview();
11440
- // Which sections (your-moon/crisp#413) are expanded -- uncontrolled and
11441
- // independent (Attio's own Emails/Notes/Tasks open without closing one
11442
- // another): a plain `Set`, reset whenever the record itself changes so a
11443
- // stale open section doesn't carry over between two different records.
11444
- const [openSections, setOpenSections] = React.useState(()=>new Set());
11445
- // 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.
11446
- React.useEffect(()=>{
11447
- setOpenSections(new Set());
11621
+ // This code includes portions derived from radix-ui/primitives (https://github.com/radix-ui/primitives)
11622
+ // Used under the MIT License: https://opensource.org/licenses/MIT
11623
+ function usePresence(present) {
11624
+ const [node, setNode] = React__namespace.useState();
11625
+ const stylesRef = React__namespace.useRef(null);
11626
+ const prevPresentRef = React__namespace.useRef(present);
11627
+ const prevAnimationNameRef = React__namespace.useRef("none");
11628
+ const initialState = present ? "mounted" : "unmounted";
11629
+ const [state, send] = useStateMachine(initialState, {
11630
+ mounted: {
11631
+ UNMOUNT: "unmounted",
11632
+ ANIMATION_OUT: "unmountSuspended"
11633
+ },
11634
+ unmountSuspended: {
11635
+ MOUNT: "mounted",
11636
+ ANIMATION_END: "unmounted"
11637
+ },
11638
+ unmounted: {
11639
+ MOUNT: "mounted"
11640
+ }
11641
+ });
11642
+ React__namespace.useEffect(()=>{
11643
+ const currentAnimationName = getAnimationName(stylesRef.current);
11644
+ prevAnimationNameRef.current = state === "mounted" ? currentAnimationName : "none";
11448
11645
  }, [
11449
- record
11646
+ state
11450
11647
  ]);
11451
- const toggleSection = (key)=>setOpenSections((prev)=>{
11452
- const next = new Set(prev);
11453
- if (next.has(key)) next.delete(key);
11454
- else next.add(key);
11455
- return next;
11456
- });
11457
- // Close on Escape while open. Simpler than crisp's own version (crisp
11458
- // additionally checks `hasOpenFloatingLayer()` so an Escape meant for a
11459
- // nested Menu/Select/Popover doesn't also close this panel) -- crispy-seed
11460
- // has no equivalent floating-stack registry to read, so a consumer nesting
11461
- // its own floating layers inside `footer`/`sections` content should stop
11462
- // propagation on its own Escape handler.
11463
- React.useEffect(()=>{
11464
- if (!open) return;
11465
- const onKey = (event)=>{
11466
- if (event.key === "Escape") onClose();
11467
- };
11468
- document.addEventListener("keydown", onKey);
11469
- return ()=>document.removeEventListener("keydown", onKey);
11648
+ useLayoutEffect2(()=>{
11649
+ const styles = stylesRef.current;
11650
+ const wasPresent = prevPresentRef.current;
11651
+ const hasPresentChanged = wasPresent !== present;
11652
+ if (hasPresentChanged) {
11653
+ const prevAnimationName = prevAnimationNameRef.current;
11654
+ const currentAnimationName = getAnimationName(styles);
11655
+ if (present) {
11656
+ send("MOUNT");
11657
+ } else if (currentAnimationName === "none" || styles?.display === "none") {
11658
+ // If there is no exit animation or the element is hidden, animations won't run
11659
+ // so we unmount instantly
11660
+ send("UNMOUNT");
11661
+ } else {
11662
+ /**
11663
+ * When `present` changes to `false`, we check changes to animation-name to
11664
+ * determine whether an animation has started. We chose this approach (reading
11665
+ * computed styles) because there is no `animationrun` event and `animationstart`
11666
+ * fires after `animation-delay` has expired which would be too late.
11667
+ */ const isAnimating = prevAnimationName !== currentAnimationName;
11668
+ if (wasPresent && isAnimating) {
11669
+ send("ANIMATION_OUT");
11670
+ } else {
11671
+ send("UNMOUNT");
11672
+ }
11673
+ }
11674
+ prevPresentRef.current = present;
11675
+ }
11470
11676
  }, [
11471
- open,
11472
- onClose
11677
+ present,
11678
+ send
11473
11679
  ]);
11474
- if (!open || !record) return null;
11475
- const heading = title ?? record.name ?? "Record";
11476
- return /*#__PURE__*/ jsxRuntime.jsx(ResizablePanel, {
11477
- side: "right",
11478
- floating: true,
11479
- resizable: resizable,
11480
- width: width,
11481
- defaultSize: defaultWidth,
11482
- onWidthChange: onWidthChange,
11483
- min: minWidth,
11484
- max: maxWidth,
11485
- children: /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11486
- className: clsx(classNames.root, className),
11487
- ...rest,
11488
- role: "dialog",
11489
- "aria-labelledby": titleId,
11490
- children: [
11491
- /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11492
- className: classNames.toolbar,
11493
- children: [
11494
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.button, {
11495
- type: "button",
11496
- className: classNames.iconbtn,
11497
- "aria-label": closeLabel,
11498
- onClick: onClose,
11499
- children: /*#__PURE__*/ jsxRuntime.jsx(IconXmarkLine_default, {
11500
- size: 16
11501
- })
11502
- }),
11503
- (onPrevious || onNext) && /*#__PURE__*/ jsxRuntime.jsxs(Primitive.span, {
11504
- className: classNames.pager,
11505
- children: [
11506
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.button, {
11507
- type: "button",
11508
- className: classNames.navbtn,
11509
- "aria-label": previousRecordLabel,
11510
- disabled: !onPrevious,
11511
- onClick: onPrevious,
11512
- children: /*#__PURE__*/ jsxRuntime.jsx(IconChevronUpLine_default, {
11513
- size: 16
11514
- })
11515
- }),
11680
+ useLayoutEffect2(()=>{
11681
+ if (node) {
11682
+ let timeoutId;
11683
+ const ownerWindow = node.ownerDocument.defaultView ?? window;
11684
+ /**
11685
+ * Triggering an ANIMATION_OUT during an ANIMATION_IN will fire an `animationcancel`
11686
+ * event for ANIMATION_IN after we have entered `unmountSuspended` state. So, we
11687
+ * make sure we only trigger ANIMATION_END for the currently active animation.
11688
+ */ const handleAnimationEnd = (event)=>{
11689
+ const currentAnimationName = getAnimationName(stylesRef.current);
11690
+ const isCurrentAnimation = currentAnimationName.includes(event.animationName);
11691
+ if (event.target === node && isCurrentAnimation) {
11692
+ // With React 18 concurrency this update is applied a frame after the
11693
+ // animation ends, creating a flash of visible content. By setting the
11694
+ // animation fill mode to "forwards", we force the node to keep the
11695
+ // styles of the last keyframe, removing the flash.
11696
+ //
11697
+ // Previously we flushed the update via ReactDom.flushSync, but with
11698
+ // exit animations this resulted in the node being removed from the
11699
+ // DOM before the synthetic animationEnd event was dispatched, meaning
11700
+ // user-provided event handlers would not be called.
11701
+ // https://github.com/radix-ui/primitives/pull/1849
11702
+ send("ANIMATION_END");
11703
+ if (!prevPresentRef.current) {
11704
+ const currentFillMode = node.style.animationFillMode;
11705
+ node.style.animationFillMode = "forwards";
11706
+ // Reset the style after the node had time to unmount (for cases
11707
+ // where the component chooses not to unmount). Doing this any
11708
+ // sooner than `setTimeout` (e.g. with `requestAnimationFrame`)
11709
+ // still causes a flash.
11710
+ timeoutId = ownerWindow.setTimeout(()=>{
11711
+ if (node.style.animationFillMode === "forwards") {
11712
+ node.style.animationFillMode = currentFillMode;
11713
+ }
11714
+ });
11715
+ }
11716
+ }
11717
+ };
11718
+ const handleAnimationStart = (event)=>{
11719
+ if (event.target === node) {
11720
+ // if animation occurred, store its name as the previous animation.
11721
+ prevAnimationNameRef.current = getAnimationName(stylesRef.current);
11722
+ }
11723
+ };
11724
+ node.addEventListener("animationstart", handleAnimationStart);
11725
+ node.addEventListener("animationcancel", handleAnimationEnd);
11726
+ node.addEventListener("animationend", handleAnimationEnd);
11727
+ return ()=>{
11728
+ ownerWindow.clearTimeout(timeoutId);
11729
+ node.removeEventListener("animationstart", handleAnimationStart);
11730
+ node.removeEventListener("animationcancel", handleAnimationEnd);
11731
+ node.removeEventListener("animationend", handleAnimationEnd);
11732
+ };
11733
+ } else {
11734
+ // Transition to the unmounted state if the node is removed prematurely.
11735
+ // We avoid doing so during cleanup as the node may change but still exist.
11736
+ send("ANIMATION_END");
11737
+ }
11738
+ }, [
11739
+ node,
11740
+ send
11741
+ ]);
11742
+ return {
11743
+ isPresent: [
11744
+ "mounted",
11745
+ "unmountSuspended"
11746
+ ].includes(state),
11747
+ ref: React__namespace.useCallback((node)=>{
11748
+ if (node) stylesRef.current = getComputedStyle(node);
11749
+ setNode(node ?? undefined);
11750
+ }, [])
11751
+ };
11752
+ }
11753
+ /* -----------------------------------------------------------------------------------------------*/ function getAnimationName(styles) {
11754
+ return styles?.animationName || "none";
11755
+ }
11756
+ function useStateMachine(initialState, machine) {
11757
+ return React__namespace.useReducer((state, event)=>{
11758
+ const nextState = machine[state][event];
11759
+ return nextState ?? state;
11760
+ }, initialState);
11761
+ }
11762
+ const PresenceContext = /*#__PURE__*/ React.createContext(null);
11763
+ PresenceContext.Provider;
11764
+
11765
+ var __defProp$1 = Object.defineProperty;
11766
+ var __name$1 = (target, value)=>__defProp$1(target, "name", {
11767
+ value,
11768
+ configurable: true
11769
+ });
11770
+ function useSize(element) {
11771
+ const [size, setSize] = React__namespace.useState(void 0);
11772
+ useLayoutEffect2(()=>{
11773
+ if (element) {
11774
+ setSize({
11775
+ width: element.offsetWidth,
11776
+ height: element.offsetHeight
11777
+ });
11778
+ const resizeObserver = new ResizeObserver((entries)=>{
11779
+ if (!Array.isArray(entries)) {
11780
+ return;
11781
+ }
11782
+ if (!entries.length) {
11783
+ return;
11784
+ }
11785
+ const entry = entries[0];
11786
+ let width;
11787
+ let height;
11788
+ if ("borderBoxSize" in entry) {
11789
+ const borderSizeEntry = entry["borderBoxSize"];
11790
+ const borderSize = Array.isArray(borderSizeEntry) ? borderSizeEntry[0] : borderSizeEntry;
11791
+ width = borderSize["inlineSize"];
11792
+ height = borderSize["blockSize"];
11793
+ } else {
11794
+ width = element.offsetWidth;
11795
+ height = element.offsetHeight;
11796
+ }
11797
+ setSize({
11798
+ width,
11799
+ height
11800
+ });
11801
+ });
11802
+ resizeObserver.observe(element, {
11803
+ box: "border-box"
11804
+ });
11805
+ return ()=>resizeObserver.unobserve(element);
11806
+ } else {
11807
+ setSize(void 0);
11808
+ }
11809
+ }, [
11810
+ element
11811
+ ]);
11812
+ return size;
11813
+ }
11814
+ __name$1(useSize, "useSize");
11815
+
11816
+ function useSupports(selector) {
11817
+ const [isSupported, setIsSupported] = React.useState(null);
11818
+ React.useEffect(()=>{
11819
+ setIsSupported(CSS.supports(selector));
11820
+ }, [
11821
+ selector
11822
+ ]);
11823
+ return isSupported;
11824
+ }
11825
+
11826
+ var __defProp = Object.defineProperty;
11827
+ var __name = (target, value)=>__defProp(target, "name", {
11828
+ value,
11829
+ configurable: true
11830
+ });
11831
+ function useCallbackRef(callback) {
11832
+ const callbackRef = React__namespace.useRef(callback);
11833
+ React__namespace.useEffect(()=>{
11834
+ callbackRef.current = callback;
11835
+ });
11836
+ return React__namespace.useMemo(()=>(...args)=>callbackRef.current?.(...args), []);
11837
+ }
11838
+ __name(useCallbackRef, "useCallbackRef");
11839
+
11840
+ function AutoHeight(userOptions = {}) {
11841
+ let emblaApi;
11842
+ let slideHeights = [];
11843
+ const heightEvents = [
11844
+ 'select',
11845
+ 'slideFocus'
11846
+ ];
11847
+ function init(emblaApiInstance) {
11848
+ emblaApi = emblaApiInstance;
11849
+ const { options: { axis }, slideRects } = emblaApi.internalEngine();
11850
+ if (axis === 'y') return;
11851
+ slideHeights = slideRects.map((slideRect)=>slideRect.height);
11852
+ heightEvents.forEach((evt)=>emblaApi.on(evt, setContainerHeight));
11853
+ setContainerHeight();
11854
+ }
11855
+ function destroy() {
11856
+ heightEvents.forEach((evt)=>emblaApi.off(evt, setContainerHeight));
11857
+ const container = emblaApi.containerNode();
11858
+ container.style.height = '';
11859
+ if (!container.getAttribute('style')) container.removeAttribute('style');
11860
+ }
11861
+ function highestInView() {
11862
+ const { slideRegistry } = emblaApi.internalEngine();
11863
+ const selectedIndexes = slideRegistry[emblaApi.selectedScrollSnap()];
11864
+ if (!selectedIndexes) return null;
11865
+ return selectedIndexes.map((index)=>slideHeights[index]).reduce((a, b)=>Math.max(a, b), 0);
11866
+ }
11867
+ function setContainerHeight() {
11868
+ const height = highestInView();
11869
+ if (height === null) return;
11870
+ emblaApi.containerNode().style.height = `${highestInView()}px`;
11871
+ }
11872
+ const self = {
11873
+ name: 'autoHeight',
11874
+ options: userOptions,
11875
+ init,
11876
+ destroy
11877
+ };
11878
+ return self;
11879
+ }
11880
+ AutoHeight.globalOptions = undefined;
11881
+
11882
+ function isObject$1(subject) {
11883
+ return Object.prototype.toString.call(subject) === '[object Object]';
11884
+ }
11885
+ function isRecord(subject) {
11886
+ return isObject$1(subject) || Array.isArray(subject);
11887
+ }
11888
+ function canUseDOM() {
11889
+ return !!(typeof window !== 'undefined' && window.document && window.document.createElement);
11890
+ }
11891
+ function areOptionsEqual(optionsA, optionsB) {
11892
+ const optionsAKeys = Object.keys(optionsA);
11893
+ const optionsBKeys = Object.keys(optionsB);
11894
+ if (optionsAKeys.length !== optionsBKeys.length) return false;
11895
+ const breakpointsA = JSON.stringify(Object.keys(optionsA.breakpoints || {}));
11896
+ const breakpointsB = JSON.stringify(Object.keys(optionsB.breakpoints || {}));
11897
+ if (breakpointsA !== breakpointsB) return false;
11898
+ return optionsAKeys.every((key)=>{
11899
+ const valueA = optionsA[key];
11900
+ const valueB = optionsB[key];
11901
+ if (typeof valueA === 'function') return `${valueA}` === `${valueB}`;
11902
+ if (!isRecord(valueA) || !isRecord(valueB)) return valueA === valueB;
11903
+ return areOptionsEqual(valueA, valueB);
11904
+ });
11905
+ }
11906
+ function sortAndMapPluginToOptions(plugins) {
11907
+ return plugins.concat().sort((a, b)=>a.name > b.name ? 1 : -1).map((plugin)=>plugin.options);
11908
+ }
11909
+ function arePluginsEqual(pluginsA, pluginsB) {
11910
+ if (pluginsA.length !== pluginsB.length) return false;
11911
+ const optionsA = sortAndMapPluginToOptions(pluginsA);
11912
+ const optionsB = sortAndMapPluginToOptions(pluginsB);
11913
+ return optionsA.every((optionA, index)=>{
11914
+ const optionB = optionsB[index];
11915
+ return areOptionsEqual(optionA, optionB);
11916
+ });
11917
+ }
11918
+
11919
+ function isNumber(subject) {
11920
+ return typeof subject === 'number';
11921
+ }
11922
+ function isString(subject) {
11923
+ return typeof subject === 'string';
11924
+ }
11925
+ function isBoolean(subject) {
11926
+ return typeof subject === 'boolean';
11927
+ }
11928
+ function isObject(subject) {
11929
+ return Object.prototype.toString.call(subject) === '[object Object]';
11930
+ }
11931
+ function mathAbs(n) {
11932
+ return Math.abs(n);
11933
+ }
11934
+ function mathSign(n) {
11935
+ return Math.sign(n);
11936
+ }
11937
+ function deltaAbs(valueB, valueA) {
11938
+ return mathAbs(valueB - valueA);
11939
+ }
11940
+ function factorAbs(valueB, valueA) {
11941
+ if (valueB === 0 || valueA === 0) return 0;
11942
+ if (mathAbs(valueB) <= mathAbs(valueA)) return 0;
11943
+ const diff = deltaAbs(mathAbs(valueB), mathAbs(valueA));
11944
+ return mathAbs(diff / valueB);
11945
+ }
11946
+ function roundToTwoDecimals(num) {
11947
+ return Math.round(num * 100) / 100;
11948
+ }
11949
+ function arrayKeys(array) {
11950
+ return objectKeys(array).map(Number);
11951
+ }
11952
+ function arrayLast(array) {
11953
+ return array[arrayLastIndex(array)];
11954
+ }
11955
+ function arrayLastIndex(array) {
11956
+ return Math.max(0, array.length - 1);
11957
+ }
11958
+ function arrayIsLastIndex(array, index) {
11959
+ return index === arrayLastIndex(array);
11960
+ }
11961
+ function arrayFromNumber(n, startAt = 0) {
11962
+ return Array.from(Array(n), (_, i)=>startAt + i);
11963
+ }
11964
+ function objectKeys(object) {
11965
+ return Object.keys(object);
11966
+ }
11967
+ function objectsMergeDeep(objectA, objectB) {
11968
+ return [
11969
+ objectA,
11970
+ objectB
11971
+ ].reduce((mergedObjects, currentObject)=>{
11972
+ objectKeys(currentObject).forEach((key)=>{
11973
+ const valueA = mergedObjects[key];
11974
+ const valueB = currentObject[key];
11975
+ const areObjects = isObject(valueA) && isObject(valueB);
11976
+ mergedObjects[key] = areObjects ? objectsMergeDeep(valueA, valueB) : valueB;
11977
+ });
11978
+ return mergedObjects;
11979
+ }, {});
11980
+ }
11981
+ function isMouseEvent(evt, ownerWindow) {
11982
+ return typeof ownerWindow.MouseEvent !== 'undefined' && evt instanceof ownerWindow.MouseEvent;
11983
+ }
11984
+ function Alignment(align, viewSize) {
11985
+ const predefined = {
11986
+ start,
11987
+ center,
11988
+ end
11989
+ };
11990
+ function start() {
11991
+ return 0;
11992
+ }
11993
+ function center(n) {
11994
+ return end(n) / 2;
11995
+ }
11996
+ function end(n) {
11997
+ return viewSize - n;
11998
+ }
11999
+ function measure(n, index) {
12000
+ if (isString(align)) return predefined[align](n);
12001
+ return align(viewSize, n, index);
12002
+ }
12003
+ const self = {
12004
+ measure
12005
+ };
12006
+ return self;
12007
+ }
12008
+ function EventStore() {
12009
+ let listeners = [];
12010
+ function add(node, type, handler, options = {
12011
+ passive: true
12012
+ }) {
12013
+ let removeListener;
12014
+ if ('addEventListener' in node) {
12015
+ node.addEventListener(type, handler, options);
12016
+ removeListener = ()=>node.removeEventListener(type, handler, options);
12017
+ } else {
12018
+ const legacyMediaQueryList = node;
12019
+ legacyMediaQueryList.addListener(handler);
12020
+ removeListener = ()=>legacyMediaQueryList.removeListener(handler);
12021
+ }
12022
+ listeners.push(removeListener);
12023
+ return self;
12024
+ }
12025
+ function clear() {
12026
+ listeners = listeners.filter((remove)=>remove());
12027
+ }
12028
+ const self = {
12029
+ add,
12030
+ clear
12031
+ };
12032
+ return self;
12033
+ }
12034
+ function Animations(ownerDocument, ownerWindow, update, render) {
12035
+ const documentVisibleHandler = EventStore();
12036
+ const fixedTimeStep = 1000 / 60;
12037
+ let lastTimeStamp = null;
12038
+ let accumulatedTime = 0;
12039
+ let animationId = 0;
12040
+ function init() {
12041
+ documentVisibleHandler.add(ownerDocument, 'visibilitychange', ()=>{
12042
+ if (ownerDocument.hidden) reset();
12043
+ });
12044
+ }
12045
+ function destroy() {
12046
+ stop();
12047
+ documentVisibleHandler.clear();
12048
+ }
12049
+ function animate(timeStamp) {
12050
+ if (!animationId) return;
12051
+ if (!lastTimeStamp) {
12052
+ lastTimeStamp = timeStamp;
12053
+ update();
12054
+ update();
12055
+ }
12056
+ const timeElapsed = timeStamp - lastTimeStamp;
12057
+ lastTimeStamp = timeStamp;
12058
+ accumulatedTime += timeElapsed;
12059
+ while(accumulatedTime >= fixedTimeStep){
12060
+ update();
12061
+ accumulatedTime -= fixedTimeStep;
12062
+ }
12063
+ const alpha = accumulatedTime / fixedTimeStep;
12064
+ render(alpha);
12065
+ if (animationId) {
12066
+ animationId = ownerWindow.requestAnimationFrame(animate);
12067
+ }
12068
+ }
12069
+ function start() {
12070
+ if (animationId) return;
12071
+ animationId = ownerWindow.requestAnimationFrame(animate);
12072
+ }
12073
+ function stop() {
12074
+ ownerWindow.cancelAnimationFrame(animationId);
12075
+ lastTimeStamp = null;
12076
+ accumulatedTime = 0;
12077
+ animationId = 0;
12078
+ }
12079
+ function reset() {
12080
+ lastTimeStamp = null;
12081
+ accumulatedTime = 0;
12082
+ }
12083
+ const self = {
12084
+ init,
12085
+ destroy,
12086
+ start,
12087
+ stop,
12088
+ update,
12089
+ render
12090
+ };
12091
+ return self;
12092
+ }
12093
+ function Axis(axis, contentDirection) {
12094
+ const isRightToLeft = contentDirection === 'rtl';
12095
+ const isVertical = axis === 'y';
12096
+ const scroll = isVertical ? 'y' : 'x';
12097
+ const cross = isVertical ? 'x' : 'y';
12098
+ const sign = !isVertical && isRightToLeft ? -1 : 1;
12099
+ const startEdge = getStartEdge();
12100
+ const endEdge = getEndEdge();
12101
+ function measureSize(nodeRect) {
12102
+ const { height, width } = nodeRect;
12103
+ return isVertical ? height : width;
12104
+ }
12105
+ function getStartEdge() {
12106
+ if (isVertical) return 'top';
12107
+ return isRightToLeft ? 'right' : 'left';
12108
+ }
12109
+ function getEndEdge() {
12110
+ if (isVertical) return 'bottom';
12111
+ return isRightToLeft ? 'left' : 'right';
12112
+ }
12113
+ function direction(n) {
12114
+ return n * sign;
12115
+ }
12116
+ const self = {
12117
+ scroll,
12118
+ cross,
12119
+ startEdge,
12120
+ endEdge,
12121
+ measureSize,
12122
+ direction
12123
+ };
12124
+ return self;
12125
+ }
12126
+ function Limit(min = 0, max = 0) {
12127
+ const length = mathAbs(min - max);
12128
+ function reachedMin(n) {
12129
+ return n < min;
12130
+ }
12131
+ function reachedMax(n) {
12132
+ return n > max;
12133
+ }
12134
+ function reachedAny(n) {
12135
+ return reachedMin(n) || reachedMax(n);
12136
+ }
12137
+ function constrain(n) {
12138
+ if (!reachedAny(n)) return n;
12139
+ return reachedMin(n) ? min : max;
12140
+ }
12141
+ function removeOffset(n) {
12142
+ if (!length) return n;
12143
+ return n - length * Math.ceil((n - max) / length);
12144
+ }
12145
+ const self = {
12146
+ length,
12147
+ max,
12148
+ min,
12149
+ constrain,
12150
+ reachedAny,
12151
+ reachedMax,
12152
+ reachedMin,
12153
+ removeOffset
12154
+ };
12155
+ return self;
12156
+ }
12157
+ function Counter(max, start, loop) {
12158
+ const { constrain } = Limit(0, max);
12159
+ const loopEnd = max + 1;
12160
+ let counter = withinLimit(start);
12161
+ function withinLimit(n) {
12162
+ return !loop ? constrain(n) : mathAbs((loopEnd + n) % loopEnd);
12163
+ }
12164
+ function get() {
12165
+ return counter;
12166
+ }
12167
+ function set(n) {
12168
+ counter = withinLimit(n);
12169
+ return self;
12170
+ }
12171
+ function add(n) {
12172
+ return clone().set(get() + n);
12173
+ }
12174
+ function clone() {
12175
+ return Counter(max, get(), loop);
12176
+ }
12177
+ const self = {
12178
+ get,
12179
+ set,
12180
+ add,
12181
+ clone
12182
+ };
12183
+ return self;
12184
+ }
12185
+ function DragHandler(axis, rootNode, ownerDocument, ownerWindow, target, dragTracker, location, animation, scrollTo, scrollBody, scrollTarget, index, eventHandler, percentOfView, dragFree, dragThreshold, skipSnaps, baseFriction, watchDrag) {
12186
+ const { cross: crossAxis, direction } = axis;
12187
+ const focusNodes = [
12188
+ 'INPUT',
12189
+ 'SELECT',
12190
+ 'TEXTAREA'
12191
+ ];
12192
+ const nonPassiveEvent = {
12193
+ passive: false
12194
+ };
12195
+ const initEvents = EventStore();
12196
+ const dragEvents = EventStore();
12197
+ const goToNextThreshold = Limit(50, 225).constrain(percentOfView.measure(20));
12198
+ const snapForceBoost = {
12199
+ mouse: 300,
12200
+ touch: 400
12201
+ };
12202
+ const freeForceBoost = {
12203
+ mouse: 500,
12204
+ touch: 600
12205
+ };
12206
+ const baseSpeed = dragFree ? 43 : 25;
12207
+ let isMoving = false;
12208
+ let startScroll = 0;
12209
+ let startCross = 0;
12210
+ let pointerIsDown = false;
12211
+ let preventScroll = false;
12212
+ let preventClick = false;
12213
+ let isMouse = false;
12214
+ function init(emblaApi) {
12215
+ if (!watchDrag) return;
12216
+ function downIfAllowed(evt) {
12217
+ if (isBoolean(watchDrag) || watchDrag(emblaApi, evt)) down(evt);
12218
+ }
12219
+ const node = rootNode;
12220
+ 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);
12221
+ }
12222
+ function destroy() {
12223
+ initEvents.clear();
12224
+ dragEvents.clear();
12225
+ }
12226
+ function addDragEvents() {
12227
+ const node = isMouse ? ownerDocument : rootNode;
12228
+ dragEvents.add(node, 'touchmove', move, nonPassiveEvent).add(node, 'touchend', up).add(node, 'mousemove', move, nonPassiveEvent).add(node, 'mouseup', up);
12229
+ }
12230
+ function isFocusNode(node) {
12231
+ const nodeName = node.nodeName || '';
12232
+ return focusNodes.includes(nodeName);
12233
+ }
12234
+ function forceBoost() {
12235
+ const boost = dragFree ? freeForceBoost : snapForceBoost;
12236
+ const type = isMouse ? 'mouse' : 'touch';
12237
+ return boost[type];
12238
+ }
12239
+ function allowedForce(force, targetChanged) {
12240
+ const next = index.add(mathSign(force) * -1);
12241
+ const baseForce = scrollTarget.byDistance(force, !dragFree).distance;
12242
+ if (dragFree || mathAbs(force) < goToNextThreshold) return baseForce;
12243
+ if (skipSnaps && targetChanged) return baseForce * 0.5;
12244
+ return scrollTarget.byIndex(next.get(), 0).distance;
12245
+ }
12246
+ function down(evt) {
12247
+ const isMouseEvt = isMouseEvent(evt, ownerWindow);
12248
+ isMouse = isMouseEvt;
12249
+ preventClick = dragFree && isMouseEvt && !evt.buttons && isMoving;
12250
+ isMoving = deltaAbs(target.get(), location.get()) >= 2;
12251
+ if (isMouseEvt && evt.button !== 0) return;
12252
+ if (isFocusNode(evt.target)) return;
12253
+ pointerIsDown = true;
12254
+ dragTracker.pointerDown(evt);
12255
+ scrollBody.useFriction(0).useDuration(0);
12256
+ target.set(location);
12257
+ addDragEvents();
12258
+ startScroll = dragTracker.readPoint(evt);
12259
+ startCross = dragTracker.readPoint(evt, crossAxis);
12260
+ eventHandler.emit('pointerDown');
12261
+ }
12262
+ function move(evt) {
12263
+ const isTouchEvt = !isMouseEvent(evt, ownerWindow);
12264
+ if (isTouchEvt && evt.touches.length >= 2) return up(evt);
12265
+ const lastScroll = dragTracker.readPoint(evt);
12266
+ const lastCross = dragTracker.readPoint(evt, crossAxis);
12267
+ const diffScroll = deltaAbs(lastScroll, startScroll);
12268
+ const diffCross = deltaAbs(lastCross, startCross);
12269
+ if (!preventScroll && !isMouse) {
12270
+ if (!evt.cancelable) return up(evt);
12271
+ preventScroll = diffScroll > diffCross;
12272
+ if (!preventScroll) return up(evt);
12273
+ }
12274
+ const diff = dragTracker.pointerMove(evt);
12275
+ if (diffScroll > dragThreshold) preventClick = true;
12276
+ scrollBody.useFriction(0.3).useDuration(0.75);
12277
+ animation.start();
12278
+ target.add(direction(diff));
12279
+ evt.preventDefault();
12280
+ }
12281
+ function up(evt) {
12282
+ const currentLocation = scrollTarget.byDistance(0, false);
12283
+ const targetChanged = currentLocation.index !== index.get();
12284
+ const rawForce = dragTracker.pointerUp(evt) * forceBoost();
12285
+ const force = allowedForce(direction(rawForce), targetChanged);
12286
+ const forceFactor = factorAbs(rawForce, force);
12287
+ const speed = baseSpeed - 10 * forceFactor;
12288
+ const friction = baseFriction + forceFactor / 50;
12289
+ preventScroll = false;
12290
+ pointerIsDown = false;
12291
+ dragEvents.clear();
12292
+ scrollBody.useDuration(speed).useFriction(friction);
12293
+ scrollTo.distance(force, !dragFree);
12294
+ isMouse = false;
12295
+ eventHandler.emit('pointerUp');
12296
+ }
12297
+ function click(evt) {
12298
+ if (preventClick) {
12299
+ evt.stopPropagation();
12300
+ evt.preventDefault();
12301
+ preventClick = false;
12302
+ }
12303
+ }
12304
+ function pointerDown() {
12305
+ return pointerIsDown;
12306
+ }
12307
+ const self = {
12308
+ init,
12309
+ destroy,
12310
+ pointerDown
12311
+ };
12312
+ return self;
12313
+ }
12314
+ function DragTracker(axis, ownerWindow) {
12315
+ const logInterval = 170;
12316
+ let startEvent;
12317
+ let lastEvent;
12318
+ function readTime(evt) {
12319
+ return evt.timeStamp;
12320
+ }
12321
+ function readPoint(evt, evtAxis) {
12322
+ const property = evtAxis || axis.scroll;
12323
+ const coord = `client${property === 'x' ? 'X' : 'Y'}`;
12324
+ return (isMouseEvent(evt, ownerWindow) ? evt : evt.touches[0])[coord];
12325
+ }
12326
+ function pointerDown(evt) {
12327
+ startEvent = evt;
12328
+ lastEvent = evt;
12329
+ return readPoint(evt);
12330
+ }
12331
+ function pointerMove(evt) {
12332
+ const diff = readPoint(evt) - readPoint(lastEvent);
12333
+ const expired = readTime(evt) - readTime(startEvent) > logInterval;
12334
+ lastEvent = evt;
12335
+ if (expired) startEvent = evt;
12336
+ return diff;
12337
+ }
12338
+ function pointerUp(evt) {
12339
+ if (!startEvent || !lastEvent) return 0;
12340
+ const diffDrag = readPoint(lastEvent) - readPoint(startEvent);
12341
+ const diffTime = readTime(evt) - readTime(startEvent);
12342
+ const expired = readTime(evt) - readTime(lastEvent) > logInterval;
12343
+ const force = diffDrag / diffTime;
12344
+ const isFlick = diffTime && !expired && mathAbs(force) > 0.1;
12345
+ return isFlick ? force : 0;
12346
+ }
12347
+ const self = {
12348
+ pointerDown,
12349
+ pointerMove,
12350
+ pointerUp,
12351
+ readPoint
12352
+ };
12353
+ return self;
12354
+ }
12355
+ function NodeRects() {
12356
+ function measure(node) {
12357
+ const { offsetTop, offsetLeft, offsetWidth, offsetHeight } = node;
12358
+ const offset = {
12359
+ top: offsetTop,
12360
+ right: offsetLeft + offsetWidth,
12361
+ bottom: offsetTop + offsetHeight,
12362
+ left: offsetLeft,
12363
+ width: offsetWidth,
12364
+ height: offsetHeight
12365
+ };
12366
+ return offset;
12367
+ }
12368
+ const self = {
12369
+ measure
12370
+ };
12371
+ return self;
12372
+ }
12373
+ function PercentOfView(viewSize) {
12374
+ function measure(n) {
12375
+ return viewSize * (n / 100);
12376
+ }
12377
+ const self = {
12378
+ measure
12379
+ };
12380
+ return self;
12381
+ }
12382
+ function ResizeHandler(container, eventHandler, ownerWindow, slides, axis, watchResize, nodeRects) {
12383
+ const observeNodes = [
12384
+ container
12385
+ ].concat(slides);
12386
+ let resizeObserver;
12387
+ let containerSize;
12388
+ let slideSizes = [];
12389
+ let destroyed = false;
12390
+ function readSize(node) {
12391
+ return axis.measureSize(nodeRects.measure(node));
12392
+ }
12393
+ function init(emblaApi) {
12394
+ if (!watchResize) return;
12395
+ containerSize = readSize(container);
12396
+ slideSizes = slides.map(readSize);
12397
+ function defaultCallback(entries) {
12398
+ for (const entry of entries){
12399
+ if (destroyed) return;
12400
+ const isContainer = entry.target === container;
12401
+ const slideIndex = slides.indexOf(entry.target);
12402
+ const lastSize = isContainer ? containerSize : slideSizes[slideIndex];
12403
+ const newSize = readSize(isContainer ? container : slides[slideIndex]);
12404
+ const diffSize = mathAbs(newSize - lastSize);
12405
+ if (diffSize >= 0.5) {
12406
+ emblaApi.reInit();
12407
+ eventHandler.emit('resize');
12408
+ break;
12409
+ }
12410
+ }
12411
+ }
12412
+ resizeObserver = new ResizeObserver((entries)=>{
12413
+ if (isBoolean(watchResize) || watchResize(emblaApi, entries)) {
12414
+ defaultCallback(entries);
12415
+ }
12416
+ });
12417
+ ownerWindow.requestAnimationFrame(()=>{
12418
+ observeNodes.forEach((node)=>resizeObserver.observe(node));
12419
+ });
12420
+ }
12421
+ function destroy() {
12422
+ destroyed = true;
12423
+ if (resizeObserver) resizeObserver.disconnect();
12424
+ }
12425
+ const self = {
12426
+ init,
12427
+ destroy
12428
+ };
12429
+ return self;
12430
+ }
12431
+ function ScrollBody(location, offsetLocation, previousLocation, target, baseDuration, baseFriction) {
12432
+ let scrollVelocity = 0;
12433
+ let scrollDirection = 0;
12434
+ let scrollDuration = baseDuration;
12435
+ let scrollFriction = baseFriction;
12436
+ let rawLocation = location.get();
12437
+ let rawLocationPrevious = 0;
12438
+ function seek() {
12439
+ const displacement = target.get() - location.get();
12440
+ const isInstant = !scrollDuration;
12441
+ let scrollDistance = 0;
12442
+ if (isInstant) {
12443
+ scrollVelocity = 0;
12444
+ previousLocation.set(target);
12445
+ location.set(target);
12446
+ scrollDistance = displacement;
12447
+ } else {
12448
+ previousLocation.set(location);
12449
+ scrollVelocity += displacement / scrollDuration;
12450
+ scrollVelocity *= scrollFriction;
12451
+ rawLocation += scrollVelocity;
12452
+ location.add(scrollVelocity);
12453
+ scrollDistance = rawLocation - rawLocationPrevious;
12454
+ }
12455
+ scrollDirection = mathSign(scrollDistance);
12456
+ rawLocationPrevious = rawLocation;
12457
+ return self;
12458
+ }
12459
+ function settled() {
12460
+ const diff = target.get() - offsetLocation.get();
12461
+ return mathAbs(diff) < 0.001;
12462
+ }
12463
+ function duration() {
12464
+ return scrollDuration;
12465
+ }
12466
+ function direction() {
12467
+ return scrollDirection;
12468
+ }
12469
+ function velocity() {
12470
+ return scrollVelocity;
12471
+ }
12472
+ function useBaseDuration() {
12473
+ return useDuration(baseDuration);
12474
+ }
12475
+ function useBaseFriction() {
12476
+ return useFriction(baseFriction);
12477
+ }
12478
+ function useDuration(n) {
12479
+ scrollDuration = n;
12480
+ return self;
12481
+ }
12482
+ function useFriction(n) {
12483
+ scrollFriction = n;
12484
+ return self;
12485
+ }
12486
+ const self = {
12487
+ direction,
12488
+ duration,
12489
+ velocity,
12490
+ seek,
12491
+ settled,
12492
+ useBaseFriction,
12493
+ useBaseDuration,
12494
+ useFriction,
12495
+ useDuration
12496
+ };
12497
+ return self;
12498
+ }
12499
+ function ScrollBounds(limit, location, target, scrollBody, percentOfView) {
12500
+ const pullBackThreshold = percentOfView.measure(10);
12501
+ const edgeOffsetTolerance = percentOfView.measure(50);
12502
+ const frictionLimit = Limit(0.1, 0.99);
12503
+ let disabled = false;
12504
+ function shouldConstrain() {
12505
+ if (disabled) return false;
12506
+ if (!limit.reachedAny(target.get())) return false;
12507
+ if (!limit.reachedAny(location.get())) return false;
12508
+ return true;
12509
+ }
12510
+ function constrain(pointerDown) {
12511
+ if (!shouldConstrain()) return;
12512
+ const edge = limit.reachedMin(location.get()) ? 'min' : 'max';
12513
+ const diffToEdge = mathAbs(limit[edge] - location.get());
12514
+ const diffToTarget = target.get() - location.get();
12515
+ const friction = frictionLimit.constrain(diffToEdge / edgeOffsetTolerance);
12516
+ target.subtract(diffToTarget * friction);
12517
+ if (!pointerDown && mathAbs(diffToTarget) < pullBackThreshold) {
12518
+ target.set(limit.constrain(target.get()));
12519
+ scrollBody.useDuration(25).useBaseFriction();
12520
+ }
12521
+ }
12522
+ function toggleActive(active) {
12523
+ disabled = !active;
12524
+ }
12525
+ const self = {
12526
+ shouldConstrain,
12527
+ constrain,
12528
+ toggleActive
12529
+ };
12530
+ return self;
12531
+ }
12532
+ function ScrollContain(viewSize, contentSize, snapsAligned, containScroll, pixelTolerance) {
12533
+ const scrollBounds = Limit(-contentSize + viewSize, 0);
12534
+ const snapsBounded = measureBounded();
12535
+ const scrollContainLimit = findScrollContainLimit();
12536
+ const snapsContained = measureContained();
12537
+ function usePixelTolerance(bound, snap) {
12538
+ return deltaAbs(bound, snap) <= 1;
12539
+ }
12540
+ function findScrollContainLimit() {
12541
+ const startSnap = snapsBounded[0];
12542
+ const endSnap = arrayLast(snapsBounded);
12543
+ const min = snapsBounded.lastIndexOf(startSnap);
12544
+ const max = snapsBounded.indexOf(endSnap) + 1;
12545
+ return Limit(min, max);
12546
+ }
12547
+ function measureBounded() {
12548
+ return snapsAligned.map((snapAligned, index)=>{
12549
+ const { min, max } = scrollBounds;
12550
+ const snap = scrollBounds.constrain(snapAligned);
12551
+ const isFirst = !index;
12552
+ const isLast = arrayIsLastIndex(snapsAligned, index);
12553
+ if (isFirst) return max;
12554
+ if (isLast) return min;
12555
+ if (usePixelTolerance(min, snap)) return min;
12556
+ if (usePixelTolerance(max, snap)) return max;
12557
+ return snap;
12558
+ }).map((scrollBound)=>parseFloat(scrollBound.toFixed(3)));
12559
+ }
12560
+ function measureContained() {
12561
+ if (contentSize <= viewSize + pixelTolerance) return [
12562
+ scrollBounds.max
12563
+ ];
12564
+ if (containScroll === 'keepSnaps') return snapsBounded;
12565
+ const { min, max } = scrollContainLimit;
12566
+ return snapsBounded.slice(min, max);
12567
+ }
12568
+ const self = {
12569
+ snapsContained,
12570
+ scrollContainLimit
12571
+ };
12572
+ return self;
12573
+ }
12574
+ function ScrollLimit(contentSize, scrollSnaps, loop) {
12575
+ const max = scrollSnaps[0];
12576
+ const min = loop ? max - contentSize : arrayLast(scrollSnaps);
12577
+ const limit = Limit(min, max);
12578
+ const self = {
12579
+ limit
12580
+ };
12581
+ return self;
12582
+ }
12583
+ function ScrollLooper(contentSize, limit, location, vectors) {
12584
+ const jointSafety = 0.1;
12585
+ const min = limit.min + jointSafety;
12586
+ const max = limit.max + jointSafety;
12587
+ const { reachedMin, reachedMax } = Limit(min, max);
12588
+ function shouldLoop(direction) {
12589
+ if (direction === 1) return reachedMax(location.get());
12590
+ if (direction === -1) return reachedMin(location.get());
12591
+ return false;
12592
+ }
12593
+ function loop(direction) {
12594
+ if (!shouldLoop(direction)) return;
12595
+ const loopDistance = contentSize * (direction * -1);
12596
+ vectors.forEach((v)=>v.add(loopDistance));
12597
+ }
12598
+ const self = {
12599
+ loop
12600
+ };
12601
+ return self;
12602
+ }
12603
+ function ScrollProgress(limit) {
12604
+ const { max, length } = limit;
12605
+ function get(n) {
12606
+ const currentLocation = n - max;
12607
+ return length ? currentLocation / -length : 0;
12608
+ }
12609
+ const self = {
12610
+ get
12611
+ };
12612
+ return self;
12613
+ }
12614
+ function ScrollSnaps(axis, alignment, containerRect, slideRects, slidesToScroll) {
12615
+ const { startEdge, endEdge } = axis;
12616
+ const { groupSlides } = slidesToScroll;
12617
+ const alignments = measureSizes().map(alignment.measure);
12618
+ const snaps = measureUnaligned();
12619
+ const snapsAligned = measureAligned();
12620
+ function measureSizes() {
12621
+ return groupSlides(slideRects).map((rects)=>arrayLast(rects)[endEdge] - rects[0][startEdge]).map(mathAbs);
12622
+ }
12623
+ function measureUnaligned() {
12624
+ return slideRects.map((rect)=>containerRect[startEdge] - rect[startEdge]).map((snap)=>-mathAbs(snap));
12625
+ }
12626
+ function measureAligned() {
12627
+ return groupSlides(snaps).map((g)=>g[0]).map((snap, index)=>snap + alignments[index]);
12628
+ }
12629
+ const self = {
12630
+ snaps,
12631
+ snapsAligned
12632
+ };
12633
+ return self;
12634
+ }
12635
+ function SlideRegistry(containSnaps, containScroll, scrollSnaps, scrollContainLimit, slidesToScroll, slideIndexes) {
12636
+ const { groupSlides } = slidesToScroll;
12637
+ const { min, max } = scrollContainLimit;
12638
+ const slideRegistry = createSlideRegistry();
12639
+ function createSlideRegistry() {
12640
+ const groupedSlideIndexes = groupSlides(slideIndexes);
12641
+ const doNotContain = !containSnaps || containScroll === 'keepSnaps';
12642
+ if (scrollSnaps.length === 1) return [
12643
+ slideIndexes
12644
+ ];
12645
+ if (doNotContain) return groupedSlideIndexes;
12646
+ return groupedSlideIndexes.slice(min, max).map((group, index, groups)=>{
12647
+ const isFirst = !index;
12648
+ const isLast = arrayIsLastIndex(groups, index);
12649
+ if (isFirst) {
12650
+ const range = arrayLast(groups[0]) + 1;
12651
+ return arrayFromNumber(range);
12652
+ }
12653
+ if (isLast) {
12654
+ const range = arrayLastIndex(slideIndexes) - arrayLast(groups)[0] + 1;
12655
+ return arrayFromNumber(range, arrayLast(groups)[0]);
12656
+ }
12657
+ return group;
12658
+ });
12659
+ }
12660
+ const self = {
12661
+ slideRegistry
12662
+ };
12663
+ return self;
12664
+ }
12665
+ function ScrollTarget(loop, scrollSnaps, contentSize, limit, targetVector) {
12666
+ const { reachedAny, removeOffset, constrain } = limit;
12667
+ function minDistance(distances) {
12668
+ return distances.concat().sort((a, b)=>mathAbs(a) - mathAbs(b))[0];
12669
+ }
12670
+ function findTargetSnap(target) {
12671
+ const distance = loop ? removeOffset(target) : constrain(target);
12672
+ const ascDiffsToSnaps = scrollSnaps.map((snap, index)=>({
12673
+ diff: shortcut(snap - distance, 0),
12674
+ index
12675
+ })).sort((d1, d2)=>mathAbs(d1.diff) - mathAbs(d2.diff));
12676
+ const { index } = ascDiffsToSnaps[0];
12677
+ return {
12678
+ index,
12679
+ distance
12680
+ };
12681
+ }
12682
+ function shortcut(target, direction) {
12683
+ const targets = [
12684
+ target,
12685
+ target + contentSize,
12686
+ target - contentSize
12687
+ ];
12688
+ if (!loop) return target;
12689
+ if (!direction) return minDistance(targets);
12690
+ const matchingTargets = targets.filter((t)=>mathSign(t) === direction);
12691
+ if (matchingTargets.length) return minDistance(matchingTargets);
12692
+ return arrayLast(targets) - contentSize;
12693
+ }
12694
+ function byIndex(index, direction) {
12695
+ const diffToSnap = scrollSnaps[index] - targetVector.get();
12696
+ const distance = shortcut(diffToSnap, direction);
12697
+ return {
12698
+ index,
12699
+ distance
12700
+ };
12701
+ }
12702
+ function byDistance(distance, snap) {
12703
+ const target = targetVector.get() + distance;
12704
+ const { index, distance: targetSnapDistance } = findTargetSnap(target);
12705
+ const reachedBound = !loop && reachedAny(target);
12706
+ if (!snap || reachedBound) return {
12707
+ index,
12708
+ distance
12709
+ };
12710
+ const diffToSnap = scrollSnaps[index] - targetSnapDistance;
12711
+ const snapDistance = distance + shortcut(diffToSnap, 0);
12712
+ return {
12713
+ index,
12714
+ distance: snapDistance
12715
+ };
12716
+ }
12717
+ const self = {
12718
+ byDistance,
12719
+ byIndex,
12720
+ shortcut
12721
+ };
12722
+ return self;
12723
+ }
12724
+ function ScrollTo(animation, indexCurrent, indexPrevious, scrollBody, scrollTarget, targetVector, eventHandler) {
12725
+ function scrollTo(target) {
12726
+ const distanceDiff = target.distance;
12727
+ const indexDiff = target.index !== indexCurrent.get();
12728
+ targetVector.add(distanceDiff);
12729
+ if (distanceDiff) {
12730
+ if (scrollBody.duration()) {
12731
+ animation.start();
12732
+ } else {
12733
+ animation.update();
12734
+ animation.render(1);
12735
+ animation.update();
12736
+ }
12737
+ }
12738
+ if (indexDiff) {
12739
+ indexPrevious.set(indexCurrent.get());
12740
+ indexCurrent.set(target.index);
12741
+ eventHandler.emit('select');
12742
+ }
12743
+ }
12744
+ function distance(n, snap) {
12745
+ const target = scrollTarget.byDistance(n, snap);
12746
+ scrollTo(target);
12747
+ }
12748
+ function index(n, direction) {
12749
+ const targetIndex = indexCurrent.clone().set(n);
12750
+ const target = scrollTarget.byIndex(targetIndex.get(), direction);
12751
+ scrollTo(target);
12752
+ }
12753
+ const self = {
12754
+ distance,
12755
+ index
12756
+ };
12757
+ return self;
12758
+ }
12759
+ function SlideFocus(root, slides, slideRegistry, scrollTo, scrollBody, eventStore, eventHandler, watchFocus) {
12760
+ const focusListenerOptions = {
12761
+ passive: true,
12762
+ capture: true
12763
+ };
12764
+ let lastTabPressTime = 0;
12765
+ function init(emblaApi) {
12766
+ if (!watchFocus) return;
12767
+ function defaultCallback(index) {
12768
+ const nowTime = new Date().getTime();
12769
+ const diffTime = nowTime - lastTabPressTime;
12770
+ if (diffTime > 10) return;
12771
+ eventHandler.emit('slideFocusStart');
12772
+ root.scrollLeft = 0;
12773
+ const group = slideRegistry.findIndex((group)=>group.includes(index));
12774
+ if (!isNumber(group)) return;
12775
+ scrollBody.useDuration(0);
12776
+ scrollTo.index(group, 0);
12777
+ eventHandler.emit('slideFocus');
12778
+ }
12779
+ eventStore.add(document, 'keydown', registerTabPress, false);
12780
+ slides.forEach((slide, slideIndex)=>{
12781
+ eventStore.add(slide, 'focus', (evt)=>{
12782
+ if (isBoolean(watchFocus) || watchFocus(emblaApi, evt)) {
12783
+ defaultCallback(slideIndex);
12784
+ }
12785
+ }, focusListenerOptions);
12786
+ });
12787
+ }
12788
+ function registerTabPress(event) {
12789
+ if (event.code === 'Tab') lastTabPressTime = new Date().getTime();
12790
+ }
12791
+ const self = {
12792
+ init
12793
+ };
12794
+ return self;
12795
+ }
12796
+ function Vector1D(initialValue) {
12797
+ let value = initialValue;
12798
+ function get() {
12799
+ return value;
12800
+ }
12801
+ function set(n) {
12802
+ value = normalizeInput(n);
12803
+ }
12804
+ function add(n) {
12805
+ value += normalizeInput(n);
12806
+ }
12807
+ function subtract(n) {
12808
+ value -= normalizeInput(n);
12809
+ }
12810
+ function normalizeInput(n) {
12811
+ return isNumber(n) ? n : n.get();
12812
+ }
12813
+ const self = {
12814
+ get,
12815
+ set,
12816
+ add,
12817
+ subtract
12818
+ };
12819
+ return self;
12820
+ }
12821
+ function Translate(axis, container) {
12822
+ const translate = axis.scroll === 'x' ? x : y;
12823
+ const containerStyle = container.style;
12824
+ let previousTarget = null;
12825
+ let disabled = false;
12826
+ function x(n) {
12827
+ return `translate3d(${n}px,0px,0px)`;
12828
+ }
12829
+ function y(n) {
12830
+ return `translate3d(0px,${n}px,0px)`;
12831
+ }
12832
+ function to(target) {
12833
+ if (disabled) return;
12834
+ const newTarget = roundToTwoDecimals(axis.direction(target));
12835
+ if (newTarget === previousTarget) return;
12836
+ containerStyle.transform = translate(newTarget);
12837
+ previousTarget = newTarget;
12838
+ }
12839
+ function toggleActive(active) {
12840
+ disabled = !active;
12841
+ }
12842
+ function clear() {
12843
+ if (disabled) return;
12844
+ containerStyle.transform = '';
12845
+ if (!container.getAttribute('style')) container.removeAttribute('style');
12846
+ }
12847
+ const self = {
12848
+ clear,
12849
+ to,
12850
+ toggleActive
12851
+ };
12852
+ return self;
12853
+ }
12854
+ function SlideLooper(axis, viewSize, contentSize, slideSizes, slideSizesWithGaps, snaps, scrollSnaps, location, slides) {
12855
+ const roundingSafety = 0.5;
12856
+ const ascItems = arrayKeys(slideSizesWithGaps);
12857
+ const descItems = arrayKeys(slideSizesWithGaps).reverse();
12858
+ const loopPoints = startPoints().concat(endPoints());
12859
+ function removeSlideSizes(indexes, from) {
12860
+ return indexes.reduce((a, i)=>{
12861
+ return a - slideSizesWithGaps[i];
12862
+ }, from);
12863
+ }
12864
+ function slidesInGap(indexes, gap) {
12865
+ return indexes.reduce((a, i)=>{
12866
+ const remainingGap = removeSlideSizes(a, gap);
12867
+ return remainingGap > 0 ? a.concat([
12868
+ i
12869
+ ]) : a;
12870
+ }, []);
12871
+ }
12872
+ function findSlideBounds(offset) {
12873
+ return snaps.map((snap, index)=>({
12874
+ start: snap - slideSizes[index] + roundingSafety + offset,
12875
+ end: snap + viewSize - roundingSafety + offset
12876
+ }));
12877
+ }
12878
+ function findLoopPoints(indexes, offset, isEndEdge) {
12879
+ const slideBounds = findSlideBounds(offset);
12880
+ return indexes.map((index)=>{
12881
+ const initial = isEndEdge ? 0 : -contentSize;
12882
+ const altered = isEndEdge ? contentSize : 0;
12883
+ const boundEdge = isEndEdge ? 'end' : 'start';
12884
+ const loopPoint = slideBounds[index][boundEdge];
12885
+ return {
12886
+ index,
12887
+ loopPoint,
12888
+ slideLocation: Vector1D(-1),
12889
+ translate: Translate(axis, slides[index]),
12890
+ target: ()=>location.get() > loopPoint ? initial : altered
12891
+ };
12892
+ });
12893
+ }
12894
+ function startPoints() {
12895
+ const gap = scrollSnaps[0];
12896
+ const indexes = slidesInGap(descItems, gap);
12897
+ return findLoopPoints(indexes, contentSize, false);
12898
+ }
12899
+ function endPoints() {
12900
+ const gap = viewSize - scrollSnaps[0] - 1;
12901
+ const indexes = slidesInGap(ascItems, gap);
12902
+ return findLoopPoints(indexes, -contentSize, true);
12903
+ }
12904
+ function canLoop() {
12905
+ return loopPoints.every(({ index })=>{
12906
+ const otherIndexes = ascItems.filter((i)=>i !== index);
12907
+ return removeSlideSizes(otherIndexes, viewSize) <= 0.1;
12908
+ });
12909
+ }
12910
+ function loop() {
12911
+ loopPoints.forEach((loopPoint)=>{
12912
+ const { target, translate, slideLocation } = loopPoint;
12913
+ const shiftLocation = target();
12914
+ if (shiftLocation === slideLocation.get()) return;
12915
+ translate.to(shiftLocation);
12916
+ slideLocation.set(shiftLocation);
12917
+ });
12918
+ }
12919
+ function clear() {
12920
+ loopPoints.forEach((loopPoint)=>loopPoint.translate.clear());
12921
+ }
12922
+ const self = {
12923
+ canLoop,
12924
+ clear,
12925
+ loop,
12926
+ loopPoints
12927
+ };
12928
+ return self;
12929
+ }
12930
+ function SlidesHandler(container, eventHandler, watchSlides) {
12931
+ let mutationObserver;
12932
+ let destroyed = false;
12933
+ function init(emblaApi) {
12934
+ if (!watchSlides) return;
12935
+ function defaultCallback(mutations) {
12936
+ for (const mutation of mutations){
12937
+ if (mutation.type === 'childList') {
12938
+ emblaApi.reInit();
12939
+ eventHandler.emit('slidesChanged');
12940
+ break;
12941
+ }
12942
+ }
12943
+ }
12944
+ mutationObserver = new MutationObserver((mutations)=>{
12945
+ if (destroyed) return;
12946
+ if (isBoolean(watchSlides) || watchSlides(emblaApi, mutations)) {
12947
+ defaultCallback(mutations);
12948
+ }
12949
+ });
12950
+ mutationObserver.observe(container, {
12951
+ childList: true
12952
+ });
12953
+ }
12954
+ function destroy() {
12955
+ if (mutationObserver) mutationObserver.disconnect();
12956
+ destroyed = true;
12957
+ }
12958
+ const self = {
12959
+ init,
12960
+ destroy
12961
+ };
12962
+ return self;
12963
+ }
12964
+ function SlidesInView(container, slides, eventHandler, threshold) {
12965
+ const intersectionEntryMap = {};
12966
+ let inViewCache = null;
12967
+ let notInViewCache = null;
12968
+ let intersectionObserver;
12969
+ let destroyed = false;
12970
+ function init() {
12971
+ intersectionObserver = new IntersectionObserver((entries)=>{
12972
+ if (destroyed) return;
12973
+ entries.forEach((entry)=>{
12974
+ const index = slides.indexOf(entry.target);
12975
+ intersectionEntryMap[index] = entry;
12976
+ });
12977
+ inViewCache = null;
12978
+ notInViewCache = null;
12979
+ eventHandler.emit('slidesInView');
12980
+ }, {
12981
+ root: container.parentElement,
12982
+ threshold
12983
+ });
12984
+ slides.forEach((slide)=>intersectionObserver.observe(slide));
12985
+ }
12986
+ function destroy() {
12987
+ if (intersectionObserver) intersectionObserver.disconnect();
12988
+ destroyed = true;
12989
+ }
12990
+ function createInViewList(inView) {
12991
+ return objectKeys(intersectionEntryMap).reduce((list, slideIndex)=>{
12992
+ const index = parseInt(slideIndex);
12993
+ const { isIntersecting } = intersectionEntryMap[index];
12994
+ const inViewMatch = inView && isIntersecting;
12995
+ const notInViewMatch = !inView && !isIntersecting;
12996
+ if (inViewMatch || notInViewMatch) list.push(index);
12997
+ return list;
12998
+ }, []);
12999
+ }
13000
+ function get(inView = true) {
13001
+ if (inView && inViewCache) return inViewCache;
13002
+ if (!inView && notInViewCache) return notInViewCache;
13003
+ const slideIndexes = createInViewList(inView);
13004
+ if (inView) inViewCache = slideIndexes;
13005
+ if (!inView) notInViewCache = slideIndexes;
13006
+ return slideIndexes;
13007
+ }
13008
+ const self = {
13009
+ init,
13010
+ destroy,
13011
+ get
13012
+ };
13013
+ return self;
13014
+ }
13015
+ function SlideSizes(axis, containerRect, slideRects, slides, readEdgeGap, ownerWindow) {
13016
+ const { measureSize, startEdge, endEdge } = axis;
13017
+ const withEdgeGap = slideRects[0] && readEdgeGap;
13018
+ const startGap = measureStartGap();
13019
+ const endGap = measureEndGap();
13020
+ const slideSizes = slideRects.map(measureSize);
13021
+ const slideSizesWithGaps = measureWithGaps();
13022
+ function measureStartGap() {
13023
+ if (!withEdgeGap) return 0;
13024
+ const slideRect = slideRects[0];
13025
+ return mathAbs(containerRect[startEdge] - slideRect[startEdge]);
13026
+ }
13027
+ function measureEndGap() {
13028
+ if (!withEdgeGap) return 0;
13029
+ const style = ownerWindow.getComputedStyle(arrayLast(slides));
13030
+ return parseFloat(style.getPropertyValue(`margin-${endEdge}`));
13031
+ }
13032
+ function measureWithGaps() {
13033
+ return slideRects.map((rect, index, rects)=>{
13034
+ const isFirst = !index;
13035
+ const isLast = arrayIsLastIndex(rects, index);
13036
+ if (isFirst) return slideSizes[index] + startGap;
13037
+ if (isLast) return slideSizes[index] + endGap;
13038
+ return rects[index + 1][startEdge] - rect[startEdge];
13039
+ }).map(mathAbs);
13040
+ }
13041
+ const self = {
13042
+ slideSizes,
13043
+ slideSizesWithGaps,
13044
+ startGap,
13045
+ endGap
13046
+ };
13047
+ return self;
13048
+ }
13049
+ function SlidesToScroll(axis, viewSize, slidesToScroll, loop, containerRect, slideRects, startGap, endGap, pixelTolerance) {
13050
+ const { startEdge, endEdge, direction } = axis;
13051
+ const groupByNumber = isNumber(slidesToScroll);
13052
+ function byNumber(array, groupSize) {
13053
+ return arrayKeys(array).filter((i)=>i % groupSize === 0).map((i)=>array.slice(i, i + groupSize));
13054
+ }
13055
+ function bySize(array) {
13056
+ if (!array.length) return [];
13057
+ return arrayKeys(array).reduce((groups, rectB, index)=>{
13058
+ const rectA = arrayLast(groups) || 0;
13059
+ const isFirst = rectA === 0;
13060
+ const isLast = rectB === arrayLastIndex(array);
13061
+ const edgeA = containerRect[startEdge] - slideRects[rectA][startEdge];
13062
+ const edgeB = containerRect[startEdge] - slideRects[rectB][endEdge];
13063
+ const gapA = !loop && isFirst ? direction(startGap) : 0;
13064
+ const gapB = !loop && isLast ? direction(endGap) : 0;
13065
+ const chunkSize = mathAbs(edgeB - gapB - (edgeA + gapA));
13066
+ if (index && chunkSize > viewSize + pixelTolerance) groups.push(rectB);
13067
+ if (isLast) groups.push(array.length);
13068
+ return groups;
13069
+ }, []).map((currentSize, index, groups)=>{
13070
+ const previousSize = Math.max(groups[index - 1] || 0);
13071
+ return array.slice(previousSize, currentSize);
13072
+ });
13073
+ }
13074
+ function groupSlides(array) {
13075
+ return groupByNumber ? byNumber(array, slidesToScroll) : bySize(array);
13076
+ }
13077
+ const self = {
13078
+ groupSlides
13079
+ };
13080
+ return self;
13081
+ }
13082
+ function Engine(root, container, slides, ownerDocument, ownerWindow, options, eventHandler) {
13083
+ // Options
13084
+ const { align, axis: scrollAxis, direction, startIndex, loop, duration, dragFree, dragThreshold, inViewThreshold, slidesToScroll: groupSlides, skipSnaps, containScroll, watchResize, watchSlides, watchDrag, watchFocus } = options;
13085
+ // Measurements
13086
+ const pixelTolerance = 2;
13087
+ const nodeRects = NodeRects();
13088
+ const containerRect = nodeRects.measure(container);
13089
+ const slideRects = slides.map(nodeRects.measure);
13090
+ const axis = Axis(scrollAxis, direction);
13091
+ const viewSize = axis.measureSize(containerRect);
13092
+ const percentOfView = PercentOfView(viewSize);
13093
+ const alignment = Alignment(align, viewSize);
13094
+ const containSnaps = !loop && !!containScroll;
13095
+ const readEdgeGap = loop || !!containScroll;
13096
+ const { slideSizes, slideSizesWithGaps, startGap, endGap } = SlideSizes(axis, containerRect, slideRects, slides, readEdgeGap, ownerWindow);
13097
+ const slidesToScroll = SlidesToScroll(axis, viewSize, groupSlides, loop, containerRect, slideRects, startGap, endGap, pixelTolerance);
13098
+ const { snaps, snapsAligned } = ScrollSnaps(axis, alignment, containerRect, slideRects, slidesToScroll);
13099
+ const contentSize = -arrayLast(snaps) + arrayLast(slideSizesWithGaps);
13100
+ const { snapsContained, scrollContainLimit } = ScrollContain(viewSize, contentSize, snapsAligned, containScroll, pixelTolerance);
13101
+ const scrollSnaps = containSnaps ? snapsContained : snapsAligned;
13102
+ const { limit } = ScrollLimit(contentSize, scrollSnaps, loop);
13103
+ // Indexes
13104
+ const index = Counter(arrayLastIndex(scrollSnaps), startIndex, loop);
13105
+ const indexPrevious = index.clone();
13106
+ const slideIndexes = arrayKeys(slides);
13107
+ // Animation
13108
+ const update = ({ dragHandler, scrollBody, scrollBounds, options: { loop } })=>{
13109
+ if (!loop) scrollBounds.constrain(dragHandler.pointerDown());
13110
+ scrollBody.seek();
13111
+ };
13112
+ const render = ({ scrollBody, translate, location, offsetLocation, previousLocation, scrollLooper, slideLooper, dragHandler, animation, eventHandler, scrollBounds, options: { loop } }, alpha)=>{
13113
+ const shouldSettle = scrollBody.settled();
13114
+ const withinBounds = !scrollBounds.shouldConstrain();
13115
+ const hasSettled = loop ? shouldSettle : shouldSettle && withinBounds;
13116
+ const hasSettledAndIdle = hasSettled && !dragHandler.pointerDown();
13117
+ if (hasSettledAndIdle) animation.stop();
13118
+ const interpolatedLocation = location.get() * alpha + previousLocation.get() * (1 - alpha);
13119
+ offsetLocation.set(interpolatedLocation);
13120
+ if (loop) {
13121
+ scrollLooper.loop(scrollBody.direction());
13122
+ slideLooper.loop();
13123
+ }
13124
+ translate.to(offsetLocation.get());
13125
+ if (hasSettledAndIdle) eventHandler.emit('settle');
13126
+ if (!hasSettled) eventHandler.emit('scroll');
13127
+ };
13128
+ const animation = Animations(ownerDocument, ownerWindow, ()=>update(engine), (alpha)=>render(engine, alpha));
13129
+ // Shared
13130
+ const friction = 0.68;
13131
+ const startLocation = scrollSnaps[index.get()];
13132
+ const location = Vector1D(startLocation);
13133
+ const previousLocation = Vector1D(startLocation);
13134
+ const offsetLocation = Vector1D(startLocation);
13135
+ const target = Vector1D(startLocation);
13136
+ const scrollBody = ScrollBody(location, offsetLocation, previousLocation, target, duration, friction);
13137
+ const scrollTarget = ScrollTarget(loop, scrollSnaps, contentSize, limit, target);
13138
+ const scrollTo = ScrollTo(animation, index, indexPrevious, scrollBody, scrollTarget, target, eventHandler);
13139
+ const scrollProgress = ScrollProgress(limit);
13140
+ const eventStore = EventStore();
13141
+ const slidesInView = SlidesInView(container, slides, eventHandler, inViewThreshold);
13142
+ const { slideRegistry } = SlideRegistry(containSnaps, containScroll, scrollSnaps, scrollContainLimit, slidesToScroll, slideIndexes);
13143
+ const slideFocus = SlideFocus(root, slides, slideRegistry, scrollTo, scrollBody, eventStore, eventHandler, watchFocus);
13144
+ // Engine
13145
+ const engine = {
13146
+ ownerDocument,
13147
+ ownerWindow,
13148
+ eventHandler,
13149
+ containerRect,
13150
+ slideRects,
13151
+ animation,
13152
+ axis,
13153
+ dragHandler: DragHandler(axis, root, ownerDocument, ownerWindow, target, DragTracker(axis, ownerWindow), location, animation, scrollTo, scrollBody, scrollTarget, index, eventHandler, percentOfView, dragFree, dragThreshold, skipSnaps, friction, watchDrag),
13154
+ eventStore,
13155
+ percentOfView,
13156
+ index,
13157
+ indexPrevious,
13158
+ limit,
13159
+ location,
13160
+ offsetLocation,
13161
+ previousLocation,
13162
+ options,
13163
+ resizeHandler: ResizeHandler(container, eventHandler, ownerWindow, slides, axis, watchResize, nodeRects),
13164
+ scrollBody,
13165
+ scrollBounds: ScrollBounds(limit, offsetLocation, target, scrollBody, percentOfView),
13166
+ scrollLooper: ScrollLooper(contentSize, limit, offsetLocation, [
13167
+ location,
13168
+ offsetLocation,
13169
+ previousLocation,
13170
+ target
13171
+ ]),
13172
+ scrollProgress,
13173
+ scrollSnapList: scrollSnaps.map(scrollProgress.get),
13174
+ scrollSnaps,
13175
+ scrollTarget,
13176
+ scrollTo,
13177
+ slideLooper: SlideLooper(axis, viewSize, contentSize, slideSizes, slideSizesWithGaps, snaps, scrollSnaps, offsetLocation, slides),
13178
+ slideFocus,
13179
+ slidesHandler: SlidesHandler(container, eventHandler, watchSlides),
13180
+ slidesInView,
13181
+ slideIndexes,
13182
+ slideRegistry,
13183
+ slidesToScroll,
13184
+ target,
13185
+ translate: Translate(axis, container)
13186
+ };
13187
+ return engine;
13188
+ }
13189
+ function EventHandler() {
13190
+ let listeners = {};
13191
+ let api;
13192
+ function init(emblaApi) {
13193
+ api = emblaApi;
13194
+ }
13195
+ function getListeners(evt) {
13196
+ return listeners[evt] || [];
13197
+ }
13198
+ function emit(evt) {
13199
+ getListeners(evt).forEach((e)=>e(api, evt));
13200
+ return self;
13201
+ }
13202
+ function on(evt, cb) {
13203
+ listeners[evt] = getListeners(evt).concat([
13204
+ cb
13205
+ ]);
13206
+ return self;
13207
+ }
13208
+ function off(evt, cb) {
13209
+ listeners[evt] = getListeners(evt).filter((e)=>e !== cb);
13210
+ return self;
13211
+ }
13212
+ function clear() {
13213
+ listeners = {};
13214
+ }
13215
+ const self = {
13216
+ init,
13217
+ emit,
13218
+ off,
13219
+ on,
13220
+ clear
13221
+ };
13222
+ return self;
13223
+ }
13224
+ const defaultOptions = {
13225
+ align: 'center',
13226
+ axis: 'x',
13227
+ container: null,
13228
+ slides: null,
13229
+ containScroll: 'trimSnaps',
13230
+ direction: 'ltr',
13231
+ slidesToScroll: 1,
13232
+ inViewThreshold: 0,
13233
+ breakpoints: {},
13234
+ dragFree: false,
13235
+ dragThreshold: 10,
13236
+ loop: false,
13237
+ skipSnaps: false,
13238
+ duration: 25,
13239
+ startIndex: 0,
13240
+ active: true,
13241
+ watchDrag: true,
13242
+ watchResize: true,
13243
+ watchSlides: true,
13244
+ watchFocus: true
13245
+ };
13246
+ function OptionsHandler(ownerWindow) {
13247
+ function mergeOptions(optionsA, optionsB) {
13248
+ return objectsMergeDeep(optionsA, optionsB || {});
13249
+ }
13250
+ function optionsAtMedia(options) {
13251
+ const optionsAtMedia = options.breakpoints || {};
13252
+ const matchedMediaOptions = objectKeys(optionsAtMedia).filter((media)=>ownerWindow.matchMedia(media).matches).map((media)=>optionsAtMedia[media]).reduce((a, mediaOption)=>mergeOptions(a, mediaOption), {});
13253
+ return mergeOptions(options, matchedMediaOptions);
13254
+ }
13255
+ function optionsMediaQueries(optionsList) {
13256
+ return optionsList.map((options)=>objectKeys(options.breakpoints || {})).reduce((acc, mediaQueries)=>acc.concat(mediaQueries), []).map(ownerWindow.matchMedia);
13257
+ }
13258
+ const self = {
13259
+ mergeOptions,
13260
+ optionsAtMedia,
13261
+ optionsMediaQueries
13262
+ };
13263
+ return self;
13264
+ }
13265
+ function PluginsHandler(optionsHandler) {
13266
+ let activePlugins = [];
13267
+ function init(emblaApi, plugins) {
13268
+ activePlugins = plugins.filter(({ options })=>optionsHandler.optionsAtMedia(options).active !== false);
13269
+ activePlugins.forEach((plugin)=>plugin.init(emblaApi, optionsHandler));
13270
+ return plugins.reduce((map, plugin)=>Object.assign(map, {
13271
+ [plugin.name]: plugin
13272
+ }), {});
13273
+ }
13274
+ function destroy() {
13275
+ activePlugins = activePlugins.filter((plugin)=>plugin.destroy());
13276
+ }
13277
+ const self = {
13278
+ init,
13279
+ destroy
13280
+ };
13281
+ return self;
13282
+ }
13283
+ function EmblaCarousel(root, userOptions, userPlugins) {
13284
+ const ownerDocument = root.ownerDocument;
13285
+ const ownerWindow = ownerDocument.defaultView;
13286
+ const optionsHandler = OptionsHandler(ownerWindow);
13287
+ const pluginsHandler = PluginsHandler(optionsHandler);
13288
+ const mediaHandlers = EventStore();
13289
+ const eventHandler = EventHandler();
13290
+ const { mergeOptions, optionsAtMedia, optionsMediaQueries } = optionsHandler;
13291
+ const { on, off, emit } = eventHandler;
13292
+ const reInit = reActivate;
13293
+ let destroyed = false;
13294
+ let engine;
13295
+ let optionsBase = mergeOptions(defaultOptions, EmblaCarousel.globalOptions);
13296
+ let options = mergeOptions(optionsBase);
13297
+ let pluginList = [];
13298
+ let pluginApis;
13299
+ let container;
13300
+ let slides;
13301
+ function storeElements() {
13302
+ const { container: userContainer, slides: userSlides } = options;
13303
+ const customContainer = isString(userContainer) ? root.querySelector(userContainer) : userContainer;
13304
+ container = customContainer || root.children[0];
13305
+ const customSlides = isString(userSlides) ? container.querySelectorAll(userSlides) : userSlides;
13306
+ slides = [].slice.call(customSlides || container.children);
13307
+ }
13308
+ function createEngine(options) {
13309
+ const engine = Engine(root, container, slides, ownerDocument, ownerWindow, options, eventHandler);
13310
+ if (options.loop && !engine.slideLooper.canLoop()) {
13311
+ const optionsWithoutLoop = Object.assign({}, options, {
13312
+ loop: false
13313
+ });
13314
+ return createEngine(optionsWithoutLoop);
13315
+ }
13316
+ return engine;
13317
+ }
13318
+ function activate(withOptions, withPlugins) {
13319
+ if (destroyed) return;
13320
+ optionsBase = mergeOptions(optionsBase, withOptions);
13321
+ options = optionsAtMedia(optionsBase);
13322
+ pluginList = withPlugins || pluginList;
13323
+ storeElements();
13324
+ engine = createEngine(options);
13325
+ optionsMediaQueries([
13326
+ optionsBase,
13327
+ ...pluginList.map(({ options })=>options)
13328
+ ]).forEach((query)=>mediaHandlers.add(query, 'change', reActivate));
13329
+ if (!options.active) return;
13330
+ engine.translate.to(engine.location.get());
13331
+ engine.animation.init();
13332
+ engine.slidesInView.init();
13333
+ engine.slideFocus.init(self);
13334
+ engine.eventHandler.init(self);
13335
+ engine.resizeHandler.init(self);
13336
+ engine.slidesHandler.init(self);
13337
+ if (engine.options.loop) engine.slideLooper.loop();
13338
+ if (container.offsetParent && slides.length) engine.dragHandler.init(self);
13339
+ pluginApis = pluginsHandler.init(self, pluginList);
13340
+ }
13341
+ function reActivate(withOptions, withPlugins) {
13342
+ const startIndex = selectedScrollSnap();
13343
+ deActivate();
13344
+ activate(mergeOptions({
13345
+ startIndex
13346
+ }, withOptions), withPlugins);
13347
+ eventHandler.emit('reInit');
13348
+ }
13349
+ function deActivate() {
13350
+ engine.dragHandler.destroy();
13351
+ engine.eventStore.clear();
13352
+ engine.translate.clear();
13353
+ engine.slideLooper.clear();
13354
+ engine.resizeHandler.destroy();
13355
+ engine.slidesHandler.destroy();
13356
+ engine.slidesInView.destroy();
13357
+ engine.animation.destroy();
13358
+ pluginsHandler.destroy();
13359
+ mediaHandlers.clear();
13360
+ }
13361
+ function destroy() {
13362
+ if (destroyed) return;
13363
+ destroyed = true;
13364
+ mediaHandlers.clear();
13365
+ deActivate();
13366
+ eventHandler.emit('destroy');
13367
+ eventHandler.clear();
13368
+ }
13369
+ function scrollTo(index, jump, direction) {
13370
+ if (!options.active || destroyed) return;
13371
+ engine.scrollBody.useBaseFriction().useDuration(jump === true ? 0 : options.duration);
13372
+ engine.scrollTo.index(index, direction || 0);
13373
+ }
13374
+ function scrollNext(jump) {
13375
+ const next = engine.index.add(1).get();
13376
+ scrollTo(next, jump, -1);
13377
+ }
13378
+ function scrollPrev(jump) {
13379
+ const prev = engine.index.add(-1).get();
13380
+ scrollTo(prev, jump, 1);
13381
+ }
13382
+ function canScrollNext() {
13383
+ const next = engine.index.add(1).get();
13384
+ return next !== selectedScrollSnap();
13385
+ }
13386
+ function canScrollPrev() {
13387
+ const prev = engine.index.add(-1).get();
13388
+ return prev !== selectedScrollSnap();
13389
+ }
13390
+ function scrollSnapList() {
13391
+ return engine.scrollSnapList;
13392
+ }
13393
+ function scrollProgress() {
13394
+ return engine.scrollProgress.get(engine.offsetLocation.get());
13395
+ }
13396
+ function selectedScrollSnap() {
13397
+ return engine.index.get();
13398
+ }
13399
+ function previousScrollSnap() {
13400
+ return engine.indexPrevious.get();
13401
+ }
13402
+ function slidesInView() {
13403
+ return engine.slidesInView.get();
13404
+ }
13405
+ function slidesNotInView() {
13406
+ return engine.slidesInView.get(false);
13407
+ }
13408
+ function plugins() {
13409
+ return pluginApis;
13410
+ }
13411
+ function internalEngine() {
13412
+ return engine;
13413
+ }
13414
+ function rootNode() {
13415
+ return root;
13416
+ }
13417
+ function containerNode() {
13418
+ return container;
13419
+ }
13420
+ function slideNodes() {
13421
+ return slides;
13422
+ }
13423
+ const self = {
13424
+ canScrollNext,
13425
+ canScrollPrev,
13426
+ containerNode,
13427
+ internalEngine,
13428
+ destroy,
13429
+ off,
13430
+ on,
13431
+ emit,
13432
+ plugins,
13433
+ previousScrollSnap,
13434
+ reInit,
13435
+ rootNode,
13436
+ scrollNext,
13437
+ scrollPrev,
13438
+ scrollProgress,
13439
+ scrollSnapList,
13440
+ scrollTo,
13441
+ selectedScrollSnap,
13442
+ slideNodes,
13443
+ slidesInView,
13444
+ slidesNotInView
13445
+ };
13446
+ activate(userOptions, userPlugins);
13447
+ setTimeout(()=>eventHandler.emit('init'), 0);
13448
+ return self;
13449
+ }
13450
+ EmblaCarousel.globalOptions = undefined;
13451
+
13452
+ function useEmblaCarousel(options = {}, plugins = []) {
13453
+ const storedOptions = React.useRef(options);
13454
+ const storedPlugins = React.useRef(plugins);
13455
+ const [emblaApi, setEmblaApi] = React.useState();
13456
+ const [viewport, setViewport] = React.useState();
13457
+ const reInit = React.useCallback(()=>{
13458
+ if (emblaApi) emblaApi.reInit(storedOptions.current, storedPlugins.current);
13459
+ }, [
13460
+ emblaApi
13461
+ ]);
13462
+ React.useEffect(()=>{
13463
+ if (areOptionsEqual(storedOptions.current, options)) return;
13464
+ storedOptions.current = options;
13465
+ reInit();
13466
+ }, [
13467
+ options,
13468
+ reInit
13469
+ ]);
13470
+ React.useEffect(()=>{
13471
+ if (arePluginsEqual(storedPlugins.current, plugins)) return;
13472
+ storedPlugins.current = plugins;
13473
+ reInit();
13474
+ }, [
13475
+ plugins,
13476
+ reInit
13477
+ ]);
13478
+ React.useEffect(()=>{
13479
+ if (canUseDOM() && viewport) {
13480
+ EmblaCarousel.globalOptions = useEmblaCarousel.globalOptions;
13481
+ const newEmblaApi = EmblaCarousel(viewport, storedOptions.current, storedPlugins.current);
13482
+ setEmblaApi(newEmblaApi);
13483
+ return ()=>newEmblaApi.destroy();
13484
+ } else {
13485
+ setEmblaApi(undefined);
13486
+ }
13487
+ }, [
13488
+ viewport,
13489
+ setEmblaApi
13490
+ ]);
13491
+ return [
13492
+ setViewport,
13493
+ emblaApi
13494
+ ];
13495
+ }
13496
+ useEmblaCarousel.globalOptions = undefined;
13497
+
13498
+ function useRenderStrategy(props) {
13499
+ const wasEverPresent = React.useRef(false);
13500
+ if (props.present) {
13501
+ wasEverPresent.current = true;
13502
+ }
13503
+ return {
13504
+ unmounted: !props.present && !wasEverPresent.current && props.lazyMount || props.unmountOnExit && !props.present && wasEverPresent.current
13505
+ };
13506
+ }
13507
+ const RenderStrategyPropsContext = /*#__PURE__*/ React.createContext(null);
13508
+ const RenderStrategyPropsProvider = RenderStrategyPropsContext.Provider;
13509
+ function useRenderStrategyPropsContext({ strict = true } = {}) {
13510
+ const context = React.useContext(RenderStrategyPropsContext);
13511
+ if (!context && strict) {
13512
+ throw new Error("useRenderStrategyPropsContext must be used within a RenderStrategyPropsProvider");
13513
+ }
13514
+ return context;
13515
+ }
13516
+ /* ID -----------------------------------------------------------------------
13517
+ -------------------------------------------------------------------------------- */ const getTriggerId = (value, id)=>`tabs:${value}:${id}:trigger-root`;
13518
+ const getListId = (id)=>`tabs:${id}:list`;
13519
+ const getContentId = (value, id)=>`tabs:${value}:${id}:content`;
13520
+ /* Query -----------------------------------------------------------------------
13521
+ -------------------------------------------------------------------------------- */ const getTriggerByValue = (listEl, value)=>{
13522
+ return listEl.querySelector(`[data-value="${value}"]`);
13523
+ };
13524
+ const getEnabledValues = (el)=>{
13525
+ if (!el) return [];
13526
+ return Array.from(el.children).filter((child)=>!child.hasAttribute("aria-disabled")).map((child)=>child.getAttribute("data-value")).filter(Boolean);
13527
+ };
13528
+ /* Prevent Drag -----------------------------------------------------------------------
13529
+ -------------------------------------------------------------------------------- */ const PREVENT_DRAG_ATTRIBUTE = "data-embla-prevent-drag";
13530
+ const isDragPrevented = (el)=>{
13531
+ return el.closest(`[${PREVENT_DRAG_ATTRIBUTE}]`) != null;
13532
+ };
13533
+ function getPrevIndex(index, length) {
13534
+ if (length <= 0) {
13535
+ throw new Error("Length must be a positive number.");
13536
+ }
13537
+ return (index - 1 + length) % length;
13538
+ }
13539
+ function getNextIndex(index, length) {
13540
+ if (length <= 0) {
13541
+ throw new Error("Length must be a positive number.");
13542
+ }
13543
+ return (index + 1) % length;
13544
+ }
13545
+ // Helper function to scroll tab into view
13546
+ function scrollTabIntoView(tabElement, listElement, options) {
13547
+ const isHorizontal = listElement.getAttribute("aria-orientation") !== "vertical";
13548
+ // Default padding is 16px if not specified
13549
+ const scrollPadding = options?.scrollPadding;
13550
+ if (isHorizontal) {
13551
+ const tabLeft = tabElement.offsetLeft;
13552
+ const tabRight = tabLeft + tabElement.offsetWidth;
13553
+ const listScrollLeft = listElement.scrollLeft;
13554
+ const listWidth = listElement.clientWidth;
13555
+ const listRight = listScrollLeft + listWidth;
13556
+ if (tabLeft < listScrollLeft) {
13557
+ // Tab is to the left of the visible area - scroll a bit more to the left
13558
+ listElement.scrollTo({
13559
+ left: Math.max(0, tabLeft - scrollPadding),
13560
+ behavior: "smooth"
13561
+ });
13562
+ } else if (tabRight > listRight) {
13563
+ // Tab is to the right of the visible area - scroll a bit more to the right
13564
+ listElement.scrollTo({
13565
+ left: tabRight - listWidth + scrollPadding,
13566
+ behavior: "smooth"
13567
+ });
13568
+ }
13569
+ } else {
13570
+ const tabTop = tabElement.offsetTop;
13571
+ const tabBottom = tabTop + tabElement.offsetHeight;
13572
+ const listScrollTop = listElement.scrollTop;
13573
+ const listHeight = listElement.clientHeight;
13574
+ const listBottom = listScrollTop + listHeight;
13575
+ if (tabTop < listScrollTop) {
13576
+ // Tab is above the visible area - scroll a bit more up
13577
+ listElement.scrollTo({
13578
+ top: Math.max(0, tabTop - scrollPadding),
13579
+ behavior: "smooth"
13580
+ });
13581
+ } else if (tabBottom > listBottom) {
13582
+ // Tab is below the visible area - scroll a bit more down
13583
+ listElement.scrollTo({
13584
+ top: tabBottom - listHeight + scrollPadding,
13585
+ behavior: "smooth"
13586
+ });
13587
+ }
13588
+ }
13589
+ }
13590
+ function useIsSSR() {
13591
+ const [isSSR, setIsSSR] = React.useState(true);
13592
+ React.useEffect(()=>{
13593
+ setIsSSR(false);
13594
+ }, []);
13595
+ return isSSR;
13596
+ }
13597
+ function useTabsState(props) {
13598
+ const [interactionState, setInteractionState] = React.useState("idle");
13599
+ const [value, setValue] = useControllableState$1({
13600
+ prop: props.value,
13601
+ defaultProp: props.defaultValue ?? undefined,
13602
+ onChange: props.onValueChange
13603
+ });
13604
+ const [focusedValue, setFocusedValue] = React.useState(null);
13605
+ const [isFocusVisible, setIsFocusVisible] = React.useState(false);
13606
+ const isFocusVisibleSupported = useSupports("selector(:focus-visible)");
13607
+ const [listEl, listRef] = React.useState(null);
13608
+ const [selectedTriggerEl, setSelectedTriggerEl] = React.useState(null);
13609
+ const selectedTriggerSize = useSize(selectedTriggerEl);
13610
+ const enabledValues = React.useMemo(()=>listEl ? getEnabledValues(listEl) : [], [
13611
+ listEl
13612
+ ]);
13613
+ const contentIndex = value ? enabledValues.indexOf(value) : -1;
13614
+ const prevIndex = contentIndex >= 0 ? getPrevIndex(contentIndex, enabledValues.length) : -1;
13615
+ const nextIndex = contentIndex >= 0 ? getNextIndex(contentIndex, enabledValues.length) : -1;
13616
+ // Scroll selected tab into view when it changes
13617
+ // TODO: this implementation is temporary, we should create some sort of hook or plugin system
13618
+ // to allow users to customize the scroll behavior.
13619
+ React.useEffect(()=>{
13620
+ if (selectedTriggerEl && listEl) {
13621
+ scrollTabIntoView(selectedTriggerEl, listEl, {
13622
+ scrollPadding: 16
13623
+ });
13624
+ }
13625
+ }, [
13626
+ selectedTriggerEl,
13627
+ listEl
13628
+ ]);
13629
+ const focusTriggerByValue = React.useCallback((value)=>{
13630
+ if (listEl) {
13631
+ getTriggerByValue(listEl, value)?.focus();
13632
+ }
13633
+ }, [
13634
+ listEl
13635
+ ]);
13636
+ const selectPrevAction = React.useCallback(()=>{
13637
+ const prevValue = enabledValues[prevIndex];
13638
+ if (!prevValue) return;
13639
+ setValue(prevValue);
13640
+ }, [
13641
+ enabledValues,
13642
+ prevIndex,
13643
+ setValue
13644
+ ]);
13645
+ const selectNextAction = React.useCallback(()=>{
13646
+ const nextValue = enabledValues[nextIndex];
13647
+ if (!nextValue) return;
13648
+ setValue(nextValue);
13649
+ }, [
13650
+ enabledValues,
13651
+ nextIndex,
13652
+ setValue
13653
+ ]);
13654
+ const selectFirstAction = React.useCallback(()=>{
13655
+ const firstValue = enabledValues[0];
13656
+ if (!firstValue) return;
13657
+ setValue(firstValue);
13658
+ }, [
13659
+ enabledValues,
13660
+ setValue
13661
+ ]);
13662
+ const selectLastAction = React.useCallback(()=>{
13663
+ const lastValue = enabledValues[enabledValues.length - 1];
13664
+ if (!lastValue) return;
13665
+ setValue(lastValue);
13666
+ }, [
13667
+ enabledValues,
13668
+ setValue
13669
+ ]);
13670
+ const setFocusedValueAction = React.useCallback((value)=>{
13671
+ setFocusedValue(value);
13672
+ }, []);
13673
+ const clearFocusedValueAction = React.useCallback(()=>{
13674
+ setFocusedValue(null);
13675
+ }, []);
13676
+ const setValueAction = React.useCallback((value)=>{
13677
+ setValue(value);
13678
+ }, [
13679
+ setValue
13680
+ ]);
13681
+ const actions = {
13682
+ selectPrev: selectPrevAction,
13683
+ selectNext: selectNextAction,
13684
+ selectFirst: selectFirstAction,
13685
+ selectLast: selectLastAction,
13686
+ setFocusedValue: setFocusedValueAction,
13687
+ clearFocusedValue: clearFocusedValueAction,
13688
+ setValue: setValueAction
13689
+ };
13690
+ const arrowPrevEvent = React.useCallback(()=>{
13691
+ if (interactionState === "focused") {
13692
+ actions.selectPrev();
13693
+ focusTriggerByValue(enabledValues[prevIndex] ?? "");
13694
+ if (isFocusVisibleSupported) {
13695
+ setIsFocusVisible(true);
13696
+ }
13697
+ }
13698
+ }, [
13699
+ interactionState,
13700
+ actions.selectPrev,
13701
+ focusTriggerByValue,
13702
+ enabledValues,
13703
+ prevIndex,
13704
+ isFocusVisibleSupported
13705
+ ]);
13706
+ const arrowNextEvent = React.useCallback(()=>{
13707
+ if (interactionState === "focused") {
13708
+ actions.selectNext();
13709
+ focusTriggerByValue(enabledValues[nextIndex] ?? "");
13710
+ if (isFocusVisibleSupported) {
13711
+ setIsFocusVisible(true);
13712
+ }
13713
+ }
13714
+ }, [
13715
+ interactionState,
13716
+ actions.selectNext,
13717
+ focusTriggerByValue,
13718
+ enabledValues,
13719
+ nextIndex,
13720
+ isFocusVisibleSupported
13721
+ ]);
13722
+ const arrowUpEvent = React.useCallback(()=>{
13723
+ if (interactionState === "focused") {
13724
+ actions.selectPrev();
13725
+ focusTriggerByValue(enabledValues[prevIndex] ?? "");
13726
+ if (isFocusVisibleSupported) {
13727
+ setIsFocusVisible(true);
13728
+ }
13729
+ }
13730
+ }, [
13731
+ interactionState,
13732
+ actions.selectPrev,
13733
+ focusTriggerByValue,
13734
+ enabledValues,
13735
+ prevIndex,
13736
+ isFocusVisibleSupported
13737
+ ]);
13738
+ const arrowDownEvent = React.useCallback(()=>{
13739
+ if (interactionState === "focused") {
13740
+ actions.selectNext();
13741
+ focusTriggerByValue(enabledValues[nextIndex] ?? "");
13742
+ if (isFocusVisibleSupported) {
13743
+ setIsFocusVisible(true);
13744
+ }
13745
+ }
13746
+ }, [
13747
+ interactionState,
13748
+ actions.selectNext,
13749
+ focusTriggerByValue,
13750
+ enabledValues,
13751
+ nextIndex,
13752
+ isFocusVisibleSupported
13753
+ ]);
13754
+ const homeEvent = React.useCallback(()=>{
13755
+ if (interactionState === "focused") {
13756
+ actions.selectFirst();
13757
+ focusTriggerByValue(enabledValues[0] ?? "");
13758
+ if (isFocusVisibleSupported) {
13759
+ setIsFocusVisible(true);
13760
+ }
13761
+ }
13762
+ }, [
13763
+ interactionState,
13764
+ actions.selectFirst,
13765
+ focusTriggerByValue,
13766
+ enabledValues,
13767
+ isFocusVisibleSupported
13768
+ ]);
13769
+ const endEvent = React.useCallback(()=>{
13770
+ if (interactionState === "focused") {
13771
+ actions.selectLast();
13772
+ focusTriggerByValue(enabledValues[enabledValues.length - 1] ?? "");
13773
+ if (isFocusVisibleSupported) {
13774
+ setIsFocusVisible(true);
13775
+ }
13776
+ }
13777
+ }, [
13778
+ interactionState,
13779
+ actions.selectLast,
13780
+ focusTriggerByValue,
13781
+ enabledValues,
13782
+ isFocusVisibleSupported
13783
+ ]);
13784
+ const tabFocusEvent = React.useCallback((value)=>{
13785
+ actions.setFocusedValue(value);
13786
+ if (interactionState === "idle") {
13787
+ setInteractionState("focused");
13788
+ }
13789
+ }, [
13790
+ interactionState,
13791
+ actions.setFocusedValue
13792
+ ]);
13793
+ const tabBlurEvent = React.useCallback(()=>{
13794
+ if (interactionState === "focused") {
13795
+ actions.clearFocusedValue();
13796
+ setInteractionState("idle");
13797
+ }
13798
+ }, [
13799
+ interactionState,
13800
+ actions.clearFocusedValue
13801
+ ]);
13802
+ const tabClickEvent = React.useCallback((value)=>{
13803
+ actions.setValue(value);
13804
+ if (interactionState === "idle") {
13805
+ setInteractionState("focused");
13806
+ }
13807
+ }, [
13808
+ interactionState,
13809
+ actions.setValue
13810
+ ]);
13811
+ const setValueEvent = React.useCallback((value)=>{
13812
+ actions.setValue(value);
13813
+ }, [
13814
+ actions.setValue
13815
+ ]);
13816
+ const selectNextEvent = React.useCallback(()=>{
13817
+ if (interactionState === "focused") {
13818
+ actions.selectNext();
13819
+ if (isFocusVisibleSupported) {
13820
+ setIsFocusVisible(true);
13821
+ }
13822
+ }
13823
+ }, [
13824
+ interactionState,
13825
+ actions.selectNext,
13826
+ isFocusVisibleSupported
13827
+ ]);
13828
+ const selectPrevEvent = React.useCallback(()=>{
13829
+ if (interactionState === "focused") {
13830
+ actions.selectPrev();
13831
+ if (isFocusVisibleSupported) {
13832
+ setIsFocusVisible(true);
13833
+ }
13834
+ }
13835
+ }, [
13836
+ interactionState,
13837
+ actions.selectPrev,
13838
+ isFocusVisibleSupported
13839
+ ]);
13840
+ const setContentIndexEvent = React.useCallback((index)=>{
13841
+ const valueFromIndex = enabledValues[index];
13842
+ if (!valueFromIndex) return;
13843
+ actions.setValue(valueFromIndex);
13844
+ }, [
13845
+ actions.setValue,
13846
+ enabledValues
13847
+ ]);
13848
+ const setIsFocusVisibleEvent = React.useCallback((isFocusVisible)=>{
13849
+ setIsFocusVisible(isFocusVisible);
13850
+ }, []);
13851
+ const setSelectedTriggerElEvent = React.useCallback((element)=>{
13852
+ setSelectedTriggerEl(element);
13853
+ }, []);
13854
+ const events = {
13855
+ arrowPrev: arrowPrevEvent,
13856
+ arrowNext: arrowNextEvent,
13857
+ arrowUp: arrowUpEvent,
13858
+ arrowDown: arrowDownEvent,
13859
+ home: homeEvent,
13860
+ end: endEvent,
13861
+ tabFocus: tabFocusEvent,
13862
+ tabBlur: tabBlurEvent,
13863
+ tabClick: tabClickEvent,
13864
+ setValue: setValueEvent,
13865
+ selectNext: selectNextEvent,
13866
+ selectPrev: selectPrevEvent,
13867
+ setContentIndex: setContentIndexEvent,
13868
+ setIsFocusVisible: setIsFocusVisibleEvent,
13869
+ setSelectedTriggerEl: setSelectedTriggerElEvent
13870
+ };
13871
+ const refs = React.useMemo(()=>({
13872
+ list: listRef
13873
+ }), []);
13874
+ const triggerRect = React.useMemo(()=>({
13875
+ width: selectedTriggerSize?.width ?? selectedTriggerEl?.offsetWidth ?? 0,
13876
+ left: selectedTriggerEl?.offsetLeft ?? 0
13877
+ }), [
13878
+ selectedTriggerSize,
13879
+ selectedTriggerEl
13880
+ ]);
13881
+ const isSSR = useIsSSR();
13882
+ return {
13883
+ refs,
13884
+ interactionState,
13885
+ value,
13886
+ isSSR,
13887
+ triggerRect,
13888
+ focusedValue,
13889
+ isFocusVisible,
13890
+ contentIndex,
13891
+ events,
13892
+ isFocusVisibleSupported
13893
+ };
13894
+ }
13895
+ function useTabs(props) {
13896
+ const autoId = React.useId();
13897
+ const { refs, interactionState, value, isSSR, events, triggerRect, focusedValue, isFocusVisible, contentIndex, isFocusVisibleSupported } = useTabsState(props);
13898
+ const { orientation = "horizontal" } = props;
13899
+ const focused = interactionState === "focused";
13900
+ const stateProps = React.useMemo(()=>domUtils.elementProps({
13901
+ "data-orientation": orientation,
13902
+ "data-focus": domUtils.dataAttr(focused),
13903
+ "data-ssr": domUtils.dataAttr(isSSR)
13904
+ }), [
13905
+ orientation,
13906
+ focused,
13907
+ isSSR
13908
+ ]);
13909
+ return {
13910
+ refs,
13911
+ value,
13912
+ contentIndex,
13913
+ triggerRect,
13914
+ selectNext: events.selectNext,
13915
+ selectPrev: events.selectPrev,
13916
+ setValue: events.setValue,
13917
+ setContentIndex: events.setContentIndex,
13918
+ stateProps,
13919
+ rootProps: domUtils.elementProps({
13920
+ ...stateProps,
13921
+ style: {
13922
+ "--indicator-left": `${triggerRect.left}px`,
13923
+ "--indicator-width": `${triggerRect.width}px`
13924
+ }
13925
+ }),
13926
+ listProps: domUtils.elementProps({
13927
+ id: getListId(autoId),
13928
+ role: "tablist",
13929
+ "aria-orientation": orientation,
13930
+ ...stateProps,
13931
+ onKeyDown (event) {
13932
+ if (event.defaultPrevented) return;
13933
+ if (event.nativeEvent.isComposing) return;
13934
+ // TODO: support activationMode="manual"
13935
+ switch(event.key){
13936
+ case "ArrowLeft":
13937
+ if (orientation !== "horizontal") return;
13938
+ event.preventDefault();
13939
+ events.arrowPrev();
13940
+ break;
13941
+ case "ArrowRight":
13942
+ if (orientation !== "horizontal") return;
13943
+ event.preventDefault();
13944
+ events.arrowNext();
13945
+ break;
13946
+ case "ArrowUp":
13947
+ if (orientation !== "vertical") return;
13948
+ event.preventDefault();
13949
+ events.arrowPrev();
13950
+ break;
13951
+ case "ArrowDown":
13952
+ if (orientation !== "vertical") return;
13953
+ event.preventDefault();
13954
+ events.arrowNext();
13955
+ break;
13956
+ case "Home":
13957
+ {
13958
+ event.preventDefault();
13959
+ events.home();
13960
+ break;
13961
+ }
13962
+ case "End":
13963
+ {
13964
+ event.preventDefault();
13965
+ events.end();
13966
+ break;
13967
+ }
13968
+ }
13969
+ }
13970
+ }),
13971
+ getTriggerProps: (props)=>{
13972
+ const { disabled: isDisabled, value: triggerValue } = props;
13973
+ const itemState = {
13974
+ isDisabled,
13975
+ isSelected: value === triggerValue,
13976
+ isFocused: focusedValue === triggerValue
13977
+ };
13978
+ const itemStateProps = {
13979
+ "data-focus": domUtils.dataAttr(itemState.isFocused),
13980
+ "data-focus-visible": domUtils.dataAttr(itemState.isFocused && isFocusVisible),
13981
+ "data-selected": domUtils.dataAttr(itemState.isSelected),
13982
+ "data-disabled": domUtils.dataAttr(itemState.isDisabled),
13983
+ "data-ssr": domUtils.dataAttr(isSSR),
13984
+ "aria-disabled": domUtils.ariaAttr(itemState.isDisabled),
13985
+ "aria-selected": domUtils.ariaAttr(itemState.isSelected)
13986
+ };
13987
+ const ref = (element)=>{
13988
+ if (element && triggerValue === value) {
13989
+ events.setSelectedTriggerEl(element);
13990
+ }
13991
+ };
13992
+ return {
13993
+ ...itemState,
13994
+ refs: {
13995
+ root: ref
13996
+ },
13997
+ stateProps: itemStateProps,
13998
+ rootProps: domUtils.buttonProps({
13999
+ id: getTriggerId(triggerValue, autoId),
14000
+ role: "tab",
14001
+ type: "button",
14002
+ disabled: isDisabled,
14003
+ tabIndex: itemState.isSelected ? 0 : -1,
14004
+ ...itemStateProps,
14005
+ "data-value": triggerValue,
14006
+ "data-orientation": orientation,
14007
+ "data-ownedby": getListId(autoId),
14008
+ "aria-controls": getContentId(triggerValue, autoId),
14009
+ onClick (event) {
14010
+ if (itemState.isDisabled) return;
14011
+ if (event.defaultPrevented) return;
14012
+ events.tabClick(triggerValue);
14013
+ },
14014
+ onFocus (event) {
14015
+ events.tabFocus(props.value);
14016
+ if (isFocusVisibleSupported) {
14017
+ events.setIsFocusVisible(event.target.matches(":focus-visible"));
14018
+ }
14019
+ },
14020
+ onBlur (event) {
14021
+ const target = event.relatedTarget;
14022
+ if (target?.getAttribute("role") !== "tab") {
14023
+ events.tabBlur();
14024
+ }
14025
+ if (isFocusVisibleSupported) {
14026
+ events.setIsFocusVisible(false);
14027
+ }
14028
+ }
14029
+ })
14030
+ };
14031
+ },
14032
+ getContentProps: (props)=>{
14033
+ const { value: contentValue } = props;
14034
+ const triggerId = getTriggerId(contentValue, autoId);
14035
+ const isSelected = value === contentValue;
14036
+ return domUtils.elementProps({
14037
+ id: getContentId(contentValue, autoId),
14038
+ tabIndex: -1,
14039
+ role: "tabpanel",
14040
+ "aria-labelledby": triggerId,
14041
+ "aria-selected": domUtils.ariaAttr(isSelected),
14042
+ "aria-hidden": !isSelected,
14043
+ "data-selected": domUtils.dataAttr(isSelected),
14044
+ "data-orientation": orientation,
14045
+ "data-ownedby": getListId(autoId),
14046
+ "data-ssr": domUtils.dataAttr(isSSR)
14047
+ });
14048
+ },
14049
+ indicatorProps: domUtils.elementProps({
14050
+ ...stateProps
14051
+ })
14052
+ };
14053
+ }
14054
+ const TabsContext = /*#__PURE__*/ React.createContext(null);
14055
+ const TabsProvider = TabsContext.Provider;
14056
+ function useTabsContext({ strict = true } = {}) {
14057
+ const context = React.useContext(TabsContext);
14058
+ if (!context && strict) {
14059
+ throw new Error("useTabsContext must be used within a Tabs");
14060
+ }
14061
+ return context;
14062
+ }
14063
+ const autoHeight = AutoHeight();
14064
+ const plugins = [
14065
+ autoHeight
14066
+ ];
14067
+ const useTabsCarouselState = (props)=>{
14068
+ const api = useTabsContext();
14069
+ const slideHeights = React.useRef(new WeakMap());
14070
+ const [emblaRef, emblaApi] = useEmblaCarousel({
14071
+ loop: props.loop,
14072
+ dragThreshold: props.dragThreshold,
14073
+ duration: 20,
14074
+ watchDrag: (_, event)=>{
14075
+ if (event.target instanceof HTMLElement) {
14076
+ if (isDragPrevented(event.target)) {
14077
+ return false;
14078
+ }
14079
+ }
14080
+ return true;
14081
+ },
14082
+ // Workaround for Embla v8 AutoHeight plugin not detecting lazy-loaded content height changes.
14083
+ // ref: https://github.com/davidjerleke/embla-carousel/discussions/1142
14084
+ // Embla's default resize handler only reInits on axis-size (width, for a horizontal carousel)
14085
+ // changes, so a slide growing taller (e.g. after lazy mount) never triggers a recalculation.
14086
+ // We reInit manually, but ONLY when a slide's *height* actually changes — reInit-ing on every
14087
+ // slide resize (including width fluctuations during horizontal scroll) re-applies Embla's
14088
+ // translate3d mid-scroll and breaks momentum scrolling in WKWebView/Safari.
14089
+ watchResize: (emblaApi, entries)=>{
14090
+ const heights = slideHeights.current;
14091
+ const slides = emblaApi.slideNodes();
14092
+ for (const entry of entries){
14093
+ const slide = slides.find((node)=>node === entry.target);
14094
+ if (!slide) continue;
14095
+ const newHeight = slide.offsetHeight;
14096
+ const prevHeight = heights.get(slide);
14097
+ heights.set(slide, newHeight);
14098
+ if (prevHeight !== undefined && newHeight !== prevHeight) {
14099
+ emblaApi.reInit();
14100
+ return false;
14101
+ }
14102
+ }
14103
+ return true;
14104
+ }
14105
+ }, plugins);
14106
+ const onSettle = useCallbackRef(props.onSettle);
14107
+ const onSwipeStart = useCallbackRef(props.onSwipeStart);
14108
+ const onSwipeEnd = useCallbackRef(props.onSwipeEnd);
14109
+ React.useEffect(()=>{
14110
+ const select = emblaApi?.on("select", ()=>{
14111
+ const contentIndex = emblaApi.selectedScrollSnap();
14112
+ api.setContentIndex(contentIndex);
14113
+ });
14114
+ const settle = emblaApi?.on("settle", ()=>{
14115
+ onSettle?.();
14116
+ });
14117
+ const pointerDown = emblaApi?.on("pointerDown", ()=>{
14118
+ onSwipeStart?.();
14119
+ });
14120
+ const pointerUp = emblaApi?.on("pointerUp", ()=>{
14121
+ onSwipeEnd?.();
14122
+ });
14123
+ return ()=>{
14124
+ select?.clear();
14125
+ settle?.clear();
14126
+ pointerDown?.clear();
14127
+ pointerUp?.clear();
14128
+ };
14129
+ }, [
14130
+ emblaApi,
14131
+ api.setContentIndex,
14132
+ onSettle,
14133
+ onSwipeStart,
14134
+ onSwipeEnd
14135
+ ]);
14136
+ const getContentIndex = useCallbackRef(()=>api.contentIndex);
14137
+ React.useEffect(()=>{
14138
+ const reInit = emblaApi?.on("reInit", ()=>{
14139
+ emblaApi?.scrollTo(getContentIndex(), true);
14140
+ });
14141
+ return ()=>{
14142
+ reInit?.clear();
14143
+ };
14144
+ }, [
14145
+ getContentIndex,
14146
+ emblaApi
14147
+ ]);
14148
+ React.useEffect(()=>{
14149
+ if (!emblaApi) return;
14150
+ const { dragHandler } = emblaApi.internalEngine();
14151
+ if (props.swipeable) {
14152
+ dragHandler.init(emblaApi);
14153
+ } else {
14154
+ dragHandler.destroy();
14155
+ }
14156
+ }, [
14157
+ emblaApi,
14158
+ props.swipeable
14159
+ ]);
14160
+ const isInitialScroll = React.useRef(true);
14161
+ React.useEffect(()=>{
14162
+ if (emblaApi && api.contentIndex !== emblaApi.selectedScrollSnap()) {
14163
+ const engine = emblaApi.internalEngine();
14164
+ /**
14165
+ * We want the content to quickly snap into place when a tab changes.
14166
+ * However, using a very small duration can result in an underdamped spring, which produces a rubber-band (oscillatory) effect.
14167
+ *
14168
+ * To avoid that, we intentionally use a low duration and friction so that the motion feels fast (snappy)
14169
+ * without overshooting. The underlying idea is to map our parameters to a physical spring model—a damped harmonic oscillator—
14170
+ * so we can control the dynamics in a physically intuitive way.
14171
+ *
14172
+ * In embla, the acceleration is modeled as:
14173
+ * a = (displacement * friction) / duration - velocity * (1 - friction)
14174
+ *
14175
+ * In a classical damped harmonic oscillator (assuming unit mass, m = 1), the acceleration is:
14176
+ * a = (stiffness * displacement - damping * velocity)
14177
+ *
14178
+ * By comparing the two equations, we can equate the coefficients for displacement and velocity:
14179
+ *
14180
+ * For the displacement term:
14181
+ * stiffness = friction / duration
14182
+ *
14183
+ * For the velocity term:
14184
+ * damping = 1 - friction
14185
+ *
14186
+ * Additionally, the damping ratio (zeta) for a damped oscillator is defined as:
14187
+ * zeta = damping / (2 * sqrt(stiffness))
14188
+ *
14189
+ * Substituting the derived expressions for stiffness and damping gives:
14190
+ * zeta = (1 - friction) / (2 * sqrt(friction / duration))
14191
+ *
14192
+ * This damping ratio (zeta) tells us the nature of the response:
14193
+ * - zeta < 1: Underdamped (oscillatory, rubber-band effect)
14194
+ * - zeta = 1: Critically damped (fast snap without overshoot)
14195
+ * - zeta > 1: Overdamped (slow response)
14196
+ *
14197
+ * For our current configuration, we use:
14198
+ *
14199
+ * duration = 4
14200
+ * friction = 0.4
14201
+ *
14202
+ * Which gives:
14203
+ *
14204
+ * stiffness = 0.4 / 4 = 0.1
14205
+ * damping = 1 - 0.4 = 0.6
14206
+ *
14207
+ * And the damping ratio becomes:
14208
+ *
14209
+ * zeta = 0.6 / (2 * sqrt(0.1))
14210
+ * = 0.6 / (2 * 0.31623...)
14211
+ * = 0.6 / 0.63246...
14212
+ * = 0.94868...
14213
+ *
14214
+ * Since zeta is close to 1, this spring is nearly critically damped. This is enough for our usage,
14215
+ * ensuring that the animation snaps quickly without overshooting (avoiding the rubber-band effect)
14216
+ * when switching tabs.
14217
+ */ if (isInitialScroll.current) {
14218
+ engine.scrollBody.useDuration(0);
14219
+ isInitialScroll.current = false;
14220
+ } else {
14221
+ engine.scrollBody.useDuration(4).useFriction(0.4);
14222
+ }
14223
+ engine.scrollTo.index(api.contentIndex, 0);
14224
+ }
14225
+ }, [
14226
+ emblaApi,
14227
+ api.contentIndex
14228
+ ]);
14229
+ return {
14230
+ refs: {
14231
+ root: emblaRef
14232
+ },
14233
+ autoHeight: props.autoHeight
14234
+ };
14235
+ };
14236
+ const useTabsCarousel = (props)=>{
14237
+ const { refs, autoHeight } = useTabsCarouselState(props);
14238
+ const stateProps = domUtils.elementProps({
14239
+ "data-carousel": "",
14240
+ "data-auto-height": domUtils.dataAttr(autoHeight)
14241
+ });
14242
+ return {
14243
+ refs,
14244
+ stateProps,
14245
+ rootProps: domUtils.elementProps({
14246
+ ...stateProps
14247
+ }),
14248
+ cameraProps: domUtils.elementProps({
14249
+ ...stateProps
14250
+ })
14251
+ };
14252
+ };
14253
+ const TabsCarouselContext = /*#__PURE__*/ React.createContext(null);
14254
+ const TabsCarouselProvider = TabsCarouselContext.Provider;
14255
+ function useTabsCarouselContext({ strict = true } = {}) {
14256
+ const context = React.useContext(TabsCarouselContext);
14257
+ if (!context && strict) {
14258
+ throw new Error("useTabsCarouselContext must be used within a TabsCarousel");
14259
+ }
14260
+ return context;
14261
+ }
14262
+ const TabsTriggerContext = /*#__PURE__*/ React.createContext(null);
14263
+ const TabsTriggerProvider = TabsTriggerContext.Provider;
14264
+ const TabsRoot$1 = /*#__PURE__*/ React.forwardRef((props, ref)=>{
14265
+ const { defaultValue, value, onValueChange, orientation, lazyMount, unmountOnExit, ...otherProps } = props;
14266
+ const api = useTabs({
14267
+ defaultValue,
14268
+ value,
14269
+ onValueChange,
14270
+ orientation
14271
+ });
14272
+ return /*#__PURE__*/ jsxRuntime.jsx(TabsProvider, {
14273
+ value: api,
14274
+ children: /*#__PURE__*/ jsxRuntime.jsx(RenderStrategyPropsProvider, {
14275
+ value: React.useMemo(()=>({
14276
+ lazyMount,
14277
+ unmountOnExit
14278
+ }), [
14279
+ lazyMount,
14280
+ unmountOnExit
14281
+ ]),
14282
+ children: /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14283
+ ref: ref,
14284
+ ...domUtils.mergeProps(api.rootProps, otherProps)
14285
+ })
14286
+ })
14287
+ });
14288
+ });
14289
+ TabsRoot$1.displayName = "TabsRoot";
14290
+ const TabsList$1 = /*#__PURE__*/ React.forwardRef((props, ref)=>{
14291
+ const api = useTabsContext();
14292
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14293
+ ref: composeRefs(api.refs.list, ref),
14294
+ ...domUtils.mergeProps(api.listProps, props)
14295
+ });
14296
+ });
14297
+ TabsList$1.displayName = "TabsList";
14298
+ const TabsTrigger$1 = /*#__PURE__*/ React.forwardRef((props, ref)=>{
14299
+ const { value, disabled, ...otherProps } = props;
14300
+ const api = useTabsContext();
14301
+ const triggerApi = api.getTriggerProps({
14302
+ value,
14303
+ disabled
14304
+ });
14305
+ return /*#__PURE__*/ jsxRuntime.jsx(TabsTriggerProvider, {
14306
+ value: triggerApi,
14307
+ children: /*#__PURE__*/ jsxRuntime.jsx(Primitive.button, {
14308
+ ref: composeRefs(triggerApi.refs.root, ref),
14309
+ ...domUtils.mergeProps(triggerApi.rootProps, otherProps)
14310
+ })
14311
+ });
14312
+ });
14313
+ TabsTrigger$1.displayName = "TabsTrigger";
14314
+ const TabsContent$1 = /*#__PURE__*/ React.forwardRef((props, ref)=>{
14315
+ const { value, children, ...otherProps } = props;
14316
+ const api = useTabsContext();
14317
+ const carouselApi = useTabsCarouselContext({
14318
+ strict: false
14319
+ });
14320
+ const inCarousel = carouselApi != null;
14321
+ const renderStrategyProps = useRenderStrategyPropsContext();
14322
+ const { unmounted } = useRenderStrategy({
14323
+ ...renderStrategyProps,
14324
+ present: api.value === value
14325
+ });
14326
+ // Outside a carousel, an unmounted tab renders nothing at all.
14327
+ // Inside a carousel, every tab must keep its slide slot even while unmounted:
14328
+ // the carousel syncs by numeric index, so collapsing the slide set (returning
14329
+ // null for lazy/exited tabs) makes embla's snap index drift from the tab's
14330
+ // logical index, and clicking/swiping to a far tab lands on the wrong one.
14331
+ // We keep the slot but still honor lazyMount/unmountOnExit for the children,
14332
+ // so expensive content stays lazy — only the empty slide wrapper persists.
14333
+ if (unmounted && !inCarousel) return null;
14334
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14335
+ ref: ref,
14336
+ ...domUtils.mergeProps(api.getContentProps({
14337
+ value
14338
+ }), carouselApi?.stateProps ?? {}, otherProps),
14339
+ children: unmounted ? null : children
14340
+ });
14341
+ });
14342
+ TabsContent$1.displayName = "TabsContent";
14343
+ const TabsIndicator$1 = /*#__PURE__*/ React.forwardRef((props, ref)=>{
14344
+ const api = useTabsContext();
14345
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14346
+ ref: ref,
14347
+ ...domUtils.mergeProps(api.indicatorProps, props)
14348
+ });
14349
+ });
14350
+ const TabsCarousel = /*#__PURE__*/ React.forwardRef((props, ref)=>{
14351
+ const { dragThreshold, loop, swipeable, autoHeight, onSettle, onSwipeStart, onSwipeEnd, ...otherProps } = props;
14352
+ const api = useTabsCarousel({
14353
+ dragThreshold,
14354
+ loop,
14355
+ swipeable,
14356
+ onSettle,
14357
+ autoHeight,
14358
+ onSwipeStart,
14359
+ onSwipeEnd
14360
+ });
14361
+ return /*#__PURE__*/ jsxRuntime.jsx(TabsCarouselProvider, {
14362
+ value: api,
14363
+ children: /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14364
+ ref: composeRefs(api.refs.root, ref),
14365
+ ...domUtils.mergeProps(api.rootProps, otherProps),
14366
+ children: props.children
14367
+ })
14368
+ });
14369
+ });
14370
+ TabsCarousel.displayName = "TabsCarousel";
14371
+ const TabsCarouselCamera = /*#__PURE__*/ React.forwardRef((props, ref)=>{
14372
+ const api = useTabsCarouselContext();
14373
+ return /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14374
+ ref: ref,
14375
+ ...domUtils.mergeProps(api.cameraProps, props)
14376
+ });
14377
+ });
14378
+ TabsCarouselCamera.displayName = "TabsCarouselCamera";
14379
+
14380
+ var Tabs_namespace = {
14381
+ __proto__: null,
14382
+ Carousel: TabsCarousel,
14383
+ CarouselCamera: TabsCarouselCamera,
14384
+ Content: TabsContent$1,
14385
+ Indicator: TabsIndicator$1,
14386
+ List: TabsList$1,
14387
+ Root: TabsRoot$1,
14388
+ Trigger: TabsTrigger$1};
14389
+
14390
+ const { withProvider, withContext } = createSlotRecipeContext(tabs.tabs);
14391
+ const TabsRoot = withProvider(Tabs_namespace.Root, "root");
14392
+ const TabsList = withContext(Tabs_namespace.List, "list");
14393
+ const TabsTrigger = withScaleFeedback(withContext(Tabs_namespace.Trigger, "trigger"));
14394
+ const TabsIndicator = withContext(Tabs_namespace.Indicator, "indicator");
14395
+ const TabsContent = withContext(Tabs_namespace.Content, "content");
14396
+ withContext(Tabs_namespace.Carousel, "carousel");
14397
+ withContext(Tabs_namespace.CarouselCamera, "carouselCamera");
14398
+
14399
+ const RESIZE_MIN_WIDTH = 360;
14400
+ const RESIZE_MAX_WIDTH = 720;
14401
+ /**
14402
+ * Right-side record peek panel -- Attio's own peek shows a Highlights card
14403
+ * area and an Activity feed (measured live). Ported from your-moon/crisp's
14404
+ * `RecordPanel` (`product/recordpanel/`), built ON `ResizablePanel`
14405
+ * (`side="right" floating`) rather than crisp's own duplicate absolute-
14406
+ * positioning/slide-in/resize-handle implementation -- the outer shell (the
14407
+ * enter/exit keyframe animation, the full-height overlay, the drag handle
14408
+ * itself) all come from that primitive; this component only renders the
14409
+ * CONTENT inside it.
14410
+ *
14411
+ * OVERLAY, not docked: `floating` (on the `ResizablePanel` this is built on)
14412
+ * now means a real `position: absolute` overlay that reserves ZERO layout
14413
+ * width -- opening this panel never shrinks or reflows a table beside it
14414
+ * (your-moon/crisp#448 reversed an earlier in-flow/docked attempt at this
14415
+ * exact spot, reported live as "table's action buttons moving"). Because of
14416
+ * that, THIS COMPONENT OWNS ITS OWN POSITIONING -- a consumer does not wrap
14417
+ * it in a flex row with the table; it renders `<RecordPreview>` as an
14418
+ * ordinary sibling of the table. The one requirement: the shared parent
14419
+ * (the table region's own wrapper) must itself be a positioned element
14420
+ * (typically `position: relative`) with a real height, since that's the
14421
+ * containing block this panel's `inset: 0` resolves against. See
14422
+ * `DataTable.stories.tsx`'s own Showcase for the pattern:
14423
+ *
14424
+ * ```tsx
14425
+ * <div style={{ position: "relative", flex: 1, minHeight: 0 }}>
14426
+ * <DataTable style={{ width: "100%", height: "100%" }} ... />
14427
+ * <RecordPreview open={open} record={record} onClose={onClose} ... />
14428
+ * </div>
14429
+ * ```
14430
+ *
14431
+ * Enter/exit: closing this panel is a state change (`open` -> false) that
14432
+ * typically clears `record` in the SAME update (see the DataTable story's
14433
+ * own `onClose={() => setPreviewRow(null)}`), so by the time an exit
14434
+ * animation should play, the values this component would render are
14435
+ * already gone. `usePresence` (`@seed-design/react-presence`, the same
14436
+ * idiom Dialog/Drawer already use for their own floating layers) keeps the
14437
+ * tree mounted for exactly the exit animation's duration; the snapshot
14438
+ * below keeps the LAST visible props around for that same window, since
14439
+ * without it the panel would animate out empty.
14440
+ */ function RecordPreview(props) {
14441
+ const shouldRender = props.open && props.record != null;
14442
+ const { isPresent, ref: panelElementRef } = usePresence(shouldRender);
14443
+ const lastVisiblePropsRef = React.useRef(props);
14444
+ if (shouldRender) lastVisiblePropsRef.current = props;
14445
+ if (!isPresent) return null;
14446
+ return /*#__PURE__*/ jsxRuntime.jsx(RecordPreviewPanel, {
14447
+ ...shouldRender ? props : lastVisiblePropsRef.current,
14448
+ animationState: shouldRender ? "open" : "closed",
14449
+ panelElementRef: panelElementRef
14450
+ });
14451
+ }
14452
+ RecordPreview.displayName = "RecordPreview";
14453
+ 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 }) {
14454
+ const titleId = React.useId();
14455
+ const classNames = recordPreview();
14456
+ // Which sections (your-moon/crisp#413) are expanded -- uncontrolled and
14457
+ // independent (Attio's own Emails/Notes/Tasks open without closing one
14458
+ // another): a plain `Set`, reset whenever the record itself changes so a
14459
+ // stale open section doesn't carry over between two different records.
14460
+ const [openSections, setOpenSections] = React.useState(()=>new Set());
14461
+ // 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.
14462
+ React.useEffect(()=>{
14463
+ setOpenSections(new Set());
14464
+ }, [
14465
+ record
14466
+ ]);
14467
+ const toggleSection = (key)=>setOpenSections((prev)=>{
14468
+ const next = new Set(prev);
14469
+ if (next.has(key)) next.delete(key);
14470
+ else next.add(key);
14471
+ return next;
14472
+ });
14473
+ // Close on Escape. Simpler than crisp's own version (crisp additionally
14474
+ // checks `hasOpenFloatingLayer()` so an Escape meant for a nested Menu/
14475
+ // Select/Popover doesn't also close this panel) -- crispy-seed has no
14476
+ // equivalent floating-stack registry to read, so a consumer nesting its
14477
+ // own floating layers inside `footer`/`sections` content should stop
14478
+ // propagation on its own Escape handler. This component is only ever
14479
+ // mounted (by `RecordPreview`, above) while genuinely open or animating
14480
+ // out, so there's no separate `open` flag to gate on here.
14481
+ React.useEffect(()=>{
14482
+ const onKey = (event)=>{
14483
+ if (event.key === "Escape") onClose();
14484
+ };
14485
+ document.addEventListener("keydown", onKey);
14486
+ return ()=>document.removeEventListener("keydown", onKey);
14487
+ }, [
14488
+ onClose
14489
+ ]);
14490
+ // Defensive only -- `RecordPreview` never mounts this without a record.
14491
+ if (!record) return null;
14492
+ const heading = title ?? record.name ?? "Record";
14493
+ // The plain (non-tabs) layout -- unchanged from before `tabs` existed.
14494
+ // Extracted so it can ALSO become the synthesized "Overview" tab's own
14495
+ // content below, rather than being re-authored as JSX twice.
14496
+ const overviewContent = /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
14497
+ children: [
14498
+ highlights && highlights.length > 0 && /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
14499
+ className: classNames.section,
14500
+ children: [
14501
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14502
+ className: classNames.sectionHead,
14503
+ children: /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
14504
+ className: classNames.sectionLabel,
14505
+ children: highlightsLabel
14506
+ })
14507
+ }),
14508
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14509
+ className: classNames.highlights,
14510
+ children: highlights.map((h)=>/*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
14511
+ className: classNames.highlight,
14512
+ children: [
14513
+ /*#__PURE__*/ jsxRuntime.jsxs(Primitive.span, {
14514
+ className: classNames.highlightLabel,
14515
+ children: [
14516
+ /*#__PURE__*/ jsxRuntime.jsx("span", {
14517
+ children: h.label
14518
+ }),
14519
+ h.icon ? /*#__PURE__*/ jsxRuntime.jsx(h.icon, {
14520
+ size: 16
14521
+ }) : null
14522
+ ]
14523
+ }),
14524
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
14525
+ className: classNames.highlightValue,
14526
+ children: h.value
14527
+ })
14528
+ ]
14529
+ }, h.key))
14530
+ })
14531
+ ]
14532
+ }),
14533
+ activity != null && /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
14534
+ className: classNames.section,
14535
+ children: [
14536
+ /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
14537
+ className: classNames.sectionHead,
14538
+ children: [
14539
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
14540
+ className: classNames.sectionLabel,
14541
+ children: activityLabel
14542
+ }),
14543
+ onViewAllActivity && /*#__PURE__*/ jsxRuntime.jsx(Primitive.button, {
14544
+ type: "button",
14545
+ className: classNames.sectionAction,
14546
+ onClick: onViewAllActivity,
14547
+ children: viewAllActivityLabel
14548
+ })
14549
+ ]
14550
+ }),
14551
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14552
+ className: classNames.activity,
14553
+ children: activity
14554
+ })
14555
+ ]
14556
+ }),
14557
+ sections && sections.length > 0 && /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14558
+ className: classNames.sections,
14559
+ children: sections.map((s)=>{
14560
+ const isOpen = openSections.has(s.key);
14561
+ const panelId = `${titleId}-section-${s.key}`;
14562
+ return /*#__PURE__*/ jsxRuntime.jsxs("div", {
14563
+ children: [
14564
+ s.content ? /*#__PURE__*/ jsxRuntime.jsxs(Primitive.button, {
14565
+ type: "button",
14566
+ className: classNames.sectionTrigger,
14567
+ "aria-expanded": isOpen,
14568
+ "aria-controls": panelId,
14569
+ onClick: ()=>toggleSection(s.key),
14570
+ children: [
14571
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
14572
+ className: classNames.sectionRowLabel,
14573
+ children: s.label
14574
+ }),
14575
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
14576
+ className: classNames.sectionRowCount,
14577
+ children: s.count
14578
+ }),
14579
+ /*#__PURE__*/ jsxRuntime.jsx(IconChevronDownLine_default, {
14580
+ className: classNames.sectionChevron,
14581
+ size: 14,
14582
+ "data-open": isOpen ? "" : undefined,
14583
+ "aria-hidden": "true"
14584
+ })
14585
+ ]
14586
+ }) : /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
14587
+ className: classNames.sectionTrigger,
14588
+ "data-inert": "",
14589
+ children: [
14590
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
14591
+ className: classNames.sectionRowLabel,
14592
+ children: s.label
14593
+ }),
14594
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
14595
+ className: classNames.sectionRowCount,
14596
+ children: s.count
14597
+ })
14598
+ ]
14599
+ }),
14600
+ s.content && isOpen && /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14601
+ id: panelId,
14602
+ className: classNames.sectionContent,
14603
+ children: s.content
14604
+ })
14605
+ ]
14606
+ }, s.key);
14607
+ })
14608
+ })
14609
+ ]
14610
+ });
14611
+ // Combine (`tabs`'s own doc comment): when there's ALSO plain overview
14612
+ // content given alongside `tabs`, prepend a synthesized tab carrying it,
14613
+ // ahead of the consumer's own tabs -- so `tabs` and the simpler Highlights/
14614
+ // Activity/sections props stay usable TOGETHER rather than one silently
14615
+ // replacing the other.
14616
+ const hasOverviewContent = highlights && highlights.length > 0 || activity != null || sections && sections.length > 0;
14617
+ const allTabs = tabs && hasOverviewContent ? [
14618
+ {
14619
+ key: "__overview",
14620
+ label: overviewTabLabel,
14621
+ content: overviewContent
14622
+ },
14623
+ ...tabs
14624
+ ] : tabs ?? [];
14625
+ return /*#__PURE__*/ jsxRuntime.jsx(ResizablePanel, {
14626
+ side: "right",
14627
+ floating: true,
14628
+ backdrop: backdrop,
14629
+ onBackdropClick: onClose,
14630
+ animationState: animationState,
14631
+ panelElementRef: panelElementRef,
14632
+ resizable: resizable,
14633
+ width: width,
14634
+ defaultSize: defaultWidth,
14635
+ onWidthChange: onWidthChange,
14636
+ min: minWidth,
14637
+ max: maxWidth,
14638
+ children: /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
14639
+ className: clsx(classNames.root, className),
14640
+ ...rest,
14641
+ role: "dialog",
14642
+ "aria-labelledby": titleId,
14643
+ children: [
14644
+ /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
14645
+ className: classNames.toolbar,
14646
+ children: [
14647
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.button, {
14648
+ type: "button",
14649
+ className: classNames.iconbtn,
14650
+ "aria-label": closeLabel,
14651
+ onClick: onClose,
14652
+ children: /*#__PURE__*/ jsxRuntime.jsx(IconXmarkLine_default, {
14653
+ size: 16
14654
+ })
14655
+ }),
14656
+ (onPrevious || onNext) && /*#__PURE__*/ jsxRuntime.jsxs(Primitive.span, {
14657
+ className: classNames.pager,
14658
+ children: [
14659
+ /*#__PURE__*/ jsxRuntime.jsx(Primitive.button, {
14660
+ type: "button",
14661
+ className: classNames.navbtn,
14662
+ "aria-label": previousRecordLabel,
14663
+ disabled: !onPrevious,
14664
+ onClick: onPrevious,
14665
+ children: /*#__PURE__*/ jsxRuntime.jsx(IconChevronUpLine_default, {
14666
+ size: 16
14667
+ })
14668
+ }),
11516
14669
  /*#__PURE__*/ jsxRuntime.jsx(Primitive.button, {
11517
14670
  type: "button",
11518
14671
  className: classNames.navbtn,
@@ -11563,121 +14716,49 @@ const RESIZE_MAX_WIDTH = 720;
11563
14716
  })
11564
14717
  ]
11565
14718
  }),
11566
- /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11567
- className: classNames.body,
11568
- children: [
11569
- highlights && highlights.length > 0 && /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11570
- className: classNames.section,
11571
- children: [
11572
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11573
- className: classNames.sectionHead,
11574
- children: /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
11575
- className: classNames.sectionLabel,
11576
- children: highlightsLabel
11577
- })
11578
- }),
11579
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11580
- className: classNames.highlights,
11581
- children: highlights.map((h)=>/*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11582
- className: classNames.highlight,
11583
- children: [
11584
- /*#__PURE__*/ jsxRuntime.jsxs(Primitive.span, {
11585
- className: classNames.highlightLabel,
11586
- children: [
11587
- /*#__PURE__*/ jsxRuntime.jsx("span", {
11588
- children: h.label
11589
- }),
11590
- h.icon ? /*#__PURE__*/ jsxRuntime.jsx(h.icon, {
11591
- size: 16
11592
- }) : null
11593
- ]
11594
- }),
11595
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
11596
- className: classNames.highlightValue,
11597
- children: h.value
11598
- })
11599
- ]
11600
- }, h.key))
11601
- })
11602
- ]
11603
- }),
11604
- activity != null && /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11605
- className: classNames.section,
11606
- children: [
11607
- /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11608
- className: classNames.sectionHead,
11609
- children: [
11610
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
11611
- className: classNames.sectionLabel,
11612
- children: activityLabel
11613
- }),
11614
- onViewAllActivity && /*#__PURE__*/ jsxRuntime.jsx(Primitive.button, {
11615
- type: "button",
11616
- className: classNames.sectionAction,
11617
- onClick: onViewAllActivity,
11618
- children: viewAllActivityLabel
11619
- })
11620
- ]
11621
- }),
11622
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11623
- className: classNames.activity,
11624
- children: activity
11625
- })
11626
- ]
11627
- }),
11628
- sections && sections.length > 0 && /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11629
- className: classNames.sections,
11630
- children: sections.map((s)=>{
11631
- const isOpen = openSections.has(s.key);
11632
- const panelId = `${titleId}-section-${s.key}`;
11633
- return /*#__PURE__*/ jsxRuntime.jsxs("div", {
11634
- children: [
11635
- s.content ? /*#__PURE__*/ jsxRuntime.jsxs(Primitive.button, {
11636
- type: "button",
11637
- className: classNames.sectionTrigger,
11638
- "aria-expanded": isOpen,
11639
- "aria-controls": panelId,
11640
- onClick: ()=>toggleSection(s.key),
11641
- children: [
11642
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
11643
- className: classNames.sectionRowLabel,
11644
- children: s.label
11645
- }),
11646
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
11647
- className: classNames.sectionRowCount,
11648
- children: s.count
11649
- }),
11650
- /*#__PURE__*/ jsxRuntime.jsx(IconChevronDownLine_default, {
11651
- className: classNames.sectionChevron,
11652
- size: 14,
11653
- "data-open": isOpen ? "" : undefined,
11654
- "aria-hidden": "true"
11655
- })
11656
- ]
11657
- }) : /*#__PURE__*/ jsxRuntime.jsxs(Primitive.div, {
11658
- className: classNames.sectionTrigger,
11659
- "data-inert": "",
14719
+ tabs && tabs.length > 0 ? /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14720
+ className: classNames.tabsRoot,
14721
+ children: /*#__PURE__*/ jsxRuntime.jsxs(TabsRoot, {
14722
+ value: activeTab,
14723
+ defaultValue: defaultActiveTab ?? allTabs[0]?.key,
14724
+ onValueChange: onTabChange,
14725
+ style: {
14726
+ display: "flex",
14727
+ flexDirection: "column",
14728
+ flex: "1 1 auto",
14729
+ minHeight: 0,
14730
+ overflow: "hidden"
14731
+ },
14732
+ children: [
14733
+ /*#__PURE__*/ jsxRuntime.jsxs(TabsList, {
14734
+ children: [
14735
+ allTabs.map((tab)=>/*#__PURE__*/ jsxRuntime.jsxs(TabsTrigger, {
14736
+ value: tab.key,
11660
14737
  children: [
11661
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
11662
- className: classNames.sectionRowLabel,
11663
- children: s.label
11664
- }),
11665
- /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
11666
- className: classNames.sectionRowCount,
11667
- children: s.count
14738
+ tab.label,
14739
+ tab.badge != null && /*#__PURE__*/ jsxRuntime.jsx(Primitive.span, {
14740
+ className: classNames.tabBadge,
14741
+ children: tab.badge
11668
14742
  })
11669
14743
  ]
11670
- }),
11671
- s.content && isOpen && /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11672
- id: panelId,
11673
- className: classNames.sectionContent,
11674
- children: s.content
11675
- })
11676
- ]
11677
- }, s.key);
11678
- })
11679
- })
11680
- ]
14744
+ }, tab.key)),
14745
+ /*#__PURE__*/ jsxRuntime.jsx(TabsIndicator, {})
14746
+ ]
14747
+ }),
14748
+ allTabs.map((tab)=>/*#__PURE__*/ jsxRuntime.jsx(TabsContent, {
14749
+ value: tab.key,
14750
+ className: classNames.tabPanel,
14751
+ style: {
14752
+ flex: "1 1 auto",
14753
+ minHeight: 0
14754
+ },
14755
+ children: tab.content
14756
+ }, tab.key))
14757
+ ]
14758
+ })
14759
+ }) : /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
14760
+ className: classNames.body,
14761
+ children: overviewContent
11681
14762
  }),
11682
14763
  footer != null && /*#__PURE__*/ jsxRuntime.jsx(Primitive.div, {
11683
14764
  className: classNames.footer,
@@ -11687,7 +14768,7 @@ const RESIZE_MAX_WIDTH = 720;
11687
14768
  })
11688
14769
  });
11689
14770
  }
11690
- RecordPreview.displayName = "RecordPreview";
14771
+ RecordPreviewPanel.displayName = "RecordPreviewPanel";
11691
14772
 
11692
14773
  exports.DataTable = DataTable;
11693
14774
  exports.DataTableViewsControl = DataTableViewsControl;