reshaped 2.2.1 → 2.3.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.
Files changed (119) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/bin/cli.js +1 -11
  3. package/bundle.css +1 -1
  4. package/bundle.js +10 -10
  5. package/cli/theming/definitions/base.d.ts +1 -1
  6. package/cli/theming/definitions/figma.d.ts +1 -1
  7. package/cli/theming/definitions/reshaped.d.ts +1 -1
  8. package/cli/theming/definitions/slate.d.ts +1 -1
  9. package/cli/theming/index.d.ts +4 -4
  10. package/cli/theming/index.js +7 -107
  11. package/components/Actionable/Actionable.js +14 -3
  12. package/components/Actionable/Actionable.module.css +1 -1
  13. package/components/Autocomplete/tests/Autocomplete.stories.js +27 -1
  14. package/components/Button/Button.js +2 -0
  15. package/components/Button/Button.module.css +1 -1
  16. package/components/Button/Button.types.d.ts +6 -0
  17. package/components/Button/ButtonGroup.d.ts +4 -0
  18. package/components/Button/ButtonGroup.js +9 -0
  19. package/components/Button/tests/Button.stories.d.ts +1 -0
  20. package/components/Button/tests/Button.stories.js +63 -0
  21. package/components/DropdownMenu/tests/DropdownMenu.stories.js +1 -0
  22. package/components/Overlay/Overlay.js +3 -4
  23. package/components/Tabs/Tabs.d.ts +1 -8
  24. package/components/Tabs/Tabs.module.css +1 -1
  25. package/components/Tabs/Tabs.types.d.ts +6 -7
  26. package/components/Tabs/TabsContext.d.ts +4 -0
  27. package/components/Tabs/TabsControlled.js +24 -1
  28. package/components/Tabs/TabsItem.d.ts +1 -8
  29. package/components/Tabs/TabsItem.js +22 -4
  30. package/components/Tabs/TabsList.js +30 -32
  31. package/components/Tabs/tests/Tabs.stories.d.ts +1 -0
  32. package/components/Tabs/tests/Tabs.stories.js +17 -0
  33. package/components/Toast/ToastContainer.js +1 -0
  34. package/components/Toast/ToastProvider.js +4 -0
  35. package/components/Toast/tests/Toast.stories.js +1 -0
  36. package/components/_private/Flyout/FlyoutContent.js +4 -2
  37. package/components/_private/Flyout/FlyoutTrigger.js +1 -1
  38. package/components/_private/Flyout/tests/Flyout.stories.d.ts +1 -0
  39. package/components/_private/Flyout/tests/Flyout.stories.js +28 -1
  40. package/components/_private/Portal/Portal.d.ts +8 -4
  41. package/components/_private/Portal/Portal.js +23 -11
  42. package/components/_private/Portal/Portal.types.d.ts +7 -2
  43. package/components/_private/Portal/index.d.ts +1 -1
  44. package/components/_private/Portal/index.js +1 -1
  45. package/components/_private/Portal/tests/Portal.stories.js +11 -12
  46. package/config/tailwind.d.ts +1 -1
  47. package/config/tailwind.js +3 -3
  48. package/hooks/_private/useFlyout.js +16 -13
  49. package/package.json +36 -28
  50. package/themes/_generator/tests/themes.stories.d.ts +6 -0
  51. package/themes/_generator/tests/themes.stories.js +32 -0
  52. package/{cli/theming → themes/_generator}/tokens/color/color.transforms.d.ts +1 -1
  53. package/{cli/theming → themes/_generator}/tokens/duration/duration.transforms.d.ts +1 -1
  54. package/{cli/theming → themes/_generator}/tokens/easing/easing.transforms.d.ts +1 -1
  55. package/{cli/theming → themes/_generator}/tokens/font/font.transforms.d.ts +1 -1
  56. package/{cli/theming → themes/_generator}/tokens/fontFamily/fontFamily.transforms.d.ts +1 -1
  57. package/{cli/theming → themes/_generator}/tokens/fontWeight/fontWeight.transforms.d.ts +1 -1
  58. package/{cli/theming → themes/_generator}/tokens/shadow/shadow.transforms.d.ts +1 -1
  59. package/{cli/theming → themes/_generator}/tokens/shadow/shadow.transforms.js +1 -1
  60. package/{cli/theming → themes/_generator}/tokens/unit/unit.transforms.d.ts +1 -1
  61. package/{cli/theming → themes/_generator}/tokens/viewport/viewport.transforms.d.ts +1 -1
  62. package/themes/_generator/transform.d.ts +7 -0
  63. package/themes/_generator/transform.js +57 -0
  64. package/{cli/theming → themes/_generator}/types.d.ts +3 -1
  65. package/{cli/theming → themes/_generator}/utilities/css.js +3 -1
  66. package/themes/_generator/utilities/generateBackgroundColors.d.ts +4 -0
  67. package/themes/_generator/utilities/generateBackgroundColors.js +54 -0
  68. package/themes/_generator/utilities/generateUnits.d.ts +4 -0
  69. package/themes/_generator/utilities/generateUnits.js +17 -0
  70. package/{cli/theming → themes/_generator}/utilities/mergeDeep.js +1 -3
  71. package/{cli/theming → themes/_generator}/utilities/resolveTokenReference.js +1 -3
  72. package/themes/index.d.ts +3 -0
  73. package/themes/index.js +5 -0
  74. package/types/config.d.ts +1 -1
  75. package/utilities/a11y.js +14 -4
  76. package/{cli/utilities → utilities}/color.d.ts +1 -1
  77. package/utilities/dom.d.ts +1 -0
  78. package/utilities/dom.js +9 -0
  79. package/cli/utilities/tests/color.test.d.ts +0 -1
  80. package/cli/utilities/tests/color.test.js +0 -63
  81. /package/{cli/theming → themes/_generator}/tokens/color/color.transforms.js +0 -0
  82. /package/{cli/theming → themes/_generator}/tokens/color/color.types.d.ts +0 -0
  83. /package/{cli/theming → themes/_generator}/tokens/color/color.types.js +0 -0
  84. /package/{cli/theming → themes/_generator}/tokens/duration/duration.transforms.js +0 -0
  85. /package/{cli/theming → themes/_generator}/tokens/duration/duration.types.d.ts +0 -0
  86. /package/{cli/theming → themes/_generator}/tokens/duration/duration.types.js +0 -0
  87. /package/{cli/theming → themes/_generator}/tokens/easing/easing.transforms.js +0 -0
  88. /package/{cli/theming → themes/_generator}/tokens/easing/easing.types.d.ts +0 -0
  89. /package/{cli/theming → themes/_generator}/tokens/easing/easing.types.js +0 -0
  90. /package/{cli/theming → themes/_generator}/tokens/font/font.transforms.js +0 -0
  91. /package/{cli/theming → themes/_generator}/tokens/font/font.types.d.ts +0 -0
  92. /package/{cli/theming → themes/_generator}/tokens/font/font.types.js +0 -0
  93. /package/{cli/theming → themes/_generator}/tokens/fontFamily/fontFamily.transforms.js +0 -0
  94. /package/{cli/theming → themes/_generator}/tokens/fontFamily/fontFamily.types.d.ts +0 -0
  95. /package/{cli/theming → themes/_generator}/tokens/fontFamily/fontFamily.types.js +0 -0
  96. /package/{cli/theming → themes/_generator}/tokens/fontWeight/fontWeight.transforms.js +0 -0
  97. /package/{cli/theming → themes/_generator}/tokens/fontWeight/fontWeight.types.d.ts +0 -0
  98. /package/{cli/theming → themes/_generator}/tokens/fontWeight/fontWeight.types.js +0 -0
  99. /package/{cli/theming → themes/_generator}/tokens/shadow/shadow.types.d.ts +0 -0
  100. /package/{cli/theming → themes/_generator}/tokens/shadow/shadow.types.js +0 -0
  101. /package/{cli/theming → themes/_generator}/tokens/transforms.d.ts +0 -0
  102. /package/{cli/theming → themes/_generator}/tokens/transforms.js +0 -0
  103. /package/{cli/theming → themes/_generator}/tokens/types.d.ts +0 -0
  104. /package/{cli/theming → themes/_generator}/tokens/types.js +0 -0
  105. /package/{cli/theming → themes/_generator}/tokens/unit/unit.transforms.js +0 -0
  106. /package/{cli/theming → themes/_generator}/tokens/unit/unit.types.d.ts +0 -0
  107. /package/{cli/theming → themes/_generator}/tokens/unit/unit.types.js +0 -0
  108. /package/{cli/theming → themes/_generator}/tokens/viewport/viewport.transforms.js +0 -0
  109. /package/{cli/theming → themes/_generator}/tokens/viewport/viewport.types.d.ts +0 -0
  110. /package/{cli/theming → themes/_generator}/tokens/viewport/viewport.types.js +0 -0
  111. /package/{cli/theming → themes/_generator}/types.js +0 -0
  112. /package/{cli/theming → themes/_generator}/utilities/css.d.ts +0 -0
  113. /package/{cli/theming → themes/_generator}/utilities/mergeDeep.d.ts +0 -0
  114. /package/{cli/theming → themes/_generator}/utilities/mergeDefinitions.d.ts +0 -0
  115. /package/{cli/theming → themes/_generator}/utilities/mergeDefinitions.js +0 -0
  116. /package/{cli/theming → themes/_generator}/utilities/resolveTokenReference.d.ts +0 -0
  117. /package/{cli/utilities → utilities}/color.js +0 -0
  118. /package/{cli/utilities → utilities}/string.d.ts +0 -0
  119. /package/{cli/utilities → utilities}/string.js +0 -0
