diff --git a/.gitignore b/.gitignore index c4686499c..dd4229320 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,7 @@ cypress/screenshots # So devs can maintain their own todo lists in the project TODO.md + +# For those using yalc to test publishes locally +.yalc +yalc.lock diff --git a/.yarn/versions/d754df84.yml b/.yarn/versions/d754df84.yml new file mode 100644 index 000000000..16ec2edc2 --- /dev/null +++ b/.yarn/versions/d754df84.yml @@ -0,0 +1,13 @@ +releases: + "@radix-ui/react-context-menu": patch + "@radix-ui/react-dropdown-menu": patch + "@radix-ui/react-hover-card": patch + "@radix-ui/react-menu": patch + "@radix-ui/react-menubar": patch + "@radix-ui/react-popover": patch + "@radix-ui/react-popper": minor + "@radix-ui/react-select": minor + "@radix-ui/react-tooltip": patch + +declined: + - primitives diff --git a/build.mjs b/build.mjs index 088d62709..f1676380a 100644 --- a/build.mjs +++ b/build.mjs @@ -1,6 +1,8 @@ import { globSync } from 'glob'; import * as esbuild from 'esbuild'; import * as tsup from 'tsup'; +import { basename } from 'path'; +import { copyFileSync } from 'fs'; async function build(path) { const file = `${path}/src/index.ts`; @@ -43,6 +45,14 @@ async function build(path) { external: [/@radix-ui\/.+/], }); console.log(`Built ${path}/dist/index.d.ts`); + + const cssFiles = globSync(`${path}/src/**/*.css`); + cssFiles.forEach((cssFile) => { + const fileName = basename(cssFile); + const dest = `${dist}/${fileName}`; + copyFileSync(cssFile, dest); + console.log(`Copied ${cssFile} to ${dest}`); + }); } globSync('packages/*/*').forEach(build); diff --git a/packages/react/popper/package.json b/packages/react/popper/package.json index 8626068d8..fcff4c6c7 100644 --- a/packages/react/popper/package.json +++ b/packages/react/popper/package.json @@ -12,6 +12,10 @@ "types": "./dist/index.d.ts", "default": "./dist/index.js" } + }, + "./styles": { + "import": "./dist/popperStyles.css", + "require": "./dist/popperStyles.css" } }, "source": "./src/index.ts", diff --git a/packages/react/popper/src/Popper.tsx b/packages/react/popper/src/Popper.tsx index 16ab3da7f..f2276b6c4 100644 --- a/packages/react/popper/src/Popper.tsx +++ b/packages/react/popper/src/Popper.tsx @@ -123,6 +123,7 @@ interface PopperContentProps extends PrimitiveDivProps { hideWhenDetached?: boolean; updatePositionStrategy?: 'optimized' | 'always'; onPlaced?: () => void; + skipStyleInjection?: boolean; } const PopperContent = React.forwardRef( @@ -141,6 +142,7 @@ const PopperContent = React.forwardRef hideWhenDetached = false, updatePositionStrategy = 'optimized', onPlaced, + skipStyleInjection, ...contentProps } = props; @@ -225,32 +227,82 @@ const PopperContent = React.forwardRef const cannotCenterArrow = middlewareData.arrow?.centerOffset !== 0; const [contentZIndex, setContentZIndex] = React.useState(); + const updateStyleVariables = React.useCallback(() => { + if (refs.floating.current) { + if (content) { + if (skipStyleInjection) { + refs.floating.current.style.zIndex = window.getComputedStyle(content).zIndex; + } else { + setContentZIndex(window.getComputedStyle(content).zIndex); + } + } + + /* if the PopperContent hasn't been placed yet (not all measurements done) + * we prevent animations so user's animation don't kick in too early referring wrong sides + */ + refs.floating.current.style.setProperty( + '--popper-content-animation', + isPositioned ? 'none' : 'unset' + ); + /* Keep off page when measuring */ + refs.floating.current.style.transform = isPositioned + ? (floatingStyles.transform as string) + : 'translate(0, -200%)'; + + refs.floating.current.style.setProperty( + '--radix-popper-transform-origin', + `${middlewareData.transformOrigin?.x} ${middlewareData.transformOrigin?.y}` + ); + + /* hide the content if using the hid emiddleware and should be hidden + * set visibility to hidden and disable pointer events so the UI behaves + * as if the PopperContent isn't there at all + */ + refs.floating.current.style.visibility = middlewareData.hide?.referenceHidden + ? 'hidden' + : 'unset'; + refs.floating.current.style.pointerEvents = middlewareData.hide?.referenceHidden + ? 'none' + : 'unset'; + + refs.floating.current.style.position = floatingStyles.position as string; + refs.floating.current.style.left = floatingStyles.left as string; + refs.floating.current.style.top = floatingStyles.top as string; + } + }, [content, floatingStyles, isPositioned, middlewareData, refs.floating, skipStyleInjection]); + useLayoutEffect(() => { - if (content) setContentZIndex(window.getComputedStyle(content).zIndex); - }, [content]); + updateStyleVariables(); + }, [updateStyleVariables]); + + debugger; return ( diff --git a/packages/react/popper/src/popperStyles.css b/packages/react/popper/src/popperStyles.css new file mode 100644 index 000000000..9374dad21 --- /dev/null +++ b/packages/react/popper/src/popperStyles.css @@ -0,0 +1,11 @@ +[data-radix-popper-content-wrapper] { + --popper-content-wrapper-z-index: 0; + --popper-content-animation: unset; + + min-width: max-content; + z-index: var(--popper-content-wrapper-z-index); +} + +[data-radix-popper-content] { + animation: var(--popper-content-animation); +} diff --git a/packages/react/select/package.json b/packages/react/select/package.json index eb663fee1..88bde1ae8 100644 --- a/packages/react/select/package.json +++ b/packages/react/select/package.json @@ -12,6 +12,10 @@ "types": "./dist/index.d.ts", "default": "./dist/index.js" } + }, + "./styles": { + "import": "./dist/selectStyles.css", + "require": "./dist/selectStyles.css" } }, "source": "./src/index.ts", diff --git a/packages/react/select/src/Select.tsx b/packages/react/select/src/Select.tsx index 53fbd1ff5..78c42437c 100644 --- a/packages/react/select/src/Select.tsx +++ b/packages/react/select/src/Select.tsx @@ -322,12 +322,21 @@ type SelectValueElement = React.ElementRef; type PrimitiveSpanProps = React.ComponentPropsWithoutRef; interface SelectValueProps extends Omit { placeholder?: React.ReactNode; + skipStyleInjection?: boolean; } const SelectValue = React.forwardRef( (props: ScopedProps, forwardedRef) => { // We ignore `className` and `style` as this part shouldn't be styled. - const { __scopeSelect, className, style, children, placeholder = '', ...valueProps } = props; + const { + __scopeSelect, + className, + style, + children, + placeholder = '', + skipStyleInjection, + ...valueProps + } = props; const context = useSelectContext(VALUE_NAME, __scopeSelect); const { onValueNodeHasChildrenChange } = context; const hasChildren = children !== undefined; @@ -339,11 +348,10 @@ const SelectValue = React.forwardRef( return ( {shouldShowPlaceholder(context.value) ? <>{placeholder} : children} @@ -491,6 +499,7 @@ interface SelectContentImplProps onPointerDownOutside?: DismissableLayerProps['onPointerDownOutside']; position?: 'item-aligned' | 'popper'; + skipStyleInjection?: boolean; } const SelectContentImpl = React.forwardRef( @@ -513,6 +522,7 @@ const SelectContentImpl = React.forwardRef context.onOpenChange(false)} > setIsPositioned(true)} ref={composedRefs} - style={{ - // flex layout so we can place the scroll buttons properly - display: 'flex', - flexDirection: 'column', - // reset the outline by default as the content MAY get focused - outline: 'none', - ...contentProps.style, - }} + style={ + skipStyleInjection + ? undefined + : { + // flex layout so we can place the scroll buttons properly + display: 'flex', + flexDirection: 'column', + // reset the outline by default as the content MAY get focused + outline: 'none', + ...contentProps.style, + } + } onKeyDown={composeEventHandlers(contentProps.onKeyDown, (event) => { const isModifierKey = event.ctrlKey || event.altKey || event.metaKey; @@ -781,13 +796,15 @@ SelectContentImpl.displayName = CONTENT_IMPL_NAME; const ITEM_ALIGNED_POSITION_NAME = 'SelectItemAlignedPosition'; type SelectItemAlignedPositionElement = React.ElementRef; -interface SelectItemAlignedPositionProps extends PrimitiveDivProps, SelectPopperPrivateProps {} +interface SelectItemAlignedPositionProps extends PrimitiveDivProps, SelectPopperPrivateProps { + skipStyleInjection?: boolean; +} const SelectItemAlignedPosition = React.forwardRef< SelectItemAlignedPositionElement, SelectItemAlignedPositionProps >((props: ScopedProps, forwardedRef) => { - const { __scopeSelect, onPlaced, ...popperProps } = props; + const { __scopeSelect, onPlaced, skipStyleInjection, ...popperProps } = props; const context = useSelectContext(CONTENT_NAME, __scopeSelect); const contentContext = useSelectContentContext(CONTENT_NAME, __scopeSelect); const [contentWrapper, setContentWrapper] = React.useState(null); @@ -930,8 +947,14 @@ const SelectItemAlignedPosition = React.forwardRef< // copy z-index from content to wrapper const [contentZIndex, setContentZIndex] = React.useState(); useLayoutEffect(() => { - if (content) setContentZIndex(window.getComputedStyle(content).zIndex); - }, [content]); + if (content && contentWrapper) { + if (skipStyleInjection) { + contentWrapper.style.zIndex = window.getComputedStyle(content).zIndex; + } else { + setContentZIndex(window.getComputedStyle(content).zIndex); + } + } + }, [content, contentWrapper, skipStyleInjection]); // When the viewport becomes scrollable at the top, the scroll up button will mount. // Because it is part of the normal flow, it will push down the viewport, thus throwing our @@ -956,25 +979,35 @@ const SelectItemAlignedPosition = React.forwardRef< onScrollButtonChange={handleScrollButtonChange} >
@@ -991,7 +1024,9 @@ const POPPER_POSITION_NAME = 'SelectPopperPosition'; type SelectPopperPositionElement = React.ElementRef; type PopperContentProps = React.ComponentPropsWithoutRef; -interface SelectPopperPositionProps extends PopperContentProps, SelectPopperPrivateProps {} +interface SelectPopperPositionProps extends PopperContentProps, SelectPopperPrivateProps { + skipStyleInjection?: boolean; +} const SelectPopperPosition = React.forwardRef< SelectPopperPositionElement, @@ -1001,30 +1036,36 @@ const SelectPopperPosition = React.forwardRef< __scopeSelect, align = 'start', collisionPadding = CONTENT_MARGIN, + skipStyleInjection, ...popperProps } = props; const popperScope = usePopperScope(__scopeSelect); return ( ); }); @@ -1050,11 +1091,12 @@ type SelectViewportElement = React.ElementRef; type PrimitiveDivProps = React.ComponentPropsWithoutRef; interface SelectViewportProps extends PrimitiveDivProps { nonce?: string; + skipStyleInjection?: boolean; } const SelectViewport = React.forwardRef( (props: ScopedProps, forwardedRef) => { - const { __scopeSelect, nonce, ...viewportProps } = props; + const { __scopeSelect, nonce, skipStyleInjection, ...viewportProps } = props; const contentContext = useSelectContentContext(VIEWPORT_NAME, __scopeSelect); const viewportContext = useSelectViewportContext(VIEWPORT_NAME, __scopeSelect); const composedRefs = useComposedRefs(forwardedRef, contentContext.onViewportChange); @@ -1062,27 +1104,33 @@ const SelectViewport = React.forwardRef {/* Hide scrollbars cross-browser and enable momentum scroll for touch devices */} -