@wistia/ui 1.4.0 → 1.4.1-beta.31db558c.cd1a0e5

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 v1.4.0
3
+ * @license @wistia/ui v1.4.1-beta.31db558c.cd1a0e5
4
4
  *
5
5
  * Copyright (c) 2024-2026, Wistia, Inc. and its affiliates.
6
6
  *
@@ -13,12 +13,6 @@ 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";
15
15
  import { Toaster, toast } from "sonner";
16
- import { isPast } from "date-fns/isPast";
17
- import { isValid } from "date-fns/isValid";
18
- import { parse } from "date-fns/parse";
19
- import { startOfDay } from "date-fns/startOfDay";
20
- import { startOfToday } from "date-fns/startOfToday";
21
- import { startOfTomorrow } from "date-fns/startOfTomorrow";
22
16
  import { getValueAndUnit } from "polished";
23
17
  import { useFilePicker as useFilePicker$1, useImperativeFilePicker as useImperativeFilePicker$1 } from "use-file-picker";
24
18
  import { FileAmountLimitValidator, FileSizeValidator, FileTypeValidator, ImageDimensionsValidator, PersistentFileAmountLimitValidator } from "use-file-picker/validators";
@@ -736,17 +730,69 @@ const MONTH_NAMES = [
736
730
  "November",
737
731
  "December"
738
732
  ];