@@ -15,4 +15,8 @@ export declare const useTabs: (value?: string) => {
15
15
  size: NonNullable<"medium" | "large" | undefined>;
16
16
  value?: string | undefined;
17
17
  setDefaultValue: (value: string) => void;
18
+ elActiveRef: React.MutableRefObject<HTMLDivElement | null>;
19
+ elPrevActiveRef: React.MutableRefObject<HTMLDivElement | null>;
20
+ selection: T.SelectionState;
21
+ setSelection: React.Dispatch<React.SetStateAction<T.SelectionState>>;
18
22
  };
@@ -5,12 +5,35 @@ import { TabsProvider } from "./TabsContext.js";
5
5
  const TabsControlled = (props) => {
6
6
  const { children, value, onChange, onSilentChange, itemWidth, variant, name, direction = "row", size = "medium", } = props;
7
7
  const id = useElementId();
8
+ const elActiveRef = React.useRef(null);
9
+ const elPrevActiveRef = React.useRef(elActiveRef.current);
10
+ const [selection, setSelection] = React.useState({
11
+ scaleX: 0,
12
+ scaleY: 0,
13
+ left: 0,
14
+ top: 0,
15
+ status: "idle",
16
+ });
8
17
  const setDefaultValue = (value) => {
9
18
  if (value === undefined)
10
19
  return;
11
20
  if (onSilentChange)
12
21
  onSilentChange({ value, name });
13
22
  };
14
- return (React.createElement(TabsProvider, { value: { value, name, size, direction, itemWidth, variant, onChange, id, setDefaultValue } }, children));
23
+ return (React.createElement(TabsProvider, { value: {
24
+ value,
25
+ name,
26
+ size,
27
+ direction,
28
+ itemWidth,
29
+ variant,
30
+ onChange,
31
+ id,
32
+ setDefaultValue,
33
+ elActiveRef,
34
+ elPrevActiveRef,
35
+ selection,
36
+ setSelection,
37
+ } }, children));
15
38
  };
