@sanity/ui 1.7.12 → 1.8.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.
@@ -57,6 +57,8 @@ export const ThemeProvider = cjs.ThemeProvider;
57
57
  export const Toast = cjs.Toast;
58
58
  export const ToastProvider = cjs.ToastProvider;
59
59
  export const Tooltip = cjs.Tooltip;
60
+ export const TooltipDelayGroupContext = cjs.TooltipDelayGroupContext;
61
+ export const TooltipDelayGroupProvider = cjs.TooltipDelayGroupProvider;
60
62
  export const Tree = cjs.Tree;
61
63
  export const TreeItem = cjs.TreeItem;
62
64
  export const VirtualList = cjs.VirtualList;
@@ -115,5 +117,6 @@ export const usePrefersReducedMotion = cjs.usePrefersReducedMotion;
115
117
  export const useRootTheme = cjs.useRootTheme;
116
118
  export const useTheme = cjs.useTheme;
117
119
  export const useToast = cjs.useToast;
120
+ export const useTooltipDelayGroup = cjs.useTooltipDelayGroup;
118
121
  export const useTree = cjs.useTree;
119
122
 
package/dist/index.d.ts CHANGED
@@ -627,6 +627,16 @@ export declare function createColorTheme(
627
627
  /** @internal */
628
628
  export declare type CSSObject = StyledObject<any>
629
629
 
630
+ /**
631
+ * @beta
632
+ */
633
+ export declare type Delay =
634
+ | number
635
+ | Partial<{
636
+ open: number
637
+ close: number
638
+ }>
639
+
630
640
  /**
631
641
  * @public
632
642
  */
@@ -2990,6 +3000,47 @@ export declare const Tooltip: ForwardRefExoticComponent<
2990
3000
  RefAttributes<HTMLDivElement>
2991
3001
  >
2992
3002
 
3003
+ /**
3004
+ * @beta
3005
+ */
3006
+ export declare const TooltipDelayGroupContext: React.Context<TooltipDelayGroupContextValue | null>
3007
+
3008
+ /**
3009
+ * @beta
3010
+ */
3011
+ export declare interface TooltipDelayGroupContextValue {
3012
+ isGroupActive: boolean
3013
+ setIsGroupActive: (nextState: React.SetStateAction<boolean>, delay?: number | undefined) => void
3014
+ setOpenTooltipId: (nextId: string | null, delay?: number | undefined) => void
3015
+ openDelay: number
3016
+ closeDelay: number
3017
+ openTooltipId: string | null
3018
+ }
3019
+
3020
+ /**
3021
+ * @public
3022
+ * Provides context for a group of tooltip elements that should share a delay
3023
+ * which temporarily becomes 1 ms after the first floating element of the group opens.
3024
+ */
3025
+ export declare function TooltipDelayGroupProvider(
3026
+ props: TooltipDelayGroupProviderProps,
3027
+ ): React.ReactElement
3028
+
3029
+ /**
3030
+ * @public
3031
+ * */
3032
+ export declare interface TooltipDelayGroupProviderProps {
3033
+ children?: React.ReactNode
3034
+ /**
3035
+ * @public Handles the delays to open or close a tooltip inside a group
3036
+ *
3037
+ * If only a `number` is passed, it will be used for both opening and closing.
3038
+ *
3039
+ * If an object `{open: number; close:number}` is passed, it can be used to set different delays for each action.
3040
+ */
3041
+ delay: Delay
3042
+ }
3043
+
2993
3044
  /**
2994
3045
  * @public
2995
3046
  */
@@ -3006,6 +3057,14 @@ export declare interface TooltipProps extends Omit<LayerProps, 'as'> {
3006
3057
  portal?: boolean | string
3007
3058
  scheme?: ThemeColorSchemeKey
3008
3059
  shadow?: number | number[]
3060
+ /**
3061
+ * @public Adds a delay to open or close the tooltip. Defaults to 0.
3062
+ *
3063
+ * If only a `number` is passed, it will be used for both opening and closing.
3064
+ *
3065
+ * If an object `{open: number; close:number}` is passed, it can be used to set different delays for each action.
3066
+ */
3067
+ delay?: Delay
3009
3068
  }
3010
3069
 
3011
3070
  /**
@@ -3189,6 +3248,11 @@ export declare function useTheme(): Theme
3189
3248
  */
3190
3249
  export declare function useToast(): ToastContextValue
3191
3250
 
3251
+ /**
3252
+ * @beta
3253
+ */
3254
+ export declare function useTooltipDelayGroup(): TooltipDelayGroupContextValue | null
3255
+
3192
3256
  /**
3193
3257
  * @beta
3194
3258
  */
package/dist/index.esm.js CHANGED
@@ -3061,9 +3061,9 @@ function getGlobalScope() {
3061
3061
  throw new Error("@sanity/ui: could not locate global scope");
3062
3062
  }
3063
3063
  const globalScope = getGlobalScope();
3064
- const key$7 = Symbol.for("@sanity/ui/context/theme");
3065
- globalScope[key$7] = globalScope[key$7] || createContext(null);
3066
- const ThemeContext = globalScope[key$7];
3064
+ const key$8 = Symbol.for("@sanity/ui/context/theme");
3065
+ globalScope[key$8] = globalScope[key$8] || createContext(null);
3066
+ const ThemeContext = globalScope[key$8];
3067
3067
  function ThemeProvider(props) {
3068
3068
  const parentTheme = useContext(ThemeContext);
3069
3069
  const {
@@ -5256,9 +5256,9 @@ const KBD = forwardRef(function KBD2(props, ref) {
5256
5256
  })
5257
5257
  });
5258
5258
  });