739
- const DATE_FORMATS = [
740
- "MMM d, yyyy",
741
- "MMM d",
742
- "MMMM d, yyyy",
743
- "MMMM d",
744
- "M/d/yyyy",
745
- "M/d",
746
- "M-d-yyyy",
747
- "M-d",
748
- "yyyy-MM-dd"
733
+ const MONTH_ABBREVIATIONS = MONTH_NAMES.map((name) => name.slice(0, 3));
734
+ const startOfDay = (date) => {
735
+ const result = new Date(date);
736
+ result.setHours(0, 0, 0, 0);
737
+ return result;
738
+ };
739
+ const startOfToday = () => startOfDay(/* @__PURE__ */ new Date());
740
+ const startOfTomorrow = () => {
741
+ const tomorrow = startOfToday();
742
+ tomorrow.setDate(tomorrow.getDate() + 1);
743
+ return tomorrow;
744
+ };
745
+ const monthIndexIn = (names, value) => names.findIndex((name) => name.toLowerCase() === value.toLowerCase());
746
+ const toNumberOrUndefined = (value) => value === void 0 ? void 0 : Number(value);
747
+ const DATE_PATTERNS = [
748
+ {
749
+ regex: new RegExp(`^(${MONTH_ABBREVIATIONS.join("|")}) (\\d{1,2})(?:, (\\d{1,4}))?$`, "i"),
750
+ toParts: ([, month, day, year]) => ({
751
+ monthIndex: monthIndexIn(MONTH_ABBREVIATIONS, month ?? ""),
752
+ day: Number(day),
753
+ year: toNumberOrUndefined(year)
754
+ })
755
+ },
756
+ {
757
+ regex: new RegExp(`^(${MONTH_NAMES.join("|")}) (\\d{1,2})(?:, (\\d{1,4}))?$`, "i"),
758
+ toParts: ([, month, day, year]) => ({
759
+ monthIndex: monthIndexIn(MONTH_NAMES, month ?? ""),
760
+ day: Number(day),
761
+ year: toNumberOrUndefined(year)
762
+ })
763
+ },
764
+ {
765
+ regex: /^(\d{1,2})\/(\d{1,2})(?:\/(\d{1,4}))?$/,
766
+ toParts: ([, month, day, year]) => ({
767
+ monthIndex: Number(month) - 1,
768
+ day: Number(day),
769
+ year: toNumberOrUndefined(year)
770
+ })
771
+ },
772
+ {
773
+ regex: /^(\d{1,2})-(\d{1,2})(?:-(\d{1,4}))?$/,
774
+ toParts: ([, month, day, year]) => ({
775
+ monthIndex: Number(month) - 1,
776
+ day: Number(day),
777
+ year: toNumberOrUndefined(year)
778
+ })
779
+ },
780
+ {
781
+ regex: /^(\d{1,4})-(\d{1,2})-(\d{1,2})$/,
782
+ toParts: ([, year, month, day]) => ({
783
+ monthIndex: Number(month) - 1,
784
+ day: Number(day),
785
+ year: Number(year)
786
+ })
787
+ }
749
788
  ];
789
+ const toValidDate = ({ monthIndex, day, year }, referenceDate) => {
790
+ if (year === 0) return null;
791
+ const resolvedYear = year ?? referenceDate.getFullYear();
792
+ const date = new Date(referenceDate.getFullYear(), 0, 1);
793
+ date.setFullYear(resolvedYear, monthIndex, day);
794
+ return date.getFullYear() === resolvedYear && date.getMonth() === monthIndex && date.getDate() === day ? date : null;
795
+ };
750
796
  const parseDateString = (str) => {
751
797
  const trimmed = str.trim();
752
798
  if (!trimmed) return null;
@@ -757,14 +803,19 @@ const parseDateString = (str) => {
757
803
  if (monthIndex !== -1) {
758
804
  const today = startOfToday();
759
805
  let date = new Date(today.getFullYear(), monthIndex, 1);
760
- if (isPast(date)) date = new Date(today.getFullYear() + 1, monthIndex, 1);
761
- return startOfDay(date);
806
+ if (date.getTime() < Date.now()) date = new Date(today.getFullYear() + 1, monthIndex, 1);
807
+ return date;
762
808
  }
763
809
  const referenceDate = /* @__PURE__ */ new Date();
764
- const parsedDate = DATE_FORMATS.map((fmt) => parse(trimmed, fmt, referenceDate)).find((date) => isValid(date));
765
- if (parsedDate !== void 0) return startOfDay(parsedDate);
810
+ for (const { regex, toParts } of DATE_PATTERNS) {
811
+ const match = trimmed.match(regex);
812
+ if (match) {
813
+ const parsed = toValidDate(toParts(match), referenceDate);
814
+ if (parsed !== null) return parsed;
815
+ }
816
+ }
766
817
  const native = new Date(trimmed);
767
- if (isValid(native)) return startOfDay(native);
818
+ if (!Number.isNaN(native.getTime())) return startOfDay(native);
768
819
  return null;
769
820
  };
770
821
  //#endregion
@@ -811,7 +862,7 @@ const getLocalDateParts = (date) => {
811
862
  //#region src/helpers/dateTime/differenceInCalendarDays.ts
812
863
  /**
813
864
  * Returns the difference in calendar days between two dates in local time,
814
- * ignoring the time of day. Essentially replicates date-fns/differenceInCalendarDays.
865
+ * ignoring the time of day.
815
866
  *
816
867
  * @param {Date|number} dateLeft - The later date (or timestamp).
817
868
  * @param {Date|number} dateRight - The earlier date (or timestamp).
@@ -1426,7 +1477,7 @@ const useFocusTrap = (active = true, options = {}) => {
1426
1477
  setTimeout(() => {
1427
1478
  if (node.ownerDocument) processNode(node);
1428
1479
  if (!node.ownerDocument) console.warn("[useFocusTrap]: The focus trap is not part of the DOM yet, so it is unable to correctly set focus. Make sure to render the ref node.", node);
1429
- });
1480
+ }, 0);
1430
1481
  ref.current = node;
1431
1482
  } else ref.current = null;
1432
1483
  }, [
@@ -5347,6 +5398,9 @@ const IconComponent = ({ useInlineStyles = true, invertColor, colorScheme = "inh
5347
5398
  });
5348
5399
  };
5349
5400
  IconComponent.displayName = "Icon_UI";
5401
+ /**
5402
+ * An `Icon` displays a visual symbol to represent an action, concept, or entity, rendering the SVG registered for its `type` at a fixed or responsive `size`.
5403
+ */
5350
5404
  const Icon = IconComponent;
5351
5405
  //#endregion
5352
5406
  //#region src/components/Link/Link.tsx
@@ -5928,6 +5982,9 @@ const StyledImage = styled.img`
5928
5982
  object-fit: ${({ $objFit }) => $objFit};
5929
5983
  object-position: ${({ $objPosition }) => $objPosition};
5930
5984
  `;
5985
+ /**
5986
+ * An `Image` displays a photo or graphic inline, with controls for border radius, object fit, and how it fills its container. It lazy-loads by default via the native `loading` attribute.
5987
+ */
5931
5988
  const Image = ({ src, alt, borderRadius, fill: fillContainer = false, fit: objFit, loading = "lazy", position: objPosition, onLoad, onError, ...props }) => /* @__PURE__ */ jsx(StyledImage, {
5932
5989
  $borderRadius: borderRadius,
5933
5990
  $fillContainer: fillContainer,
@@ -7285,6 +7342,9 @@ const StyledHiddenCheckboxInput = styled.input`
7285
7342
  ${visuallyHiddenStyle}
7286
7343
  position: relative;
7287
7344
  `;
7345
+ /**
7346
+ * A `Checkbox` lets users select one or more options from a set, or toggle a single option on or off. It supports both standalone controlled usage and grouped usage via `CheckboxGroup`.
7347
+ */
7288
7348
  const Checkbox = ({ checked, disabled = false, id, label, description, name, onChange, size = "md", value, required = false, hideLabel = false, ref, ...props }) => {
7289
7349
  const generatedId = useId();
7290
7350
  const computedId = isNonEmptyString(id) ? id : `wistia-ui-checkbox-${generatedId}`;
@@ -8550,6 +8610,9 @@ const StyledSwitchThumb = styled.div`
8550
8610
  const StyledHiddenSwitchInput = styled.input`
8551
8611
  ${visuallyHiddenStyle}
8552
8612
  `;
8613
+ /**
8614
+ * A `Switch` allows users to toggle between two mutually exclusive states, such as on and off.
8615
+ */
8553
8616
  const Switch = ({ align = "left", checked, disabled = false, id, label, description, name, onChange, size = "md", value, required = false, hideLabel = false, ref, ...props }) => {
8554
8617
  const generatedId = useId();
8555
8618
  const computedId = isNonEmptyString(id) ? id : `wistia-ui-switch-${generatedId}`;
@@ -9766,6 +9829,9 @@ const defaultDisplayValues = (values, onRemove) => {
9766
9829
  onClickRemoveLabel: `Remove ${selectedValue}`
9767
9830
  }, selectedValue));
9768
9831
  };
9832
+ /**
9833
+ * A `Combobox` is a searchable single- or multi-select input. Users can filter options by typing, and selected values in multi-select mode are displayed as removable `Tag` components.
9834
+ */
9769
9835
  const Combobox = ({ placeholder, value, onChange, searchValue, onSearchValueChange, displayValues = defaultDisplayValues, children, flipPopover = true, fullWidth = true }) => {
9770
9836
  const [isOpen, setIsOpen] = useState(false);
9771
9837
  const [isPending, startTransition] = useTransition();
@@ -9991,13 +10057,13 @@ const ContextMenuPopup = styled(Menu$1.Popup)`
9991
10057
  ${menuContentCss}
9992
10058
  outline: none;
9993
10059
  `;
10060
+ const NOOP$4 = () => null;
9994
10061
  /**
9995
10062
  * The ContextMenu is an extended implementation of the [Menu](/components/Menu) component that allows for right-click context menus.
9996
10063
  * It can be used in two ways:
9997
10064
  * 1. By providing a `triggerRef`, which will render the menu when the referenced element is right-clicked.
9998
10065
  * 2. By providing a `position` prop, which will render the menu at the specified coordinates.
9999
10066
  */
10000
- const NOOP$4 = () => null;
10001
10067
  const ContextMenu = ({ position, triggerRef, children, side = "bottom", onRequestClose = NOOP$4, compact = false }) => {
10002
10068
  const [isRightClicked, setIsRightClicked] = useState(false);
10003
10069
  const [menuPosition, setMenuPosition] = useState(position ?? {
@@ -12506,6 +12572,9 @@ const GridComponent = ({ ref, children, columns = "auto", minChildWidth = "auto"
12506
12572
  children
12507
12573
  });
12508
12574
  };
12575
+ /**
12576
+ * A `Grid` arranges its children in a CSS grid layout with configurable `columns`, `gap`, and responsive breakpoint support.
12577
+ */
12509
12578
  const Grid = makePolymorphic(GridComponent);
12510
12579
  //#endregion
12511
12580
  //#region src/components/PreviewCard/PreviewCard.tsx
@@ -13514,6 +13583,9 @@ const keyToString = (key) => {
13514
13583
  default: return key.toUpperCase();
13515
13584
  }
13516
13585
  };
13586
+ /**
13587
+ * A `KeyboardShortcut` displays one or more keyboard keys with an optional label, rendering each key in a styled `kbd` element. Platform-specific keys like `Cmd`/`Meta` are rendered with the appropriate symbol for the detected operating system.
13588
+ */
13517
13589
  const KeyboardShortcut = ({ label, keyboardKeys, fullWidth = false, ...otherProps }) => /* @__PURE__ */ jsxs(StyledKeyboardShortcut, {
13518
13590
  $fullWidth: fullWidth,
13519
13591
  ...otherProps,
@@ -13634,6 +13706,9 @@ const renderListFromArray = (listItems, variant, otherProps) => {
13634
13706
  return /* @__PURE__ */ jsx(ListItem, { children: item }, key);
13635
13707
  }), variant, otherProps);
13636
13708
  };
13709
+ /**
13710
+ * A `List` groups related items in bulleted, ordered, or separator-based layouts, composed with `ListItem` children or a flat/nested `items` array.
13711
+ */
13637
13712
  const List = ({ children, items, variant, ...otherProps }) => {
13638
13713
  const listVariant = variant ?? "bullets";
13639
13714
  if (isNotNil(children)) {
@@ -14749,6 +14824,9 @@ const StyledRadioInput = styled.div`
14749
14824
  const StyledHiddenRadioInput = styled.input`
14750
14825
  ${visuallyHiddenStyle}
14751
14826
  `;
14827
+ /**
14828
+ * A `Radio` lets users select exactly one option from a visible set of choices.
14829
+ */
14752
14830
  const Radio = ({ checked, disabled = false, id, label, description, name, onChange, size = "md", value = "false", required = false, hideLabel = false, ref, ...props }) => {
14753
14831
  const generatedId = useId();
14754
14832
  const computedId = isNonEmptyString(id) ? id : `wistia-ui-radio-${generatedId}`;
@@ -15032,6 +15110,9 @@ const RadioCardDefaultLayout = ({ icon, label, description, showIndicator = true
15032
15110
  RadioCardDefaultLayout.displayName = "RadioCardDefaultLayout_UI";
15033
15111
  //#endregion
15034
15112
  //#region src/components/RadioCard/RadioCard.tsx
15113
+ /**
15114
+ * A `RadioCard` lets users select exactly one option from a set of visually rich, card-based choices.
15115
+ */
15035
15116
  const RadioCard = ({ icon, label, description, showIndicator, isGated, children, ref, ...rootProps }) => {
15036
15117
  return /* @__PURE__ */ jsx(RadioCardRoot, {
15037
15118
  ref,
@@ -15652,6 +15733,9 @@ const StyledSidebar = styled.nav`
15652
15733
  overflow: hidden;
15653
15734
  border-right: 1px solid var(--wui-color-border);
15654
15735
  `;
15736
+ /**
15737
+ * The `Sidebar` provides vertical navigation with a composable header, scrollable content area, and footer.
15738
+ */
15655
15739
  const Sidebar = ({ children, ref, ...props }) => {
15656
15740
  return /* @__PURE__ */ jsx(StyledSidebar, {
15657
15741
  ref,
@@ -15895,6 +15979,9 @@ const StyledSliderThumb = styled(Slider$1.Thumb)`
15895
15979
  }
15896
15980
  `;
15897
15981
  const DEFAULT_INITIAL_VALUE = [0];
15982
+ /**
15983
+ * A `Slider` lets users select a value or range from a continuous scale via a draggable thumb.
15984
+ */
15898
15985
  const Slider = ({ "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, disabled = false, initialValue = DEFAULT_INITIAL_VALUE, max = 100, min = 0, onChange, onValueCommitted, step = 1, value, "data-testid": dataTestId = "ui-slider", ...otherProps }) => {
15899
15986
  if (!(isNonEmptyString(ariaLabel) || isNonEmptyString(ariaLabelledby))) throw new Error("UI Slider: Sliders should have an accessible name. Add a label using the aria-label or aria-labelledby prop.");
15900
15987
  const values = value ?? initialValue;
@@ -16122,6 +16209,9 @@ const useTabsValue = () => {
16122
16209
  };
16123
16210
  //#endregion
16124
16211
  //#region src/components/Tabs/Tabs.tsx
16212
+ /**
16213
+ * The `Tabs` component lets users switch between panels of content. It is composed from the `TabsList`, `TabsTrigger`, and `TabsContent` subcomponents.
16214
+ */
16125
16215
  const Tabs = ({ children, value: valueProp, onValueChange: onValueChangeProp, defaultValue, ...props }) => {
16126
16216
  const [internalValue, setInternalValue] = useState(valueProp ?? defaultValue);
16127
16217
  const isControlled = isNotNil(valueProp);