diff --git a/.changeset/yellow-pots-hear.md b/.changeset/yellow-pots-hear.md new file mode 100644 index 00000000..ec472214 --- /dev/null +++ b/.changeset/yellow-pots-hear.md @@ -0,0 +1,5 @@ +--- +"@telegraph/modal": patch +--- + +Remove layer concept because of modal re-rendering issues diff --git a/packages/modal/package.json b/packages/modal/package.json index 240b9f70..94b98ffa 100644 --- a/packages/modal/package.json +++ b/packages/modal/package.json @@ -31,6 +31,7 @@ "dependencies": { "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-focus-scope": "^1.1.0", + "@radix-ui/react-portal": "^1.1.2", "@radix-ui/react-visually-hidden": "^1.1.0", "@telegraph/button": "workspace:^", "@telegraph/helpers": "workspace:^", diff --git a/packages/modal/src/Modal/Modal.helpers.tsx b/packages/modal/src/Modal/Modal.helpers.tsx deleted file mode 100644 index f6f6a9c5..00000000 --- a/packages/modal/src/Modal/Modal.helpers.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { - DismissableLayer, - type DismissableLayerProps, -} from "@radix-ui/react-dismissable-layer"; - -type DismissableWrapperProps = DismissableLayerProps & { - id: string; - layers: Array; - children: React.ReactNode; -}; - -// -// Handles the logic for when a global action like "esc" or clicking outside -// should close the modal. When stacking modals, we don't want to close the -// modal if we are not on the top-most layer. -// -const DismissableWrapper = ({ - id, - layers, - children, - ...props -}: DismissableWrapperProps) => { - const isTopLayer = id === layers[layers.length - 1]; - - if (isTopLayer) { - return {children}; - } - - return <>{children}; -}; - -export { DismissableWrapper }; diff --git a/packages/modal/src/Modal/Modal.tsx b/packages/modal/src/Modal/Modal.tsx index 4b239b02..b58062c8 100755 --- a/packages/modal/src/Modal/Modal.tsx +++ b/packages/modal/src/Modal/Modal.tsx @@ -1,4 +1,5 @@ import * as Dialog from "@radix-ui/react-dialog"; +import { DismissableLayer } from "@radix-ui/react-dismissable-layer"; import { FocusScope } from "@radix-ui/react-focus-scope"; import * as Portal from "@radix-ui/react-portal"; import { useControllableState } from "@radix-ui/react-use-controllable-state"; @@ -15,7 +16,6 @@ import { Box, Stack } from "@telegraph/layout"; import { AnimatePresence, motion } from "framer-motion"; import React from "react"; -import { DismissableWrapper } from "./Modal.helpers"; import { useModalStacking } from "./ModalStacking"; type RootProps = Omit< @@ -58,7 +58,6 @@ const RootComponent = ({ a11yTitle, a11yDescription, children, - layer: layerProp, // Focus scope props trapped, onMountAutoFocus, @@ -73,8 +72,8 @@ const RootComponent = ({ const stacking = useModalStacking(); React.useEffect(() => { if (!stacking || !open || stacking.layers.includes(id)) return; - stacking.addLayer(id, { layer: layerProp }); - }, [id, layerProp, stacking, open]); + stacking.addLayer(id); + }, [id, stacking, open]); const layer = stacking.layers?.indexOf(id) || 0; const layersLength = stacking.layers?.length || 0; @@ -82,9 +81,7 @@ const RootComponent = ({ const isTopLayer = id === stacking.layers[stacking.layers.length - 1]; return ( - { event.preventDefault(); stacking.removeTopLayer(); @@ -191,7 +188,7 @@ const RootComponent = ({ )} - + ); }; diff --git a/packages/modal/src/Modal/ModalStacking.tsx b/packages/modal/src/Modal/ModalStacking.tsx index 77a70e75..e95f5489 100644 --- a/packages/modal/src/Modal/ModalStacking.tsx +++ b/packages/modal/src/Modal/ModalStacking.tsx @@ -1,13 +1,9 @@ import React from "react"; -type AddLayerOptions = { - layer?: number; -}; - const ModalStackingContext = React.createContext<{ layers: Array; setLayers: React.Dispatch>>; - addLayer: (id: string, options: AddLayerOptions) => void; + addLayer: (id: string) => void; removeLayer: (id: string) => void; removeTopLayer: () => void; }>({ @@ -25,22 +21,8 @@ type ModalStackingProviderProps = { const ModalStackingProvider = ({ children }: ModalStackingProviderProps) => { const [layers, setLayers] = React.useState>([]); - const addLayer = (id: string, options: AddLayerOptions = {}) => { - const { layer } = options; - - // If a layer is specified, insert the layer at the specified index - // so that the modal renders at the correct layer in the stack. - if (typeof layer === "number") { - return setLayers((current) => { - if (current.length - 1 < layer) { - return [...current, id]; - } - - return [...current.slice(0, layer), id, ...current.slice(layer)]; - }); - } else { - setLayers((current) => [...current, id]); - } + const addLayer = (id: string) => { + setLayers((current) => [...current, id]); }; const removeLayer = (id: string) => { diff --git a/yarn.lock b/yarn.lock index 41ec0b42..011514ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4511,6 +4511,26 @@ __metadata: languageName: node linkType: hard +"@radix-ui/react-portal@npm:^1.1.2": + version: 1.1.2 + resolution: "@radix-ui/react-portal@npm:1.1.2" + dependencies: + "@radix-ui/react-primitive": "npm:2.0.0" + "@radix-ui/react-use-layout-effect": "npm:1.1.0" + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 10c0/836967330893b16b85371775ed1a59e038ce99189f4851cfa976bde2710d704c2a9e49e0a5206e7ac3fcf8a67ddd2d126b8352a88f295d6ef49d04e269736ed1 + languageName: node + linkType: hard + "@radix-ui/react-presence@npm:1.0.1": version: 1.0.1 resolution: "@radix-ui/react-presence@npm:1.0.1" @@ -6677,6 +6697,7 @@ __metadata: "@knocklabs/typescript-config": "npm:^0.0.2" "@radix-ui/react-dialog": "npm:^1.1.1" "@radix-ui/react-focus-scope": "npm:^1.1.0" + "@radix-ui/react-portal": "npm:^1.1.2" "@radix-ui/react-visually-hidden": "npm:^1.1.0" "@telegraph/button": "workspace:^" "@telegraph/helpers": "workspace:^"