@wistia/ui 0.26.0 → 0.26.1

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.0
3
+ * @license @wistia/ui v0.26.1
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, useLayoutEffect, useMemo, useRef, useState, useTransition } from "react";
11
+ import { Children, cloneElement, createContext, forwardRef, 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, 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";
@@ -1809,7 +1809,7 @@ const Action = ({ actionButton }) => {
1809
1809
  /**
1810
1810
  * A Toast is a short, non-intrusive notification message that briefly appears on the screen to provide feedback to the user.
1811
1811
  *
1812
- * This page is for screenshots only. For usage, see the [`useToast`](../?path=/docs/hooks-usetoast--docs) hook.
1812
+ * This page is for screenshots only. For usage, see the [`useToast`](/hooks/useToast) hook.
1813
1813
  */
1814
1814
  const Toast = ({ action, message, colorScheme = "inherit", icon, ...props }) => {
1815
1815
  return /* @__PURE__ */ jsxs(StyledToast, {
@@ -5694,7 +5694,7 @@ const StyledLabel$3 = styled.span`
5694
5694
  text-align: left;
5695
5695
  `;
5696
5696
  /**
5697
- * Action Button component is used as one of a group of main actions on the page. It composes [Button]().
5697
+ * Action Button component is used as one of a group of main actions on the page. It composes [Button](/components/Button).
5698
5698
  */
5699
5699
  const ActionButton = forwardRef(({ icon, colorScheme = "standard", disabled = false, forceState, hoverColorScheme, variant = "default", children, ...props }, ref) => {
5700
5700
  const resolvedHoverColorScheme = hoverColorScheme ?? (variant === "gated" ? "purple" : "inherit");
@@ -6513,8 +6513,8 @@ const StyledButton$1 = styled(Button)`
6513
6513
  line-height: 1;
6514
6514
  `;
6515
6515
  /**
6516
- * IconButton behaves like a [Button](?path=/docs/components-button--docs),
6517
- * but only accepts an [Icon](?path=/docs/components-icon--docs) as a child.
6516
+ * IconButton behaves like a [Button](/components/Button),
6517
+ * but only accepts an [Icon](/components/Icon) as a child.
6518
6518
  */
6519
6519
  const IconButton = forwardRef(({ children, label, size = "md", ...props }, ref) => {
6520
6520
  const responsiveSize = useResponsiveProp(size);
@@ -6632,7 +6632,7 @@ const StyledBreadcrumbs = styled.nav`
6632
6632
  const BUFFER_WIDTH = 10;
6633
6633
  /**
6634
6634
  * Used to display a screens's location within a hierarchical structure. Each `Breadcrumb` is a composed version of
6635
- * [Button](?path=/docs/components-button--docs). See it's documentation for additional functionality.
6635
+ * [Button](/components/Button). See it's documentation for additional functionality.
6636
6636
  */
6637
6637
  const Breadcrumbs = ({ children, ...props }) => {
6638
6638
  const { isXsAndDown } = useMq();
@@ -6719,7 +6719,7 @@ const prominenceMap = {
6719
6719
  };
6720
6720
  /**
6721
6721
  * A flexible container component that groups related content with consistent spacing and styling.
6722
- * Cards can be used to visually distinguish content sections. Extends the [Box component.](https://wistia.github.io/vhs/storybook-ui/?path=/docs/components-box--docs)
6722
+ * Cards can be used to visually distinguish content sections. Extends the [Box component.](/components/Box)
6723
6723
  */
6724
6724
  const Card = ({ children, border = false, borderRadius = "border-radius-04", colorScheme = "inherit", direction = "column", gap = "space-03", height, paddingSize = "space-04", prominence = "secondary", width, ...props }) => /* @__PURE__ */ jsx(StyledCard$1, {
6725
6725
  $backgroundColor: prominenceMap[prominence].backgroundColor,
@@ -7978,7 +7978,7 @@ const VISUAL_OFFSET = 6;
7978
7978
  const ARROW_EXTENSION = 6;
7979
7979
  /**
7980
7980
  * Tooltips are a way to provide additional information or context to a user when they hover or focus an element.
7981
- * For elements that are not interactive, consider using a [Popover]() instead.
7981
+ * For elements that are not interactive, consider using a [Popover](/components/Popover) instead.
7982
7982
  *
7983
7983
  */
7984
7984
  const Tooltip = ({ delay = 500, direction = "top", content, children, forceOpen, hideArrow = false, container = null }) => {
@@ -8727,7 +8727,7 @@ const StyledInputContainer = styled.div`
8727
8727
  }
8728
8728
  `;
8729
8729
  /**
8730
- * Capture user input with a text field. Should be used within a [Form]() and [FormField]().
8730
+ * Capture user input with a text field. Should be used within a [Form](/components/Form) and [FormField](/components/FormField).
8731
8731
  */
8732
8732
  const Input = forwardRef(({ fullWidth = true, fullHeight = false, monospace = false, type = "text", autoSelect = false, leftIcon, rightIcon, ...props }, externalRef) => {
8733
8733
  const internalRef = useRef(null);
@@ -9826,7 +9826,7 @@ const ContextMenuPopup = styled(Menu$1.Popup)`
9826
9826
  outline: none;
9827
9827
  `;
9828
9828
  /**
9829
- * The ContextMenu is an extended implementation of the [Menu]() component that allows for right-click context menus.
9829
+ * The ContextMenu is an extended implementation of the [Menu](/components/Menu) component that allows for right-click context menus.
9830
9830
  * It can be used in two ways:
9831
9831
  * 1. By providing a `triggerRef`, which will render the menu when the referenced element is right-clicked.
9832
9832
  * 2. By providing a `position` prop, which will render the menu at the specified coordinates.
@@ -10217,7 +10217,7 @@ DataListItem.displayName = "DataListItem_UI";
10217
10217
  //#endregion
10218
10218
  //#region src/components/DataList/DataListItemLabel.tsx
10219
10219
  /**
10220
- * The label of the `DataListItem`. Extends the [Text]() component.
10220
+ * The label of the `DataListItem`. Extends the [Text](/components/Text) component.
10221
10221
  */
10222
10222
  const DataListItemLabel = (props) => {
10223
10223
  return /* @__PURE__ */ jsx(Text, {
@@ -10230,7 +10230,7 @@ DataListItemLabel.displayName = "DataListItemLabel_UI";
10230
10230
  //#endregion
10231
10231
  //#region src/components/DataList/DataListItemValue.tsx
10232
10232
  /**
10233
- * The value of the `DataListItem`. Extends the [Text]() component.
10233
+ * The value of the `DataListItem`. Extends the [Text](/components/Text) component.
10234
10234
  */
10235
10235
  const DataListItemValue = (props) => {
10236
10236
  return /* @__PURE__ */ jsx(Text, {
@@ -11444,6 +11444,711 @@ const InputClickToCopy = forwardRef(({ value, onCopy, disabled = false, ...props
11444
11444
  });
11445
11445
  InputClickToCopy.displayName = "InputClickToCopy_UI";
11446
11446
  //#endregion
11447
+ //#region src/components/InputDuration/constants.ts
11448
+ const SECONDS_PER_MINUTE = 60;
11449
+ const MINUTES_PER_HOUR = 60;
11450
+ const MAX_MINUTES_UNIT_VALUE = SECONDS_PER_MINUTE - 1;
11451
+ const SECONDS_PER_TEN_MINUTES = SECONDS_PER_MINUTE * 10;
11452
+ const SECONDS_PER_HOUR = SECONDS_PER_MINUTE * MINUTES_PER_HOUR;
11453
+ const TEN_HOURS = SECONDS_PER_HOUR * 10;
11454
+ const ONE_HUNDREDTH_OF_A_SECOND = .01;
11455
+ const MAX_TIME = TEN_HOURS - ONE_HUNDREDTH_OF_A_SECOND;
11456
+ const NUMBER_STRING_SET = new Set([
11457
+ "0",
11458
+ "1",
11459
+ "2",
11460
+ "3",
11461
+ "4",
11462
+ "5",
11463
+ "6",
11464
+ "7",
11465
+ "8",
11466
+ "9"
11467
+ ]);
11468
+ //#endregion
11469
+ //#region src/components/InputDuration/durationFormatting.ts
11470
+ const hoursFormatter = new Intl.NumberFormat("en-US", {
11471
+ maximumFractionDigits: 0,
11472
+ useGrouping: false
11473
+ });
11474
+ const minutesFormatter = new Intl.NumberFormat("en-US", {
11475
+ maximumFractionDigits: 0,
11476
+ minimumIntegerDigits: 1
11477
+ });
11478
+ const tensOfMinutesFormatter = new Intl.NumberFormat("en-US", {
11479
+ maximumFractionDigits: 0,
11480
+ minimumIntegerDigits: 2
11481
+ });
11482
+ const getSecondsFormatter = (decimalUnitLength) => {
11483
+ return new Intl.NumberFormat("en-US", {
11484
+ maximumFractionDigits: decimalUnitLength,
11485
+ minimumFractionDigits: decimalUnitLength,
11486
+ minimumIntegerDigits: 2
11487
+ });
11488
+ };
11489
+ const isValidHour = (number) => Number.isInteger(number) && number >= 0;
11490
+ const isValidMinute = (number) => Number.isInteger(number) && number >= 0 && number <= MAX_MINUTES_UNIT_VALUE;
11491
+ const isValidSecond = (number) => Number.isFinite(number) && number >= 0 && number < SECONDS_PER_MINUTE;
11492
+ const durationValueToSeconds = (formattedTimeString) => {
11493
+ const segments = formattedTimeString.split(":");
11494
+ if (segments.length <= 3) {
11495
+ const [seconds = 0, minutes = 0, hours = 0] = segments.reverse().map(Number);
11496
+ if (isValidSecond(seconds) && isValidMinute(minutes) && isValidHour(hours)) return hours * SECONDS_PER_HOUR + minutes * SECONDS_PER_MINUTE + seconds;
11497
+ }
11498
+ throw new SyntaxError(`Invalid time (${formattedTimeString})`);
11499
+ };
11500
+ const getFormattedHours = ({ hours, maxUnit }) => {
11501
+ if (maxUnit !== "hours") return null;
11502
+ return hoursFormatter.format(hours);
11503
+ };
11504
+ const getFormattedMinutes = ({ minutes, maxUnit }) => {
11505
+ if (maxUnit === "seconds") return null;
11506
+ if (maxUnit === "minutes") return minutesFormatter.format(minutes);
11507
+ return tensOfMinutesFormatter.format(minutes);
11508
+ };
11509
+ const getFormattedSeconds = ({ seconds, decimalUnitLength }) => {
11510
+ return getSecondsFormatter(decimalUnitLength).format(seconds);
11511
+ };
11512
+ const convertSecondsToHMSUnits = (durationInSeconds) => {
11513
+ const wholeSeconds = Math.trunc(durationInSeconds);
11514
+ const fractionalSeconds = durationInSeconds - wholeSeconds;
11515
+ const { hours, minutes, seconds } = buildTimeDuration(wholeSeconds * milisecondsInSecond);
11516
+ return {
11517
+ hours,
11518
+ minutes,
11519
+ seconds: seconds + fractionalSeconds
11520
+ };
11521
+ };
11522
+ const secondsToDurationValue = ({ seconds: durationInSeconds, maxUnit, decimalUnitLength }) => {
11523
+ const { hours, minutes, seconds } = convertSecondsToHMSUnits(durationInSeconds);
11524
+ return [
11525
+ getFormattedHours({
11526
+ hours,
11527
+ maxUnit
11528
+ }),
11529
+ getFormattedMinutes({
11530
+ minutes,
11531
+ maxUnit
11532
+ }),
11533
+ getFormattedSeconds({
11534
+ seconds,
11535
+ decimalUnitLength
11536
+ })
11537
+ ].filter(Boolean).join(":");
11538
+ };
11539
+ const getMaxUnit = (seconds) => {
11540
+ if (seconds >= SECONDS_PER_HOUR) return "hours";
11541
+ if (seconds >= SECONDS_PER_TEN_MINUTES) return "tens-of-minutes";
11542
+ if (seconds >= SECONDS_PER_MINUTE) return "minutes";
11543
+ return "seconds";
11544
+ };
11545
+ const clampSeconds = ({ maxSeconds, minSeconds, seconds }) => {
11546
+ return Math.min(Math.max(seconds, minSeconds), maxSeconds);
11547
+ };
11548
+ const roundSeconds = (seconds, decimalUnitLength) => {
11549
+ const multiplier = decimalUnitLength === 0 ? 1 : 100;
11550
+ return Math.round(seconds * multiplier) / multiplier;
11551
+ };
11552
+ const getDurationBounds = ({ decimalUnitLength, maxSeconds, minSeconds }) => {
11553
+ const roundedMaxSeconds = roundSeconds(maxSeconds, decimalUnitLength);
11554
+ const roundedMinSeconds = roundSeconds(minSeconds, decimalUnitLength);
11555
+ if (roundedMaxSeconds < roundedMinSeconds) return {
11556
+ maxSeconds: roundedMaxSeconds,
11557
+ minSeconds: roundedMaxSeconds
11558
+ };
11559
+ return {
11560
+ maxSeconds: roundedMaxSeconds,
11561
+ minSeconds: roundedMinSeconds
11562
+ };
11563
+ };
11564
+ //#endregion
11565
+ //#region src/components/InputDuration/durationSelection.ts
11566
+ const getEmptySelectionRanges = () => ({
11567
+ hours: {
11568
+ start: null,
11569
+ end: null
11570
+ },
11571
+ "tens-of-minutes": {
11572
+ start: null,
11573
+ end: null
11574
+ },
11575
+ minutes: {
11576
+ start: null,
11577
+ end: null
11578
+ },
11579
+ seconds: {
11580
+ start: null,
11581
+ end: null
11582
+ },
11583
+ "hundredths-of-seconds": {
11584
+ start: null,
11585
+ end: null
11586
+ }
11587
+ });
11588
+ const getSegmentRange = ({ segment, offset }) => {
11589
+ return {
11590
+ nextOffset: offset + segment.length + 1,
11591
+ range: {
11592
+ start: offset,
11593
+ end: offset + segment.length
11594
+ }
11595
+ };
11596
+ };
11597
+ const getSecondsRanges = ({ segment, offset }) => {
11598
+ const decimalSeparatorIndex = segment.indexOf(".");
11599
+ const ranges = {
11600
+ seconds: {
11601
+ start: offset,
11602
+ end: offset + (decimalSeparatorIndex === -1 ? segment.length : decimalSeparatorIndex)
11603
+ },
11604
+ "hundredths-of-seconds": {
11605
+ start: null,
11606
+ end: null
11607
+ }
11608
+ };
11609
+ if (decimalSeparatorIndex !== -1 && decimalSeparatorIndex < segment.length - 1) ranges["hundredths-of-seconds"] = {
11610
+ start: offset + decimalSeparatorIndex + 1,
11611
+ end: offset + segment.length
11612
+ };
11613
+ return ranges;
11614
+ };
11615
+ const getSelectionRanges = ({ inputValue, maxUnit }) => {
11616
+ const segments = inputValue.split(":");
11617
+ let offset = 0;
11618
+ if (maxUnit === "hours") {
11619
+ const [hours = "", minutes = "", seconds = ""] = segments;
11620
+ const hoursResult = getSegmentRange({
11621
+ segment: hours,
11622
+ offset
11623
+ });
11624
+ offset = hoursResult.nextOffset;
11625
+ const minutesResult = getSegmentRange({
11626
+ segment: minutes,
11627
+ offset
11628
+ });
11629
+ offset = minutesResult.nextOffset;
11630
+ return {
11631
+ ...getEmptySelectionRanges(),
11632
+ hours: hoursResult.range,
11633
+ "tens-of-minutes": minutesResult.range,
11634
+ ...getSecondsRanges({
11635
+ segment: seconds,
11636
+ offset
11637
+ })
11638
+ };
11639
+ }
11640
+ if (maxUnit === "tens-of-minutes") {
11641
+ const [minutes = "", seconds = ""] = segments;
11642
+ const minutesResult = getSegmentRange({
11643
+ segment: minutes,
11644
+ offset
11645
+ });
11646
+ offset = minutesResult.nextOffset;
11647
+ return {
11648
+ ...getEmptySelectionRanges(),
11649
+ "tens-of-minutes": minutesResult.range,
11650
+ ...getSecondsRanges({
11651
+ segment: seconds,
11652
+ offset
11653
+ })
11654
+ };
11655
+ }
11656
+ if (maxUnit === "minutes") {
11657
+ const [minutes = "", seconds = ""] = segments;
11658
+ const minutesResult = getSegmentRange({
11659
+ segment: minutes,
11660
+ offset
11661
+ });
11662
+ offset = minutesResult.nextOffset;
11663
+ return {
11664
+ ...getEmptySelectionRanges(),
11665
+ minutes: minutesResult.range,
11666
+ ...getSecondsRanges({
11667
+ segment: seconds,
11668
+ offset
11669
+ })
11670
+ };
11671
+ }
11672
+ const [seconds = ""] = segments;
11673
+ return {
11674
+ ...getEmptySelectionRanges(),
11675
+ ...getSecondsRanges({
11676
+ segment: seconds,
11677
+ offset
11678
+ })
11679
+ };
11680
+ };
11681
+ //#endregion
11682
+ //#region src/components/InputDuration/durationUnits.ts
11683
+ const DURATION_UNITS = new Set([
11684
+ "hours",
11685
+ "hundredths-of-seconds",
11686
+ "minutes",
11687
+ "seconds",
11688
+ "tens-of-minutes"
11689
+ ]);
11690
+ const isDurationUnit = (unit) => {
11691
+ return isNotNil(unit) && DURATION_UNITS.has(unit);
11692
+ };
11693
+ //#endregion
11694
+ //#region src/components/InputDuration/useInputDuration.ts
11695
+ const useInputDuration = ({ decimalUnitLength, maxSeconds, minSeconds, onChangeValueInSeconds, valueInSeconds }) => {
11696
+ const [inputSelection, setInputSelection] = useState(null);
11697
+ const inputRef = useRef(null);
11698
+ const anyKeyIsDownRef = useRef(false);
11699
+ const selectedUnitRef = useRef(null);
11700
+ const secondDigitMightBeTypedNext = useRef(false);
11701
+ const { maxSeconds: roundedMaxSeconds, minSeconds: roundedMinSeconds } = useMemo(() => getDurationBounds({
11702
+ decimalUnitLength,
11703
+ maxSeconds,
11704
+ minSeconds
11705
+ }), [
11706
+ decimalUnitLength,
11707
+ maxSeconds,
11708
+ minSeconds
11709
+ ]);
11710
+ const maxUnit = useMemo(() => getMaxUnit(roundedMaxSeconds), [roundedMaxSeconds]);
11711
+ const boundedValueInSeconds = useMemo(() => clampSeconds({
11712
+ maxSeconds: roundedMaxSeconds,
11713
+ minSeconds: roundedMinSeconds,
11714
+ seconds: valueInSeconds
11715
+ }), [
11716
+ roundedMaxSeconds,
11717
+ roundedMinSeconds,
11718
+ valueInSeconds
11719
+ ]);
11720
+ const inputValue = useMemo(() => secondsToDurationValue({
11721
+ decimalUnitLength,
11722
+ maxUnit,
11723
+ seconds: boundedValueInSeconds
11724
+ }), [
11725
+ boundedValueInSeconds,
11726
+ decimalUnitLength,
11727
+ maxUnit
11728
+ ]);
11729
+ const selectionRanges = useMemo(() => getSelectionRanges({
11730
+ inputValue,
11731
+ maxUnit
11732
+ }), [inputValue, maxUnit]);
11733
+ const decimalStep = decimalUnitLength === 0 ? 0 : ONE_HUNDREDTH_OF_A_SECOND;
11734
+ const maybeSetInputValueAndTriggerOnChange = useCallback((candidateInputValue) => {
11735
+ const seconds = durationValueToSeconds(candidateInputValue);
11736
+ if (seconds < roundedMinSeconds || seconds > roundedMaxSeconds) return;
11737
+ onChangeValueInSeconds(seconds);
11738
+ }, [
11739
+ onChangeValueInSeconds,
11740
+ roundedMaxSeconds,
11741
+ roundedMinSeconds
11742
+ ]);
11743
+ const selectUnit = useCallback((unit) => {
11744
+ selectedUnitRef.current = unit;
11745
+ if (unit == null) {
11746
+ setInputSelection(null);
11747
+ return;
11748
+ }
11749
+ const { start, end } = selectionRanges[unit];
11750
+ if (start == null || end == null) {
11751
+ setInputSelection(null);
11752
+ return;
11753
+ }
11754
+ inputRef.current?.setSelectionRange(start, end);
11755
+ setInputSelection({
11756
+ start,
11757
+ end
11758
+ });
11759
+ }, [selectionRanges]);
11760
+ const getFirstSelectableUnit = useCallback(() => {
11761
+ const result = Object.entries(selectionRanges).find(([, { start, end }]) => {
11762
+ return start !== null && end !== null;
11763
+ })?.[0] ?? null;
11764
+ if (!isDurationUnit(result)) return null;
11765
+ return result;
11766
+ }, [selectionRanges]);
11767
+ const selectFirstUnit = useCallback(() => {
11768
+ selectUnit(getFirstSelectableUnit());
11769
+ }, [getFirstSelectableUnit, selectUnit]);
11770
+ const getUnitForSelectionIndices = useCallback(({ selectionStart, selectionEnd }) => {
11771
+ const result = Object.entries(selectionRanges).find(([, { start, end }]) => {
11772
+ if (start === null || end === null) return false;
11773
+ return selectionStart >= start && selectionEnd <= end;
11774
+ })?.[0] ?? null;
11775
+ if (!isDurationUnit(result)) return null;
11776
+ return result;
11777
+ }, [selectionRanges]);
11778
+ const selectedUnit = useMemo(() => {
11779
+ if (inputSelection == null) return null;
11780
+ const { start: selectionStart, end: selectionEnd } = inputSelection;
11781
+ const foundUnit = getUnitForSelectionIndices({
11782
+ selectionStart,
11783
+ selectionEnd
11784
+ });
11785
+ if (!isDurationUnit(foundUnit)) return null;
11786
+ return foundUnit;
11787
+ }, [getUnitForSelectionIndices, inputSelection]);
11788
+ const selectUnitToRight = useCallback(() => {
11789
+ if (inputSelection == null) return;
11790
+ const unitToSelect = getUnitForSelectionIndices({
11791
+ selectionEnd: inputSelection.end + 1,
11792
+ selectionStart: inputSelection.end + 1
11793
+ });
11794
+ if (unitToSelect == null) return;
11795
+ selectedUnitRef.current = unitToSelect;
11796
+ requestAnimationFrame(() => {
11797
+ selectUnit(unitToSelect);
11798
+ });
11799
+ }, [
11800
+ getUnitForSelectionIndices,
11801
+ inputSelection,
11802
+ selectUnit
11803
+ ]);
11804
+ const selectUnitToLeft = useCallback(() => {
11805
+ if (inputSelection == null) return;
11806
+ const unitToSelect = getUnitForSelectionIndices({
11807
+ selectionEnd: inputSelection.start - 1,
11808
+ selectionStart: inputSelection.start - 1
11809
+ });
11810
+ if (unitToSelect == null) return;
11811
+ selectUnit(unitToSelect);
11812
+ }, [
11813
+ getUnitForSelectionIndices,
11814
+ inputSelection,
11815
+ selectUnit
11816
+ ]);
11817
+ const incrementBySeconds = useCallback((secondsToIncrementBy) => {
11818
+ const newValue = durationValueToSeconds(inputValue) + secondsToIncrementBy;
11819
+ if (newValue > roundedMaxSeconds) return;
11820
+ maybeSetInputValueAndTriggerOnChange(secondsToDurationValue({
11821
+ decimalUnitLength,
11822
+ maxUnit,
11823
+ seconds: newValue
11824
+ }));
11825
+ }, [
11826
+ decimalUnitLength,
11827
+ inputValue,
11828
+ maxUnit,
11829
+ maybeSetInputValueAndTriggerOnChange,
11830
+ roundedMaxSeconds
11831
+ ]);
11832
+ const decrementBySeconds = useCallback((secondsToDecrementBy) => {
11833
+ const newValue = durationValueToSeconds(inputValue) - secondsToDecrementBy;
11834
+ if (newValue < roundedMinSeconds) return;
11835
+ maybeSetInputValueAndTriggerOnChange(secondsToDurationValue({
11836
+ decimalUnitLength,
11837
+ maxUnit,
11838
+ seconds: newValue
11839
+ }));
11840
+ }, [
11841
+ decimalUnitLength,
11842
+ inputValue,
11843
+ maxUnit,
11844
+ maybeSetInputValueAndTriggerOnChange,
11845
+ roundedMinSeconds
11846
+ ]);
11847
+ const incrementSelectedUnit = useCallback(() => {
11848
+ if (selectedUnit == null) return;
11849
+ if (selectedUnit === "hours") {
11850
+ incrementBySeconds(SECONDS_PER_HOUR);
11851
+ return;
11852
+ }
11853
+ if (selectedUnit === "minutes" || selectedUnit === "tens-of-minutes") {
11854
+ incrementBySeconds(SECONDS_PER_MINUTE);
11855
+ return;
11856
+ }
11857
+ if (selectedUnit === "seconds") {
11858
+ incrementBySeconds(1);
11859
+ return;
11860
+ }
11861
+ incrementBySeconds(decimalStep);
11862
+ }, [
11863
+ decimalStep,
11864
+ incrementBySeconds,
11865
+ selectedUnit
11866
+ ]);
11867
+ const decrementSelectedUnit = useCallback(() => {
11868
+ if (selectedUnit == null) return;
11869
+ if (selectedUnit === "hours") {
11870
+ decrementBySeconds(SECONDS_PER_HOUR);
11871
+ return;
11872
+ }
11873
+ if (selectedUnit === "minutes" || selectedUnit === "tens-of-minutes") {
11874
+ decrementBySeconds(SECONDS_PER_MINUTE);
11875
+ return;
11876
+ }
11877
+ if (selectedUnit === "seconds") {
11878
+ decrementBySeconds(1);
11879
+ return;
11880
+ }
11881
+ decrementBySeconds(decimalStep);
11882
+ }, [
11883
+ decimalStep,
11884
+ decrementBySeconds,
11885
+ selectedUnit
11886
+ ]);
11887
+ const setSelectedUnitToZero = useCallback(() => {
11888
+ if (selectedUnit == null || inputSelection == null) return;
11889
+ const textToLeftOfSelection = inputValue.slice(0, inputSelection.start);
11890
+ const textToRightOfSelection = inputValue.slice(inputSelection.end);
11891
+ if (selectedUnit === "hours") {
11892
+ maybeSetInputValueAndTriggerOnChange(`0${textToRightOfSelection}`);
11893
+ selectUnitToRight();
11894
+ return;
11895
+ }
11896
+ if (selectedUnit === "tens-of-minutes") {
11897
+ maybeSetInputValueAndTriggerOnChange(`${textToLeftOfSelection}00${textToRightOfSelection}`);
11898
+ selectUnitToRight();
11899
+ return;
11900
+ }
11901
+ if (selectedUnit === "minutes") {
11902
+ maybeSetInputValueAndTriggerOnChange(`0${textToRightOfSelection}`);
11903
+ selectUnitToRight();
11904
+ return;
11905
+ }
11906
+ if (selectedUnit === "seconds") {
11907
+ maybeSetInputValueAndTriggerOnChange(`${textToLeftOfSelection}00${textToRightOfSelection}`);
11908
+ selectUnitToRight();
11909
+ return;
11910
+ }
11911
+ maybeSetInputValueAndTriggerOnChange(`${textToLeftOfSelection}${"0".repeat(decimalUnitLength)}`);
11912
+ selectUnitToRight();
11913
+ }, [
11914
+ decimalUnitLength,
11915
+ inputSelection,
11916
+ inputValue,
11917
+ maybeSetInputValueAndTriggerOnChange,
11918
+ selectUnitToRight,
11919
+ selectedUnit
11920
+ ]);
11921
+ const onInputSelect = useCallback((event) => {
11922
+ if (anyKeyIsDownRef.current) return;
11923
+ const { selectionStart, selectionEnd } = event.currentTarget;
11924
+ if (selectionStart === null || selectionEnd === null) return;
11925
+ selectUnit(getUnitForSelectionIndices({
11926
+ selectionStart,
11927
+ selectionEnd
11928
+ }));
11929
+ }, [getUnitForSelectionIndices, selectUnit]);
11930
+ const onFocus = useCallback(() => {
11931
+ const selectFirstUnitIfAllTextIsSelected = () => {
11932
+ const input = inputRef.current;
11933
+ if (input == null) return;
11934
+ if (input.selectionStart === 0 && input.selectionEnd === input.value.length) selectFirstUnit();
11935
+ };
11936
+ selectFirstUnitIfAllTextIsSelected();
11937
+ requestAnimationFrame(selectFirstUnitIfAllTextIsSelected);
11938
+ }, [selectFirstUnit]);
11939
+ const restoreSelectionToSelectedUnit = useCallback(() => {
11940
+ selectUnit(selectedUnitRef.current);
11941
+ }, [selectUnit]);
11942
+ useLayoutEffect(() => {
11943
+ if (inputRef.current == null || document.activeElement !== inputRef.current) return;
11944
+ selectUnit(selectedUnitRef.current);
11945
+ }, [inputValue, selectUnit]);
11946
+ const prepareForPossibleSecondDigit = useCallback(() => {
11947
+ secondDigitMightBeTypedNext.current = true;
11948
+ restoreSelectionToSelectedUnit();
11949
+ }, [restoreSelectionToSelectedUnit]);
11950
+ const onTypeNumber = useCallback((number) => {
11951
+ if (inputSelection == null) return;
11952
+ const selectedText = inputValue.slice(inputSelection.start, inputSelection.end);
11953
+ const textToLeftOfSelection = inputValue.slice(0, inputSelection.start);
11954
+ const textToRightOfSelection = inputValue.slice(inputSelection.end);
11955
+ if (selectedUnit === "hours") {
11956
+ maybeSetInputValueAndTriggerOnChange(`${number.toString()}${textToRightOfSelection}`);
11957
+ selectUnitToRight();
11958
+ return;
11959
+ }
11960
+ if (selectedUnit === "tens-of-minutes" && number >= SECONDS_PER_MINUTE / 10 && !secondDigitMightBeTypedNext.current) {
11961
+ maybeSetInputValueAndTriggerOnChange(`${textToLeftOfSelection}0${number.toString()}${textToRightOfSelection}`);
11962
+ secondDigitMightBeTypedNext.current = false;
11963
+ selectUnitToRight();
11964
+ return;
11965
+ }
11966
+ if (selectedUnit === "tens-of-minutes" && !secondDigitMightBeTypedNext.current) {
11967
+ maybeSetInputValueAndTriggerOnChange(`${textToLeftOfSelection}${number.toString()}0${textToRightOfSelection}`);
11968
+ prepareForPossibleSecondDigit();
11969
+ return;
11970
+ }
11971
+ if (selectedUnit === "tens-of-minutes" && secondDigitMightBeTypedNext.current) {
11972
+ maybeSetInputValueAndTriggerOnChange(`${textToLeftOfSelection}${selectedText[0]}${number.toString()}${textToRightOfSelection}`);
11973
+ secondDigitMightBeTypedNext.current = false;
11974
+ selectUnitToRight();
11975
+ return;
11976
+ }
11977
+ if (selectedUnit === "minutes") {
11978
+ maybeSetInputValueAndTriggerOnChange(`${number.toString()}${textToRightOfSelection}`);
11979
+ selectUnitToRight();
11980
+ return;
11981
+ }
11982
+ if (selectedUnit === "seconds" && number >= SECONDS_PER_MINUTE / 10 && !secondDigitMightBeTypedNext.current) {
11983
+ maybeSetInputValueAndTriggerOnChange(`${textToLeftOfSelection}0${number.toString()}${textToRightOfSelection}`);
11984
+ secondDigitMightBeTypedNext.current = false;
11985
+ selectUnitToRight();
11986
+ return;
11987
+ }
11988
+ if (selectedUnit === "seconds" && !secondDigitMightBeTypedNext.current) {
11989
+ maybeSetInputValueAndTriggerOnChange(`${textToLeftOfSelection}${number.toString()}0${textToRightOfSelection}`);
11990
+ prepareForPossibleSecondDigit();
11991
+ return;
11992
+ }
11993
+ if (selectedUnit === "seconds" && secondDigitMightBeTypedNext.current) {
11994
+ maybeSetInputValueAndTriggerOnChange(`${textToLeftOfSelection}${selectedText[0]}${number.toString()}${textToRightOfSelection}`);
11995
+ secondDigitMightBeTypedNext.current = false;
11996
+ selectUnitToRight();
11997
+ return;
11998
+ }
11999
+ if (selectedUnit === "hundredths-of-seconds" && !secondDigitMightBeTypedNext.current) {
12000
+ maybeSetInputValueAndTriggerOnChange(`${textToLeftOfSelection}${number.toString()}0`);
12001
+ prepareForPossibleSecondDigit();
12002
+ return;
12003
+ }
12004
+ if (selectedUnit === "hundredths-of-seconds" && secondDigitMightBeTypedNext.current) {
12005
+ maybeSetInputValueAndTriggerOnChange(`${textToLeftOfSelection}${selectedText[0]}${number.toString()}`);
12006
+ secondDigitMightBeTypedNext.current = false;
12007
+ restoreSelectionToSelectedUnit();
12008
+ }
12009
+ }, [
12010
+ inputSelection,
12011
+ inputValue,
12012
+ maybeSetInputValueAndTriggerOnChange,
12013
+ prepareForPossibleSecondDigit,
12014
+ restoreSelectionToSelectedUnit,
12015
+ selectUnitToRight,
12016
+ selectedUnit
12017
+ ]);
12018
+ return {
12019
+ inputRef,
12020
+ inputValue,
12021
+ onFocus,
12022
+ onInputSelect,
12023
+ onKeyDown: useCallback((event) => {
12024
+ anyKeyIsDownRef.current = true;
12025
+ if (inputRef.current == null || inputSelection == null) return;
12026
+ const { start: selectionStart, end: selectionEnd } = inputSelection;
12027
+ const { key } = event;
12028
+ if (key === "ArrowRight") {
12029
+ event.preventDefault();
12030
+ secondDigitMightBeTypedNext.current = false;
12031
+ selectUnitToRight();
12032
+ return;
12033
+ }
12034
+ if (key === "ArrowLeft") {
12035
+ event.preventDefault();
12036
+ secondDigitMightBeTypedNext.current = false;
12037
+ selectUnitToLeft();
12038
+ return;
12039
+ }
12040
+ if (key === "ArrowUp") {
12041
+ event.preventDefault();
12042
+ secondDigitMightBeTypedNext.current = false;
12043
+ incrementSelectedUnit();
12044
+ restoreSelectionToSelectedUnit();
12045
+ return;
12046
+ }
12047
+ if (key === "ArrowDown") {
12048
+ event.preventDefault();
12049
+ secondDigitMightBeTypedNext.current = false;
12050
+ decrementSelectedUnit();
12051
+ restoreSelectionToSelectedUnit();
12052
+ return;
12053
+ }
12054
+ if (key === "Tab" && !event.shiftKey && selectionEnd < inputRef.current.value.length) {
12055
+ event.preventDefault();
12056
+ secondDigitMightBeTypedNext.current = false;
12057
+ selectUnitToRight();
12058
+ return;
12059
+ }
12060
+ if (key === "Tab" && event.shiftKey && selectionStart > 0) {
12061
+ event.preventDefault();
12062
+ secondDigitMightBeTypedNext.current = false;
12063
+ selectUnitToLeft();
12064
+ return;
12065
+ }
12066
+ if (key === ":") {
12067
+ event.preventDefault();
12068
+ secondDigitMightBeTypedNext.current = false;
12069
+ selectUnitToRight();
12070
+ return;
12071
+ }
12072
+ if (key === "." && decimalUnitLength > 0) {
12073
+ event.preventDefault();
12074
+ secondDigitMightBeTypedNext.current = false;
12075
+ selectUnit("hundredths-of-seconds");
12076
+ return;
12077
+ }
12078
+ if (NUMBER_STRING_SET.has(key)) {
12079
+ event.preventDefault();
12080
+ onTypeNumber(Number(key));
12081
+ return;
12082
+ }
12083
+ if (key === "Backspace") {
12084
+ event.preventDefault();
12085
+ setSelectedUnitToZero();
12086
+ restoreSelectionToSelectedUnit();
12087
+ }
12088
+ }, [
12089
+ decimalUnitLength,
12090
+ decrementSelectedUnit,
12091
+ incrementSelectedUnit,
12092
+ inputSelection,
12093
+ onTypeNumber,
12094
+ restoreSelectionToSelectedUnit,
12095
+ selectUnit,
12096
+ selectUnitToLeft,
12097
+ selectUnitToRight,
12098
+ setSelectedUnitToZero
12099
+ ]),
12100
+ onKeyUp: useCallback(() => {
12101
+ anyKeyIsDownRef.current = false;
12102
+ }, [])
12103
+ };
12104
+ };
12105
+ //#endregion
12106
+ //#region src/components/InputDuration/InputDuration.tsx
12107
+ const EXTRA_INPUT_WIDTH_CH = 4;
12108
+ const getInputWidth = (inputValue) => {
12109
+ return `calc(${inputValue.length + EXTRA_INPUT_WIDTH_CH}ch + var(--wui-input-horizontal-padding) + var(--wui-input-horizontal-padding))`;
12110
+ };
12111
+ /**
12112
+ * InputDuration lets users edit a duration value using a keyboard-friendly time input.
12113
+ */
12114
+ const InputDuration = forwardRef(({ decimalUnitLength = 2, disabled = false, fullWidth = false, maxSeconds = MAX_TIME, minSeconds = 0, onChangeValueInSeconds, valueInSeconds, onFocus, style, ...props }, externalRef) => {
12115
+ const { inputRef, inputValue, onFocus: onInputFocus, onInputSelect, onKeyDown, onKeyUp } = useInputDuration({
12116
+ decimalUnitLength,
12117
+ maxSeconds,
12118
+ minSeconds,
12119
+ onChangeValueInSeconds,
12120
+ valueInSeconds
12121
+ });
12122
+ const inputWidth = fullWidth ? void 0 : getInputWidth(inputValue);
12123
+ useImperativeHandle(externalRef, () => inputRef.current);
12124
+ const handleFocus = (event) => {
12125
+ onInputFocus();
12126
+ onFocus?.(event);
12127
+ };
12128
+ return /* @__PURE__ */ jsx(Input, {
12129
+ ...props,
12130
+ ref: inputRef,
12131
+ disabled,
12132
+ fullWidth,
12133
+ inputMode: "numeric",
12134
+ monospace: true,
12135
+ onChange: () => {},
12136
+ onFocus: handleFocus,
12137
+ onKeyDown,
12138
+ onKeyUp,
12139
+ onSelect: onInputSelect,
12140
+ style: {
12141
+ width: inputWidth,
12142
+ textAlign: "right",
12143
+ caretColor: "transparent",
12144
+ ...style
12145
+ },
12146
+ type: "text",
12147
+ value: inputValue
12148
+ });
12149
+ });
12150
+ InputDuration.displayName = "InputDuration_UI";
12151
+ //#endregion
11447
12152
  //#region src/components/InputPassword/InputPassword.tsx
11448
12153
  const StyledIconButton = styled(IconButton)`
11449
12154
  /* override size for icon button since prop gets changed by Input */
@@ -14157,7 +14862,7 @@ const StyledSplitButton = styled.span`
14157
14862
  }
14158
14863
  `;
14159
14864
  /**
14160
- * A SplitButton is an extension of [Button]() that adds a menu to the right hand side of the button.
14865
+ * A SplitButton is an extension of [Button](/components/Button) that adds a menu to the right hand side of the button.
14161
14866
  */
14162
14867
  const SplitButton = ({ children, menuLabel = "Select an option", menuIcon = /* @__PURE__ */ jsx(Icon, { type: "caret-down" }), menuItems, disabled = false, colorScheme = "inherit", variant = "solid", secondaryAction, size = "md", unstyled = false, menuProps = {}, ...props }) => {
14163
14868
  return /* @__PURE__ */ jsxs(StyledSplitButton, {
@@ -15029,6 +15734,6 @@ const WistiaLogo = ({ description, height = 100, hoverColor, href, iconOnly = fa
15029
15734
  };
15030
15735
  WistiaLogo.displayName = "WistiaLogo_UI";
15031
15736
  //#endregion
15032
- 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, 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, 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, iconSizeMap, isKeyboardKey, mediaDurationString, mergeRefs, millisecondsToDurationISOString, monthDayStringNumeric, mq, parseDateString, sessionDurationString, timeAgoString, timeOnlyString, useActiveMq, useAriaLive, useBoolean, useClipboard, useElementObserver, useFilePicker, useFocusTrap, useForceUpdate, useFormState, useImperativeFilePicker, useIsHovered, useKey, useKeyPress, useLocalStorage, useLockBodyScroll, useMq, useOnClickOutside, usePreviousValue, useToast, useWindowSize, validateWithYup };
15737
+ 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, 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, iconSizeMap, isKeyboardKey, mediaDurationString, mergeRefs, millisecondsToDurationISOString, monthDayStringNumeric, mq, parseDateString, sessionDurationString, timeAgoString, timeOnlyString, useActiveMq, useAriaLive, useBoolean, useClipboard, useElementObserver, useFilePicker, useFocusTrap, useForceUpdate, useFormState, useImperativeFilePicker, useIsHovered, useKey, useKeyPress, useLocalStorage, useLockBodyScroll, useMq, useOnClickOutside, usePreviousValue, useToast, useWindowSize, validateWithYup };
15033
15738
 
15034
15739
  //# sourceMappingURL=index.js.map