5259
- const key$6 = Symbol.for("@sanity/ui/context/boundaryElement");
5260
- globalScope[key$6] = globalScope[key$6] || createContext(null);
5261
- const BoundaryElementContext = globalScope[key$6];
5259
+ const key$7 = Symbol.for("@sanity/ui/context/boundaryElement");
5260
+ globalScope[key$7] = globalScope[key$7] || createContext(null);
5261
+ const BoundaryElementContext = globalScope[key$7];
5262
5262
  function BoundaryElementProvider(props) {
5263
5263
  const {
5264
5264
  children,
@@ -5387,9 +5387,9 @@ function getLayerContext(contextValue) {
5387
5387
  }
5388
5388
  throw new Error("could not get layer context");
5389
5389
  }
5390
- const key$5 = Symbol.for("@sanity/ui/context/layer");
5391
- globalScope[key$5] = globalScope[key$5] || createContext(null);
5392
- const LayerContext = globalScope[key$5];
5390
+ const key$6 = Symbol.for("@sanity/ui/context/layer");
5391
+ globalScope[key$6] = globalScope[key$6] || createContext(null);
5392
+ const LayerContext = globalScope[key$6];
5393
5393
  function useLayer() {
5394
5394
  const value = useContext(LayerContext);
5395
5395
  if (!value) {
@@ -5626,7 +5626,7 @@ const Layer = forwardRef(function Layer2(props, ref) {
5626
5626
  })
5627
5627
  });
5628
5628
  });
5629
- const key$4 = Symbol.for("@sanity/ui/context/portal");
5629
+ const key$5 = Symbol.for("@sanity/ui/context/portal");
5630
5630
  const elementKey = Symbol.for("@sanity/ui/context/portal/element");
5631
5631
  globalScope[elementKey] = null;
5632
5632
  const defaultContextValue = {
@@ -5645,8 +5645,8 @@ const defaultContextValue = {
5645
5645
  return globalScope[elementKey];
5646
5646
  }
5647
5647
  };
