@sanity/ui 1.7.12 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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) {
@@ -6812,6 +6812,22 @@ const TextInput = forwardRef(function TextInput2(props, forwardedRef) {
6812
6812
  }), suffixNode]
6813
6813
  });
6814
6814
  });
6815
+ function useDelayedState(initialState) {
6816
+ const [state, setState] = useState(initialState);
6817
+ const delayedAction = useRef();
6818
+ const onStateChange = useCallback((nextState, delay) => {
6819
+ const action = () => {
6820
+ setState(nextState);
6821
+ };
6822
+ if (delayedAction.current) {
6823
+ clearTimeout(delayedAction.current);
6824
+ delayedAction.current = void 0;
6825
+ }
6826
+ if (!delay) return action();
6827
+ delayedAction.current = setTimeout(action, delay);
6828
+ }, []);
6829
+ return [state, onStateChange];
6830
+ }
6815
6831
  var __freeze$f = Object.freeze;
6816
6832
  var __defProp$f = Object.defineProperty;
6817
6833
  var __template$f = (cooked, raw) => __freeze$f(__defProp$f(cooked, "raw", {
@@ -6841,6 +6857,40 @@ const TooltipArrow = forwardRef(function TooltipArrow2(props, ref) {
6841
6857
  })
6842
6858
  });
6843
6859
  });
6860
+ const key$4 = Symbol.for("@sanity/ui/context/tooltipDelayGroup");
6861
+ globalScope[key$4] = globalScope[key$4] || createContext(null);
6862
+ const TooltipDelayGroupContext = globalScope[key$4];
6863
+ function useTooltipDelayGroup() {
6864
+ const value = useContext(TooltipDelayGroupContext);
6865
+ return value;
6866
+ }
6867
+ function TooltipDelayGroupProvider(props) {
6868
+ const {
6869
+ children,
6870
+ delay
6871
+ } = props;
6872
+ const [isGroupActive, setIsGroupActive] = useDelayedState(false);
6873
+ const [openTooltipId, setOpenTooltipId] = useDelayedState(null);
6874
+ const isInsideContext = useTooltipDelayGroup();
6875
+ if (isInsideContext) {
6876
+ throw new Error("TooltipDelayGroupProvider cannot be nested inside another TooltipDelayGroupProvider");
6877
+ }
6878
+ const openDelay = typeof delay === "number" ? delay : (delay == null ? void 0 : delay.open) || 0;
6879
+ const closeDelay = typeof delay === "number" ? delay : (delay == null ? void 0 : delay.close) || 0;
6880
+ const value = useMemo(() => ({
6881
+ isGroupActive,
6882
+ setIsGroupActive,
6883
+ openTooltipId,
6884
+ setOpenTooltipId,
6885
+ // When the group is active, we want the next tooltip to open immediately.
6886
+ openDelay: isGroupActive ? 1 : openDelay,
6887
+ closeDelay
6888
+ }), [closeDelay, isGroupActive, openDelay, openTooltipId, setIsGroupActive, setOpenTooltipId]);
6889
+ return /* @__PURE__ */jsx(TooltipDelayGroupContext.Provider, {
6890
+ value,
6891
+ children
6892
+ });
6893
+ }
6844
6894
  var __freeze$e = Object.freeze;
6845
6895
  var __defProp$e = Object.defineProperty;