16
39
  export default TabsControlled;
@@ -1,11 +1,4 @@
1
1
  import React from "react";
2
2
  import type * as T from "./Tabs.types";
3
- declare const _default: React.ForwardRefExoticComponent<Partial<T.ItemProps> & {
4
- value: string;
5
- active?: boolean | undefined;
6
- visuallySelected?: boolean | undefined;
7
- attributes?: (Omit<import("../../types/global").Attributes<"button", void>, "ref"> & Omit<React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>, `data-${string}` | "form" | "slot" | "style" | "title" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "accessKey" | "autoFocus" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "nonce" | "placeholder" | "spellCheck" | "translate" | "radioGroup" | "about" | "content" | "datatype" | "inlist" | "prefix" | "property" | "rel" | "resource" | "rev" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "disabled" | "color" | "suppressHydrationWarning" | "id" | "lang" | "tabIndex" | "role" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-braillelabel" | "aria-brailleroledescription" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colindextext" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-description" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowindextext" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "children" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onResize" | "onResizeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "value" | "type" | keyof React.ClassAttributes<HTMLButtonElement> | "formAction" | "formEncType" | "formMethod" | "formNoValidate" | "formTarget" | "name"> & {
8
- ref?: React.RefObject<HTMLAnchorElement | HTMLButtonElement> | undefined;
9
- }) | undefined;
10
- } & React.RefAttributes<HTMLDivElement>>;
3
+ declare const _default: React.ForwardRefExoticComponent<T.ItemProps & React.RefAttributes<HTMLAnchorElement | HTMLButtonElement>>;
11
4
  export default _default;
@@ -8,8 +8,11 @@ import Text from "../Text/index.js";
8
8
  import { useTabs } from "./TabsContext.js";
9
9
  import s from "./Tabs.module.css";