5648
- globalScope[key$4] = globalScope[key$4] || createContext(defaultContextValue);
5649
- const PortalContext = globalScope[key$4];
5648
+ globalScope[key$5] = globalScope[key$5] || createContext(defaultContextValue);
5649
+ const PortalContext = globalScope[key$5];
5650
5650
  function usePortal() {
5651
5651
  const value = useContext(PortalContext);
5652
5652
  if (!value) {
@@ -6668,6 +6668,11 @@ const Presentation = styled.span(responsiveRadiusStyle, textInputRepresentationS
6668
6668
  const LeftBox = styled(Box)(_d$3 || (_d$3 = __template$g(["\n position: absolute;\n top: 0;\n left: 0;\n"])));
6669
6669
  const RightBox = styled(Box)(_e$1 || (_e$1 = __template$g(["\n position: absolute;\n top: 0;\n right: 0;\n"])));
6670
6670
  const RightCard = styled(Card)(_f$1 || (_f$1 = __template$g(["\n background-color: transparent;\n position: absolute;\n top: 0;\n right: 0;\n"])));
6671
+ const TextInputClearButton = styled(Button)({
6672
+ "&:not([hidden])": {
6673
+ display: "block"
6674
+ }
6675
+ });
6671
6676
  const TextInput = forwardRef(function TextInput2(props, forwardedRef) {
6672
6677
  const {
6673
6678
  border = true,
@@ -6763,7 +6768,7 @@ const TextInput = forwardRef(function TextInput2(props, forwardedRef) {
6763
6768
  padding: clearButtonBoxPadding,
6764
6769
  style: CLEAR_BUTTON_BOX_STYLE,
6765
6770
  tone: customValidity ? "critical" : "inherit",
6766
- children: /* @__PURE__ */jsx(Button, {
6771
+ children: /* @__PURE__ */jsx(TextInputClearButton, {
6767
6772
  "aria-label": "Clear",
6768
6773
  "data-qa": "clear-button",
6769
6774
  fontSize,
@@ -6812,6 +6817,22 @@ const TextInput = forwardRef(function TextInput2(props, forwardedRef) {
6812
6817
  }), suffixNode]
6813
6818
  });
6814
6819
  });
6820
+ function useDelayedState(initialState) {
6821
+ const [state, setState] = useState(initialState);
6822
+ const delayedAction = useRef();
6823
+ const onStateChange = useCallback((nextState, delay) => {
6824
+ const action = () => {
6825
+ setState(nextState);
6826
+ };
6827
+ if (delayedAction.current) {
6828
+ clearTimeout(delayedAction.current);
6829
+ delayedAction.current = void 0;
6830
+ }
6831
+ if (!delay) return action();
6832
+ delayedAction.current = setTimeout(action, delay);
6833
+ }, []);
6834
+ return [state, onStateChange];
6835
+ }
6815
6836
  var __freeze$f = Object.freeze;
6816
6837
  var __defProp$f = Object.defineProperty;
6817
6838
  var __template$f = (cooked, raw) => __freeze$f(__defProp$f(cooked, "raw", {
@@ -6841,6 +6862,40 @@ const TooltipArrow = forwardRef(function TooltipArrow2(props, ref) {
6841
6862
  })
6842
6863
  });
6843
6864
  });
6865
+ const key$4 = Symbol.for("@sanity/ui/context/tooltipDelayGroup");
6866
+ globalScope[key$4] = globalScope[key$4] || createContext(null);
6867
+ const TooltipDelayGroupContext = globalScope[key$4];
6868
+ function useTooltipDelayGroup() {
6869
+ const value = useContext(TooltipDelayGroupContext);
6870
+ return value;
6871
+ }
6872
+ function TooltipDelayGroupProvider(props) {
6873
+ const {
6874
+ children,
6875
+ delay
6876
+ } = props;
6877
+ const [isGroupActive, setIsGroupActive] = useDelayedState(false);
6878
+ const [openTooltipId, setOpenTooltipId] = useDelayedState(null);
6879
+ const isInsideContext = useTooltipDelayGroup();
6880
+ if (isInsideContext) {
6881
+ throw new Error("TooltipDelayGroupProvider cannot be nested inside another TooltipDelayGroupProvider");
6882
+ }
6883
+ const openDelay = typeof delay === "number" ? delay : (delay == null ? void 0 : delay.open) || 0;
6884
+ const closeDelay = typeof delay === "number" ? delay : (delay == null ? void 0 : delay.close) || 0;
6885
+ const value = useMemo(() => ({
6886
+ isGroupActive,
6887
+ setIsGroupActive,
6888
+ openTooltipId,
6889
+ setOpenTooltipId,
6890
+ // When the group is active, we want the next tooltip to open immediately.
6891
+ openDelay: isGroupActive ? 1 : openDelay,
6892
+ closeDelay
6893
+ }), [closeDelay, isGroupActive, openDelay, openTooltipId, setIsGroupActive, setOpenTooltipId]);
6894
+ return /* @__PURE__ */jsx(TooltipDelayGroupContext.Provider, {
6895
+ value,
6896
+ children
6897
+ });
6898
+ }
6844
6899
  var __freeze$e = Object.freeze;
6845
6900
  var __defProp$e = Object.defineProperty;
6846
6901
  var __template$e = (cooked, raw) => __freeze$e(__defProp$e(cooked, "raw", {
@@ -6864,6 +6919,7 @@ const Tooltip = forwardRef(function Tooltip2(props, ref) {
6864
6919
  scheme,
6865
6920
  shadow = 2,
6866
6921
  zOffset = (_a2 = theme.sanity.layer) == null ? void 0 : _a2.tooltip.zOffset,
6922
+ delay,
6867
6923
  ...restProps
6868
6924
  } = props;
6869
6925
  const fallbackPlacements = useArrayProp(fallbackPlacementsProp);
@@ -6934,18 +6990,41 @@ const Tooltip = forwardRef(function Tooltip2(props, ref) {
6934
6990
  if (staticSide) style[staticSide] = -15;
6935
6991
  return style;
6936
6992
  }, [arrowX, arrowY, staticSide]);
6937
- const [isOpen, setIsOpen] = useState(false);
6938
- const handleBlur = useCallback(() => setIsOpen(false), []);
6939
- const handleFocus = useCallback(() => setIsOpen(true), []);
6940
- const handleMouseEnter = useCallback(() => setIsOpen(true), []);
6941
- const handleMouseLeave = useCallback(() => setIsOpen(false), []);
6993
+ const tooltipId = useId();
6994
+ const [isOpen, setIsOpen] = useDelayedState(false);
6995
+ const delayGroupContext = useTooltipDelayGroup();
6996
+ const showTooltip = isOpen || (delayGroupContext == null ? void 0 : delayGroupContext.openTooltipId) === tooltipId;
6997
+ const isInsideGroup = delayGroupContext !== null;
6998
+ const openDelayProp = typeof delay === "number" ? delay : (delay == null ? void 0 : delay.open) || 0;
6999
+ const closeDelayProp = typeof delay === "number" ? delay : (delay == null ? void 0 : delay.close) || 0;
7000
+ const openDelay = isInsideGroup ? delayGroupContext.openDelay : openDelayProp;
7001
+ const closeDelay = isInsideGroup ? delayGroupContext.closeDelay : closeDelayProp;
7002
+ const handleIsOpenChange = useCallback(open => {
7003
+ if (isInsideGroup) {
7004
+ if (open) {
7005
+ delayGroupContext.setIsGroupActive(open, openDelay);
7006
+ delayGroupContext.setOpenTooltipId(tooltipId, openDelay);
7007
+ } else {
7008
+ const minimumGroupDeactivateDelay = 200;
7009
+ const groupDeactivateDelay = closeDelay > minimumGroupDeactivateDelay ? closeDelay : minimumGroupDeactivateDelay;
7010
+ delayGroupContext.setIsGroupActive(open, groupDeactivateDelay);
7011
+ delayGroupContext.setOpenTooltipId(null, closeDelay);
7012
+ }
7013
+ } else {
7014
+ setIsOpen(open, open ? openDelay : closeDelay);
7015
+ }
7016
+ }, [isInsideGroup, delayGroupContext, openDelay, tooltipId, closeDelay, setIsOpen]);
7017
+ const handleBlur = useCallback(() => handleIsOpenChange(false), [handleIsOpenChange]);
7018
+ const handleFocus = useCallback(() => handleIsOpenChange(true), [handleIsOpenChange]);
7019
+ const handleMouseEnter = useCallback(() => handleIsOpenChange(true), [handleIsOpenChange]);
7020
+ const handleMouseLeave = useCallback(() => handleIsOpenChange(false), [handleIsOpenChange]);
6942
7021
  useEffect(() => {
6943
7022
  if (!isOpen) return;
6944
7023
  function handleWindowMouseMove(event) {
6945
7024
  if (!referenceElement) return;
6946
7025
  const isHoveringReference = referenceElement === event.target || event.target instanceof Node && referenceElement.contains(event.target);
6947
7026
  if (!isHoveringReference) {
6948
- setIsOpen(false);
7027
+ handleIsOpenChange(false);
6949
7028
  window.removeEventListener("mousemove", handleWindowMouseMove);
6950
7029
  }
6951
7030
  }
@@ -6953,13 +7032,13 @@ const Tooltip = forwardRef(function Tooltip2(props, ref) {
6953
7032
  return () => {
6954
7033
  window.removeEventListener("mousemove", handleWindowMouseMove);
6955
7034
  };
6956
- }, [isOpen, referenceElement]);
7035
+ }, [isOpen, referenceElement, handleIsOpenChange]);
6957
7036
  useEffect(() => {
6958
- if (disabled) setIsOpen(false);
6959
- }, [disabled]);
7037
+ if (disabled) handleIsOpenChange(false);
7038
+ }, [disabled, handleIsOpenChange]);
6960
7039
  useEffect(() => {
6961
- if (!content) setIsOpen(false);
6962
- }, [content]);
7040
+ if (!content) handleIsOpenChange(false);
7041
+ }, [content, handleIsOpenChange]);
6963
7042
  useEffect(() => refs.setReference(referenceElement), [referenceElement, refs]);
6964
7043
  const setArrow = useCallback(arrowEl => {
6965
7044
  arrowRef.current = arrowEl;
@@ -7010,7 +7089,7 @@ const Tooltip = forwardRef(function Tooltip2(props, ref) {
7010
7089
  })
7011
7090
  });
7012
7091
  return /* @__PURE__ */jsxs(Fragment, {
7013
- children: [child, isOpen && /* @__PURE__ */jsx(Fragment, {
7092
+ children: [child, showTooltip && /* @__PURE__ */jsx(Fragment, {
7014
7093
  children: portal ? /* @__PURE__ */jsx(Portal, {
7015
7094
  __unstable_name: typeof portal === "string" ? portal : void 0,
7016
7095
  children: root
@@ -9589,5 +9668,5 @@ const TreeItem = memo(function TreeItem2(props) {
9589
9668
  })]
9590
9669
  });
9591
9670
  });
9592
- export { Autocomplete, Avatar, AvatarCounter, AvatarStack, Badge, BoundaryElementProvider, Box, Breadcrumbs, Button, Card, Checkbox, Code, CodeSkeleton, Container, Dialog, DialogContext, DialogProvider, ElementQuery, ErrorBoundary, Flex, Grid, Heading, HeadingSkeleton, Hotkeys, Inline, KBD, Label, LabelSkeleton, Layer, LayerProvider, Menu, MenuButton, MenuDivider, MenuGroup, MenuItem, Popover, Portal, PortalProvider, Radio, Select, Skeleton, Spinner, SrOnly, Stack, Switch, Tab, TabList, TabPanel, Text, TextArea, TextInput, TextSkeleton, ThemeColorProvider, ThemeProvider, Toast, ToastProvider, Tooltip, Tree, TreeItem, VirtualList, _ResizeObserver, _elementSizeObserver, _fillCSSObject, _getArrayProp, _getResponsiveSpace, _hasFocus, _isEnterToClickElement, _isScrollable, _raf, _raf2, _responsive, attemptFocus, containsOrEqualsElement, createColorTheme, focusFirstDescendant, focusLastDescendant, hexToRgb, hslToRgb, isFocusable, isHTMLAnchorElement, isHTMLButtonElement, isHTMLElement, isHTMLInputElement, isHTMLSelectElement, isHTMLTextAreaElement, multiply$1 as multiply, parseColor, rem, responsiveCodeFontStyle, responsiveHeadingFont, responsiveLabelFont, responsiveTextAlignStyle, responsiveTextFont, rgbToHex, rgbToHsl, rgba, screen$1 as screen, studioTheme, useArrayProp, useBoundaryElement, useClickOutside, useCustomValidity, useDialog, useElementRect, useElementSize, useForwardedRef, useGlobalKeyDown, useLayer, useMediaIndex, usePortal, usePrefersDark, usePrefersReducedMotion, useRootTheme, useTheme, useToast, useTree };
9671
+ export { Autocomplete, Avatar, AvatarCounter, AvatarStack, Badge, BoundaryElementProvider, Box, Breadcrumbs, Button, Card, Checkbox, Code, CodeSkeleton, Container, Dialog, DialogContext, DialogProvider, ElementQuery, ErrorBoundary, Flex, Grid, Heading, HeadingSkeleton, Hotkeys, Inline, KBD, Label, LabelSkeleton, Layer, LayerProvider, Menu, MenuButton, MenuDivider, MenuGroup, MenuItem, Popover, Portal, PortalProvider, Radio, Select, Skeleton, Spinner, SrOnly, Stack, Switch, Tab, TabList, TabPanel, Text, TextArea, TextInput, TextSkeleton, ThemeColorProvider, ThemeProvider, Toast, ToastProvider, Tooltip, TooltipDelayGroupContext, TooltipDelayGroupProvider, Tree, TreeItem, VirtualList, _ResizeObserver, _elementSizeObserver, _fillCSSObject, _getArrayProp, _getResponsiveSpace, _hasFocus, _isEnterToClickElement, _isScrollable, _raf, _raf2, _responsive, attemptFocus, containsOrEqualsElement, createColorTheme, focusFirstDescendant, focusLastDescendant, hexToRgb, hslToRgb, isFocusable, isHTMLAnchorElement, isHTMLButtonElement, isHTMLElement, isHTMLInputElement, isHTMLSelectElement, isHTMLTextAreaElement, multiply$1 as multiply, parseColor, rem, responsiveCodeFontStyle, responsiveHeadingFont, responsiveLabelFont, responsiveTextAlignStyle, responsiveTextFont, rgbToHex, rgbToHsl, rgba, screen$1 as screen, studioTheme, useArrayProp, useBoundaryElement, useClickOutside, useCustomValidity, useDialog, useElementRect, useElementSize, useForwardedRef, useGlobalKeyDown, useLayer, useMediaIndex, usePortal, usePrefersDark, usePrefersReducedMotion, useRootTheme, useTheme, useToast, useTooltipDelayGroup, useTree };
9593
9672
  //# sourceMappingURL=index.esm.js.map