@wistia/ui 0.26.8-beta.e6adee61.1fbb76a → 0.26.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
2
  /*
3
- * @license @wistia/ui v0.26.8-beta.e6adee61.1fbb76a
3
+ * @license @wistia/ui v0.26.8
4
4
  *
5
5
  * Copyright (c) 2024-2026, Wistia, Inc. and its affiliates.
6
6
  *
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import { Tooltip as Tooltip$1 } from "@base-ui/react/tooltip";
11
- import { Children, cloneElement, createContext, forwardRef, isValidElement, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useTransition } from "react";
11
+ import { Children, cloneElement, createContext, isValidElement, useCallback, useContext, useEffect, useId, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState, useTransition } from "react";
12
12
  import { createGlobalStyle, css, keyframes, styled } from "styled-components";
13
13
  import { isArray, isBoolean, isDate, isEmptyString, isFunction, isNil, isNonEmptyArray, isNonEmptyString, isNotNil, isNotUndefined, isNumber, isRecord, isString, isUndefined } from "@wistia/type-guards";
14
14
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -37,7 +37,6 @@ import { matchSorter } from "match-sorter";
37
37
  import { createPortal } from "react-dom";
38
38
  import { ContextMenu as ContextMenu$1 } from "@base-ui/react/context-menu";
39
39
  import { Menu as Menu$1 } from "@base-ui/react/menu";
40
- import { DayPicker } from "@daypicker/react";
41
40
  import { ValidationError } from "yup";
42
41
  import { PreviewCard as PreviewCard$1 } from "@base-ui/react/preview-card";
43
42
  import ReactMarkdown from "react-markdown";
@@ -8717,46 +8716,23 @@ const inputCss = css`
8717
8716
  --wui-input-line-height: 16px;
8718
8717
  --wui-input-font-weight: var(--wui-typography-weight-body);
8719
8718
  `;
8720
- /**
8721
- * Applies the shared visual styling for input-like form controls (background,
8722
- * outline, focus/error/disabled states, typography). Consumes the tokens from
8723
- * `inputCss`, which must be applied to the element itself or an ancestor.
8724
- */
8725
- const inputShellCss = css`
8726
- padding: var(--wui-input-vertical-padding) var(--wui-input-horizontal-padding);
8727
- background-color: var(--wui-input-color-bg);
8728
- border: none;
8729
- outline: 1px solid var(--wui-input-color-border);
8730
- outline-offset: -1px;
8731
- border-radius: var(--wui-input-border-radius);
8732
- font-size: var(--wui-input-font-size);
8733
- line-height: var(--wui-input-line-height);
8734
- font-weight: var(--wui-input-font-weight);
8735
- color: var(--wui-input-color-text);
8736
-
8737
- &:focus,
8738
- &[aria-expanded='true'] {
8739
- outline-width: 2px;
8740
- outline-color: var(--wui-input-color-border-focus);
8741
- outline-offset: -2px;
8742
- }
8743
-
8744
- &[aria-invalid='true'] {
8745
- outline-color: var(--wui-input-color-border-error);
8746
- }
8747
-
8748
- &:disabled {
8749
- outline-color: var(--wui-color-border-disabled);
8750
- background-color: var(--wui-color-bg-surface-disabled);
8751
- }
8752
- `;
8753
8719
  //#endregion
8754
8720
  //#region src/components/Input/Input.tsx
8755
8721
  const inputStyles = css`
8756
8722
  ${inputCss}
8757
8723
  input,
8758
8724
  textarea {
8759
- ${inputShellCss}
8725
+ padding: var(--wui-input-vertical-padding) var(--wui-input-horizontal-padding);
8726
+ background-color: var(--wui-input-color-bg);
8727
+ border: none;
8728
+ outline: 1px solid var(--wui-input-color-border);
8729
+ outline-offset: -1px;
8730
+ border-radius: var(--wui-input-border-radius);
8731
+ font-size: var(--wui-input-font-size);
8732
+ line-height: var(--wui-input-line-height);
8733
+ font-weight: var(--wui-input-font-weight);
8734
+ color: var(--wui-input-color-text);
8735
+
8760
8736
  &:read-only {
8761
8737
  outline-style: dashed;
8762
8738
  }
@@ -8765,6 +8741,21 @@ const inputStyles = css`
8765
8741
  color: var(--wui-input-placeholder);
8766
8742
  opacity: 1; /* Firefox */
8767
8743
  }
8744
+
8745
+ &:focus {
8746
+ outline-width: 2px;
8747
+ outline-color: var(--wui-input-color-border-focus);
8748
+ outline-offset: -2px;
8749
+ }
8750
+
8751
+ &[aria-invalid='true'] {
8752
+ outline-color: var(--wui-input-color-border-error);
8753
+ }
8754
+
8755
+ &:disabled {
8756
+ outline-color: var(--wui-color-border-disabled);
8757
+ background-color: var(--wui-color-bg-surface-disabled);
8758
+ }
8768
8759
  }