10
10
  const TabsItem = (props, ref) => {
11
- const { value, children, icon, active, visuallySelected, attributes } = props;
12
- const { onChange, panelId, name, size } = useTabs(value);
11
+ const { value, children, icon, href, attributes } = props;
12
+ const { onChange, panelId, name, size, value: tabsValue, selection, elActiveRef, elPrevActiveRef, } = useTabs(value);
13
+ const itemRef = React.useRef(null);
14
+ const active = tabsValue === value;
15
+ const visuallySelected = active && selection.status === "idle";
13
16
  const itemClassNames = classNames(s.item, visuallySelected && s["--item-active"]);
14
17
  const isFormControl = !!name;
15
18
  const tabAttributes = {
@@ -17,12 +20,27 @@ const TabsItem = (props, ref) => {
17
20
  tabIndex: active ? 0 : -1,
18
21
  "aria-selected": active,
19
22
  };
23
+ const updateRefs = React.useCallback(() => {
24
+ if (!("current" in itemRef)) {
25
+ throw new Error("Reshaped, Tabs: TabItem is expecting an object ref format but received a function ref");
26
+ }
27
+ elPrevActiveRef.current = elActiveRef.current;
28
+ elActiveRef.current = itemRef.current;
29
+ }, [elActiveRef, elPrevActiveRef]);
20
30
  const handleChange = () => {
31
+ if (href && !onChange)
32
+ return;
33
+ updateRefs();
21
34
  if (onChange)
22
35
  onChange({ value, name });
23
36
  };
24
- return (React.createElement("div", { className: itemClassNames, ref: ref, role: "presentation" },
25
- React.createElement(Actionable, { insetFocus: true, onClick: !name ? handleChange : undefined, className: s.button, as: name ? "label" : undefined, attributes: Object.assign(Object.assign(Object.assign({}, attributes), (!isFormControl && tabAttributes)), { "aria-controls": panelId }) },
37
+ React.useEffect(() => {
38
+ if (!active)
39
+ return;
40
+ updateRefs();
41
+ }, [active, updateRefs]);
42
+ return (React.createElement("div", Object.assign({}, attributes, { className: itemClassNames, ref: itemRef, role: "presentation" }),
43
+ React.createElement(Actionable, { ref: ref, href: href, insetFocus: true, onClick: !name ? handleChange : undefined, className: s.button, as: name ? "label" : undefined, attributes: Object.assign(Object.assign({}, (!isFormControl && tabAttributes)), { "aria-controls": panelId }) },
26
44
  name && (React.createElement(HiddenInput, { type: "radio", name: name, value: value, checked: visuallySelected, onChange: handleChange, className: s.radio })),
27
45
  React.createElement("span", { className: s.buttonContent },
28
46
  icon && React.createElement(Icon, { svg: icon, className: s.icon, size: 4 }),
@@ -11,20 +11,18 @@ import IconChevronLeft from "../../icons/ChevronLeft.js";
11
11
  import TabsItem from "./TabsItem.js";
12
12
  import { useTabs } from "./TabsContext.js";
13
13
  import s from "./Tabs.module.css";
14
+ const findParentItem = (el, rootEl) => {
15
+ if (el === rootEl || !el)
16
+ return null;
17
+ if (el.classList.contains(s.listItem))
18
+ return el;
19
+ return findParentItem(el.parentElement, rootEl);
20
+ };
14
21
  const TabsList = (props) => {
15
22
  const { children, className, attributes } = props;
16
- const { value, setDefaultValue, itemWidth, variant, name, direction, size } = useTabs();
23
+ const { value, setDefaultValue, itemWidth, variant, name, direction, size, selection, setSelection, elActiveRef, elPrevActiveRef, } = useTabs();
17
24
  const [rtl] = useRTL();
18
25
  const elScrollableRef = React.useRef(null);
19
- const elActiveRef = React.useRef(null);
20
- const elPrevActiveRef = React.useRef(elActiveRef.current);
21
- const [selection, setSelection] = React.useState({
22
- scaleX: 0,
23
- scaleY: 0,
24
- left: 0,
25
- top: 0,
26
- status: "idle",
27
- });
28
26
  const [cutOffSide, setCutOffSide] = React.useState(null);
29
27
  const rootClassNames = classNames(s.root, size && s[`--size-${size}`], direction && s[`--direction-${direction}`], itemWidth && s[`--item-width-${itemWidth}`], variant && s[`--variant-${variant}`], cutOffSide && s[`--cut-off-${cutOffSide}`], className);
30
28
  const selectorClassNames = classNames(s.selector, selection.status === "idle" && s["--selector-hidden"], selection.status === "animated" && s["--selector-animated"]);
@@ -46,11 +44,16 @@ const TabsList = (props) => {
46
44
  setSelection((selectionStyle) => (Object.assign(Object.assign({}, selectionStyle), { status: "idle" })));
47
45
  };
48
46
  const getElementSelectionStyle = React.useCallback((el) => {
47
+ if (!elScrollableRef.current)
48
+ return null;
49
+ const itemEl = findParentItem(el, elScrollableRef.current);
50
+ if (!itemEl)
51
+ return null;
49
52
  return {
50
- scaleX: el.clientWidth,
51
- scaleY: el.clientHeight,
52
- top: el.offsetTop,
53
- left: el.offsetLeft,
53
+ scaleX: itemEl.clientWidth,
54
+ scaleY: itemEl.clientHeight,
55
+ top: itemEl.offsetTop,
56
+ left: itemEl.offsetLeft,
54
57
  };
55
58
  }, []);
56
59
  const { ref: hotkeysRef } = useHotkeys({
@@ -89,16 +92,20 @@ const TabsList = (props) => {
89
92
  }, [value]);
90
93
  useIsomorphicLayoutEffect(() => {
91
94
  // Do not update selection on mount, until we receive new activeId
92
- if (elActiveRef.current === elPrevActiveRef.current)
95
+ if (!elPrevActiveRef.current || elPrevActiveRef.current === elActiveRef.current)
93
96
  return;
94
97
  const selectionStyle = getElementSelectionStyle(elPrevActiveRef.current);
98
+ if (!selectionStyle)
99
+ return;
95
100
  setSelection(Object.assign(Object.assign({}, selectionStyle), { status: "prepared" }));
96
101
  }, [value, getElementSelectionStyle]);
97
102
  useIsomorphicLayoutEffect(() => {
98
- if (selection.status === "prepared") {
99
- const selectionStyle = getElementSelectionStyle(elActiveRef.current);
100
- setSelection(Object.assign(Object.assign({}, selectionStyle), { status: "animated" }));
101
- }
103
+ if (selection.status !== "prepared" || !elActiveRef.current)
104
+ return;
105
+ const selectionStyle = getElementSelectionStyle(elActiveRef.current);
106
+ if (!selectionStyle)
107
+ return;
108
+ setSelection(Object.assign(Object.assign({}, selectionStyle), { status: "animated" }));
102
109
  }, [selection]);
103
110
  useIsomorphicLayoutEffect(() => {
104
111
  const elScrollable = elScrollableRef.current;
@@ -135,19 +142,10 @@ const TabsList = (props) => {
135
142
  return (React.createElement("div", Object.assign({}, attributes, { className: rootClassNames }),
136
143
  React.createElement("div", { className: s.inner, ref: elScrollableRef },
137
144
  React.createElement("div", { className: s.list, role: "tablist", ref: hotkeysRef },
138
- React.Children.map(children, (child) => {
139
- if (!child || child.type !== TabsItem)
140
- return React.createElement("div", { className: s.item }, child);
141
- const childValue = child.props.value;
142
- const isActive = childValue === value;
143
- return (React.createElement(TabsItem, Object.assign({}, child.props, { ref: (node) => {
144
- if (!node)
145
- return;
146
- if (isActive) {
147
- elPrevActiveRef.current = elActiveRef.current || node;
148
- elActiveRef.current = node;
149
- }
150
- }, value: childValue, key: childValue, active: isActive, visuallySelected: isActive && selection.status === "idle" })));
145
+ React.Children.map(children, (child, index) => {
146
+ if (!child)
147
+ return null;
148
+ return (React.createElement("div", { className: s.listItem, key: child.props.value || child.key || index }, child));
151
149
  }),
152
150
  React.createElement("div", { onTransitionEnd: handleTransitionEnd, className: selectorClassNames, style: {
153
151
  "--rs-tab-selection-x": selection.left,
@@ -10,4 +10,5 @@ export declare const composition: () => React.JSX.Element;
10
10
  export declare const icon: () => React.JSX.Element;
11
11
  export declare const equalWidth: () => React.JSX.Element;
12
12
  export declare const selection: () => React.JSX.Element;
13
+ export declare const navigation: () => React.JSX.Element;
13
14
  export declare const edgeCases: () => React.JSX.Element;
@@ -211,6 +211,23 @@ export const selection = () => (<Example>
211
211
  </Tabs>
212
212
  </Example.Item>
213
213
  </Example>);
214
+ export const navigation = () => (<Example>
215
+ <Example.Item title="href, no onChange">
216
+ <Tabs value="2">
217
+ <Tabs.List>
218
+ <Tabs.Item value="1" href="#item-1" icon={IconZap}>
219
+ Item 1
220
+ </Tabs.Item>
221
+ <Tabs.Item value="2" href="#item-2" icon={IconZap}>
222
+ Long item 2
223
+ </Tabs.Item>
224
+ <Tabs.Item value="3" href="#item-3" icon={IconZap}>
225
+ Very long item 3
226
+ </Tabs.Item>
227
+ </Tabs.List>
228
+ </Tabs>
229
+ </Example.Item>
230
+ </Example>);
214
231
  export const edgeCases = () => (<Example>
215
232
  <Example.Item title="Viewport overflow">
216
233
  <Tabs>
@@ -53,6 +53,7 @@ const ToastContainer = (props) => {
53
53
  if (wrapperRef.current) {
54
54
  setToastHeight(wrapperRef.current.clientHeight);
55
55
  }
56
+ console.log("show");
56
57
  show(id);
57
58
  startTimer();
58
59
  }, [show, id, startTimer]);
@@ -27,6 +27,8 @@ const toastReducer = (state, action) => {
27
27
  nextState = Object.assign({}, state);
28
28
  positions.forEach((position) => {
29
29
  nextState[position] = nextState[position].map((item) => {
30
+ if (item.status !== "entering")
31
+ return item;
30
32
  return item.id === showId ? Object.assign(Object.assign({}, item), { status: "entered" }) : item;
31
33
  });
32
34
  });
@@ -52,6 +54,7 @@ const ToastProvider = (props) => {
52
54
  const [data, dispatch] = React.useReducer(toastReducer, defaultContextData.queues);
53
55
  const add = React.useCallback((toastProps) => {
54
56
  const id = generateId();
57
+ console.log("add");
55
58
  dispatch({ type: "add", payload: { toastProps, id } });
56
59
  return id;
57
60
  }, []);
@@ -59,6 +62,7 @@ const ToastProvider = (props) => {
59
62
  dispatch({ type: "show", payload: { id } });
60
63
  }, []);
61
64
  const hide = React.useCallback((id) => {
65
+ console.log("hide");
62
66
  dispatch({ type: "hide", payload: { id } });
63
67
  }, []);
64
68
  const remove = React.useCallback((id) => {
@@ -20,6 +20,7 @@ const Base = () => {
20
20
  <Button onClick={() => toast.hide(id)}>Show</Button>,
21
21
  ],
22
22
  });
23
+ toast.hide(id);
23
24
  }}>
24
25
  Show toast
25
26
  </Button>);
@@ -3,11 +3,12 @@ import React from "react";
3
3
  import { classNames } from "../../../utilities/helpers.js";
4
4
  import useIsomorphicLayoutEffect from "../../../hooks/useIsomorphicLayoutEffect.js";
5
5
  import Portal from "../Portal/index.js";
6
+ import { getClosestScrollableParent } from "../../../utilities/dom.js";
6
7
  import { useFlyoutContext } from "./Flyout.context.js";
7
8
  import s from "./Flyout.module.css";
8
9
  const FlyoutContent = (props) => {
9
10
  const { children, className, attributes } = props;
10
- const { flyout, id, flyoutElRef, handleTransitionEnd, triggerType, handleMouseEnter, handleMouseLeave, handleContentMouseDown, handleContentMouseUp, contentGap, contentClassName, contentAttributes, trapFocusMode, } = useFlyoutContext();
11
+ const { flyout, id, flyoutElRef, triggerElRef, handleTransitionEnd, triggerType, handleMouseEnter, handleMouseLeave, handleContentMouseDown, handleContentMouseUp, contentGap, contentClassName, contentAttributes, trapFocusMode, } = useFlyoutContext();
11
12
  const { styles, status, position } = flyout;
12
13
  const [mounted, setMounted] = React.useState(false);
13
14
  useIsomorphicLayoutEffect(() => {
@@ -37,6 +38,7 @@ const FlyoutContent = (props) => {
37
38
  // eslint-disable-next-line jsx-a11y/no-static-element-interactions
38
39
  React.createElement("div", { className: contentClassNames, style: Object.assign(Object.assign({}, styles), { "--rs-flyout-gap": contentGap }), ref: flyoutElRef, onTransitionEnd: handleTransitionEnd, onMouseEnter: triggerType === "hover" ? handleMouseEnter : undefined, onMouseLeave: triggerType === "hover" ? handleMouseLeave : undefined, onMouseDown: handleContentMouseDown, onTouchStart: handleContentMouseDown, onMouseUp: handleContentMouseUp, onTouchEnd: handleContentMouseUp },
39
40
  React.createElement("div", Object.assign({ role: role }, attributes, { id: id, "aria-modal": triggerType === "click", style: contentAttributes === null || contentAttributes === void 0 ? void 0 : contentAttributes.style, className: innerClassNames }), children)));
40
- return React.createElement(Portal, null, content);
41
+ const closestScrollable = getClosestScrollableParent(triggerElRef.current);
42
+ return (React.createElement(Portal, { targetRef: closestScrollable === document.body ? undefined : { current: closestScrollable } }, content));
41
43
  };
42
44
  export default FlyoutContent;
@@ -24,7 +24,7 @@ const FlyoutTrigger = (props) => {
24
24
  childrenAttributes.onMouseEnter = handleMouseEnter;
25
25
  childrenAttributes.onMouseLeave = handleMouseLeave;
26
26
  }
27
- if ((triggerType === "hover" && trapFocusMode !== "action-menu") || triggerType === "focus") {
27
+ if (triggerType === "hover" || triggerType === "focus") {
28
28
  childrenAttributes.onFocus = handleFocus;
29
29
  childrenAttributes["aria-describedby"] = id;
30
30
  }
@@ -12,5 +12,6 @@ export declare const modeDialogHover: () => React.JSX.Element;
12
12
  export declare const modeActionMenuHover: () => React.JSX.Element;
13
13
  export declare const modeContentMenuHover: () => React.JSX.Element;
14
14
  export declare const testWidthOverflowOnMobile: () => React.JSX.Element;
15
+ export declare const testInsideScrollArea: () => React.JSX.Element;
15
16
  export declare const widthTrigger: () => React.JSX.Element;
16
17
  export declare const scopedTheming: () => React.JSX.Element;
@@ -10,6 +10,7 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  return t;
11
11
  };
12
12
  import React from "react";
13
+ import { Example } from "../../../../utilities/storybook/index.js";
13
14
  import View from "../../../View/index.js";
14
15
  import Theme from "../../../Theme/index.js";
15
16
  import Button from "../../../Button/index.js";
@@ -18,7 +19,9 @@ export default { title: "Utilities/Internal/Flyout" };
18
19
  const Demo = (props) => {
19
20
  const { position = "bottom-start", children } = props, rest = __rest(props, ["position", "children"]);
20
21
  return (<Flyout triggerType="click" position={position} {...rest}>
21
- <Flyout.Trigger>{(attributes) => <button {...attributes}>{position}</button>}</Flyout.Trigger>
22
+ <Flyout.Trigger>
23
+ {(attributes) => <Button attributes={attributes}>{position}</Button>}
24
+ </Flyout.Trigger>
22
25
  <Flyout.Content>
23
26
  <div style={{
24
27
  background: "var(--rs-color-background-elevation-overlay)",
@@ -92,6 +95,30 @@ export const testWidthOverflowOnMobile = () => (<Demo position="bottom-start" wi
92
95
  <button type="button">Item 2</button>
93
96
  <button type="button">Close</button>
94
97
  </Demo>);
98
+ export const testInsideScrollArea = () => (<Example>
99
+ <Example.Item title="should move the content on area scroll">
100
+ <div style={{ overflow: "scroll", height: 200, margin: 40, position: "relative" }}>
101
+ <Flyout triggerType="click" position="bottom-start">
102
+ <Flyout.Trigger>{(attributes) => <button {...attributes}>Foo</button>}</Flyout.Trigger>
103
+ <Flyout.Content>
104
+ <div style={{
105
+ background: "var(--rs-color-background-elevation-overlay)",
106
+ padding: "var(--rs-unit-x4)",
107
+ height: 100,
108
+ width: 160,
109
+ borderRadius: "var(--rs-unit-radius-medium)",
110
+ border: "1px solid var(--rs-color-border-neutral-faded)",
111
+ boxSizing: "border-box",
112
+ }}>
113
+ {"Content"}
114
+ </div>
115
+ </Flyout.Content>
116
+ </Flyout>
117
+ <View height="300px" backgroundColor="neutral-faded"/>
118
+ </div>
119
+ <div style={{ height: 2000 }}/>
120
+ </Example.Item>
121
+ </Example>);
95
122
  export const widthTrigger = () => (<Flyout triggerType="click" width="trigger" position="bottom">
96
123
  <Flyout.Trigger>
97
124
  {(attributes) => <button {...attributes}>Trigger with long text</button>}
@@ -1,9 +1,13 @@
1
- /// <reference types="react" />
1
+ import React from "react";
2
2
  import type * as T from "./Portal.types";
3
- export declare const usePortal: () => T.Context;
3
+ export declare const usePortalScope: () => T.Context;
4
4
  /**
5
5
  * Disclaimer: Works only for components that don't show the portal immediately
6
6
  * That gives Portal time to receive scope on first render
7
7
  */
8
- declare const PortalProvider: (props: T.Props) => JSX.Element;
9
- export default PortalProvider;
8
+ declare const Portal: {
9
+ (props: T.Props): JSX.Element;
10
+ Scope: typeof PortalScope;
11
+ };
12
+ declare function PortalScope<T extends HTMLElement>(props: T.ScopeProps<T>): React.JSX.Element;
13
+ export default Portal;
@@ -2,20 +2,32 @@
2
2
  import React from "react";
3
3
  import ReactDOM from "react-dom";
4
4
  import Theme from "../../Theme/index.js";
5
- const PortalContext = React.createContext({ scopeRef: undefined });
6
- export const usePortal = () => {
7
- return React.useContext(PortalContext);
5
+ const PortalScopeContext = React.createContext({});
6
+ export const usePortalScope = () => {
7
+ return React.useContext(PortalScopeContext);
8
8
  };
9
9
  /**
10
10
  * Disclaimer: Works only for components that don't show the portal immediately
11
11
  * That gives Portal time to receive scope on first render
12
12
  */
13
- const PortalProvider = (props) => {
14
- var _a;
15
- const { children, scopeRef } = props;
16
- const portal = usePortal();
17
- const nextScopeRef = scopeRef || portal.scopeRef;
18
- return ReactDOM.createPortal(React.createElement(PortalContext.Provider, { value: { scopeRef: nextScopeRef } },
19
- React.createElement(Theme, null, children)), ((_a = portal.scopeRef) === null || _a === void 0 ? void 0 : _a.current) || document.body);
13
+ const Portal = (props) => {
14
+ const { children, targetRef } = props;
15
+ /**
16
+ * Check for parent portal to render inside it
17
+ * To avoid z-iondex issues
18
+ * Example:
19
+ * Dropdown rendered on the page should render under the modal
20
+ * Dropdown inside the modal should render above it
21
+ */
22
+ const portal = usePortalScope();
23
+ const nextScopeRef = targetRef || portal.scopeRef;
24
+ /* Preserve the current theme when rendered in body */
25
+ return ReactDOM.createPortal(React.createElement(Theme, null, children), (nextScopeRef === null || nextScopeRef === void 0 ? void 0 : nextScopeRef.current) || document.body);
20
26
  };
21
- export default PortalProvider;
27
+ function PortalScope(props) {
28
+ const { children } = props;
29
+ const ref = React.useRef(null);
30
+ return (React.createElement(PortalScopeContext.Provider, { value: { scopeRef: ref } }, children(ref)));
31
+ }
32
+ Portal.Scope = PortalScope;
33
+ export default Portal;
@@ -1,6 +1,11 @@
1
1
  import React from "react";
2
2
  export type Props = {
3
3
  children?: React.ReactNode;
4
- scopeRef?: React.RefObject<HTMLElement | null>;
4
+ targetRef?: React.RefObject<HTMLElement | null>;
5
+ };
6
+ export type ScopeProps<T extends HTMLElement> = {
7
+ children: (ref: React.RefObject<T>) => React.ReactNode;
8
+ };
9
+ export type Context = {
10
+ scopeRef: React.RefObject<HTMLElement | null>;
5
11
  };
6
- export type Context = Pick<Props, "scopeRef">;
@@ -1,2 +1,2 @@
1
- export { default, usePortal } from "./Portal";
1
+ export { default } from "./Portal";
2
2
  export type { Props as PortalProps } from "./Portal.types";
@@ -1 +1 @@
1
- export { default, usePortal } from "./Portal.js";
1
+ export { default } from "./Portal.js";
@@ -3,16 +3,15 @@ import Portal from "../index.js";
3
3
  export default { title: "Utilities/Internal/Portal" };
4
4
  export const base = () => {
5
5
  const ref = React.useRef(null);
6
- return (<div style={{ border: "2px solid #333", padding: 16 }}>
7
- App
8
- <Portal scopeRef={ref}>
9
- <div ref={ref} style={{ border: "2px solid green", padding: 16 }}>
10
- Portal scope
11
- <div style={{ border: "2px solid red", padding: 16 }}>
12
- Stays in red
13
- <Portal>Ported to green</Portal>
14
- </div>
15
- </div>
16
- </Portal>
17
- </div>);
6
+ const [mounted, setMounted] = React.useState(false);
7
+ React.useEffect(() => {
8
+ setMounted(true);
9
+ }, []);
10
+ return (<>
11
+ <div style={{ border: "2px solid #333", padding: 16 }}>
12
+ App
13
+ {mounted && <Portal targetRef={ref}>Ported to green</Portal>}
14
+ </div>
15
+ <div ref={ref} style={{ border: "2px solid green", padding: 16 }}/>
16
+ </>);
18
17
  };
@@ -1,2 +1,2 @@
1
- import { UserThemeDefinition } from "../cli/theming/tokens/types";
1
+ import { UserThemeDefinition } from "../themes/_generator/tokens/types";
2
2
  export declare const getTheme: (theme?: UserThemeDefinition) => Record<"backgroundColor" | "textColor" | "borderColor" | "colors" | "borderRadius" | "spacing" | "boxShadow" | "screens", Record<string, string>>;
@@ -6,9 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getTheme = void 0;
7
7
  const reshaped_1 = __importDefault(require("../cli/theming/definitions/reshaped"));
8
8
  const base_1 = __importDefault(require("../cli/theming/definitions/base"));
9
- const string_1 = require("../cli/utilities/string");
10
- const color_1 = require("../cli/utilities/color");
11
- const mergeDefinitions_1 = __importDefault(require("../cli/theming/utilities/mergeDefinitions"));
9
+ const string_1 = require("../utilities/string");
10
+ const color_1 = require("../utilities/color");
11
+ const mergeDefinitions_1 = __importDefault(require("../themes/_generator/utilities/mergeDefinitions"));
12
12
  const getTheme = (theme) => {
13
13
  const config = {
14
14
  backgroundColor: {},
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import useRTL from "../useRTL.js";
3
- import { usePortal } from "../../components/_private/Portal/index.js";
4
3
  import { onNextFrame } from "../../utilities/animation.js";
4
+ import { getClosestScrollableParent } from "../../utilities/dom.js";
5
5
  const SCREEN_OFFSET = 16;
6
6
  const topPos = ["top-start", "top", "top-end"];
7
7
  const bottomPos = ["bottom-start", "bottom", "bottom-end"];
@@ -51,8 +51,8 @@ const fullyVisible = (bounds) => {
51
51
  /**
52
52
  * Calculate styles for the current position
53
53
  */
54
- const calculatePosition = (originBounds, targetBounds, options) => {
55
- const { position: passedPosition, rtl, width, isInsidePortal } = options;
54
+ const calculatePosition = (originBounds, targetBounds, parentOffset, options) => {
55
+ const { position: passedPosition, rtl, width } = options;
56
56
  let left = 0;
57
57
  let top = 0;
58
58
  let position = passedPosition;
@@ -116,9 +116,8 @@ const calculatePosition = (originBounds, targetBounds, options) => {
116
116
  if (top === undefined || left === undefined) {
117
117
  throw Error(`[Reshaped, flyout]: ${position} position is not valid`);
118
118
  }
119
- // When rendered inside portal, we don't need to accommodate for the page scroll
120
- top = Math.round(top + (isInsidePortal ? 0 : window.pageYOffset || 0));
121
- left = Math.round(left + (isInsidePortal ? 0 : window.pageXOffset || 0));
119
+ top = Math.round(top + (window.scrollY || 0) - parentOffset.top);
120
+ left = Math.round(left + (window.scrollX || 0) - parentOffset.left);
122
121
  let widthStyle = Math.ceil(targetBounds.width);
123
122
  const height = Math.ceil(targetBounds.height);
124
123
  if (width === "full") {
@@ -175,7 +174,13 @@ const flyout = (origin, target, options) => {
175
174
  }
176
175
  document.body.appendChild(targetClone);
177
176
  const targetBounds = targetClone.getBoundingClientRect();
178
- let calculated = calculatePosition(originBounds, targetBounds, options);
177
+ const scrollableParent = getClosestScrollableParent(origin);
178
+ const boundsDelta = scrollableParent.getBoundingClientRect();
179
+ const parentOffset = {
180
+ top: boundsDelta.top + document.documentElement.scrollTop - scrollableParent.scrollTop,
181
+ left: boundsDelta.left + document.documentElement.scrollLeft - scrollableParent.scrollLeft,
182
+ };
183
+ let calculated = calculatePosition(originBounds, targetBounds, parentOffset, options);
179
184
  if (!fullyVisible(calculated.styles) && !forcePosition) {
180
185
  const order = getPositionOrder(position);
181
186
  const mobileOrder = order.filter((position) => position === "top" || position === "bottom");
@@ -183,7 +188,7 @@ const flyout = (origin, target, options) => {
183
188
  const { fullWidth } = extraOptions;
184
189
  testOrder.some((currentPosition) => {
185
190
  const calculateOptions = Object.assign(Object.assign({}, options), { width: fullWidth ? "full" : options.width, position: currentPosition });
186
- const tested = calculatePosition(originBounds, targetBounds, calculateOptions);
191
+ const tested = calculatePosition(originBounds, targetBounds, parentOffset, calculateOptions);
187
192
  if (fullyVisible(tested.styles)) {
188
193
  calculated = tested;
189
194
  return true;
@@ -202,7 +207,8 @@ const flyout = (origin, target, options) => {
202
207
  const flyoutReducer = (state, action) => {
203
208
  switch (action.type) {
204
209
  case "render":
205
- return Object.assign(Object.assign({}, state), { status: "rendered" });
210
+ // Disable events before it's positioned to avoid mouseleave getting triggered
211
+ return Object.assign(Object.assign({}, state), { status: "rendered", styles: { pointerEvents: "none" } });
206
212
  case "position":
207
213
  return Object.assign(Object.assign({}, state), { status: state.status === "visible" ? "visible" : "positioned", position: action.payload.position, styles: Object.assign(Object.assign({}, defaultStyles), action.payload.styles) });
208
214
  case "show":
@@ -217,8 +223,6 @@ const flyoutReducer = (state, action) => {
217
223
  };
218
224
  const useFlyout = (originRef, targetRef, options) => {
219
225
  const { position: defaultPosition = "bottom", forcePosition, width } = options;
220
- const { scopeRef } = usePortal();
221
- const isInsidePortal = !!(scopeRef === null || scopeRef === void 0 ? void 0 : scopeRef.current);
222
226
  const [isRTL] = useRTL();
223
227
  const [state, dispatch] = React.useReducer(flyoutReducer, {
224
228
  position: defaultPosition,
@@ -245,10 +249,9 @@ const useFlyout = (originRef, targetRef, options) => {
245
249
  position: defaultPosition,
246
250
  forcePosition,
247
251
  rtl: isRTL,
248
- isInsidePortal,
249
252
  });
250
253
  dispatch({ type: "position", payload: nextFlyoutData });
251
- }, [originRef, targetRef, defaultPosition, isRTL, forcePosition, width, isInsidePortal]);
254
+ }, [originRef, targetRef, defaultPosition, isRTL, forcePosition, width]);
252
255
  React.useEffect(() => {
253
256
  if (state.status === "rendered")
254
257
  updatePosition();