reshaped 2.2.1 → 2.3.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.
- package/CHANGELOG.md +16 -1
- package/bin/cli.js +1 -11
- package/bundle.css +1 -1
- package/bundle.js +10 -10
- package/cli/theming/definitions/base.d.ts +1 -1
- package/cli/theming/definitions/figma.d.ts +1 -1
- package/cli/theming/definitions/reshaped.d.ts +1 -1
- package/cli/theming/definitions/slate.d.ts +1 -1
- package/cli/theming/index.d.ts +4 -4
- package/cli/theming/index.js +7 -107
- package/components/Actionable/Actionable.js +14 -3
- package/components/Actionable/Actionable.module.css +1 -1
- package/components/Autocomplete/tests/Autocomplete.stories.js +27 -1
- package/components/Button/Button.js +2 -0
- package/components/Button/Button.module.css +1 -1
- package/components/Button/Button.types.d.ts +6 -0
- package/components/Button/ButtonGroup.d.ts +4 -0
- package/components/Button/ButtonGroup.js +9 -0
- package/components/Button/tests/Button.stories.d.ts +1 -0
- package/components/Button/tests/Button.stories.js +63 -0
- package/components/DropdownMenu/tests/DropdownMenu.stories.js +1 -0
- package/components/Overlay/Overlay.js +4 -4
- package/components/Tabs/Tabs.d.ts +1 -8
- package/components/Tabs/Tabs.module.css +1 -1
- package/components/Tabs/Tabs.types.d.ts +6 -7
- package/components/Tabs/TabsContext.d.ts +4 -0
- package/components/Tabs/TabsControlled.js +24 -1
- package/components/Tabs/TabsItem.d.ts +1 -8
- package/components/Tabs/TabsItem.js +22 -4
- package/components/Tabs/TabsList.js +30 -32
- package/components/Tabs/tests/Tabs.stories.d.ts +1 -0
- package/components/Tabs/tests/Tabs.stories.js +17 -0
- package/components/Toast/ToastContainer.js +1 -0
- package/components/Toast/ToastProvider.js +4 -0
- package/components/Toast/tests/Toast.stories.js +1 -0
- package/components/_private/Flyout/FlyoutContent.js +4 -2
- package/components/_private/Flyout/tests/Flyout.stories.d.ts +1 -0
- package/components/_private/Flyout/tests/Flyout.stories.js +25 -0
- package/components/_private/Portal/Portal.d.ts +8 -4
- package/components/_private/Portal/Portal.js +23 -11
- package/components/_private/Portal/Portal.types.d.ts +7 -2
- package/components/_private/Portal/index.d.ts +1 -1
- package/components/_private/Portal/index.js +1 -1
- package/components/_private/Portal/tests/Portal.stories.js +11 -12
- package/config/tailwind.d.ts +1 -1
- package/config/tailwind.js +3 -3
- package/hooks/_private/useFlyout.js +16 -13
- package/package.json +36 -28
- package/themes/_generator/tests/themes.stories.d.ts +6 -0
- package/themes/_generator/tests/themes.stories.js +32 -0
- package/{cli/theming → themes/_generator}/tokens/color/color.transforms.d.ts +1 -1
- package/{cli/theming → themes/_generator}/tokens/duration/duration.transforms.d.ts +1 -1
- package/{cli/theming → themes/_generator}/tokens/easing/easing.transforms.d.ts +1 -1
- package/{cli/theming → themes/_generator}/tokens/font/font.transforms.d.ts +1 -1
- package/{cli/theming → themes/_generator}/tokens/fontFamily/fontFamily.transforms.d.ts +1 -1
- package/{cli/theming → themes/_generator}/tokens/fontWeight/fontWeight.transforms.d.ts +1 -1
- package/{cli/theming → themes/_generator}/tokens/shadow/shadow.transforms.d.ts +1 -1
- package/{cli/theming → themes/_generator}/tokens/shadow/shadow.transforms.js +1 -1
- package/{cli/theming → themes/_generator}/tokens/unit/unit.transforms.d.ts +1 -1
- package/{cli/theming → themes/_generator}/tokens/viewport/viewport.transforms.d.ts +1 -1
- package/themes/_generator/transform.d.ts +7 -0
- package/themes/_generator/transform.js +57 -0
- package/{cli/theming → themes/_generator}/types.d.ts +3 -1
- package/{cli/theming → themes/_generator}/utilities/css.js +3 -1
- package/themes/_generator/utilities/generateBackgroundColors.d.ts +4 -0
- package/themes/_generator/utilities/generateBackgroundColors.js +54 -0
- package/themes/_generator/utilities/generateUnits.d.ts +4 -0
- package/themes/_generator/utilities/generateUnits.js +17 -0
- package/{cli/theming → themes/_generator}/utilities/mergeDeep.js +1 -3
- package/{cli/theming → themes/_generator}/utilities/resolveTokenReference.js +1 -3
- package/themes/index.d.ts +3 -0
- package/themes/index.js +5 -0
- package/types/config.d.ts +1 -1
- package/utilities/a11y.js +12 -3
- package/{cli/utilities → utilities}/color.d.ts +1 -1
- package/utilities/dom.d.ts +1 -0
- package/utilities/dom.js +9 -0
- package/cli/utilities/tests/color.test.d.ts +0 -1
- package/cli/utilities/tests/color.test.js +0 -63
- /package/{cli/theming → themes/_generator}/tokens/color/color.transforms.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/color/color.types.d.ts +0 -0
- /package/{cli/theming → themes/_generator}/tokens/color/color.types.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/duration/duration.transforms.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/duration/duration.types.d.ts +0 -0
- /package/{cli/theming → themes/_generator}/tokens/duration/duration.types.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/easing/easing.transforms.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/easing/easing.types.d.ts +0 -0
- /package/{cli/theming → themes/_generator}/tokens/easing/easing.types.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/font/font.transforms.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/font/font.types.d.ts +0 -0
- /package/{cli/theming → themes/_generator}/tokens/font/font.types.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/fontFamily/fontFamily.transforms.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/fontFamily/fontFamily.types.d.ts +0 -0
- /package/{cli/theming → themes/_generator}/tokens/fontFamily/fontFamily.types.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/fontWeight/fontWeight.transforms.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/fontWeight/fontWeight.types.d.ts +0 -0
- /package/{cli/theming → themes/_generator}/tokens/fontWeight/fontWeight.types.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/shadow/shadow.types.d.ts +0 -0
- /package/{cli/theming → themes/_generator}/tokens/shadow/shadow.types.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/transforms.d.ts +0 -0
- /package/{cli/theming → themes/_generator}/tokens/transforms.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/types.d.ts +0 -0
- /package/{cli/theming → themes/_generator}/tokens/types.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/unit/unit.transforms.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/unit/unit.types.d.ts +0 -0
- /package/{cli/theming → themes/_generator}/tokens/unit/unit.types.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/viewport/viewport.transforms.js +0 -0
- /package/{cli/theming → themes/_generator}/tokens/viewport/viewport.types.d.ts +0 -0
- /package/{cli/theming → themes/_generator}/tokens/viewport/viewport.types.js +0 -0
- /package/{cli/theming → themes/_generator}/types.js +0 -0
- /package/{cli/theming → themes/_generator}/utilities/css.d.ts +0 -0
- /package/{cli/theming → themes/_generator}/utilities/mergeDeep.d.ts +0 -0
- /package/{cli/theming → themes/_generator}/utilities/mergeDefinitions.d.ts +0 -0
- /package/{cli/theming → themes/_generator}/utilities/mergeDefinitions.js +0 -0
- /package/{cli/theming → themes/_generator}/utilities/resolveTokenReference.d.ts +0 -0
- /package/{cli/utilities → utilities}/color.js +0 -0
- /package/{cli/utilities → utilities}/string.d.ts +0 -0
- /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: {
|
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<
|
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,
|
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
|
-
|
25
|
-
|
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:
|
51
|
-
scaleY:
|
52
|
-
top:
|
53
|
-
left:
|
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 (
|
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
|
99
|
-
|
100
|
-
|
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
|
140
|
-
return
|
141
|
-
|
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>
|
@@ -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) => {
|
@@ -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
|
-
|
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;
|
@@ -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";
|
@@ -92,6 +93,30 @@ export const testWidthOverflowOnMobile = () => (<Demo position="bottom-start" wi
|
|
92
93
|
<button type="button">Item 2</button>
|
93
94
|
<button type="button">Close</button>
|
94
95
|
</Demo>);
|
96
|
+
export const testInsideScrollArea = () => (<Example>
|
97
|
+
<Example.Item title="should move the content on area scroll">
|
98
|
+
<div style={{ overflow: "scroll", height: 200, margin: 40, position: "relative" }}>
|
99
|
+
<Flyout triggerType="click" position="bottom-start">
|
100
|
+
<Flyout.Trigger>{(attributes) => <button {...attributes}>Foo</button>}</Flyout.Trigger>
|
101
|
+
<Flyout.Content>
|
102
|
+
<div style={{
|
103
|
+
background: "var(--rs-color-background-elevation-overlay)",
|
104
|
+
padding: "var(--rs-unit-x4)",
|
105
|
+
height: 100,
|
106
|
+
width: 160,
|
107
|
+
borderRadius: "var(--rs-unit-radius-medium)",
|
108
|
+
border: "1px solid var(--rs-color-border-neutral-faded)",
|
109
|
+
boxSizing: "border-box",
|
110
|
+
}}>
|
111
|
+
{"Content"}
|
112
|
+
</div>
|
113
|
+
</Flyout.Content>
|
114
|
+
</Flyout>
|
115
|
+
<View height="300px" backgroundColor="neutral-faded"/>
|
116
|
+
</div>
|
117
|
+
<div style={{ height: 2000 }}/>
|
118
|
+
</Example.Item>
|
119
|
+
</Example>);
|
95
120
|
export const widthTrigger = () => (<Flyout triggerType="click" width="trigger" position="bottom">
|
96
121
|
<Flyout.Trigger>
|
97
122
|
{(attributes) => <button {...attributes}>Trigger with long text</button>}
|
@@ -1,9 +1,13 @@
|
|
1
|
-
|
1
|
+
import React from "react";
|
2
2
|
import type * as T from "./Portal.types";
|
3
|
-
export declare const
|
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
|
9
|
-
|
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
|
6
|
-
export const
|
7
|
-
return React.useContext(
|
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
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
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
|
-
|
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
|
1
|
+
export { default } from "./Portal";
|
2
2
|
export type { Props as PortalProps } from "./Portal.types";
|
@@ -1 +1 @@
|
|
1
|
-
export { default
|
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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
};
|
package/config/tailwind.d.ts
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
import { UserThemeDefinition } from "../
|
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>>;
|
package/config/tailwind.js
CHANGED
@@ -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("../
|
10
|
-
const color_1 = require("../
|
11
|
-
const mergeDefinitions_1 = __importDefault(require("../
|
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
|
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
|
-
|
120
|
-
|
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
|
-
|
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
|
-
|
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
|
254
|
+
}, [originRef, targetRef, defaultPosition, isRTL, forcePosition, width]);
|
252
255
|
React.useEffect(() => {
|
253
256
|
if (state.status === "rendered")
|
254
257
|
updatePosition();
|