8769
8760
  `;
8770
8761
  const calculateTextareaHeight = (rows = 1) => `calc((var(--wui-input-line-height) * ${rows}) + (var(--wui-input-vertical-padding) * 2));`;
@@ -10397,1441 +10388,468 @@ const DataListItemValue = (props) => {
10397
10388
  };
10398
10389
  DataListItemValue.displayName = "DataListItemValue_UI";
10399
10390
  //#endregion
10400
- //#region src/private/helpers/getControls/getControlProps.tsx
10401
- const getControlProps = (isOpen, onOpenChange) => {
10402
- return isNotNil(onOpenChange) && isNotNil(isOpen) ? {
10403
- open: isOpen,
10404
- onOpenChange
10405
- } : {};
10391
+ //#region src/components/Divider/Divider.tsx
10392
+ const horizontalBorderCss = css`
10393
+ border-top-color: var(--wui-color-border);
10394
+ border-top-style: solid;
10395
+ border-top-width: 1px;
10396
+ clear: both; /* for horizontal dividers, ensure it clears any floats */
10397
+ height: 0;
10398
+ margin-left: var(--wui-divider-inset);
10399
+ margin-right: var(--wui-divider-inset);
10400
+ `;
10401
+ const verticalBorderCss = css`
10402
+ background-color: var(--wui-color-border);
10403
+ max-width: 1px;
10404
+ min-height: 100%;
10405
+ width: 1px;
10406
+ margin-top: var(--wui-divider-inset);
10407
+ margin-bottom: var(--wui-divider-inset);
10408
+ `;
10409
+ const DividerComponent = styled.div`
10410
+ ${({ $orientation }) => {
10411
+ switch ($orientation) {
10412
+ case "vertical": return verticalBorderCss;
10413
+ default: return horizontalBorderCss;
10414
+ }
10415
+ }}
10416
+ --wui-divider-inset: ${({ $inset }) => `var(--wui-${$inset})`};
10417
+
10418
+ align-self: stretch;
10419
+ `;
10420
+ /**
10421
+ * A line used to visually separate content; note that dividers have no external margin/spacing on their own.
10422
+ */
10423
+ const Divider = ({ orientation = "horizontal", inset = "space-00", ...props }) => {
10424
+ const responsiveOrientation = useResponsiveProp(orientation);
10425
+ return /* @__PURE__ */ jsx(DividerComponent, {
10426
+ $inset: useResponsiveProp(inset),
10427
+ $orientation: responsiveOrientation,
10428
+ "aria-orientation": responsiveOrientation,
10429
+ role: "separator",
10430
+ ...props
10431
+ });
10406
10432
  };
10433
+ Divider.displayName = "Divider_UI";
10407
10434
  //#endregion
10408
- //#region src/components/Popover/PopoverArrow.tsx
10409
- const StyledArrowSvg = styled.svg`
10410
- pointer-events: none;
10411
-
10412
- /* override inline positioning and rotate the arrow so the circle sits near the trigger and the stalk extends down toward the popup. */
10413
- &[data-side='top'] {
10414
- top: 100% !important;
10435
+ //#region src/components/EditableHeading/EditableHeading.tsx
10436
+ const StyledInput$1 = styled(Input)`
10437
+ &:not([rows]) {
10438
+ min-height: unset;
10415
10439
  }
10416
10440
 
10417
- &[data-side='bottom'] {
10418
- top: 0 !important;
10419
- transform: rotate(180deg);
10420
- transform-origin: center 0;
10441
+ &:focus {
10442
+ height: ${({ $height }) => `${$height}px !important`};
10421
10443
  }
10422
10444
 
10423
- &[data-side='left'] {
10424
- right: 0;
10425
- left: auto;
10426
- transform: translateX(52px) rotate(-90deg);
10427
- transform-origin: center center;
10428
- }
10445
+ && {
10446
+ ${({ $variant }) => variantStyleMap$1[$variant]}
10447
+ /* The input font styles (edit mode) needs the same font styles as Heading */
10448
+ --wui-input-font-size: var(--font-size);
10449
+ --wui-input-font-weight: var(--font-weight);
10450
+ --wui-input-line-height: var(--line-height);
10429
10451
 
10430
- &[data-side='right'] {
10431
- left: 0;
10432
- right: auto;
10433
- transform: translateX(-52px) rotate(90deg);
10434
- transform-origin: center center;
10435
- }
10436
- `;
10437
- const StyledPath = styled.path`
10438
- fill: var(--wui-color-border-secondary);
10439
- `;
10440
- const circleAnimation = keyframes`
10441
- 0% {
10442
- opacity: var(--wui-popover-arrow-circle-starting-opacity);
10443
- }
10444
- 100% {
10445
- opacity: 0;
10452
+ font-family: var(--font-family);
10453
+ width: 100%;
10454
+ padding: var(--wui-space-02);
10455
+ border: none;
10456
+ height: ${({ $height }) => `${$height}px`};
10457
+ min-height: ${({ $height }) => `${$height}px`};
10458
+ resize: none;
10446
10459
  }
10447
10460
  `;
10448
- const StyledCircle = styled.circle`
10449
- stroke: var(--wui-color-border-active);
10450
- animation-duration: 2s;
10451
- animation-iteration-count: infinite;
10452
- animation-direction: alternate;
10453
- animation-timing-function: ease-in-out;
10454
-
10455
- &[data-wui-popover-arrow-inner-circle] {
10456
- --wui-popover-arrow-circle-starting-opacity: 0.36;
10457
-
10458
- opacity: var(--wui-popover-arrow-circle-starting-opacity);
10459
- }
10460
-
10461
- &[data-wui-popover-arrow-outer-circle] {
10462
- --wui-popover-arrow-circle-starting-opacity: 0.2;
10463
-
10464
- animation-direction: alternate-reverse;
10465
- opacity: 0.11;
10461
+ const editableStyles = css`
10462
+ &:has(+ :focus-within) {
10463
+ background: var(--wui-color-bg-surface-hover);
10466
10464
  }
10467
10465
 
10468
- @media (prefers-reduced-motion: no-preference) {
10469
- ${({ $isAnimated }) => $isAnimated && css`
10470
- animation-name: ${circleAnimation};
10471
- `}
10466
+ &:hover {
10467
+ background: var(--wui-color-bg-surface-hover);
10468
+ cursor: pointer;
10472
10469
  }
10473
10470
  `;
10474
- const PopoverArrow = ({ isAnimated }) => {
10475
- return /* @__PURE__ */ jsx(Popover$1.Arrow, { render: /* @__PURE__ */ jsxs(StyledArrowSvg, {
10476
- fill: "none",
10477
- height: "56",
10478
- viewBox: "0 0 48 56",
10479
- width: "48",
10480
- xmlns: "http://www.w3.org/2000/svg",
10481
- children: [
10482
- /* @__PURE__ */ jsx(StyledPath, { d: "M24 26.6667C21.0545 26.6667 18.6667 29.0545 18.6667 32C18.6667 34.9455 21.0545 37.3333 24 37.3333C26.9455 37.3333 29.3333 34.9455 29.3333 32C29.3333 29.0545 26.9455 26.6667 24 26.6667ZM23 0V32H25V0H23Z" }),
10483
- /* @__PURE__ */ jsx(StyledCircle, {
10484
- $isAnimated: isAnimated,
10485
- cx: "24",
10486
- cy: "32",
10487
- "data-wui-popover-arrow-inner-circle": true,
10488
- r: "10",
10489
- strokeWidth: "4"
10490
- }),
10491
- /* @__PURE__ */ jsx(StyledCircle, {
10492
- $isAnimated: isAnimated,
10493
- cx: "24",
10494
- cy: "32",
10495
- "data-wui-popover-arrow-outer-circle": true,
10496
- r: "20",
10497
- strokeWidth: "8"
10498
- })
10499
- ]
10500
- }) });
10501
- };
10502
- PopoverArrow.displayName = "PopoverArrow_UI";
10503
- //#endregion
10504
- //#region src/components/Popover/Popover.tsx
10505
- const StyledPopup$3 = styled(Popover$1.Popup)`
10506
- ${({ $colorScheme }) => getColorScheme($colorScheme)};
10507
- ${({ $unstyled }) => !$unstyled && css`
10508
- border-radius: var(--wui-border-radius-02);
10509
- padding: var(--wui-space-04);
10510
- max-width: var(--wui-popover-max-width, 320px);
10511
- max-height: var(--wui-popover-max-height, auto);
10512
- background-color: var(--wui-color-bg-surface);
10513
- box-shadow: var(--wui-elevation-01);
10514
- border: 1px solid var(--wui-color-border);
10515
- overflow: auto;
10516
- `}
10517
-
10518
- ${({ $withArrow }) => $withArrow && css`
10519
- overflow: visible;
10520
- `}
10521
-
10522
- [data-wui-popover-close] {
10523
- position: absolute;
10524
- top: var(--wui-space-02);
10525
- right: var(--wui-space-02);
10526
- }
10471
+ const StyledHeading = styled(Heading)`
10472
+ width: 100%;
10473
+ border-radius: var(--wui-border-radius-02);
10474
+ padding: var(--wui-space-02);
10475
+ overflow-wrap: anywhere;
10476
+ transition: all var(--wui-motion-duration-01) var(--wui-motion-ease);
10477
+ ${({ $editingDisabled }) => !$editingDisabled && editableStyles}
10527
10478
  `;
10528
10479
  /**
10529
- * Displays rich content in a portal, triggered by a button.
10530
- *
10531
- * For more control custom anchor, no injected close button, etc. compose
10532
- * the primitives directly: `PopoverRoot`, `PopoverTrigger`, `PopoverAnchor`,
10533
- * `PopoverPortal`, `PopoverContent`, `PopoverClose`.
10480
+ * A special heading component that allows for inline editing. When clicked or activated via keyboard,
10481
+ * it transforms into an editable text field. In its default state, it renders as a heading with
10482
+ * an accessible button that enables editing mode. The component handles empty values, keyboard
10483
+ * navigation (Enter to save, Escape to cancel), and provides callbacks for value and state changes.
10534
10484
  */
10535
- const Popover = ({ children, trigger, isOpen, hideCloseButton = false, maxWidth, maxHeight, onOpenChange, unstyled = false, withArrow = false, isAnimated = false, colorScheme = "inherit", style, side, align, nativeButton = true, ...props }) => {
10536
- const sideOffset = withArrow ? 32 : 8;
10537
- const mergedStyle = {
10538
- "--wui-popover-max-width": maxWidth,
10539
- "--wui-popover-max-height": maxHeight,
10540
- ...style
10485
+ const EditableHeading = ({ children, onValueChange, onEditingChange, tooltipText = "Click to edit title", ariaLabel = "Edit title", variant = "heading1", __forceEditing = false, editingDisabled = false }) => {
10486
+ const [isEditing, setIsEditing] = useState(false);
10487
+ const [value, setValue] = useState(children);
10488
+ const [previousValue, setPreviousValue] = useState(children);
10489
+ const [headingHeight, setHeadingHeight] = useState("60");
10490
+ const headingRef = useRef(null);
10491
+ const handleSetEditing = (editing) => {
10492
+ if (editingDisabled) return;
10493
+ if (editing && headingRef.current) setHeadingHeight(`${headingRef.current.offsetHeight}`);
10494
+ if (editing) setPreviousValue(value);
10495
+ setIsEditing(editing);
10496
+ onEditingChange?.(editing);
10541
10497
  };
10542
- return /* @__PURE__ */ jsxs(Popover$1.Root, {
10543
- ...getControlProps(isOpen, onOpenChange),
10544
- children: [/* @__PURE__ */ jsx(Popover$1.Trigger, {
10545
- nativeButton,
10546
- render: trigger
10547
- }), /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, {
10548
- align,
10549
- collisionAvoidance: {
10550
- side: "flip",
10551
- align: "shift",
10552
- fallbackAxisSide: "none"
10553
- },
10554
- side,
10555
- sideOffset,
10556
- style: { zIndex: "var(--wui-zindex-popover)" },
10557
- children: /* @__PURE__ */ jsxs(StyledPopup$3, {
10558
- $colorScheme: colorScheme,
10559
- ...props,
10560
- $unstyled: unstyled,
10561
- $withArrow: withArrow,
10562
- role: "dialog",
10563
- style: mergedStyle,
10564
- children: [
10565
- !hideCloseButton && /* @__PURE__ */ jsx(Popover$1.Close, { render: /* @__PURE__ */ jsx(IconButton, {
10566
- "data-wui-popover-close": true,
10567
- label: "Close",
10568
- variant: "ghost",
10569
- children: /* @__PURE__ */ jsx(Icon, { type: "close" })
10570
- }) }),
10571
- withArrow ? /* @__PURE__ */ jsx(PopoverArrow, { isAnimated }) : null,
10572
- /* @__PURE__ */ jsx("div", { children })
10573
- ]
10574
- })
10575
- }) })]
10576
- });
10577
- };
10578
- Popover.displayName = "Popover_UI";
10579
- //#endregion
10580
- //#region src/components/Popover/PopoverAnchorContext.tsx
10581
- const PopoverAnchorContext = createContext(null);
10582
- const PopoverAnchorProvider = PopoverAnchorContext;
10583
- const usePopoverAnchor = () => useContext(PopoverAnchorContext);
10584
- const useCreatePopoverAnchorRef = () => useRef(null);
10585
- //#endregion
10586
- //#region src/components/Popover/PopoverAnchor.tsx
10587
- /**
10588
- * Positions the popover relative to an element other than the trigger. Useful
10589
- * when the element the user interacts with (e.g. a text input) is separate
10590
- * from what the popover attaches to.
10591
- *
10592
- * Wrap the anchor element and nest `PopoverTrigger` inside it.
10593
- */
10594
- const PopoverAnchor = ({ children }) => {
10595
- const sharedRef = usePopoverAnchor();
10596
- return /* @__PURE__ */ jsx("div", {
10597
- ref: useCallback((node) => {
10598
- if (sharedRef) sharedRef.current = node;
10599
- }, [sharedRef]),
10600
- children
10601
- });
10602
- };
10603
- PopoverAnchor.displayName = "PopoverAnchor_UI";
10604
- //#endregion
10605
- //#region src/components/Popover/PopoverClose.tsx
10606
- /**
10607
- * An element that closes the popover when activated. Defaults to a `<button>`;
10608
- * pass `render` to merge the close behavior onto a single child element. For
10609
- * a pre-styled icon close button, use `PopoverCloseButton`.
10610
- */
10611
- const PopoverClose = ({ ref, ...props }) => /* @__PURE__ */ jsx(Popover$1.Close, {
10612
- ref,
10613
- ...props
10614
- });
10615
- //#endregion
10616
- //#region src/components/Popover/PopoverCloseButton.tsx
10617
- /**
10618
- * A pre-styled close button intended to be placed inside a `PopoverContent`.
10619
- * For a custom close control, use `PopoverClose` directly.
10620
- */
10621
- const PopoverCloseButton = ({ label = "Close" }) => /* @__PURE__ */ jsx(PopoverClose, { render: /* @__PURE__ */ jsx(IconButton, {
10622
- "data-wui-popover-close": true,
10623
- label,
10624
- variant: "ghost",
10625
- children: /* @__PURE__ */ jsx(Icon, { type: "close" })
10626
- }) });
10627
- PopoverCloseButton.displayName = "PopoverCloseButton_UI";
10628
- //#endregion
10629
- //#region src/components/Popover/PopoverContent.tsx
10630
- const StyledPopup$2 = styled(Popover$1.Popup)`
10631
- ${({ $colorScheme }) => getColorScheme($colorScheme)};
10632
- ${({ $unstyled }) => !$unstyled && css`
10633
- border-radius: var(--wui-border-radius-02);
10634
- padding: var(--wui-space-04);
10635
- max-width: var(--wui-popover-max-width);
10636
- max-height: var(--wui-popover-max-height);
10637
- background-color: var(--wui-color-bg-surface);
10638
- box-shadow: var(--wui-elevation-01);
10639
- border: 1px solid var(--wui-color-border);
10640
- overflow: auto;
10641
- `}
10642
-
10643
- [data-wui-popover-close] {
10644
- position: absolute;
10645
- top: var(--wui-space-02);
10646
- right: var(--wui-space-02);
10647
- }
10648
- `;
10649
- const DEFAULT_SIDE_OFFSET = 8;
10650
- const DEFAULT_MAX_WIDTH = "320px";
10651
- const DEFAULT_MAX_HEIGHT = "auto";
10652
- /**
10653
- * The styled popover surface. Place inside a `PopoverRoot` (typically wrapped
10654
- * in a `PopoverPortal`). Wraps a `Positioner` and `Popup` internally.
10655
- */
10656
- const PopoverContent = ({ colorScheme = "inherit", unstyled = false, maxWidth = DEFAULT_MAX_WIDTH, maxHeight = DEFAULT_MAX_HEIGHT, sideOffset = DEFAULT_SIDE_OFFSET, side, align, style, role = "dialog", children, ref, ...props }) => {
10657
- const sharedAnchorRef = usePopoverAnchor();
10658
- const hasAnchor = sharedAnchorRef?.current != null;
10659
- const mergedStyle = {
10660
- "--wui-popover-max-width": maxWidth,
10661
- "--wui-popover-max-height": maxHeight,
10662
- ...style
10663
- };
10664
- return /* @__PURE__ */ jsx(Popover$1.Positioner, {
10665
- align,
10666
- collisionAvoidance: {
10667
- side: "flip",
10668
- align: "shift",
10669
- fallbackAxisSide: "none"
10670
- },
10671
- side,
10672
- sideOffset,
10673
- style: { zIndex: "var(--wui-zindex-popover)" },
10674
- ...hasAnchor ? { anchor: sharedAnchorRef } : {},
10675
- children: /* @__PURE__ */ jsx(StyledPopup$2, {
10676
- ref,
10677
- $colorScheme: colorScheme,
10678
- $unstyled: unstyled,
10679
- role,
10680
- style: mergedStyle,
10681
- ...props,
10682
- children
10683
- })
10684
- });
10685
- };
10686
- //#endregion
10687
- //#region src/components/Popover/PopoverPortal.tsx
10688
- /**
10689
- * Portals the popover content out of the DOM hierarchy of its trigger so it can
10690
- * escape clipping ancestors (`overflow: hidden`, transformed containers, etc.).
10691
- */
10692
- const PopoverPortal = ({ children }) => /* @__PURE__ */ jsx(Popover$1.Portal, { children });
10693
- PopoverPortal.displayName = "PopoverPortal_UI";
10694
- //#endregion
10695
- //#region src/components/Popover/PopoverRoot.tsx
10696
- /**
10697
- * The root of a composable popover. Wrap `PopoverTrigger` (or `PopoverAnchor`)
10698
- * and `PopoverContent` inside it.
10699
- *
10700
- * For the common "button opens a panel" case, prefer the bundled `Popover`.
10701
- */
10702
- const PopoverRoot = ({ isOpen, onOpenChange, children }) => {
10703
- const anchorRef = useCreatePopoverAnchorRef();
10704
- return /* @__PURE__ */ jsx(Popover$1.Root, {
10705
- ...getControlProps(isOpen, onOpenChange),
10706
- children: /* @__PURE__ */ jsx(PopoverAnchorProvider, {
10707
- value: anchorRef,
10708
- children
10709
- })
10710
- });
10711
- };
10712
- PopoverRoot.displayName = "PopoverRoot_UI";
10713
- //#endregion
10714
- //#region src/components/Popover/PopoverTrigger.tsx
10715
- /**
10716
- * The button that toggles the popover open and closed. By default it renders a
10717
- * `<button>` wrapping its children; pass `render` to merge the trigger
10718
- * behavior onto a single child element (e.g. a `Button` or `IconButton`).
10719
- */
10720
- const PopoverTrigger = ({ nativeButton = true, ref, ...props }) => /* @__PURE__ */ jsx(Popover$1.Trigger, {
10721
- ref,
10722
- nativeButton,
10723
- ...props
10724
- });
10725
- //#endregion
10726
- //#region src/components/DatePicker/DatePickerContext.tsx
10727
- const DatePickerContext = createContext(null);
10728
- const DatePickerProvider = DatePickerContext.Provider;
10729
- const useDatePickerContext = () => {
10730
- const context = useContext(DatePickerContext);
10731
- if (context === null) throw new Error("useDatePickerContext must be used within a DatePickerProvider");
10732
- return context;
10733
- };
10734
- //#endregion
10735
- //#region src/components/DatePicker/datePickerUtils.ts
10736
- /**
10737
- * Formats a Date into a human-readable string (e.g. "Jun 3, 2021").
10738
- * Returns an empty string for null/undefined.
10739
- */
10740
- const formatDate = (date) => {
10741
- if (date === null) return "";
10742
- return dateOnlyString(date);
10743
- };
10744
- /**
10745
- * Formats a Date as an ISO 8601 date-only string (`YYYY-MM-DD`) in local time.
10746
- * Intended for form submission — unlike the locale-formatted display string,
10747
- * this is stable across locales and trivial for backends to parse.
10748
- */
10749
- const toISODate = (date) => {
10750
- return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
10751
- };
10752
- const stripTime = (date) => new Date(date.getFullYear(), date.getMonth(), date.getDate());
10753
- /**
10754
- * Returns true if the date falls within the min/max range (inclusive).
10755
- */
10756
- const isDateInRange = (date, minDate, maxDate) => {
10757
- const dateOnly = stripTime(date);
10758
- if (minDate !== void 0 && dateOnly < stripTime(minDate)) return false;
10759
- if (maxDate !== void 0 && dateOnly > stripTime(maxDate)) return false;
10760
- return true;
10761
- };
10762
- /**
10763
- * Returns true if the date matches any of the disabled matchers.
10764
- * Supports a subset of react-day-picker Matcher types:
10765
- * - Date objects (exact match by day)
10766
- * - { dayOfWeek: number[] }
10767
- * - { before: Date } / { after: Date }
10768
- * - { from: Date, to: Date }
10769
- */
10770
- const isDateDisabled = (date, disabledDates) => {
10771
- if (disabledDates === void 0) return false;
10772
- const matchers = Array.isArray(disabledDates) ? disabledDates : [disabledDates];
10773
- const dateOnly = stripTime(date);
10774
- return matchers.some((matcher) => {
10775
- if (matcher instanceof Date) return dateOnly.getTime() === stripTime(matcher).getTime();
10776
- if (typeof matcher !== "object") return false;
10777
- if ("dayOfWeek" in matcher && Array.isArray(matcher.dayOfWeek)) return matcher.dayOfWeek.includes(date.getDay());
10778
- if ("before" in matcher && matcher.before instanceof Date) return dateOnly < stripTime(matcher.before);
10779
- if ("after" in matcher && matcher.after instanceof Date) return dateOnly > stripTime(matcher.after);
10780
- if ("from" in matcher && "to" in matcher) {
10781
- const { from } = matcher;
10782
- const { to } = matcher;
10783
- if (from instanceof Date && to instanceof Date) return dateOnly >= stripTime(from) && dateOnly <= stripTime(to);
10784
- }
10785
- return false;
10786
- });
10787
- };
10788
- /**
10789
- * Builds a combined disabled matcher array for react-day-picker from
10790
- * minDate, maxDate, and user-provided disabledDates.
10791
- */
10792
- const buildDisabledMatcher = (minDate, maxDate, disabledDates) => {
10793
- const matchers = [];
10794
- if (minDate !== void 0) matchers.push({ before: minDate });
10795
- if (maxDate !== void 0) matchers.push({ after: maxDate });
10796
- if (disabledDates !== void 0) if (Array.isArray(disabledDates)) matchers.push(...disabledDates);
10797
- else matchers.push(disabledDates);
10798
- return matchers;
10799
- };
10800
- //#endregion
10801
- //#region src/components/DatePicker/DatePickerInput.tsx
10802
- const StyledMobileTrigger = styled.button`
10803
- ${inputCss}
10804
- ${inputShellCss}
10805
-
10806
- display: inline-flex;
10807
- align-items: center;
10808
- justify-content: space-between;
10809
- gap: var(--wui-space-02);
10810
- width: 100%;
10811
- font-family: inherit;
10812
- text-align: left;
10813
- cursor: pointer;
10814
-
10815
- &:disabled {
10816
- cursor: not-allowed;
10817
- }
10818
-
10819
- &[data-empty='true'] {
10820
- color: var(--wui-input-placeholder);
10821
- }
10822
- `;
10823
- const DatePickerInput = ({ placeholder = "Select a date...", fullWidth, name, id, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy }) => {
10824
- const { selectedDate, isOpen, inputText, disabled, isMobile, popoverId, inputRef, popoverContentRef, handleInputChange, handleCommit, handleCancel, openPopover } = useDatePickerContext();
10825
- const submittedValue = selectedDate !== null ? toISODate(selectedDate) : "";
10826
- const handleClick = () => {
10827
- if (!isOpen) openPopover();
10828
- };
10829
- if (isMobile) {
10830
- const handleMobileKeyDown = (event) => {
10831
- if (event.key === "Escape" && isOpen) {
10832
- event.preventDefault();
10833
- handleCancel();
10834
- }
10835
- };
10836
- const isEmpty = inputText.length === 0;
10837
- return /* @__PURE__ */ jsxs(PopoverAnchor, { children: [/* @__PURE__ */ jsxs(StyledMobileTrigger, {
10838
- ref: inputRef,
10839
- "aria-controls": isOpen ? popoverId : void 0,
10840
- "aria-describedby": ariaDescribedBy,
10841
- "aria-expanded": isOpen,
10842
- "aria-haspopup": "dialog",
10843
- ...ariaInvalid !== void 0 ? { "aria-invalid": ariaInvalid } : {},
10844
- $fullWidth: fullWidth ?? false,
10845
- "data-empty": isEmpty,
10846
- disabled,
10847
- id,
10848
- type: "button",
10849
- onClick: handleClick,
10850
- onKeyDown: handleMobileKeyDown,
10851
- children: [/* @__PURE__ */ jsx("span", { children: isEmpty ? placeholder : inputText }), /* @__PURE__ */ jsx(Icon, {
10852
- type: "calendar",
10853
- size: "md"
10854
- })]
10855
- }), name !== void 0 && /* @__PURE__ */ jsx("input", {
10856
- name,
10857
- type: "hidden",
10858
- value: submittedValue
10859
- })] });
10860
- }
10861
- const handleBlur = (event) => {
10862
- const relatedTarget = event.relatedTarget;
10863
- if (relatedTarget !== null && popoverContentRef.current?.contains(relatedTarget)) return;
10864
- handleCommit();
10865
- };
10866
- const focusGrid = () => {
10867
- const popover = popoverContentRef.current;
10868
- if (popover === null) return;
10869
- (popover.querySelector(".rdp-day_button[tabindex=\"0\"]") ?? popover.querySelector(".rdp-day_button:not([disabled])") ?? popover).focus();
10498
+ const handleFinishEditing = () => {
10499
+ const trimmedValue = value.trim();
10500
+ if (trimmedValue === "") setValue(previousValue);
10501
+ else if (trimmedValue !== previousValue && onValueChange) onValueChange(trimmedValue);
10502
+ else setValue(trimmedValue);
10503
+ handleSetEditing(false);
10870
10504
  };
10871
10505
  const handleKeyDown = (event) => {
10872
- if (event.key === "Enter") {
10873
- event.preventDefault();
10874
- handleCommit();
10875
- return;
10876
- }
10877
- if (event.key === "Escape" && isOpen) {
10506
+ if (event.key === "Enter" && !event.shiftKey) {
10878
10507
  event.preventDefault();
10879
- handleCancel();
10880
- return;
10508
+ handleFinishEditing();
10881
10509
  }
10882
- if (event.key === "ArrowDown") {
10883
- event.preventDefault();
10884
- if (!isOpen) openPopover();
10885
- if (!event.altKey) requestAnimationFrame(() => {
10886
- focusGrid();
10887
- });
10510
+ if (event.key === "Escape") {
10511
+ setValue(previousValue);
10512
+ handleSetEditing(false);
10888
10513
  }
10889
10514
  };
10890
- return /* @__PURE__ */ jsxs(PopoverAnchor, { children: [/* @__PURE__ */ jsx(Input, {
10891
- ref: inputRef,
10892
- "aria-controls": popoverId,
10893
- "aria-describedby": ariaDescribedBy,
10894
- "aria-expanded": isOpen,
10895
- ...ariaInvalid !== void 0 ? { "aria-invalid": ariaInvalid } : {},
10896
- autoSelect: true,
10897
- disabled,
10898
- ...fullWidth !== void 0 ? { fullWidth } : {},
10899
- id,
10900
- placeholder,
10901
- rightIcon: /* @__PURE__ */ jsx(Icon, { type: "calendar" }),
10902
- role: "combobox",
10903
- value: inputText,
10904
- onBlur: handleBlur,
10905
- onChange: (event) => {
10906
- handleInputChange(event.target.value);
10515
+ const HeadingComponent = /* @__PURE__ */ jsx(StyledHeading, {
10516
+ ref: headingRef,
10517
+ $editingDisabled: editingDisabled,
10518
+ onClick: () => handleSetEditing(true),
10519
+ variant,
10520
+ children: value
10521
+ });
10522
+ if (editingDisabled) return HeadingComponent;
10523
+ if (isEditing || __forceEditing) return /* @__PURE__ */ jsx(StyledInput$1, {
10524
+ $height: headingHeight,
10525
+ $variant: variant,
10526
+ autoFocus: true,
10527
+ fullWidth: true,
10528
+ onBlur: handleFinishEditing,
10529
+ onChange: (event) => setValue(event.target.value),
10530
+ onFocus: (event) => {
10531
+ const { length } = event.currentTarget.value;
10532
+ event.currentTarget.setSelectionRange(length, length);
10907
10533
  },
10908
- onClick: handleClick,
10909
- onKeyDown: handleKeyDown
10910
- }), name !== void 0 && /* @__PURE__ */ jsx("input", {
10911
- name,
10912
- type: "hidden",
10913
- value: submittedValue
10914
- })] });
10534
+ onKeyDown: handleKeyDown,
10535
+ type: "multiline",
10536
+ value
10537
+ });
10538
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Tooltip, {
10539
+ content: tooltipText,
10540
+ children: HeadingComponent
10541
+ }), /* @__PURE__ */ jsx(ScreenReaderOnly, { children: /* @__PURE__ */ jsx("button", {
10542
+ "aria-label": ariaLabel,
10543
+ onClick: () => handleSetEditing(true),
10544
+ type: "button"
10545
+ }) })] });
10915
10546
  };
10916
- DatePickerInput.displayName = "DatePickerInput_UI";
10917
10547
  //#endregion
10918
- //#region src/private/components/Calendar/CalendarNavigation.tsx
10919
- const StyledNav = styled.div`
10920
- position: absolute;
10921
- inset-block-start: 0;
10922
- inset-inline-end: 0;
10923
- display: flex;
10924
- align-items: center;
10548
+ //#region src/components/EditableText/EditableTextRoot.tsx
10549
+ const LARGE_PADDING = "var(--wui-space-02)";
10550
+ const SMALL_PADDING = "var(--wui-space-01)";
10551
+ const getPaddingForVariant = (variant) => {
10552
+ const largePaddingVariants = [
10553
+ "hero",
10554
+ "heading1",
10555
+ "heading2",
10556
+ "heading3",
10557
+ "body1",
10558
+ "label1",
10559
+ "body1Mono"
10560
+ ];
10561
+ const smallPaddingVariants = [
10562
+ "heading4",
10563
+ "heading5",
10564
+ "heading6",
10565
+ "body2",
10566
+ "body3",
10567
+ "body4",
10568
+ "label2",
10569
+ "label3",
10570
+ "label4",
10571
+ "body2Mono",
10572
+ "body3Mono",
10573
+ "body4Mono"
10574
+ ];
10575
+ if (largePaddingVariants.includes(variant)) return LARGE_PADDING;
10576
+ if (smallPaddingVariants.includes(variant)) return SMALL_PADDING;
10577
+ return SMALL_PADDING;
10578
+ };
10579
+ const StyledEditableTextRoot = styled.div`
10580
+ display: contents;
10581
+
10582
+ --wui-editable-text-padding: ${({ $typographicVariant }) => getPaddingForVariant($typographicVariant)};
10583
+ --wui-editable-text-border-radius: var(--wui-border-radius-01);
10925
10584
  `;
10926
- const CalendarNavigation = ({ onPreviousClick, onNextClick, previousMonth, nextMonth }) => {
10927
- return /* @__PURE__ */ jsxs(StyledNav, { children: [/* @__PURE__ */ jsx(IconButton, {
10928
- disabled: !previousMonth,
10929
- label: "Go to previous month",
10930
- onClick: onPreviousClick,
10931
- size: "md",
10932
- variant: "ghost",
10933
- children: /* @__PURE__ */ jsx(Icon, { type: "caret-left" })
10934
- }), /* @__PURE__ */ jsx(IconButton, {
10935
- disabled: !nextMonth,
10936
- label: "Go to next month",
10937
- onClick: onNextClick,
10938
- size: "md",
10939
- variant: "ghost",
10940
- children: /* @__PURE__ */ jsx(Icon, { type: "caret-right" })
10941
- })] });
10585
+ const EditableTextContext = createContext(null);
10586
+ const EditableTextRoot = ({ children, defaultValue = "", value: controlledValue, onValueChange, onValueCommit, onValueRevert, onEditingChange, typographicVariant = "body2", submitMode = "both", readOnly = false, id, label, placeholder = "Click to edit this text", minLines = 1, maxLines, finalFocusEl, ...props }) => {
10587
+ const isControlled = controlledValue !== void 0;
10588
+ const [internalValue, setInternalValue] = useState(defaultValue);
10589
+ const [originalValue, setOriginalValue] = useState(defaultValue);
10590
+ const [isEditing, setIsEditing] = useState(false);
10591
+ const value = isControlled ? controlledValue : internalValue;
10592
+ const generatedId = useId();
10593
+ const computedId = isNonEmptyString(id) ? id : `wistia-ui-editable-text-${generatedId}`;
10594
+ const handleSetIsEditing = useCallback((editing) => {
10595
+ if (editing && !isEditing) setOriginalValue(value);
10596
+ setIsEditing(editing);
10597
+ onEditingChange?.(editing);
10598
+ }, [
10599
+ isEditing,
10600
+ value,
10601
+ onEditingChange
10602
+ ]);
10603
+ const setValue = useCallback((newValue) => {
10604
+ if (!isControlled) setInternalValue(newValue);
10605
+ onValueChange?.(newValue);
10606
+ }, [isControlled, onValueChange]);
10607
+ const context = useMemo(() => {
10608
+ return {
10609
+ isEditing,
10610
+ setIsEditing: handleSetIsEditing,
10611
+ value,
10612
+ setValue,
10613
+ originalValue,
10614
+ onValueCommit,
10615
+ onValueRevert,
10616
+ typographicVariant,
10617
+ submitMode,
10618
+ readOnly,
10619
+ id: computedId,
10620
+ label,
10621
+ placeholder,
10622
+ minLines,
10623
+ maxLines,
10624
+ finalFocusEl
10625
+ };
10626
+ }, [
10627
+ isEditing,
10628
+ handleSetIsEditing,
10629
+ value,
10630
+ setValue,
10631
+ originalValue,
10632
+ onValueCommit,
10633
+ onValueRevert,
10634
+ typographicVariant,
10635
+ submitMode,
10636
+ readOnly,
10637
+ computedId,
10638
+ label,
10639
+ placeholder,
10640
+ minLines,
10641
+ maxLines,
10642
+ finalFocusEl
10643
+ ]);
10644
+ const getState = () => {
10645
+ if (readOnly) return "read-only";
10646
+ if (isEditing) return "editing";
10647
+ return "idle";
10648
+ };
10649
+ return /* @__PURE__ */ jsx(StyledEditableTextRoot, {
10650
+ $typographicVariant: typographicVariant,
10651
+ "data-testid": "editable-text-root",
10652
+ "data-wui-editable-text-root": true,
10653
+ "data-wui-editable-text-state": getState(),
10654
+ ...props,
10655
+ children: /* @__PURE__ */ jsx(EditableTextContext, {
10656
+ value: context,
10657
+ children
10658
+ })
10659
+ });
10942
10660
  };
10943
- CalendarNavigation.displayName = "CalendarNavigation";
10944
10661
  //#endregion
10945
- //#region src/private/components/Calendar/calendarStyles.ts
10946
- const calendarCss = css`
10947
- /* stylelint-disable selector-class-pattern, no-descending-specificity --
10948
- @daypicker/react ships class names with underscores (e.g. rdp-day_button, rdp-range_start).
10949
- We can't rename library classes, so the kebab-case pattern doesn't apply here. The
10950
- descending-specificity rule is disabled because state rules (.rdp-today, .rdp-selected,
10951
- .rdp-range_*) intentionally layer on top of the base .rdp-day_button block. */
10952
-
10953
- /* Root */
10954
- .rdp-root {
10955
- --rdp-day-size: 36px;
10956
-
10957
- font-family: var(--wui-typography-family-default);
10958
- font-size: var(--wui-typography-body-3-size);
10959
- font-weight: var(--wui-typography-body-3-weight);
10960
- user-select: none;
10961
- position: relative;
10962
- }
10963
-
10964
- /* Months layout */
10965
- .rdp-months {
10966
- display: flex;
10967
- gap: var(--wui-space-05);
10968
- }
10969
-
10970
- .rdp-month {
10971
- display: flex;
10972
- flex-direction: column;
10973
- }
10974
-
10975
- /* Caption / navigation row */
10976
- .rdp-month_caption {
10977
- display: flex;
10978
- align-items: center;
10979
- justify-content: space-between;
10980
- padding-left: var(--wui-space-01);
10981
- }
10982
-
10983
- .rdp-caption_label {
10984
- font-family: var(--wui-typography-heading-4-family);
10985
- font-weight: var(--wui-typography-heading-4-weight);
10986
- font-size: var(--wui-typography-heading-4-size);
10987
- line-height: var(--wui-typography-heading-4-line-height);
10988
- min-height: 32px;
10989
- display: flex;
10990
- align-items: center;
10991
- white-space: nowrap;
10992
- }
10993
-
10994
- /* Grid */
10995
- .rdp-month_grid {
10996
- border-collapse: collapse;
10997
- border-spacing: 0;
10998
- }
10662
+ //#region src/components/EditableText/EditableTextDisplay.tsx
10663
+ const StyledEditableTextDisplay = styled.div`
10664
+ ${({ $typographicVariant }) => getTypographicStyles($typographicVariant)}
10665
+ padding: var(--wui-editable-text-padding);
10666
+ border-radius: var(--wui-editable-text-border-radius);
10667
+ margin: 0;
10668
+ transition: all var(--wui-motion-duration-02) var(--wui-motion-ease);
10669
+ ${({ $maxLines }) => {
10670
+ if (isNotNil($maxLines)) return css`
10671
+ ${ellipsisStyle};
10672
+ ${lineClampCss($maxLines)};
10673
+ `;
10674
+ }}
10675
+ ${({ $minLines }) => isNotNil($minLines) && css`
10676
+ min-height: calc(${$minLines}lh + calc(var(--wui-editable-text-padding) * 2));
10677
+ `}
10678
+ word-break: normal;
10679
+ overflow-wrap: anywhere;
10999
10680
 
11000
- /* Weekday headers */
11001
- .rdp-weekday {
10681
+ &[data-wui-editable-text-display='placeholder'] {
11002
10682
  color: var(--wui-color-text-secondary);
11003
- font-weight: var(--wui-typography-label-3-weight);
11004
- font-size: var(--wui-typography-label-3-size);
11005
- line-height: var(--wui-typography-label-3-line-height);
11006
- text-align: center;
11007
- width: var(--rdp-day-size);
11008
- height: var(--rdp-day-size);
11009
- padding: 0;
11010
- }
11011
-
11012
- /* Day cells */
11013
- .rdp-day {
11014
- width: var(--rdp-day-size);
11015
- height: var(--rdp-day-size);
11016
- text-align: center;
11017
- padding: 0;
11018
10683
  }
11019
10684
 
11020
- /* Day button — base */
11021
- .rdp-day_button {
11022
- --rdp-day-button-size: calc(var(--rdp-day-size) - 4px);
11023
-
11024
- display: inline-flex;
11025
- align-items: center;
11026
- justify-content: center;
11027
- width: var(--rdp-day-button-size);
11028
- height: var(--rdp-day-button-size);
11029
- border: none;
11030
- border-radius: var(--wui-border-radius-rounded);
11031
- background: none;
10685
+ &:has(button) {
10686
+ user-select: none;
11032
10687
  cursor: pointer;
11033
- font-family: var(--wui-typography-label-3-family);
11034
- font-size: var(--wui-typography-label-3-size);
11035
- font-weight: var(--wui-typography-label-3-weight);
11036
- line-height: var(--wui-typography-label-3-line-height);
11037
- color: var(--wui-color-text);
11038
- padding: 0;
11039
- transition: background-color var(--wui-motion-duration-01) var(--wui-motion-ease);
11040
10688
 
11041
- &:hover:not([disabled]) {
10689
+ &:hover,
10690
+ &:focus-within {
11042
10691
  background-color: var(--wui-color-bg-surface-hover);
11043
10692
  }
11044
-
11045
- &:focus-visible {
11046
- outline: 2px solid var(--wui-color-focus-ring);
11047
- outline-offset: 1px;
11048
- }
11049
- }
11050
-
11051
- /* Day button — today */
11052
- .rdp-today .rdp-day_button {
11053
- color: var(--wui-color-text-button-blue);
11054
- font-weight: var(--wui-typography-weight-label-bold);
11055
- }
11056
-
11057
- /* Day button — outside month */
11058
- .rdp-outside .rdp-day_button {
11059
- color: var(--wui-color-text-secondary);
11060
- }
11061
-
11062
- /* Day button — selected */
11063
- .rdp-selected .rdp-day_button {
11064
- background-color: var(--wui-color-bg-fill);
11065
- color: var(--wui-color-text-on-fill);
11066
-
11067
- &:hover:not([disabled]) {
11068
- background-color: var(--wui-color-bg-fill-hover);
11069
- }
11070
- }
11071
-
11072
- /* Day button — disabled */
11073
- .rdp-disabled .rdp-day_button {
11074
- color: var(--wui-color-text-disabled);
11075
- font-weight: var(--wui-typography-weight-body);
11076
- cursor: not-allowed;
11077
- }
11078
-
11079
- /* Day button — disabled + selected (forced state: match disabled button) */
11080
- .rdp-selected.rdp-disabled .rdp-day_button {
11081
- background-color: var(--wui-color-bg-surface-disabled);
11082
- color: var(--wui-color-text-disabled);
11083
- }
11084
-
11085
- /* Range — cell backgrounds (the connecting band on the <td>) */
11086
- .rdp-range_start {
11087
- border-top-left-radius: var(--wui-border-radius-rounded);
11088
- border-bottom-left-radius: var(--wui-border-radius-rounded);
11089
- background-color: var(--wui-color-bg-surface-selected);
11090
- }
11091
-
11092
- .rdp-range_end {
11093
- border-top-right-radius: var(--wui-border-radius-rounded);
11094
- border-bottom-right-radius: var(--wui-border-radius-rounded);
11095
- background-color: var(--wui-color-bg-surface-selected);
11096
- }
11097
-
11098
- .rdp-range_middle {
11099
- background-color: var(--wui-color-bg-surface-selected);
11100
- }
11101
-
11102
- .rdp-range_start.rdp-range_end {
11103
- background-color: transparent;
11104
- }
11105
-
11106
- /* Range — button overrides */
11107
- .rdp-range_start .rdp-day_button,
11108
- .rdp-range_end .rdp-day_button {
11109
- background-color: var(--wui-color-bg-fill);
11110
- color: var(--wui-color-text-on-fill);
11111
- }
11112
-
11113
- .rdp-range_middle .rdp-day_button {
11114
- color: var(--wui-color-text-selected);
11115
- background: none;
11116
-
11117
- &:hover:not([disabled]) {
11118
- background-color: var(--wui-color-bg-surface-selected-hover);
11119
- }
11120
- }
11121
-
11122
- /* Hidden */
11123
- .rdp-hidden {
11124
- visibility: hidden;
11125
10693
  }
11126
10694
  `;
11127
- //#endregion
11128
- //#region src/private/components/Calendar/Calendar.tsx
11129
- const StyledCalendarWrapper = styled.div`
11130
- ${calendarCss}
11131
- `;
11132
- /**
11133
- * Removes keys with `undefined` values so that exactOptionalPropertyTypes
11134
- * does not complain when we spread into DayPicker.
11135
- */
11136
- const stripUndefined = (obj) => Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== void 0));
11137
- /**
11138
- * A calendar grid for selecting dates or date ranges. Built on
11139
- * @daypicker/react and styled with WUI design tokens.
11140
- */
11141
- const Calendar = (props) => {
11142
- const { mode, selected, onSelect, month, defaultMonth, onMonthChange, startMonth, endMonth, disabled, numberOfMonths = 1, showOutsideDays, weekStartsOn, today, ...rest } = props;
11143
- const rangeMin = mode === "range" ? props.min : void 0;
11144
- const rangeMax = mode === "range" ? props.max : void 0;
11145
- const dayPickerProps = stripUndefined({
11146
- ...mode === "range" ? {
11147
- mode: "range",
11148
- selected,
11149
- onSelect,
11150
- min: rangeMin,
11151
- max: rangeMax
11152
- } : {
11153
- mode: "single",
11154
- selected,
11155
- onSelect
11156
- },
11157
- components: { Nav: CalendarNavigation },
11158
- defaultMonth,
11159
- disabled,
11160
- endMonth,
11161
- month,
11162
- numberOfMonths,
11163
- onMonthChange,
11164
- showOutsideDays,
11165
- startMonth,
11166
- today,
11167
- weekStartsOn
11168
- });
11169
- return /* @__PURE__ */ jsx(StyledCalendarWrapper, {
11170
- ...rest,
11171
- children: /* @__PURE__ */ jsx(DayPicker, { ...dayPickerProps })
11172
- });
11173
- };
11174
- Calendar.displayName = "Calendar";
11175
- //#endregion
11176
- //#region src/components/DatePicker/DatePickerPopover.tsx
11177
- const DatePickerPopover = () => {
11178
- const { selectedDate, previewDate, displayedMonth, align, side, minDate, maxDate, disabledDates, popoverId, inputRef, popoverContentRef, handleDaySelect, handleCancel, setDisplayedMonth } = useDatePickerContext();
11179
- const disabledMatcher = buildDisabledMatcher(minDate, maxDate, disabledDates);
11180
- const handleOpenAutoFocus = (event) => {
11181
- event.preventDefault();
11182
- };
11183
- const handleCloseAutoFocus = (event) => {
11184
- event.preventDefault();
11185
- };
11186
- const handlePointerDownOutside = (event) => {
11187
- const { target } = event.detail.originalEvent;
11188
- if (target instanceof Node && inputRef.current?.contains(target)) event.preventDefault();
11189
- };
11190
- const handleEscapeKeyDown = (event) => {
11191
- event.preventDefault();
11192
- handleCancel();
10695
+ const EditableTextDisplayComponent = ({ ref, asTrigger, renderAs, ...props }) => {
10696
+ const context = useContext(EditableTextContext);
10697
+ if (!context) throw new Error("EditableTextDisplay must be used within an EditableTextRoot context");
10698
+ const { value, typographicVariant, setIsEditing, placeholder, maxLines, isEditing, minLines } = context;
10699
+ const triggerButtonRef = useRef(null);
10700
+ const handleTriggerClick = () => {
10701
+ setIsEditing(true);
11193
10702
  };
11194
- return /* @__PURE__ */ jsx(PopoverPortal, { children: /* @__PURE__ */ jsx(PopoverContent, {
11195
- ref: popoverContentRef,
11196
- align,
11197
- id: popoverId,
11198
- maxWidth: "none",
11199
- side,
11200
- onCloseAutoFocus: handleCloseAutoFocus,
11201
- onEscapeKeyDown: handleEscapeKeyDown,
11202
- onOpenAutoFocus: handleOpenAutoFocus,
11203
- onPointerDownOutside: handlePointerDownOutside,
11204
- children: /* @__PURE__ */ jsx(Calendar, {
11205
- ...disabledMatcher.length > 0 ? { disabled: disabledMatcher } : {},
11206
- ...maxDate !== void 0 ? { endMonth: maxDate } : {},
11207
- mode: "single",
11208
- month: displayedMonth,
11209
- selected: previewDate ?? selectedDate ?? void 0,
11210
- ...minDate !== void 0 ? { startMonth: minDate } : {},
11211
- onMonthChange: setDisplayedMonth,
11212
- onSelect: handleDaySelect
11213
- })
11214
- }) });
11215
- };
11216
- DatePickerPopover.displayName = "DatePickerPopover_UI";
11217
- //#endregion
11218
- //#region src/components/DatePicker/DatePicker.tsx
11219
- /**
11220
- * A date picker that combines a text input with natural language parsing
11221
- * and a calendar popover for visual date selection.
11222
- */
11223
- const DatePicker = forwardRef(({ value, onChange, defaultValue, minDate, maxDate, disabledDates, disabled = false, placeholder, fullWidth, align = "start", side = "bottom", name, id, "aria-invalid": ariaInvalid, "aria-describedby": ariaDescribedBy, ...rest }, ref) => {
11224
- const isControlled = value !== void 0;
11225
- const [internalDate, setInternalDate] = useState(defaultValue ?? null);
11226
- const selectedDate = isControlled ? value : internalDate;
11227
- const [isOpen, setIsOpen] = useState(false);
11228
- const [inputText, setInputText] = useState(() => formatDate(selectedDate));
11229
- const [previewDate, setPreviewDate] = useState(null);
11230
- const [displayedMonth, setDisplayedMonth] = useState(() => selectedDate ?? /* @__PURE__ */ new Date());
11231
- const inputRef = useRef(null);
11232
- const popoverContentRef = useRef(null);
11233
- const skipNextCommitRef = useRef(false);
11234
- const popoverId = useId();
11235
- const { isSmAndDown } = useMq();
11236
- const commitDate = useCallback((date) => {
11237
- if (isControlled) onChange(date);
11238
- else setInternalDate(date);
11239
- setInputText(formatDate(date));
11240
- setPreviewDate(null);
11241
- }, [isControlled, onChange]);
11242
- const handleInputChange = useCallback((text) => {
11243
- setInputText(text);
11244
- const parsed = parseDateString(text);
11245
- if (parsed !== null) {
11246
- const inRange = isDateInRange(parsed, minDate, maxDate);
11247
- const isDisabled = isDateDisabled(parsed, disabledDates);
11248
- if (inRange && !isDisabled) {
11249
- setPreviewDate(parsed);
11250
- setDisplayedMonth(parsed);
11251
- } else setPreviewDate(null);
11252
- } else setPreviewDate(null);
11253
- }, [
11254
- minDate,
11255
- maxDate,
11256
- disabledDates
11257
- ]);
11258
- const handleCommit = useCallback(() => {
11259
- if (previewDate !== null) {
11260
- commitDate(previewDate);
11261
- setIsOpen(false);
11262
- return;
11263
- }
11264
- setInputText(formatDate(selectedDate));
11265
- setPreviewDate(null);
11266
- }, [
11267
- previewDate,
11268
- selectedDate,
11269
- commitDate
11270
- ]);
11271
- const handleCancel = useCallback(() => {
11272
- skipNextCommitRef.current = true;
11273
- setInputText(formatDate(selectedDate));
11274
- setPreviewDate(null);
11275
- setIsOpen(false);
11276
- inputRef.current?.focus();
11277
- }, [selectedDate]);
11278
- const openPopover = useCallback(() => {
11279
- setDisplayedMonth(selectedDate ?? /* @__PURE__ */ new Date());
11280
- setIsOpen(true);
11281
- }, [selectedDate]);
11282
- const handleDaySelect = useCallback((date) => {
11283
- if (date !== void 0) {
11284
- skipNextCommitRef.current = true;
11285
- commitDate(date);
11286
- setDisplayedMonth(date);
11287
- setIsOpen(false);
11288
- inputRef.current?.focus();
11289
- requestAnimationFrame(() => {
11290
- const input = inputRef.current;
11291
- if (input instanceof HTMLInputElement) input.setSelectionRange(input.value.length, input.value.length);
11292
- });
11293
- }
11294
- }, [commitDate]);
11295
- const handleOpenChange = useCallback((open) => {
11296
- if (open) setDisplayedMonth(selectedDate ?? /* @__PURE__ */ new Date());
11297
- else if (skipNextCommitRef.current) skipNextCommitRef.current = false;
11298
- else handleCommit();
11299
- setIsOpen(open);
11300
- }, [handleCommit, selectedDate]);
11301
- useEffect(() => {
11302
- if (!isControlled) return;
11303
- setInputText(formatDate(value));
11304
- setPreviewDate(null);
11305
- if (value !== null) setDisplayedMonth(value);
11306
- }, [isControlled, value]);
11307
- const contextValue = useMemo(() => ({
11308
- selectedDate,
11309
- isOpen,
11310
- inputText,
11311
- previewDate,
11312
- displayedMonth,
11313
- disabled,
11314
- isMobile: isSmAndDown,
11315
- align,
11316
- side,
11317
- minDate,
11318
- maxDate,
11319
- disabledDates,
11320
- popoverId,
11321
- inputRef,
11322
- popoverContentRef,
11323
- setIsOpen,
11324
- setInputText,
11325
- handleInputChange,
11326
- handleCommit,
11327
- handleCancel,
11328
- handleDaySelect,
11329
- openPopover,
11330
- setDisplayedMonth
11331
- }), [
11332
- selectedDate,
11333
- isOpen,
11334
- inputText,
11335
- previewDate,
11336
- displayedMonth,
11337
- disabled,
11338
- isSmAndDown,
11339
- align,
11340
- side,
11341
- minDate,
11342
- maxDate,
11343
- disabledDates,
11344
- popoverId,
11345
- handleInputChange,
11346
- handleCommit,
11347
- handleCancel,
11348
- handleDaySelect,
11349
- openPopover
11350
- ]);
11351
- return /* @__PURE__ */ jsx("div", {
11352
- ref,
11353
- ...rest,
11354
- children: /* @__PURE__ */ jsx(PopoverRoot, {
11355
- isOpen,
11356
- onOpenChange: handleOpenChange,
11357
- children: /* @__PURE__ */ jsxs(DatePickerProvider, {
11358
- value: contextValue,
11359
- children: [/* @__PURE__ */ jsx(DatePickerInput, {
11360
- "aria-describedby": ariaDescribedBy,
11361
- "aria-invalid": ariaInvalid,
11362
- fullWidth,
11363
- id,
11364
- name,
11365
- placeholder
11366
- }), /* @__PURE__ */ jsx(DatePickerPopover, {})]
11367
- })
10703
+ const elementType = renderAs ?? getDefaultTypographicElement(typographicVariant);
10704
+ const displayText = value.length > 0 ? value : placeholder;
10705
+ const isPlaceholderVisible = value.length === 0 && Boolean(placeholder);
10706
+ if (isEditing) return null;
10707
+ if (asTrigger && !context.readOnly) return /* @__PURE__ */ jsx(ClickRegion, {
10708
+ targetRef: triggerButtonRef,
10709
+ children: /* @__PURE__ */ jsxs(StyledEditableTextDisplay, {
10710
+ ref,
10711
+ $maxLines: maxLines,
10712
+ $minLines: minLines,
10713
+ $typographicVariant: typographicVariant,
10714
+ as: elementType,
10715
+ "data-wui-editable-text-display": isPlaceholderVisible ? "placeholder" : "value",
10716
+ ...props,
10717
+ children: [displayText, /* @__PURE__ */ jsx("button", {
10718
+ ref: triggerButtonRef,
10719
+ onClick: handleTriggerClick,
10720
+ style: { ...visuallyHiddenStyle },
10721
+ type: "button",
10722
+ children: "Edit text"
10723
+ })]
11368
10724
  })
11369
10725
  });
11370
- });
11371
- DatePicker.displayName = "DatePicker_UI";
11372
- //#endregion
11373
- //#region src/components/Divider/Divider.tsx
11374
- const horizontalBorderCss = css`
11375
- border-top-color: var(--wui-color-border);
11376
- border-top-style: solid;
11377
- border-top-width: 1px;
11378
- clear: both; /* for horizontal dividers, ensure it clears any floats */
11379
- height: 0;
11380
- margin-left: var(--wui-divider-inset);
11381
- margin-right: var(--wui-divider-inset);
11382
- `;
11383
- const verticalBorderCss = css`
11384
- background-color: var(--wui-color-border);
11385
- max-width: 1px;
11386
- min-height: 100%;
11387
- width: 1px;
11388
- margin-top: var(--wui-divider-inset);
11389
- margin-bottom: var(--wui-divider-inset);
11390
- `;
11391
- const DividerComponent = styled.div`
11392
- ${({ $orientation }) => {
11393
- switch ($orientation) {
11394
- case "vertical": return verticalBorderCss;
11395
- default: return horizontalBorderCss;
11396
- }
11397
- }}
11398
- --wui-divider-inset: ${({ $inset }) => `var(--wui-${$inset})`};
11399
-
11400
- align-self: stretch;
11401
- `;
11402
- /**
11403
- * A line used to visually separate content; note that dividers have no external margin/spacing on their own.
11404
- */
11405
- const Divider = ({ orientation = "horizontal", inset = "space-00", ...props }) => {
11406
- const responsiveOrientation = useResponsiveProp(orientation);
11407
- return /* @__PURE__ */ jsx(DividerComponent, {
11408
- $inset: useResponsiveProp(inset),
11409
- $orientation: responsiveOrientation,
11410
- "aria-orientation": responsiveOrientation,
11411
- role: "separator",
11412
- ...props
10726
+ return /* @__PURE__ */ jsx(StyledEditableTextDisplay, {
10727
+ ref,
10728
+ $maxLines: maxLines,
10729
+ $minLines: minLines,
10730
+ $typographicVariant: typographicVariant,
10731
+ as: elementType,
10732
+ "data-placeholder-visible": isPlaceholderVisible || void 0,
10733
+ "data-wui-editable-text-display": true,
10734
+ ...props,
10735
+ children: displayText
11413
10736
  });
11414
10737
  };
11415
- Divider.displayName = "Divider_UI";
10738
+ const EditableTextDisplay = makePolymorphic(EditableTextDisplayComponent);
11416
10739
  //#endregion
11417
- //#region src/components/EditableHeading/EditableHeading.tsx
11418
- const StyledInput$1 = styled(Input)`
11419
- &:not([rows]) {
11420
- min-height: unset;
11421
- }
11422
-
11423
- &:focus {
11424
- height: ${({ $height }) => `${$height}px !important`};
11425
- }
11426
-
10740
+ //#region src/components/EditableText/EditableTextInput.tsx
10741
+ const StyledInput = styled(Input)`
11427
10742
  && {
11428
- ${({ $variant }) => variantStyleMap$1[$variant]}
11429
- /* The input font styles (edit mode) needs the same font styles as Heading */
11430
- --wui-input-font-size: var(--font-size);
11431
- --wui-input-font-weight: var(--font-weight);
11432
- --wui-input-line-height: var(--line-height);
11433
-
11434
- font-family: var(--font-family);
11435
- width: 100%;
11436
- padding: var(--wui-space-02);
11437
- border: none;
11438
- height: ${({ $height }) => `${$height}px`};
11439
- min-height: ${({ $height }) => `${$height}px`};
10743
+ ${({ $minLines }) => isNotNil($minLines) && `min-height: calc(${$minLines}lh + calc(var(--wui-editable-text-padding) * 2));`}
10744
+ ${({ $maxLines }) => isNotNil($maxLines) && `max-height: calc(${$maxLines}lh + calc(var(--wui-editable-text-padding) * 2));`}
10745
+ ${({ $typographicVariant }) => getTypographicStyles($typographicVariant)}
10746
+ background-color: var(--wui-color-bg-surface);
10747
+ border-radius: var(--wui-editable-text-border-radius);
10748
+ padding: var(--wui-editable-text-padding);
11440
10749
  resize: none;
11441
10750
  }
11442
10751
  `;
11443
- const editableStyles = css`
11444
- &:has(+ :focus-within) {
11445
- background: var(--wui-color-bg-surface-hover);
11446
- }
11447
-
11448
- &:hover {
11449
- background: var(--wui-color-bg-surface-hover);
11450
- cursor: pointer;
11451
- }
11452
- `;
11453
- const StyledHeading = styled(Heading)`
11454
- width: 100%;
11455
- border-radius: var(--wui-border-radius-02);
11456
- padding: var(--wui-space-02);
11457
- overflow-wrap: anywhere;
11458
- transition: all var(--wui-motion-duration-01) var(--wui-motion-ease);
11459
- ${({ $editingDisabled }) => !$editingDisabled && editableStyles}
11460
- `;
11461
- /**
11462
- * A special heading component that allows for inline editing. When clicked or activated via keyboard,
11463
- * it transforms into an editable text field. In its default state, it renders as a heading with
11464
- * an accessible button that enables editing mode. The component handles empty values, keyboard
11465
- * navigation (Enter to save, Escape to cancel), and provides callbacks for value and state changes.
11466
- */
11467
- const EditableHeading = ({ children, onValueChange, onEditingChange, tooltipText = "Click to edit title", ariaLabel = "Edit title", variant = "heading1", __forceEditing = false, editingDisabled = false }) => {
11468
- const [isEditing, setIsEditing] = useState(false);
11469
- const [value, setValue] = useState(children);
11470
- const [previousValue, setPreviousValue] = useState(children);
11471
- const [headingHeight, setHeadingHeight] = useState("60");
11472
- const headingRef = useRef(null);
11473
- const handleSetEditing = (editing) => {
11474
- if (editingDisabled) return;
11475
- if (editing && headingRef.current) setHeadingHeight(`${headingRef.current.offsetHeight}`);
11476
- if (editing) setPreviousValue(value);
11477
- setIsEditing(editing);
11478
- onEditingChange?.(editing);
11479
- };
11480
- const handleFinishEditing = () => {
11481
- const trimmedValue = value.trim();
11482
- if (trimmedValue === "") setValue(previousValue);
11483
- else if (trimmedValue !== previousValue && onValueChange) onValueChange(trimmedValue);
11484
- else setValue(trimmedValue);
11485
- handleSetEditing(false);
10752
+ const EditableTextInput = (props) => {
10753
+ const context = useContext(EditableTextContext);
10754
+ if (!context) throw new Error("EditableTextInput must be used within an EditableTextRoot context");
10755
+ const { isEditing, value, setValue, onValueCommit, setIsEditing, typographicVariant, submitMode, originalValue, onValueRevert, id, placeholder, minLines, maxLines, finalFocusEl } = context;
10756
+ const inputRef = useRef(null);
10757
+ useEffect(() => {
10758
+ if (inputRef.current) {
10759
+ if (isEditing) {
10760
+ const element = inputRef.current;
10761
+ const { style } = element;
10762
+ style.height = "0px";
10763
+ const { scrollHeight } = element;
10764
+ style.height = `${scrollHeight}px`;
10765
+ }
10766
+ if (isEditing) inputRef.current.focus();
10767
+ }
10768
+ }, [value, isEditing]);
10769
+ const handleChange = (event) => {
10770
+ setValue(event.target.value);
11486
10771
  };
11487
10772
  const handleKeyDown = (event) => {
11488
- if (event.key === "Enter" && !event.shiftKey) {
10773
+ if ((submitMode === "enter" || submitMode === "both") && event.key === "Enter" && !event.shiftKey) {
11489
10774
  event.preventDefault();
11490
- handleFinishEditing();
10775
+ onValueCommit?.(value);
10776
+ setIsEditing(false);
10777
+ setTimeout(() => {
10778
+ const element = finalFocusEl?.();
10779
+ if (element) element.focus();
10780
+ }, 0);
11491
10781
  }
11492
10782
  if (event.key === "Escape") {
11493
- setValue(previousValue);
11494
- handleSetEditing(false);
10783
+ event.preventDefault();
10784
+ setValue(originalValue);
10785
+ onValueRevert?.(originalValue);
10786
+ setIsEditing(false);
10787
+ setTimeout(() => {
10788
+ const element = finalFocusEl?.();
10789
+ if (element) element.focus();
10790
+ }, 0);
11495
10791
  }
11496
10792
  };
11497
- const HeadingComponent = /* @__PURE__ */ jsx(StyledHeading, {
11498
- ref: headingRef,
11499
- $editingDisabled: editingDisabled,
11500
- onClick: () => handleSetEditing(true),
11501
- variant,
11502
- children: value
11503
- });
11504
- if (editingDisabled) return HeadingComponent;
11505
- if (isEditing || __forceEditing) return /* @__PURE__ */ jsx(StyledInput$1, {
11506
- $height: headingHeight,
11507
- $variant: variant,
11508
- autoFocus: true,
11509
- fullWidth: true,
11510
- onBlur: handleFinishEditing,
11511
- onChange: (event) => setValue(event.target.value),
10793
+ const handleBlur = () => {
10794
+ if (submitMode === "blur" || submitMode === "both") {
10795
+ onValueCommit?.(value);
10796
+ setIsEditing(false);
10797
+ setTimeout(() => {
10798
+ const element = finalFocusEl?.();
10799
+ if (element) element.focus();
10800
+ }, 0);
10801
+ }
10802
+ };
10803
+ if (!isEditing) return null;
10804
+ return /* @__PURE__ */ jsx(StyledInput, {
10805
+ ref: inputRef,
10806
+ $maxLines: maxLines ?? "infinity",
10807
+ $minLines: minLines,
10808
+ $typographicVariant: typographicVariant,
10809
+ "data-wui-editable-text-input": true,
10810
+ id,
10811
+ onBlur: handleBlur,
10812
+ onChange: handleChange,
11512
10813
  onFocus: (event) => {
11513
10814
  const { length } = event.currentTarget.value;
11514
10815
  event.currentTarget.setSelectionRange(length, length);
11515
10816
  },
11516
10817
  onKeyDown: handleKeyDown,
10818
+ placeholder,
11517
10819
  type: "multiline",
11518
- value
11519
- });
11520
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Tooltip, {
11521
- content: tooltipText,
11522
- children: HeadingComponent
11523
- }), /* @__PURE__ */ jsx(ScreenReaderOnly, { children: /* @__PURE__ */ jsx("button", {
11524
- "aria-label": ariaLabel,
11525
- onClick: () => handleSetEditing(true),
11526
- type: "button"
11527
- }) })] });
11528
- };
11529
- //#endregion
11530
- //#region src/components/EditableText/EditableTextRoot.tsx
11531
- const LARGE_PADDING = "var(--wui-space-02)";
11532
- const SMALL_PADDING = "var(--wui-space-01)";
11533
- const getPaddingForVariant = (variant) => {
11534
- const largePaddingVariants = [
11535
- "hero",
11536
- "heading1",
11537
- "heading2",
11538
- "heading3",
11539
- "body1",
11540
- "label1",
11541
- "body1Mono"
11542
- ];
11543
- const smallPaddingVariants = [
11544
- "heading4",
11545
- "heading5",
11546
- "heading6",
11547
- "body2",
11548
- "body3",
11549
- "body4",
11550
- "label2",
11551
- "label3",
11552
- "label4",
11553
- "body2Mono",
11554
- "body3Mono",
11555
- "body4Mono"
11556
- ];
11557
- if (largePaddingVariants.includes(variant)) return LARGE_PADDING;
11558
- if (smallPaddingVariants.includes(variant)) return SMALL_PADDING;
11559
- return SMALL_PADDING;
11560
- };
11561
- const StyledEditableTextRoot = styled.div`
11562
- display: contents;
11563
-
11564
- --wui-editable-text-padding: ${({ $typographicVariant }) => getPaddingForVariant($typographicVariant)};
11565
- --wui-editable-text-border-radius: var(--wui-border-radius-01);
11566
- `;
11567
- const EditableTextContext = createContext(null);
11568
- const EditableTextRoot = ({ children, defaultValue = "", value: controlledValue, onValueChange, onValueCommit, onValueRevert, onEditingChange, typographicVariant = "body2", submitMode = "both", readOnly = false, id, label, placeholder = "Click to edit this text", minLines = 1, maxLines, finalFocusEl, ...props }) => {
11569
- const isControlled = controlledValue !== void 0;
11570
- const [internalValue, setInternalValue] = useState(defaultValue);
11571
- const [originalValue, setOriginalValue] = useState(defaultValue);
11572
- const [isEditing, setIsEditing] = useState(false);
11573
- const value = isControlled ? controlledValue : internalValue;
11574
- const generatedId = useId();
11575
- const computedId = isNonEmptyString(id) ? id : `wistia-ui-editable-text-${generatedId}`;
11576
- const handleSetIsEditing = useCallback((editing) => {
11577
- if (editing && !isEditing) setOriginalValue(value);
11578
- setIsEditing(editing);
11579
- onEditingChange?.(editing);
11580
- }, [
11581
- isEditing,
11582
- value,
11583
- onEditingChange
11584
- ]);
11585
- const setValue = useCallback((newValue) => {
11586
- if (!isControlled) setInternalValue(newValue);
11587
- onValueChange?.(newValue);
11588
- }, [isControlled, onValueChange]);
11589
- const context = useMemo(() => {
11590
- return {
11591
- isEditing,
11592
- setIsEditing: handleSetIsEditing,
11593
- value,
11594
- setValue,
11595
- originalValue,
11596
- onValueCommit,
11597
- onValueRevert,
11598
- typographicVariant,
11599
- submitMode,
11600
- readOnly,
11601
- id: computedId,
11602
- label,
11603
- placeholder,
11604
- minLines,
11605
- maxLines,
11606
- finalFocusEl
11607
- };
11608
- }, [
11609
- isEditing,
11610
- handleSetIsEditing,
11611
10820
  value,
11612
- setValue,
11613
- originalValue,
11614
- onValueCommit,
11615
- onValueRevert,
11616
- typographicVariant,
11617
- submitMode,
11618
- readOnly,
11619
- computedId,
11620
- label,
11621
- placeholder,
11622
- minLines,
11623
- maxLines,
11624
- finalFocusEl
11625
- ]);
11626
- const getState = () => {
11627
- if (readOnly) return "read-only";
11628
- if (isEditing) return "editing";
11629
- return "idle";
11630
- };
11631
- return /* @__PURE__ */ jsx(StyledEditableTextRoot, {
11632
- $typographicVariant: typographicVariant,
11633
- "data-testid": "editable-text-root",
11634
- "data-wui-editable-text-root": true,
11635
- "data-wui-editable-text-state": getState(),
11636
- ...props,
11637
- children: /* @__PURE__ */ jsx(EditableTextContext, {
11638
- value: context,
11639
- children
11640
- })
10821
+ ...props
11641
10822
  });
11642
10823
  };
11643
10824
  //#endregion
11644
- //#region src/components/EditableText/EditableTextDisplay.tsx
11645
- const StyledEditableTextDisplay = styled.div`
11646
- ${({ $typographicVariant }) => getTypographicStyles($typographicVariant)}
11647
- padding: var(--wui-editable-text-padding);
11648
- border-radius: var(--wui-editable-text-border-radius);
11649
- margin: 0;
11650
- transition: all var(--wui-motion-duration-02) var(--wui-motion-ease);
11651
- ${({ $maxLines }) => {
11652
- if (isNotNil($maxLines)) return css`
11653
- ${ellipsisStyle};
11654
- ${lineClampCss($maxLines)};
11655
- `;
11656
- }}
11657
- ${({ $minLines }) => isNotNil($minLines) && css`
11658
- min-height: calc(${$minLines}lh + calc(var(--wui-editable-text-padding) * 2));
11659
- `}
11660
- word-break: normal;
11661
- overflow-wrap: anywhere;
11662
-
11663
- &[data-wui-editable-text-display='placeholder'] {
11664
- color: var(--wui-color-text-secondary);
11665
- }
11666
-
11667
- &:has(button) {
11668
- user-select: none;
11669
- cursor: pointer;
11670
-
11671
- &:hover,
11672
- &:focus-within {
11673
- background-color: var(--wui-color-bg-surface-hover);
11674
- }
11675
- }
11676
- `;
11677
- const EditableTextDisplayComponent = ({ ref, asTrigger, renderAs, ...props }) => {
10825
+ //#region src/components/EditableText/EditableTextLabel.tsx
10826
+ const EditableTextLabel = ({ ...props }) => {
11678
10827
  const context = useContext(EditableTextContext);
11679
- if (!context) throw new Error("EditableTextDisplay must be used within an EditableTextRoot context");
11680
- const { value, typographicVariant, setIsEditing, placeholder, maxLines, isEditing, minLines } = context;
11681
- const triggerButtonRef = useRef(null);
11682
- const handleTriggerClick = () => {
11683
- setIsEditing(true);
11684
- };
11685
- const elementType = renderAs ?? getDefaultTypographicElement(typographicVariant);
11686
- const displayText = value.length > 0 ? value : placeholder;
11687
- const isPlaceholderVisible = value.length === 0 && Boolean(placeholder);
11688
- if (isEditing) return null;
11689
- if (asTrigger && !context.readOnly) return /* @__PURE__ */ jsx(ClickRegion, {
11690
- targetRef: triggerButtonRef,
11691
- children: /* @__PURE__ */ jsxs(StyledEditableTextDisplay, {
11692
- ref,
11693
- $maxLines: maxLines,
11694
- $minLines: minLines,
11695
- $typographicVariant: typographicVariant,
11696
- as: elementType,
11697
- "data-wui-editable-text-display": isPlaceholderVisible ? "placeholder" : "value",
11698
- ...props,
11699
- children: [displayText, /* @__PURE__ */ jsx("button", {
11700
- ref: triggerButtonRef,
11701
- onClick: handleTriggerClick,
11702
- style: { ...visuallyHiddenStyle },
11703
- type: "button",
11704
- children: "Edit text"
11705
- })]
11706
- })
11707
- });
11708
- return /* @__PURE__ */ jsx(StyledEditableTextDisplay, {
11709
- ref,
11710
- $maxLines: maxLines,
11711
- $minLines: minLines,
11712
- $typographicVariant: typographicVariant,
11713
- as: elementType,
11714
- "data-placeholder-visible": isPlaceholderVisible || void 0,
11715
- "data-wui-editable-text-display": true,
10828
+ if (!context) throw new Error("EditableTextLabel must be used within an EditableTextRoot context");
10829
+ const { id, label, isEditing } = context;
10830
+ return /* @__PURE__ */ jsx(Label, {
10831
+ ...isEditing && { htmlFor: id },
11716
10832
  ...props,
11717
- children: displayText
10833
+ children: label
11718
10834
  });
11719
10835
  };
11720
- const EditableTextDisplay = makePolymorphic(EditableTextDisplayComponent);
11721
10836
  //#endregion
11722
- //#region src/components/EditableText/EditableTextInput.tsx
11723
- const StyledInput = styled(Input)`
11724
- && {
11725
- ${({ $minLines }) => isNotNil($minLines) && `min-height: calc(${$minLines}lh + calc(var(--wui-editable-text-padding) * 2));`}
11726
- ${({ $maxLines }) => isNotNil($maxLines) && `max-height: calc(${$maxLines}lh + calc(var(--wui-editable-text-padding) * 2));`}
11727
- ${({ $typographicVariant }) => getTypographicStyles($typographicVariant)}
11728
- background-color: var(--wui-color-bg-surface);
11729
- border-radius: var(--wui-editable-text-border-radius);
11730
- padding: var(--wui-editable-text-padding);
11731
- resize: none;
11732
- }
11733
- `;
11734
- const EditableTextInput = (props) => {
11735
- const context = useContext(EditableTextContext);
11736
- if (!context) throw new Error("EditableTextInput must be used within an EditableTextRoot context");
11737
- const { isEditing, value, setValue, onValueCommit, setIsEditing, typographicVariant, submitMode, originalValue, onValueRevert, id, placeholder, minLines, maxLines, finalFocusEl } = context;
11738
- const inputRef = useRef(null);
11739
- useEffect(() => {
11740
- if (inputRef.current) {
11741
- if (isEditing) {
11742
- const element = inputRef.current;
11743
- const { style } = element;
11744
- style.height = "0px";
11745
- const { scrollHeight } = element;
11746
- style.height = `${scrollHeight}px`;
11747
- }
11748
- if (isEditing) inputRef.current.focus();
11749
- }
11750
- }, [value, isEditing]);
11751
- const handleChange = (event) => {
11752
- setValue(event.target.value);
11753
- };
11754
- const handleKeyDown = (event) => {
11755
- if ((submitMode === "enter" || submitMode === "both") && event.key === "Enter" && !event.shiftKey) {
11756
- event.preventDefault();
11757
- onValueCommit?.(value);
11758
- setIsEditing(false);
11759
- setTimeout(() => {
11760
- const element = finalFocusEl?.();
11761
- if (element) element.focus();
11762
- }, 0);
11763
- }
11764
- if (event.key === "Escape") {
11765
- event.preventDefault();
11766
- setValue(originalValue);
11767
- onValueRevert?.(originalValue);
11768
- setIsEditing(false);
11769
- setTimeout(() => {
11770
- const element = finalFocusEl?.();
11771
- if (element) element.focus();
11772
- }, 0);
11773
- }
11774
- };
11775
- const handleBlur = () => {
11776
- if (submitMode === "blur" || submitMode === "both") {
11777
- onValueCommit?.(value);
11778
- setIsEditing(false);
11779
- setTimeout(() => {
11780
- const element = finalFocusEl?.();
11781
- if (element) element.focus();
11782
- }, 0);
11783
- }
11784
- };
11785
- if (!isEditing) return null;
11786
- return /* @__PURE__ */ jsx(StyledInput, {
11787
- ref: inputRef,
11788
- $maxLines: maxLines ?? "infinity",
11789
- $minLines: minLines,
11790
- $typographicVariant: typographicVariant,
11791
- "data-wui-editable-text-input": true,
11792
- id,
11793
- onBlur: handleBlur,
11794
- onChange: handleChange,
11795
- onFocus: (event) => {
11796
- const { length } = event.currentTarget.value;
11797
- event.currentTarget.setSelectionRange(length, length);
11798
- },
11799
- onKeyDown: handleKeyDown,
11800
- placeholder,
11801
- type: "multiline",
11802
- value,
11803
- ...props
11804
- });
11805
- };
11806
- //#endregion
11807
- //#region src/components/EditableText/EditableTextLabel.tsx
11808
- const EditableTextLabel = ({ ...props }) => {
11809
- const context = useContext(EditableTextContext);
11810
- if (!context) throw new Error("EditableTextLabel must be used within an EditableTextRoot context");
11811
- const { id, label, isEditing } = context;
11812
- return /* @__PURE__ */ jsx(Label, {
11813
- ...isEditing && { htmlFor: id },
11814
- ...props,
11815
- children: label
11816
- });
11817
- };
11818
- //#endregion
11819
- //#region src/components/EditableText/EditableText.tsx
11820
- /**
11821
- * Used for inline editing of text.
11822
- */
11823
- const EditableText = ({ hideLabel = true, ...props }) => /* @__PURE__ */ jsxs(EditableTextRoot, {
11824
- ...props,
11825
- children: [
11826
- /* @__PURE__ */ jsx(EditableTextLabel, { screenReaderOnly: hideLabel }),
11827
- /* @__PURE__ */ jsx(EditableTextInput, {}),
11828
- /* @__PURE__ */ jsx(EditableTextDisplay, { asTrigger: true })
11829
- ]
11830
- });
11831
- EditableText.displayName = "EditableText_UI";
11832
- //#endregion
11833
- //#region src/components/EditableText/EditableTextSubmitButton.tsx
11834
- const EditableTextSubmitButton = ({ children }) => {
10837
+ //#region src/components/EditableText/EditableText.tsx
10838
+ /**
10839
+ * Used for inline editing of text.
10840
+ */
10841
+ const EditableText = ({ hideLabel = true, ...props }) => /* @__PURE__ */ jsxs(EditableTextRoot, {
10842
+ ...props,
10843
+ children: [
10844
+ /* @__PURE__ */ jsx(EditableTextLabel, { screenReaderOnly: hideLabel }),
10845
+ /* @__PURE__ */ jsx(EditableTextInput, {}),
10846
+ /* @__PURE__ */ jsx(EditableTextDisplay, { asTrigger: true })
10847
+ ]
10848
+ });
10849
+ EditableText.displayName = "EditableText_UI";
10850
+ //#endregion
10851
+ //#region src/components/EditableText/EditableTextSubmitButton.tsx
10852
+ const EditableTextSubmitButton = ({ children }) => {
11835
10853
  const context = useContext(EditableTextContext);
11836
10854
  if (!context) throw new Error("EditableTextSubmitButton must be used within an EditableTextRoot context");
11837
10855
  const { setIsEditing, value, onValueCommit, finalFocusEl, isEditing } = context;
@@ -12452,7 +11470,7 @@ const previewCardClose = keyframes`
12452
11470
  transform: scale(0.96);
12453
11471
  }
12454
11472
  `;
12455
- const StyledPopup$1 = styled(PreviewCard$1.Popup)`
11473
+ const StyledPopup$3 = styled(PreviewCard$1.Popup)`
12456
11474
  --preview-card-animation-duration: var(--wui-motion-duration-03);
12457
11475
  --preview-card-animation-ease: var(--wui-motion-ease-out);
12458
11476
 
@@ -12502,7 +11520,7 @@ const PreviewCard = ({ children, trigger, maxWidth = "320px", maxHeight = "auto"
12502
11520
  side,
12503
11521
  sideOffset: 8,
12504
11522
  style: { zIndex: "var(--wui-zindex-popover)" },
12505
- children: /* @__PURE__ */ jsx(StyledPopup$1, {
11523
+ children: /* @__PURE__ */ jsx(StyledPopup$3, {
12506
11524
  $colorScheme: colorScheme,
12507
11525
  $paddingSize: paddingSize,
12508
11526
  ...props,
@@ -14089,448 +13107,774 @@ const FilterMenu = ({ value, onChange, searchValue, onSearchValueChange, childre
14089
13107
  });
14090
13108
  };
14091
13109
  //#endregion
14092
- //#region src/components/Meter/Meter.tsx
14093
- const MeterWrapper = styled.div`
14094
- --meter-height: 16px;
13110
+ //#region src/components/Meter/Meter.tsx
13111
+ const MeterWrapper = styled.div`
13112
+ --meter-height: 16px;
13113
+
13114
+ display: flex;
13115
+ flex-direction: column;
13116
+ gap: var(--wui-space-02);
13117
+ `;
13118
+ const MeterLabelContainer = styled.div`
13119
+ display: flex;
13120
+ justify-content: space-between;
13121
+ align-items: baseline;
13122
+ `;
13123
+ const MeterLabel = styled.div`
13124
+ font-family: var(--wui-typography-heading-5-family);
13125
+ line-height: var(--wui-typography-heading-5-line-height);
13126
+ font-size: var(--wui-typography-heading-5-size);
13127
+ font-weight: var(--wui-typography-heading-5-weight);
13128
+ `;
13129
+ const MeterLabelMeta = styled.div`
13130
+ font-family: var(--wui-typography-heading-5-family);
13131
+ line-height: var(--wui-typography-heading-5-line-height);
13132
+ font-size: var(--wui-typography-heading-5-size);
13133
+ font-weight: var(--wui-typography-heading-5-weight);
13134
+ `;
13135
+ const MeterBarContainer = styled.div`
13136
+ position: relative;
13137
+ overflow: hidden;
13138
+ background-color: var(--wui-color-bg-surface-secondary);
13139
+ border-radius: var(--wui-border-radius-rounded);
13140
+ width: 100%;
13141
+ height: var(--meter-height);
13142
+ transform: translateZ(0);
13143
+ `;
13144
+ const MeterSegmentBar = styled.div`
13145
+ position: absolute;
13146
+ top: 0;
13147
+ left: ${({ $offset }) => $offset}%;
13148
+ width: ${({ $width }) => $width}%;
13149
+ height: 100%;
13150
+ background-color: ${({ $color }) => `var(--wui-${$color})`};
13151
+
13152
+ &:first-child {
13153
+ border-top-left-radius: var(--wui-border-radius-rounded);
13154
+ border-bottom-left-radius: var(--wui-border-radius-rounded);
13155
+ }
13156
+
13157
+ &:last-child {
13158
+ border-top-right-radius: var(--wui-border-radius-rounded);
13159
+ border-bottom-right-radius: var(--wui-border-radius-rounded);
13160
+ }
13161
+ `;
13162
+ const MeterDescription = styled.div`
13163
+ line-height: var(--wui-typography-label-3-line-height);
13164
+ font-size: var(--wui-typography-label-3-size);
13165
+ font-weight: var(--wui-typography-label-3-weight);
13166
+ color: var(--wui-color-text-secondary);
13167
+ `;
13168
+ const MeterKey = styled.div`
13169
+ display: flex;
13170
+ flex-wrap: wrap;
13171
+ gap: var(--wui-space-03);
13172
+ `;
13173
+ const MeterKeyItem = styled.div`
13174
+ display: flex;
13175
+ align-items: center;
13176
+ gap: var(--wui-space-01);
13177
+ `;
13178
+ const MeterKeyColorIndicator = styled.div`
13179
+ width: 8px;
13180
+ height: 8px;
13181
+ border-radius: 50%;
13182
+ background-color: ${({ $color }) => `var(--wui-${$color})`};
13183
+ flex-shrink: 0;
13184
+ `;
13185
+ const MeterKeyLabel = styled.span`
13186
+ line-height: var(--wui-typography-label-3-line-height);
13187
+ font-size: var(--wui-typography-label-3-size);
13188
+ font-weight: var(--wui-typography-label-3-weight);
13189
+ color: var(--wui-color-text-secondary);
13190
+ `;
13191
+ const nodeToString = (node) => {
13192
+ if (typeof node === "string") return node;
13193
+ if (typeof node === "number") return String(node);
13194
+ if (node == null || typeof node === "boolean") return "";
13195
+ if (Array.isArray(node)) return node.map(nodeToString).join("");
13196
+ if (typeof node === "object" && "props" in node) {
13197
+ const element = node;
13198
+ if (element.props?.children != null) return nodeToString(element.props.children);
13199
+ }
13200
+ return "";
13201
+ };
13202
+ /**
13203
+ * A meter component that visually represents one to many segments as portions of a whole
13204
+ */
13205
+ const Meter = ({ segments, label, labelMeta, description, hideKey = false, max = 100, "aria-label": ariaLabel, ...props }) => {
13206
+ const labelId = useId();
13207
+ const descriptionId = useId();
13208
+ const segmentsWithOffsets = segments.reduce((acc, segment) => {
13209
+ const offset = acc.reduce((sum, prev) => sum + prev.widthPercent, 0);
13210
+ const widthPercent = segment.value / max * 100;
13211
+ acc.push({
13212
+ ...segment,
13213
+ offset,
13214
+ widthPercent
13215
+ });
13216
+ return acc;
13217
+ }, []);
13218
+ const keySegments = segmentsWithOffsets.filter((segment) => isNotNil(segment.label));
13219
+ const totalValue = segments.reduce((sum, segment) => sum + segment.value, 0);
13220
+ const generateAriaDescription = () => {
13221
+ const segmentDescriptions = segments.filter((segment) => isNotNil(segment.label)).map((segment) => `${nodeToString(segment.label)}: ${segment.value}%`).join(", ");
13222
+ const totalDescription = `Total: ${totalValue} out of ${max}`;
13223
+ return segmentDescriptions ? `${segmentDescriptions}. ${totalDescription}` : totalDescription;
13224
+ };
13225
+ const effectiveAriaLabel = ariaLabel ?? (isNotNil(label) ? nodeToString(label) : "Data meter");
13226
+ return /* @__PURE__ */ jsxs(MeterWrapper, {
13227
+ ...props,
13228
+ children: [
13229
+ /* @__PURE__ */ jsx(ScreenReaderOnly, {
13230
+ id: descriptionId,
13231
+ text: generateAriaDescription()
13232
+ }),
13233
+ isNotNil(label) || isNotNil(labelMeta) ? /* @__PURE__ */ jsxs(MeterLabelContainer, {
13234
+ id: labelId,
13235
+ children: [isNotNil(label) ? /* @__PURE__ */ jsx(MeterLabel, { children: label }) : null, isNotNil(labelMeta) ? /* @__PURE__ */ jsx(MeterLabelMeta, { children: labelMeta }) : null]
13236
+ }) : null,
13237
+ /* @__PURE__ */ jsx(MeterBarContainer, {
13238
+ "aria-describedby": descriptionId,
13239
+ "aria-label": effectiveAriaLabel,
13240
+ "aria-labelledby": isNotNil(label) || isNotNil(labelMeta) ? labelId : void 0,
13241
+ "aria-valuemax": max,
13242
+ "aria-valuemin": 0,
13243
+ "aria-valuenow": totalValue,
13244
+ role: "meter",
13245
+ children: segmentsWithOffsets.map((segment) => /* @__PURE__ */ jsx(MeterSegmentBar, {
13246
+ $color: segment.color,
13247
+ $offset: segment.offset,
13248
+ $width: segment.widthPercent,
13249
+ "aria-hidden": "true"
13250
+ }, `${segment.color}-${segment.value}-${segment.offset}`))
13251
+ }),
13252
+ isNotNil(description) ? /* @__PURE__ */ jsx(MeterDescription, { children: description }) : null,
13253
+ !hideKey && keySegments.length > 0 ? /* @__PURE__ */ jsx(MeterKey, {
13254
+ "aria-label": "Meter legend",
13255
+ role: "list",
13256
+ children: keySegments.map((segment) => /* @__PURE__ */ jsxs(MeterKeyItem, {
13257
+ role: "listitem",
13258
+ children: [/* @__PURE__ */ jsx(MeterKeyColorIndicator, {
13259
+ $color: segment.color,
13260
+ "aria-hidden": "true"
13261
+ }), /* @__PURE__ */ jsx(MeterKeyLabel, { children: segment.label })]
13262
+ }, `${segment.color}-${segment.value}-${segment.offset}-${nodeToString(segment.label)}`))
13263
+ }) : null
13264
+ ]
13265
+ });
13266
+ };
13267
+ Meter.displayName = "Meter_UI";
13268
+ //#endregion
13269
+ //#region src/components/Modal/ModalCloseButton.tsx
13270
+ const ModalCloseButton = () => {
13271
+ return /* @__PURE__ */ jsx(Dialog.Close, { render: /* @__PURE__ */ jsx(IconButton, {
13272
+ label: "Dismiss modal",
13273
+ size: "sm",
13274
+ style: { alignSelf: "start" },
13275
+ variant: "ghost",
13276
+ children: /* @__PURE__ */ jsx(Icon, { type: "close" })
13277
+ }) });
13278
+ };
13279
+ //#endregion
13280
+ //#region src/components/Modal/ModalHeader.tsx
13281
+ const Header = styled.header`
13282
+ display: flex;
13283
+ order: 1;
13284
+ padding: 0 var(--wui-space-05);
13285
+ padding-bottom: 0;
13286
+ gap: var(--wui-space-01);
13287
+ justify-content: ${({ $hideTitle }) => $hideTitle ? "flex-end" : "space-between"}; /* ensure ModalCloseButton is right-aligned */
13288
+
13289
+ /* Dialog.Title creates an h2 element which inherits some margin */
13290
+ h2 {
13291
+ margin: 0;
13292
+ padding-right: ${({ $hideCloseButon }) => $hideCloseButon ? "0" : "var(--wui-space-03)"};
13293
+ }
13294
+
13295
+ button {
13296
+ position: absolute;
13297
+ right: var(--wui-space-03);
13298
+ top: var(--wui-space-03);
13299
+ }
13300
+ `;
13301
+ const Title = styled(Dialog.Title)`
13302
+ font-family: var(--wui-typography-heading-2-family);
13303
+ line-height: var(--wui-typography-heading-2-line-height);
13304
+ font-size: var(--wui-typography-heading-2-size);
13305
+ font-weight: var(--wui-typography-heading-2-weight);
13306
+ overflow-wrap: anywhere;
13307
+ `;
13308
+ const ModalHeader = ({ title, hideTitle, hideCloseButton }) => {
13309
+ return /* @__PURE__ */ jsxs(Header, {
13310
+ $hideCloseButon: hideCloseButton,
13311
+ $hideTitle: hideTitle,
13312
+ children: [hideTitle ? /* @__PURE__ */ jsx(ScreenReaderOnly, { children: /* @__PURE__ */ jsx(Title, { children: title }) }) : /* @__PURE__ */ jsx(Title, { children: title }), hideCloseButton ? null : /* @__PURE__ */ jsx(ModalCloseButton, {})]
13313
+ });
13314
+ };
13315
+ //#endregion
13316
+ //#region src/components/Modal/constants.ts
13317
+ const DEFAULT_MODAL_WIDTH = "532px";
13318
+ //#endregion
13319
+ //#region src/components/Modal/ModalContent.tsx
13320
+ const modalEnter = keyframes`
13321
+ from {
13322
+ opacity: 0;
13323
+ transform: translateX(-50%) translateY(calc(var(--wui-modal-translate-y) + 24px));
13324
+ }
13325
+
13326
+ to {
13327
+ opacity: 1;
13328
+ transform: translateX(-50%) translateY(var(--wui-modal-translate-y));
13329
+ }
13330
+ `;
13331
+ const modalExit = keyframes`
13332
+ from {
13333
+ opacity: 1;
13334
+ transform: translateX(-50%) translateY(var(--wui-modal-translate-y));
13335
+ }
13336
+
13337
+ to {
13338
+ opacity: 0;
13339
+ transform: translateX(-50%) translateY(calc(var(--wui-modal-translate-y) + 24px));
13340
+ }
13341
+ `;
13342
+ const positionStyleMap = {
13343
+ centered: css`
13344
+ --wui-modal-screen-offset: var(--wui-space-05);
13345
+ --wui-modal-translate-y: -50%;
13346
+
13347
+ height: auto;
13348
+ max-height: calc(100vh - var(--wui-modal-screen-offset) * 2);
13349
+ top: 50%;
13350
+ left: 50%;
13351
+ transform: translateX(-50%) translateY(var(--wui-modal-translate-y));
13352
+ transform-origin: left center;
13353
+ animation: ${modalEnter} var(--wui-motion-duration-04) var(--wui-motion-ease-out);
13354
+
13355
+ &[data-closed] {
13356
+ animation: ${modalExit} var(--wui-motion-duration-03) var(--wui-motion-ease-out) forwards;
13357
+ }
13358
+ `,
13359
+ "fixed-top": css`
13360
+ --wui-modal-screen-offset-top: 15vh;
13361
+ --wui-modal-screen-offset-bottom: 5vh;
13362
+ --wui-modal-translate-y: 0%;
13363
+
13364
+ height: auto;
13365
+ max-height: calc(
13366
+ 100vh - var(--wui-modal-screen-offset-top) - var(--wui-modal-screen-offset-bottom)
13367
+ );
13368
+ top: var(--wui-modal-screen-offset-top);
13369
+ left: 50%;
13370
+ transform: translateX(-50%) translateY(var(--wui-modal-translate-y));
13371
+ transform-origin: left center;
13372
+ animation: ${modalEnter} var(--wui-motion-duration-04) var(--wui-motion-ease-out);
13373
+
13374
+ &[data-closed] {
13375
+ animation: ${modalExit} var(--wui-motion-duration-03) var(--wui-motion-ease-out) forwards;
13376
+ }
13377
+ `,
13378
+ "right-side-panel": css`
13379
+ --wui-modal-screen-offset: var(--wui-space-05);
13380
+
13381
+ height: calc(100vh - var(--wui-modal-screen-offset) * 2);
13382
+ top: var(--wui-modal-screen-offset);
13383
+ right: var(--wui-modal-screen-offset);
13384
+ animation: ${keyframes`
13385
+ from {
13386
+ opacity: 0;
13387
+ transform: translateX(100%);
13388
+ }
13389
+
13390
+ to {
13391
+ opacity: 1;
13392
+ transform: translateX(0);
13393
+ }
13394
+ `} var(--wui-motion-duration-05) var(--wui-motion-ease-out);
13395
+ transform-origin: right center;
13396
+
13397
+ &[data-closed] {
13398
+ animation: ${keyframes`
13399
+ from {
13400
+ opacity: 1;
13401
+ transform: translateX(0);
13402
+ }
13403
+
13404
+ to {
13405
+ opacity: 0;
13406
+ transform: translateX(100%);
13407
+ }
13408
+ `} var(--wui-motion-duration-03) var(--wui-motion-ease-in) forwards;
13409
+ }
13410
+ `
13411
+ };
13412
+ const StyledModalContent = styled(Dialog.Popup)`
13413
+ position: fixed;
13414
+ display: flex;
13415
+ flex-direction: column;
13416
+ gap: var(--wui-space-03);
13417
+ top: 0;
13418
+ left: 0;
13419
+ width: 100vw;
13420
+ height: 100vh;
13421
+ z-index: var(--wui-zindex-modal);
13422
+ box-sizing: border-box;
13423
+ padding: var(--wui-space-05) 0;
13424
+ background-color: var(--wui-color-bg-app);
13425
+
13426
+ ${mq.smAndUp} {
13427
+ position: fixed;
13428
+ top: unset;
13429
+ left: unset;
13430
+ width: calc(100vw - var(--wui-space-05) * 2);
13431
+ max-width: ${({ $width }) => $width ?? "532px"};
13432
+ border-radius: var(--wui-border-radius-03);
13433
+ animation-duration: var(--wui-motion-duration-03);
13434
+ animation-timing-function: var(--wui-motion-ease-out);
13435
+ will-change: transform, opacity;
13436
+ ${({ $positionVariant }) => positionStyleMap[$positionVariant]}
13437
+ }
13438
+ `;
13439
+ const ModalContent = ({ width, positionVariant, initialFocusRef, children, ref, ...props }) => {
13440
+ return /* @__PURE__ */ jsx(StyledModalContent, {
13441
+ ref,
13442
+ $positionVariant: positionVariant,
13443
+ $width: width,
13444
+ ...isNotNil(initialFocusRef) ? { initialFocus: initialFocusRef } : {},
13445
+ ...props,
13446
+ children
13447
+ });
13448
+ };
13449
+ //#endregion
13450
+ //#region src/components/Modal/ModalOverlay.tsx
13451
+ const backdropShow = keyframes`
13452
+ from {
13453
+ opacity: 0;
13454
+ }
14095
13455
 
14096
- display: flex;
14097
- flex-direction: column;
14098
- gap: var(--wui-space-02);
14099
- `;
14100
- const MeterLabelContainer = styled.div`
14101
- display: flex;
14102
- justify-content: space-between;
14103
- align-items: baseline;
14104
- `;
14105
- const MeterLabel = styled.div`
14106
- font-family: var(--wui-typography-heading-5-family);
14107
- line-height: var(--wui-typography-heading-5-line-height);
14108
- font-size: var(--wui-typography-heading-5-size);
14109
- font-weight: var(--wui-typography-heading-5-weight);
14110
- `;
14111
- const MeterLabelMeta = styled.div`
14112
- font-family: var(--wui-typography-heading-5-family);
14113
- line-height: var(--wui-typography-heading-5-line-height);
14114
- font-size: var(--wui-typography-heading-5-size);
14115
- font-weight: var(--wui-typography-heading-5-weight);
14116
- `;
14117
- const MeterBarContainer = styled.div`
14118
- position: relative;
14119
- overflow: hidden;
14120
- background-color: var(--wui-color-bg-surface-secondary);
14121
- border-radius: var(--wui-border-radius-rounded);
14122
- width: 100%;
14123
- height: var(--meter-height);
14124
- transform: translateZ(0);
13456
+ to {
13457
+ opacity: 1;
13458
+ }
14125
13459
  `;
14126
- const MeterSegmentBar = styled.div`
14127
- position: absolute;
14128
- top: 0;
14129
- left: ${({ $offset }) => $offset}%;
14130
- width: ${({ $width }) => $width}%;
14131
- height: 100%;
14132
- background-color: ${({ $color }) => `var(--wui-${$color})`};
14133
-
14134
- &:first-child {
14135
- border-top-left-radius: var(--wui-border-radius-rounded);
14136
- border-bottom-left-radius: var(--wui-border-radius-rounded);
13460
+ const backdropHide = keyframes`
13461
+ from {
13462
+ opacity: 1;
14137
13463
  }
14138
13464
 
14139
- &:last-child {
14140
- border-top-right-radius: var(--wui-border-radius-rounded);
14141
- border-bottom-right-radius: var(--wui-border-radius-rounded);
13465
+ to {
13466
+ opacity: 0;
14142
13467
  }
14143
13468
  `;
14144
- const MeterDescription = styled.div`
14145
- line-height: var(--wui-typography-label-3-line-height);
14146
- font-size: var(--wui-typography-label-3-size);
14147
- font-weight: var(--wui-typography-label-3-weight);
14148
- color: var(--wui-color-text-secondary);
14149
- `;
14150
- const MeterKey = styled.div`
14151
- display: flex;
14152
- flex-wrap: wrap;
14153
- gap: var(--wui-space-03);
13469
+ const ModalOverlay = styled(Dialog.Backdrop)`
13470
+ animation: ${backdropShow} var(--wui-motion-duration-02);
13471
+ background: var(--wui-color-backdrop);
13472
+ inset: 0;
13473
+ position: fixed;
13474
+ z-index: var(--wui-zindex-backdrop);
13475
+
13476
+ &[data-closed] {
13477
+ animation: ${backdropHide} var(--wui-motion-duration-02) forwards;
13478
+ }
14154
13479
  `;
14155
- const MeterKeyItem = styled.div`
13480
+ //#endregion
13481
+ //#region src/components/Modal/Modal.tsx
13482
+ const ModalHiddenDescription = styled(Dialog.Description)({ ...visuallyHiddenStyle });
13483
+ const ModalBody = styled.div`
13484
+ flex-direction: column;
14156
13485
  display: flex;
14157
- align-items: center;
14158
- gap: var(--wui-space-01);
13486
+ flex: 1 0 0;
13487
+ padding: 0 var(--wui-space-05);
14159
13488
  `;
14160
- const MeterKeyColorIndicator = styled.div`
14161
- width: 8px;
14162
- height: 8px;
14163
- border-radius: 50%;
14164
- background-color: ${({ $color }) => `var(--wui-${$color})`};
14165
- flex-shrink: 0;
13489
+ const ModalScrollArea = styled.div`
13490
+ order: 2;
13491
+ max-height: 90vh;
13492
+ overflow-y: auto;
13493
+ height: 100%;
14166
13494
  `;
14167
- const MeterKeyLabel = styled.span`
14168
- line-height: var(--wui-typography-label-3-line-height);
14169
- font-size: var(--wui-typography-label-3-size);
14170
- font-weight: var(--wui-typography-label-3-weight);
14171
- color: var(--wui-color-text-secondary);
13495
+ const ModalFooter = styled.footer`
13496
+ padding: 0 var(--wui-space-05);
13497
+ order: 3;
14172
13498
  `;
14173
- const nodeToString = (node) => {
14174
- if (typeof node === "string") return node;
14175
- if (typeof node === "number") return String(node);
14176
- if (node == null || typeof node === "boolean") return "";
14177
- if (Array.isArray(node)) return node.map(nodeToString).join("");
14178
- if (typeof node === "object" && "props" in node) {
14179
- const element = node;
14180
- if (element.props?.children != null) return nodeToString(element.props.children);
14181
- }
14182
- return "";
14183
- };
14184
13499
  /**
14185
- * A meter component that visually represents one to many segments as portions of a whole
13500
+ * A Modal is a focused UI element that appears atop the main interface, requiring
13501
+ * user interaction or dismissal before returning to the underlying content.
14186
13502
  */
14187
- const Meter = ({ segments, label, labelMeta, description, hideKey = false, max = 100, "aria-label": ariaLabel, ...props }) => {
14188
- const labelId = useId();
14189
- const descriptionId = useId();
14190
- const segmentsWithOffsets = segments.reduce((acc, segment) => {
14191
- const offset = acc.reduce((sum, prev) => sum + prev.widthPercent, 0);
14192
- const widthPercent = segment.value / max * 100;
14193
- acc.push({
14194
- ...segment,
14195
- offset,
14196
- widthPercent
14197
- });
14198
- return acc;
14199
- }, []);
14200
- const keySegments = segmentsWithOffsets.filter((segment) => isNotNil(segment.label));
14201
- const totalValue = segments.reduce((sum, segment) => sum + segment.value, 0);
14202
- const generateAriaDescription = () => {
14203
- const segmentDescriptions = segments.filter((segment) => isNotNil(segment.label)).map((segment) => `${nodeToString(segment.label)}: ${segment.value}%`).join(", ");
14204
- const totalDescription = `Total: ${totalValue} out of ${max}`;
14205
- return segmentDescriptions ? `${segmentDescriptions}. ${totalDescription}` : totalDescription;
14206
- };
14207
- const effectiveAriaLabel = ariaLabel ?? (isNotNil(label) ? nodeToString(label) : "Data meter");
14208
- return /* @__PURE__ */ jsxs(MeterWrapper, {
14209
- ...props,
14210
- children: [
14211
- /* @__PURE__ */ jsx(ScreenReaderOnly, {
14212
- id: descriptionId,
14213
- text: generateAriaDescription()
14214
- }),
14215
- isNotNil(label) || isNotNil(labelMeta) ? /* @__PURE__ */ jsxs(MeterLabelContainer, {
14216
- id: labelId,
14217
- children: [isNotNil(label) ? /* @__PURE__ */ jsx(MeterLabel, { children: label }) : null, isNotNil(labelMeta) ? /* @__PURE__ */ jsx(MeterLabelMeta, { children: labelMeta }) : null]
14218
- }) : null,
14219
- /* @__PURE__ */ jsx(MeterBarContainer, {
14220
- "aria-describedby": descriptionId,
14221
- "aria-label": effectiveAriaLabel,
14222
- "aria-labelledby": isNotNil(label) || isNotNil(labelMeta) ? labelId : void 0,
14223
- "aria-valuemax": max,
14224
- "aria-valuemin": 0,
14225
- "aria-valuenow": totalValue,
14226
- role: "meter",
14227
- children: segmentsWithOffsets.map((segment) => /* @__PURE__ */ jsx(MeterSegmentBar, {
14228
- $color: segment.color,
14229
- $offset: segment.offset,
14230
- $width: segment.widthPercent,
14231
- "aria-hidden": "true"
14232
- }, `${segment.color}-${segment.value}-${segment.offset}`))
14233
- }),
14234
- isNotNil(description) ? /* @__PURE__ */ jsx(MeterDescription, { children: description }) : null,
14235
- !hideKey && keySegments.length > 0 ? /* @__PURE__ */ jsx(MeterKey, {
14236
- "aria-label": "Meter legend",
14237
- role: "list",
14238
- children: keySegments.map((segment) => /* @__PURE__ */ jsxs(MeterKeyItem, {
14239
- role: "listitem",
14240
- children: [/* @__PURE__ */ jsx(MeterKeyColorIndicator, {
14241
- $color: segment.color,
14242
- "aria-hidden": "true"
14243
- }), /* @__PURE__ */ jsx(MeterKeyLabel, { children: segment.label })]
14244
- }, `${segment.color}-${segment.value}-${segment.offset}-${nodeToString(segment.label)}`))
14245
- }) : null
14246
- ]
13503
+ const Modal = ({ children, footer, hideCloseButton = false, hideTitle = false, initialFocusRef, isOpen, onRequestClose, positionVariant = "centered", title, width = DEFAULT_MODAL_WIDTH, ref, ...props }) => {
13504
+ return /* @__PURE__ */ jsx(Dialog.Root, {
13505
+ onOpenChange: (open) => {
13506
+ if (!open && isNotNil(onRequestClose)) onRequestClose();
13507
+ },
13508
+ open: isOpen,
13509
+ children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [/* @__PURE__ */ jsx(ModalOverlay, {}), /* @__PURE__ */ jsxs(ModalContent, {
13510
+ ref,
13511
+ initialFocusRef,
13512
+ positionVariant,
13513
+ width,
13514
+ ...props,
13515
+ children: [
13516
+ /* @__PURE__ */ jsx(ModalScrollArea, { children: /* @__PURE__ */ jsx(ModalBody, { children }) }),
13517
+ isNotNil(footer) ? /* @__PURE__ */ jsx(ModalFooter, { children: footer }) : null,
13518
+ hideCloseButton && hideTitle ? null : /* @__PURE__ */ jsx(ModalHeader, {
13519
+ hideCloseButton,
13520
+ hideTitle,
13521
+ title
13522
+ }),
13523
+ /* @__PURE__ */ jsx(ModalHiddenDescription, {})
13524
+ ]
13525
+ })] })
14247
13526
  });
14248
13527
  };
14249
- Meter.displayName = "Meter_UI";
14250
13528
  //#endregion
14251
- //#region src/components/Modal/ModalCloseButton.tsx
14252
- const ModalCloseButton = () => {
14253
- return /* @__PURE__ */ jsx(Dialog.Close, { render: /* @__PURE__ */ jsx(IconButton, {
14254
- label: "Dismiss modal",
14255
- size: "sm",
14256
- style: { alignSelf: "start" },
14257
- variant: "ghost",
14258
- children: /* @__PURE__ */ jsx(Icon, { type: "close" })
14259
- }) });
13529
+ //#region src/components/Modal/ModalCallouts.tsx
13530
+ const ModalCallouts = ({ children }) => {
13531
+ return /* @__PURE__ */ jsx(Stack, {
13532
+ direction: "horizontal",
13533
+ gap: "space-08",
13534
+ children
13535
+ });
13536
+ };
13537
+ ModalCallouts.displayName = "ModalCallouts_UI";
13538
+ const ModalCallout = ({ title, image, children }) => {
13539
+ return /* @__PURE__ */ jsxs(Stack, {
13540
+ direction: "vertical",
13541
+ children: [
13542
+ image,
13543
+ /* @__PURE__ */ jsx(Heading, {
13544
+ variant: "heading4",
13545
+ children: title
13546
+ }),
13547
+ children
13548
+ ]
13549
+ });
14260
13550
  };
13551
+ ModalCallout.displayName = "ModalCallout_UI";
14261
13552
  //#endregion
14262
- //#region src/components/Modal/ModalHeader.tsx
14263
- const Header = styled.header`
14264
- display: flex;
14265
- order: 1;
14266
- padding: 0 var(--wui-space-05);
14267
- padding-bottom: 0;
14268
- gap: var(--wui-space-01);
14269
- justify-content: ${({ $hideTitle }) => $hideTitle ? "flex-end" : "space-between"}; /* ensure ModalCloseButton is right-aligned */
13553
+ //#region src/private/helpers/getControls/getControlProps.tsx
13554
+ const getControlProps = (isOpen, onOpenChange) => {
13555
+ return isNotNil(onOpenChange) && isNotNil(isOpen) ? {
13556
+ open: isOpen,
13557
+ onOpenChange
13558
+ } : {};
13559
+ };
13560
+ //#endregion
13561
+ //#region src/components/Popover/PopoverArrow.tsx
13562
+ const StyledArrowSvg = styled.svg`
13563
+ pointer-events: none;
14270
13564
 
14271
- /* Dialog.Title creates an h2 element which inherits some margin */
14272
- h2 {
14273
- margin: 0;
14274
- padding-right: ${({ $hideCloseButon }) => $hideCloseButon ? "0" : "var(--wui-space-03)"};
13565
+ /* override inline positioning and rotate the arrow so the circle sits near the trigger and the stalk extends down toward the popup. */
13566
+ &[data-side='top'] {
13567
+ top: 100% !important;
14275
13568
  }
14276
13569
 
14277
- button {
14278
- position: absolute;
14279
- right: var(--wui-space-03);
14280
- top: var(--wui-space-03);
13570
+ &[data-side='bottom'] {
13571
+ top: 0 !important;
13572
+ transform: rotate(180deg);
13573
+ transform-origin: center 0;
14281
13574
  }
14282
- `;
14283
- const Title = styled(Dialog.Title)`
14284
- font-family: var(--wui-typography-heading-2-family);
14285
- line-height: var(--wui-typography-heading-2-line-height);
14286
- font-size: var(--wui-typography-heading-2-size);
14287
- font-weight: var(--wui-typography-heading-2-weight);
14288
- overflow-wrap: anywhere;
14289
- `;
14290
- const ModalHeader = ({ title, hideTitle, hideCloseButton }) => {
14291
- return /* @__PURE__ */ jsxs(Header, {
14292
- $hideCloseButon: hideCloseButton,
14293
- $hideTitle: hideTitle,
14294
- children: [hideTitle ? /* @__PURE__ */ jsx(ScreenReaderOnly, { children: /* @__PURE__ */ jsx(Title, { children: title }) }) : /* @__PURE__ */ jsx(Title, { children: title }), hideCloseButton ? null : /* @__PURE__ */ jsx(ModalCloseButton, {})]
14295
- });
14296
- };
14297
- //#endregion
14298
- //#region src/components/Modal/constants.ts
14299
- const DEFAULT_MODAL_WIDTH = "532px";
14300
- //#endregion
14301
- //#region src/components/Modal/ModalContent.tsx
14302
- const modalEnter = keyframes`
14303
- from {
14304
- opacity: 0;
14305
- transform: translateX(-50%) translateY(calc(var(--wui-modal-translate-y) + 24px));
13575
+
13576
+ &[data-side='left'] {
13577
+ right: 0;
13578
+ left: auto;
13579
+ transform: translateX(52px) rotate(-90deg);
13580
+ transform-origin: center center;
14306
13581
  }
14307
13582
 
14308
- to {
14309
- opacity: 1;
14310
- transform: translateX(-50%) translateY(var(--wui-modal-translate-y));
13583
+ &[data-side='right'] {
13584
+ left: 0;
13585
+ right: auto;
13586
+ transform: translateX(-52px) rotate(90deg);
13587
+ transform-origin: center center;
14311
13588
  }
14312
13589
  `;
14313
- const modalExit = keyframes`
14314
- from {
14315
- opacity: 1;
14316
- transform: translateX(-50%) translateY(var(--wui-modal-translate-y));
13590
+ const StyledPath = styled.path`
13591
+ fill: var(--wui-color-border-secondary);
13592
+ `;
13593
+ const circleAnimation = keyframes`
13594
+ 0% {
13595
+ opacity: var(--wui-popover-arrow-circle-starting-opacity);
14317
13596
  }
14318
-
14319
- to {
13597
+ 100% {
14320
13598
  opacity: 0;
14321
- transform: translateX(-50%) translateY(calc(var(--wui-modal-translate-y) + 24px));
14322
13599
  }
14323
13600
  `;
14324
- const positionStyleMap = {
14325
- centered: css`
14326
- --wui-modal-screen-offset: var(--wui-space-05);
14327
- --wui-modal-translate-y: -50%;
13601
+ const StyledCircle = styled.circle`
13602
+ stroke: var(--wui-color-border-active);
13603
+ animation-duration: 2s;
13604
+ animation-iteration-count: infinite;
13605
+ animation-direction: alternate;
13606
+ animation-timing-function: ease-in-out;
14328
13607
 
14329
- height: auto;
14330
- max-height: calc(100vh - var(--wui-modal-screen-offset) * 2);
14331
- top: 50%;
14332
- left: 50%;
14333
- transform: translateX(-50%) translateY(var(--wui-modal-translate-y));
14334
- transform-origin: left center;
14335
- animation: ${modalEnter} var(--wui-motion-duration-04) var(--wui-motion-ease-out);
13608
+ &[data-wui-popover-arrow-inner-circle] {
13609
+ --wui-popover-arrow-circle-starting-opacity: 0.36;
14336
13610
 
14337
- &[data-closed] {
14338
- animation: ${modalExit} var(--wui-motion-duration-03) var(--wui-motion-ease-out) forwards;
13611
+ opacity: var(--wui-popover-arrow-circle-starting-opacity);
14339
13612
  }
14340
- `,
14341
- "fixed-top": css`
14342
- --wui-modal-screen-offset-top: 15vh;
14343
- --wui-modal-screen-offset-bottom: 5vh;
14344
- --wui-modal-translate-y: 0%;
14345
-
14346
- height: auto;
14347
- max-height: calc(
14348
- 100vh - var(--wui-modal-screen-offset-top) - var(--wui-modal-screen-offset-bottom)
14349
- );
14350
- top: var(--wui-modal-screen-offset-top);
14351
- left: 50%;
14352
- transform: translateX(-50%) translateY(var(--wui-modal-translate-y));
14353
- transform-origin: left center;
14354
- animation: ${modalEnter} var(--wui-motion-duration-04) var(--wui-motion-ease-out);
14355
13613
 
14356
- &[data-closed] {
14357
- animation: ${modalExit} var(--wui-motion-duration-03) var(--wui-motion-ease-out) forwards;
14358
- }
14359
- `,
14360
- "right-side-panel": css`
14361
- --wui-modal-screen-offset: var(--wui-space-05);
13614
+ &[data-wui-popover-arrow-outer-circle] {
13615
+ --wui-popover-arrow-circle-starting-opacity: 0.2;
14362
13616
 
14363
- height: calc(100vh - var(--wui-modal-screen-offset) * 2);
14364
- top: var(--wui-modal-screen-offset);
14365
- right: var(--wui-modal-screen-offset);
14366
- animation: ${keyframes`
14367
- from {
14368
- opacity: 0;
14369
- transform: translateX(100%);
13617
+ animation-direction: alternate-reverse;
13618
+ opacity: 0.11;
14370
13619
  }
14371
13620
 
14372
- to {
14373
- opacity: 1;
14374
- transform: translateX(0);
13621
+ @media (prefers-reduced-motion: no-preference) {
13622
+ ${({ $isAnimated }) => $isAnimated && css`
13623
+ animation-name: ${circleAnimation};
13624
+ `}
14375
13625
  }
14376
- `} var(--wui-motion-duration-05) var(--wui-motion-ease-out);
14377
- transform-origin: right center;
13626
+ `;
13627
+ const PopoverArrow = ({ isAnimated }) => {
13628
+ return /* @__PURE__ */ jsx(Popover$1.Arrow, { render: /* @__PURE__ */ jsxs(StyledArrowSvg, {
13629
+ fill: "none",
13630
+ height: "56",
13631
+ viewBox: "0 0 48 56",
13632
+ width: "48",
13633
+ xmlns: "http://www.w3.org/2000/svg",
13634
+ children: [
13635
+ /* @__PURE__ */ jsx(StyledPath, { d: "M24 26.6667C21.0545 26.6667 18.6667 29.0545 18.6667 32C18.6667 34.9455 21.0545 37.3333 24 37.3333C26.9455 37.3333 29.3333 34.9455 29.3333 32C29.3333 29.0545 26.9455 26.6667 24 26.6667ZM23 0V32H25V0H23Z" }),
13636
+ /* @__PURE__ */ jsx(StyledCircle, {
13637
+ $isAnimated: isAnimated,
13638
+ cx: "24",
13639
+ cy: "32",
13640
+ "data-wui-popover-arrow-inner-circle": true,
13641
+ r: "10",
13642
+ strokeWidth: "4"
13643
+ }),
13644
+ /* @__PURE__ */ jsx(StyledCircle, {
13645
+ $isAnimated: isAnimated,
13646
+ cx: "24",
13647
+ cy: "32",
13648
+ "data-wui-popover-arrow-outer-circle": true,
13649
+ r: "20",
13650
+ strokeWidth: "8"
13651
+ })
13652
+ ]
13653
+ }) });
13654
+ };
13655
+ PopoverArrow.displayName = "PopoverArrow_UI";
13656
+ //#endregion
13657
+ //#region src/components/Popover/Popover.tsx
13658
+ const StyledPopup$2 = styled(Popover$1.Popup)`
13659
+ ${({ $colorScheme }) => getColorScheme($colorScheme)};
13660
+ ${({ $unstyled }) => !$unstyled && css`
13661
+ border-radius: var(--wui-border-radius-02);
13662
+ padding: var(--wui-space-04);
13663
+ max-width: var(--wui-popover-max-width, 320px);
13664
+ max-height: var(--wui-popover-max-height, auto);
13665
+ background-color: var(--wui-color-bg-surface);
13666
+ box-shadow: var(--wui-elevation-01);
13667
+ border: 1px solid var(--wui-color-border);
13668
+ overflow: auto;
13669
+ `}
14378
13670
 
14379
- &[data-closed] {
14380
- animation: ${keyframes`
14381
- from {
14382
- opacity: 1;
14383
- transform: translateX(0);
14384
- }
13671
+ ${({ $withArrow }) => $withArrow && css`
13672
+ overflow: visible;
13673
+ `}
14385
13674
 
14386
- to {
14387
- opacity: 0;
14388
- transform: translateX(100%);
14389
- }
14390
- `} var(--wui-motion-duration-03) var(--wui-motion-ease-in) forwards;
13675
+ [data-wui-popover-close] {
13676
+ position: absolute;
13677
+ top: var(--wui-space-02);
13678
+ right: var(--wui-space-02);
14391
13679
  }
14392
- `
13680
+ `;
13681
+ /**
13682
+ * Displays rich content in a portal, triggered by a button.
13683
+ *
13684
+ * For more control — custom anchor, no injected close button, etc. — compose
13685
+ * the primitives directly: `PopoverRoot`, `PopoverTrigger`, `PopoverAnchor`,
13686
+ * `PopoverPortal`, `PopoverContent`, `PopoverClose`.
13687
+ */
13688
+ const Popover = ({ children, trigger, isOpen, hideCloseButton = false, maxWidth, maxHeight, onOpenChange, unstyled = false, withArrow = false, isAnimated = false, colorScheme = "inherit", style, side, align, nativeButton = true, ...props }) => {
13689
+ const sideOffset = withArrow ? 32 : 8;
13690
+ const mergedStyle = {
13691
+ "--wui-popover-max-width": maxWidth,
13692
+ "--wui-popover-max-height": maxHeight,
13693
+ ...style
13694
+ };
13695
+ return /* @__PURE__ */ jsxs(Popover$1.Root, {
13696
+ ...getControlProps(isOpen, onOpenChange),
13697
+ children: [/* @__PURE__ */ jsx(Popover$1.Trigger, {
13698
+ nativeButton,
13699
+ render: trigger
13700
+ }), /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, {
13701
+ align,
13702
+ collisionAvoidance: {
13703
+ side: "flip",
13704
+ align: "shift",
13705
+ fallbackAxisSide: "none"
13706
+ },
13707
+ side,
13708
+ sideOffset,
13709
+ style: { zIndex: "var(--wui-zindex-popover)" },
13710
+ children: /* @__PURE__ */ jsxs(StyledPopup$2, {
13711
+ $colorScheme: colorScheme,
13712
+ ...props,
13713
+ $unstyled: unstyled,
13714
+ $withArrow: withArrow,
13715
+ role: "dialog",
13716
+ style: mergedStyle,
13717
+ children: [
13718
+ !hideCloseButton && /* @__PURE__ */ jsx(Popover$1.Close, { render: /* @__PURE__ */ jsx(IconButton, {
13719
+ "data-wui-popover-close": true,
13720
+ label: "Close",
13721
+ variant: "ghost",
13722
+ children: /* @__PURE__ */ jsx(Icon, { type: "close" })
13723
+ }) }),
13724
+ withArrow ? /* @__PURE__ */ jsx(PopoverArrow, { isAnimated }) : null,
13725
+ /* @__PURE__ */ jsx("div", { children })
13726
+ ]
13727
+ })
13728
+ }) })]
13729
+ });
14393
13730
  };
14394
- const StyledModalContent = styled(Dialog.Popup)`
14395
- position: fixed;
14396
- display: flex;
14397
- flex-direction: column;
14398
- gap: var(--wui-space-03);
14399
- top: 0;
14400
- left: 0;
14401
- width: 100vw;
14402
- height: 100vh;
14403
- z-index: var(--wui-zindex-modal);
14404
- box-sizing: border-box;
14405
- padding: var(--wui-space-05) 0;
14406
- background-color: var(--wui-color-bg-app);
14407
-
14408
- ${mq.smAndUp} {
14409
- position: fixed;
14410
- top: unset;
14411
- left: unset;
14412
- width: calc(100vw - var(--wui-space-05) * 2);
14413
- max-width: ${({ $width }) => $width ?? "532px"};
14414
- border-radius: var(--wui-border-radius-03);
14415
- animation-duration: var(--wui-motion-duration-03);
14416
- animation-timing-function: var(--wui-motion-ease-out);
14417
- will-change: transform, opacity;
14418
- ${({ $positionVariant }) => positionStyleMap[$positionVariant]}
14419
- }
14420
- `;
14421
- const ModalContent = ({ width, positionVariant, initialFocusRef, children, ref, ...props }) => {
14422
- return /* @__PURE__ */ jsx(StyledModalContent, {
14423
- ref,
14424
- $positionVariant: positionVariant,
14425
- $width: width,
14426
- ...isNotNil(initialFocusRef) ? { initialFocus: initialFocusRef } : {},
14427
- ...props,
13731
+ Popover.displayName = "Popover_UI";
13732
+ //#endregion
13733
+ //#region src/components/Popover/PopoverAnchorContext.tsx
13734
+ const PopoverAnchorContext = createContext(null);
13735
+ const PopoverAnchorProvider = PopoverAnchorContext;
13736
+ const usePopoverAnchor = () => useContext(PopoverAnchorContext);
13737
+ const useCreatePopoverAnchorRef = () => useRef(null);
13738
+ //#endregion
13739
+ //#region src/components/Popover/PopoverAnchor.tsx
13740
+ /**
13741
+ * Positions the popover relative to an element other than the trigger. Useful
13742
+ * when the element the user interacts with (e.g. a text input) is separate
13743
+ * from what the popover attaches to.
13744
+ *
13745
+ * Wrap the anchor element and nest `PopoverTrigger` inside it.
13746
+ */
13747
+ const PopoverAnchor = ({ children }) => {
13748
+ const sharedRef = usePopoverAnchor();
13749
+ return /* @__PURE__ */ jsx("div", {
13750
+ ref: useCallback((node) => {
13751
+ if (sharedRef) sharedRef.current = node;
13752
+ }, [sharedRef]),
14428
13753
  children
14429
13754
  });
14430
13755
  };
13756
+ PopoverAnchor.displayName = "PopoverAnchor_UI";
14431
13757
  //#endregion
14432
- //#region src/components/Modal/ModalOverlay.tsx
14433
- const backdropShow = keyframes`
14434
- from {
14435
- opacity: 0;
14436
- }
14437
-
14438
- to {
14439
- opacity: 1;
14440
- }
14441
- `;
14442
- const backdropHide = keyframes`
14443
- from {
14444
- opacity: 1;
14445
- }
14446
-
14447
- to {
14448
- opacity: 0;
14449
- }
14450
- `;
14451
- const ModalOverlay = styled(Dialog.Backdrop)`
14452
- animation: ${backdropShow} var(--wui-motion-duration-02);
14453
- background: var(--wui-color-backdrop);
14454
- inset: 0;
14455
- position: fixed;
14456
- z-index: var(--wui-zindex-backdrop);
13758
+ //#region src/components/Popover/PopoverClose.tsx
13759
+ /**
13760
+ * An element that closes the popover when activated. Defaults to a `<button>`;
13761
+ * pass `render` to merge the close behavior onto a single child element. For
13762
+ * a pre-styled icon close button, use `PopoverCloseButton`.
13763
+ */
13764
+ const PopoverClose = ({ ref, ...props }) => /* @__PURE__ */ jsx(Popover$1.Close, {
13765
+ ref,
13766
+ ...props
13767
+ });
13768
+ //#endregion
13769
+ //#region src/components/Popover/PopoverCloseButton.tsx
13770
+ /**
13771
+ * A pre-styled close button intended to be placed inside a `PopoverContent`.
13772
+ * For a custom close control, use `PopoverClose` directly.
13773
+ */
13774
+ const PopoverCloseButton = ({ label = "Close" }) => /* @__PURE__ */ jsx(PopoverClose, { render: /* @__PURE__ */ jsx(IconButton, {
13775
+ "data-wui-popover-close": true,
13776
+ label,
13777
+ variant: "ghost",
13778
+ children: /* @__PURE__ */ jsx(Icon, { type: "close" })
13779
+ }) });
13780
+ PopoverCloseButton.displayName = "PopoverCloseButton_UI";
13781
+ //#endregion
13782
+ //#region src/components/Popover/PopoverContent.tsx
13783
+ const StyledPopup$1 = styled(Popover$1.Popup)`
13784
+ ${({ $colorScheme }) => getColorScheme($colorScheme)};
13785
+ ${({ $unstyled }) => !$unstyled && css`
13786
+ border-radius: var(--wui-border-radius-02);
13787
+ padding: var(--wui-space-04);
13788
+ max-width: var(--wui-popover-max-width);
13789
+ max-height: var(--wui-popover-max-height);
13790
+ background-color: var(--wui-color-bg-surface);
13791
+ box-shadow: var(--wui-elevation-01);
13792
+ border: 1px solid var(--wui-color-border);
13793
+ overflow: auto;
13794
+ `}
14457
13795
 
14458
- &[data-closed] {
14459
- animation: ${backdropHide} var(--wui-motion-duration-02) forwards;
13796
+ [data-wui-popover-close] {
13797
+ position: absolute;
13798
+ top: var(--wui-space-02);
13799
+ right: var(--wui-space-02);
14460
13800
  }
14461
13801
  `;
14462
- //#endregion
14463
- //#region src/components/Modal/Modal.tsx
14464
- const ModalHiddenDescription = styled(Dialog.Description)({ ...visuallyHiddenStyle });
14465
- const ModalBody = styled.div`
14466
- flex-direction: column;
14467
- display: flex;
14468
- flex: 1 0 0;
14469
- padding: 0 var(--wui-space-05);
14470
- `;
14471
- const ModalScrollArea = styled.div`
14472
- order: 2;
14473
- max-height: 90vh;
14474
- overflow-y: auto;
14475
- height: 100%;
14476
- `;
14477
- const ModalFooter = styled.footer`
14478
- padding: 0 var(--wui-space-05);
14479
- order: 3;
14480
- `;
13802
+ const DEFAULT_SIDE_OFFSET = 8;
13803
+ const DEFAULT_MAX_WIDTH = "320px";
13804
+ const DEFAULT_MAX_HEIGHT = "auto";
14481
13805
  /**
14482
- * A Modal is a focused UI element that appears atop the main interface, requiring
14483
- * user interaction or dismissal before returning to the underlying content.
13806
+ * The styled popover surface. Place inside a `PopoverRoot` (typically wrapped
13807
+ * in a `PopoverPortal`). Wraps a `Positioner` and `Popup` internally.
14484
13808
  */
14485
- const Modal = ({ children, footer, hideCloseButton = false, hideTitle = false, initialFocusRef, isOpen, onRequestClose, positionVariant = "centered", title, width = DEFAULT_MODAL_WIDTH, ref, ...props }) => {
14486
- return /* @__PURE__ */ jsx(Dialog.Root, {
14487
- onOpenChange: (open) => {
14488
- if (!open && isNotNil(onRequestClose)) onRequestClose();
13809
+ const PopoverContent = ({ colorScheme = "inherit", unstyled = false, maxWidth = DEFAULT_MAX_WIDTH, maxHeight = DEFAULT_MAX_HEIGHT, sideOffset = DEFAULT_SIDE_OFFSET, side, align, style, role = "dialog", children, ref, ...props }) => {
13810
+ const sharedAnchorRef = usePopoverAnchor();
13811
+ const hasAnchor = sharedAnchorRef?.current != null;
13812
+ const mergedStyle = {
13813
+ "--wui-popover-max-width": maxWidth,
13814
+ "--wui-popover-max-height": maxHeight,
13815
+ ...style
13816
+ };
13817
+ return /* @__PURE__ */ jsx(Popover$1.Positioner, {
13818
+ align,
13819
+ collisionAvoidance: {
13820
+ side: "flip",
13821
+ align: "shift",
13822
+ fallbackAxisSide: "none"
14489
13823
  },
14490
- open: isOpen,
14491
- children: /* @__PURE__ */ jsxs(Dialog.Portal, { children: [/* @__PURE__ */ jsx(ModalOverlay, {}), /* @__PURE__ */ jsxs(ModalContent, {
13824
+ side,
13825
+ sideOffset,
13826
+ style: { zIndex: "var(--wui-zindex-popover)" },
13827
+ ...hasAnchor ? { anchor: sharedAnchorRef } : {},
13828
+ children: /* @__PURE__ */ jsx(StyledPopup$1, {
14492
13829
  ref,
14493
- initialFocusRef,
14494
- positionVariant,
14495
- width,
13830
+ $colorScheme: colorScheme,
13831
+ $unstyled: unstyled,
13832
+ role,
13833
+ style: mergedStyle,
14496
13834
  ...props,
14497
- children: [
14498
- /* @__PURE__ */ jsx(ModalScrollArea, { children: /* @__PURE__ */ jsx(ModalBody, { children }) }),
14499
- isNotNil(footer) ? /* @__PURE__ */ jsx(ModalFooter, { children: footer }) : null,
14500
- hideCloseButton && hideTitle ? null : /* @__PURE__ */ jsx(ModalHeader, {
14501
- hideCloseButton,
14502
- hideTitle,
14503
- title
14504
- }),
14505
- /* @__PURE__ */ jsx(ModalHiddenDescription, {})
14506
- ]
14507
- })] })
13835
+ children
13836
+ })
14508
13837
  });
14509
13838
  };
14510
13839
  //#endregion
14511
- //#region src/components/Modal/ModalCallouts.tsx
14512
- const ModalCallouts = ({ children }) => {
14513
- return /* @__PURE__ */ jsx(Stack, {
14514
- direction: "horizontal",
14515
- gap: "space-08",
14516
- children
14517
- });
14518
- };
14519
- ModalCallouts.displayName = "ModalCallouts_UI";
14520
- const ModalCallout = ({ title, image, children }) => {
14521
- return /* @__PURE__ */ jsxs(Stack, {
14522
- direction: "vertical",
14523
- children: [
14524
- image,
14525
- /* @__PURE__ */ jsx(Heading, {
14526
- variant: "heading4",
14527
- children: title
14528
- }),
13840
+ //#region src/components/Popover/PopoverPortal.tsx
13841
+ /**
13842
+ * Portals the popover content out of the DOM hierarchy of its trigger so it can
13843
+ * escape clipping ancestors (`overflow: hidden`, transformed containers, etc.).
13844
+ */
13845
+ const PopoverPortal = ({ children }) => /* @__PURE__ */ jsx(Popover$1.Portal, { children });
13846
+ PopoverPortal.displayName = "PopoverPortal_UI";
13847
+ //#endregion
13848
+ //#region src/components/Popover/PopoverRoot.tsx
13849
+ /**
13850
+ * The root of a composable popover. Wrap `PopoverTrigger` (or `PopoverAnchor`)
13851
+ * and `PopoverContent` inside it.
13852
+ *
13853
+ * For the common "button opens a panel" case, prefer the bundled `Popover`.
13854
+ */
13855
+ const PopoverRoot = ({ isOpen, onOpenChange, children }) => {
13856
+ const anchorRef = useCreatePopoverAnchorRef();
13857
+ return /* @__PURE__ */ jsx(Popover$1.Root, {
13858
+ ...getControlProps(isOpen, onOpenChange),
13859
+ children: /* @__PURE__ */ jsx(PopoverAnchorProvider, {
13860
+ value: anchorRef,
14529
13861
  children
14530
- ]
13862
+ })
14531
13863
  });
14532
13864
  };
14533
- ModalCallout.displayName = "ModalCallout_UI";
13865
+ PopoverRoot.displayName = "PopoverRoot_UI";
13866
+ //#endregion
13867
+ //#region src/components/Popover/PopoverTrigger.tsx
13868
+ /**
13869
+ * The button that toggles the popover open and closed. By default it renders a
13870
+ * `<button>` wrapping its children; pass `render` to merge the trigger
13871
+ * behavior onto a single child element (e.g. a `Button` or `IconButton`).
13872
+ */
13873
+ const PopoverTrigger = ({ nativeButton = true, ref, ...props }) => /* @__PURE__ */ jsx(Popover$1.Trigger, {
13874
+ ref,
13875
+ nativeButton,
13876
+ ...props
13877
+ });
14534
13878
  //#endregion
14535
13879
  //#region src/components/ProgressBar/ProgressBar.tsx
14536
13880
  const ProgressBarWrapper = styled.div`
@@ -16603,6 +15947,6 @@ const WistiaLogo = ({ description, height = 100, hoverColor, href, iconOnly = fa
16603
15947
  };
16604
15948
  WistiaLogo.displayName = "WistiaLogo_UI";
16605
15949
  //#endregion
16606
- export { ActionButton, Avatar, Badge, Banner, Box, Breadcrumb, Breadcrumbs, Button, ButtonGroup, Card, Center, Checkbox, CheckboxGroup, CheckboxMenuItem, ClickRegion, Collapsible, CollapsibleContent, CollapsibleTrigger, CollapsibleTriggerIcon, ColorGrid, ColorGridOption, ColorList, ColorListGroup, ColorListOption, ColorPicker, ColorPickerPopoverContent, ColorPickerSection, ColorPickerTrigger, ColorSchemeWrapper, Combobox, ComboboxOption, ContextMenu, ContrastControls, CustomizableThemeWrapper, DataCard, DataCardHoverArrow, DataCardTrend, DataCards, DataList, DataListItem, DataListItemLabel, DataListItemValue, DatePicker, Divider, EditableHeading, EditableText, EditableTextCancelButton, EditableTextContext, EditableTextDisplay, EditableTextInput, EditableTextLabel, EditableTextRoot, EditableTextSubmitButton, EditableTextTrigger, Ellipsis, FeatureCard, FeatureCardImage, FileAmountLimitValidator, FileSizeValidator, FileTypeValidator, FilterMenu, FilterMenuItem, Form, FormErrorSummary, FormField, FormGroup, Grid, Heading, HexColorInput, HueSlider, Icon, IconButton, Image, ImageDimensionsValidator, Input, InputClickToCopy, InputDuration, InputPassword, KeyboardShortcut, Label, Link, List, ListItem, Mark, Markdown, Menu, MenuItem, MenuItemDescription, MenuItemLabel, MenuLabel, MenuRadioGroup, Meter, Modal, ModalCallout, ModalCallouts, PersistentFileAmountLimitValidator, Popover, PopoverAnchor, PopoverArrow, PopoverClose, PopoverCloseButton, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger, PreviewCard, ProgressBar, Radio, RadioCard, RadioCardImage, RadioGroup, RadioMenuItem, SaturationAndValuePicker, ScreenReaderOnly, ScrollArea, SegmentedControl, SegmentedControlItem, Select, SelectOption, SelectOptionGroup, Sidebar, SidebarButton, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarSearchInput, SidebarTitle, Slider, SplitButton, Stack, SubMenu, Switch, Table, TableBody, TableCell, TableFoot, TableHead, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, Text, Thumbnail, ThumbnailBadge, ThumbnailCollage, Tooltip, UIProvider, ValueNameOrHexCode, ValueSwatch, WistiaLogo, buildTimeDuration, calculateContrast, coerceToBoolean, colorSchemeOptions, copyToClipboard, dateOnlyISOString, dateOnlyString, dateOnlyStringForSentence, dateOnlyStringNumeric, dateTime, dateTimeRounded, dateTimeString, dateTimeStringForSentence, dateTimeToDate, dateTimeToISO, dateToDateTime, dateUTCOffset, dayOfWeekString, getBackgroundGradient, getSemiRandomBackgroundGradient, iconSizeMap, isKeyboardKey, isUrl, mediaDurationString, mergeRefs, millisecondsToDurationISOString, monthDayStringNumeric, mq, parseDateString, sessionDurationString, stripExtension, timeAgoString, timeOnlyString, useActiveMq, useAriaLive, useBoolean, useClipboard, useElementObserver, useFilePicker, useFocusTrap, useForceUpdate, useFormState, useImperativeFilePicker, useIsHovered, useKey, useKeyPress, useLocalStorage, useLockBodyScroll, useMq, useOnClickOutside, usePreviousValue, useToast, useWindowSize, validateWithYup };
15950
+ export { ActionButton, Avatar, Badge, Banner, Box, Breadcrumb, Breadcrumbs, Button, ButtonGroup, Card, Center, Checkbox, CheckboxGroup, CheckboxMenuItem, ClickRegion, Collapsible, CollapsibleContent, CollapsibleTrigger, CollapsibleTriggerIcon, ColorGrid, ColorGridOption, ColorList, ColorListGroup, ColorListOption, ColorPicker, ColorPickerPopoverContent, ColorPickerSection, ColorPickerTrigger, ColorSchemeWrapper, Combobox, ComboboxOption, ContextMenu, ContrastControls, CustomizableThemeWrapper, DataCard, DataCardHoverArrow, DataCardTrend, DataCards, DataList, DataListItem, DataListItemLabel, DataListItemValue, Divider, EditableHeading, EditableText, EditableTextCancelButton, EditableTextContext, EditableTextDisplay, EditableTextInput, EditableTextLabel, EditableTextRoot, EditableTextSubmitButton, EditableTextTrigger, Ellipsis, FeatureCard, FeatureCardImage, FileAmountLimitValidator, FileSizeValidator, FileTypeValidator, FilterMenu, FilterMenuItem, Form, FormErrorSummary, FormField, FormGroup, Grid, Heading, HexColorInput, HueSlider, Icon, IconButton, Image, ImageDimensionsValidator, Input, InputClickToCopy, InputDuration, InputPassword, KeyboardShortcut, Label, Link, List, ListItem, Mark, Markdown, Menu, MenuItem, MenuItemDescription, MenuItemLabel, MenuLabel, MenuRadioGroup, Meter, Modal, ModalCallout, ModalCallouts, PersistentFileAmountLimitValidator, Popover, PopoverAnchor, PopoverArrow, PopoverClose, PopoverCloseButton, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger, PreviewCard, ProgressBar, Radio, RadioCard, RadioCardImage, RadioGroup, RadioMenuItem, SaturationAndValuePicker, ScreenReaderOnly, ScrollArea, SegmentedControl, SegmentedControlItem, Select, SelectOption, SelectOptionGroup, Sidebar, SidebarButton, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupLabel, SidebarHeader, SidebarSearchInput, SidebarTitle, Slider, SplitButton, Stack, SubMenu, Switch, Table, TableBody, TableCell, TableFoot, TableHead, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Tag, Text, Thumbnail, ThumbnailBadge, ThumbnailCollage, Tooltip, UIProvider, ValueNameOrHexCode, ValueSwatch, WistiaLogo, buildTimeDuration, calculateContrast, coerceToBoolean, colorSchemeOptions, copyToClipboard, dateOnlyISOString, dateOnlyString, dateOnlyStringForSentence, dateOnlyStringNumeric, dateTime, dateTimeRounded, dateTimeString, dateTimeStringForSentence, dateTimeToDate, dateTimeToISO, dateToDateTime, dateUTCOffset, dayOfWeekString, getBackgroundGradient, getSemiRandomBackgroundGradient, iconSizeMap, isKeyboardKey, isUrl, mediaDurationString, mergeRefs, millisecondsToDurationISOString, monthDayStringNumeric, mq, parseDateString, sessionDurationString, stripExtension, timeAgoString, timeOnlyString, useActiveMq, useAriaLive, useBoolean, useClipboard, useElementObserver, useFilePicker, useFocusTrap, useForceUpdate, useFormState, useImperativeFilePicker, useIsHovered, useKey, useKeyPress, useLocalStorage, useLockBodyScroll, useMq, useOnClickOutside, usePreviousValue, useToast, useWindowSize, validateWithYup };
16607
15951
 
16608
15952
  //# sourceMappingURL=index.js.map