6846
6896
  var __template$e = (cooked, raw) => __freeze$e(__defProp$e(cooked, "raw", {
@@ -6864,6 +6914,7 @@ const Tooltip = forwardRef(function Tooltip2(props, ref) {
6864
6914
  scheme,
6865
6915
  shadow = 2,
6866
6916
  zOffset = (_a2 = theme.sanity.layer) == null ? void 0 : _a2.tooltip.zOffset,
6917
+ delay,
6867
6918
  ...restProps
6868
6919
  } = props;
6869
6920
  const fallbackPlacements = useArrayProp(fallbackPlacementsProp);
@@ -6934,18 +6985,41 @@ const Tooltip = forwardRef(function Tooltip2(props, ref) {
6934
6985
  if (staticSide) style[staticSide] = -15;
6935
6986
  return style;
6936
6987
  }, [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), []);
6988
+ const tooltipId = useId();
6989
+ const [isOpen, setIsOpen] = useDelayedState(false);
6990
+ const delayGroupContext = useTooltipDelayGroup();
6991
+ const showTooltip = isOpen || (delayGroupContext == null ? void 0 : delayGroupContext.openTooltipId) === tooltipId;
6992
+ const isInsideGroup = delayGroupContext !== null;
6993
+ const openDelayProp = typeof delay === "number" ? delay : (delay == null ? void 0 : delay.open) || 0;
6994
+ const closeDelayProp = typeof delay === "number" ? delay : (delay == null ? void 0 : delay.close) || 0;
6995
+ const openDelay = isInsideGroup ? delayGroupContext.openDelay : openDelayProp;
6996
+ const closeDelay = isInsideGroup ? delayGroupContext.closeDelay : closeDelayProp;
6997
+ const handleIsOpenChange = useCallback(open => {
6998
+ if (isInsideGroup) {
6999
+ if (open) {
7000
+ delayGroupContext.setIsGroupActive(open, openDelay);
7001
+ delayGroupContext.setOpenTooltipId(tooltipId, openDelay);
7002
+ } else {
7003
+ const minimumGroupDeactivateDelay = 200;
7004
+ const groupDeactivateDelay = closeDelay > minimumGroupDeactivateDelay ? closeDelay : minimumGroupDeactivateDelay;
7005
+ delayGroupContext.setIsGroupActive(open, groupDeactivateDelay);
7006
+ delayGroupContext.setOpenTooltipId(null, closeDelay);
7007
+ }
7008
+ } else {
7009
+ setIsOpen(open, open ? openDelay : closeDelay);
7010
+ }
7011
+ }, [isInsideGroup, delayGroupContext, openDelay, tooltipId, closeDelay, setIsOpen]);
7012
+ const handleBlur = useCallback(() => handleIsOpenChange(false), [handleIsOpenChange]);
7013
+ const handleFocus = useCallback(() => handleIsOpenChange(true), [handleIsOpenChange]);
7014
+ const handleMouseEnter = useCallback(() => handleIsOpenChange(true), [handleIsOpenChange]);
7015
+ const handleMouseLeave = useCallback(() => handleIsOpenChange(false), [handleIsOpenChange]);
6942
7016
  useEffect(() => {
6943
7017
  if (!isOpen) return;
6944
7018
  function handleWindowMouseMove(event) {
6945
7019
  if (!referenceElement) return;
6946
7020
  const isHoveringReference = referenceElement === event.target || event.target instanceof Node && referenceElement.contains(event.target);
6947
7021
  if (!isHoveringReference) {
6948
- setIsOpen(false);
7022
+ handleIsOpenChange(false);
6949
7023
  window.removeEventListener("mousemove", handleWindowMouseMove);
6950
7024
  }
6951
7025
  }
@@ -6953,13 +7027,13 @@ const Tooltip = forwardRef(function Tooltip2(props, ref) {
6953
7027
  return () => {
6954
7028
  window.removeEventListener("mousemove", handleWindowMouseMove);
6955
7029
  };
6956
- }, [isOpen, referenceElement]);
7030
+ }, [isOpen, referenceElement, handleIsOpenChange]);
6957
7031
  useEffect(() => {
6958
- if (disabled) setIsOpen(false);
6959
- }, [disabled]);
7032
+ if (disabled) handleIsOpenChange(false);
7033
+ }, [disabled, handleIsOpenChange]);
6960
7034
  useEffect(() => {
6961
- if (!content) setIsOpen(false);
6962
- }, [content]);
7035
+ if (!content) handleIsOpenChange(false);
7036
+ }, [content, handleIsOpenChange]);
6963
7037
  useEffect(() => refs.setReference(referenceElement), [referenceElement, refs]);
6964
7038
  const setArrow = useCallback(arrowEl => {
6965
7039
  arrowRef.current = arrowEl;
@@ -7010,7 +7084,7 @@ const Tooltip = forwardRef(function Tooltip2(props, ref) {
7010
7084
  })
7011
7085
  });
7012
7086
  return /* @__PURE__ */jsxs(Fragment, {
7013
- children: [child, isOpen && /* @__PURE__ */jsx(Fragment, {
7087
+ children: [child, showTooltip && /* @__PURE__ */jsx(Fragment, {
7014
7088
  children: portal ? /* @__PURE__ */jsx(Portal, {
7015
7089
  __unstable_name: typeof portal === "string" ? portal : void 0,
7016
7090
  children: root
@@ -9589,5 +9663,5 @@ const TreeItem = memo(function TreeItem2(props) {
9589
9663
  })]
9590
9664
  });
9591
9665
  });
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 };
9666
+ 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
9667
  //# sourceMappingURL=index.esm.js.map