diff --git a/apps/builder/app/builder/features/style-panel/sections/backgrounds/backgrounds.tsx b/apps/builder/app/builder/features/style-panel/sections/backgrounds/backgrounds.tsx index 5055d648d487..ae007adf8b3d 100644 --- a/apps/builder/app/builder/features/style-panel/sections/backgrounds/backgrounds.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/backgrounds/backgrounds.tsx @@ -36,6 +36,7 @@ export const Section = () => { onAdd={() => { addRepeatedStyleItem(styles, parseCssFragment("none", ["background"])); }} + collapsible > { styles, }) ); + const dots = getDots(styles); return ( { onOpenChange={setIsOpen} trigger={ diff --git a/apps/builder/app/builder/features/style-panel/sections/transitions/transitions.tsx b/apps/builder/app/builder/features/style-panel/sections/transitions/transitions.tsx index 940014b8a875..216cd9cf6344 100644 --- a/apps/builder/app/builder/features/style-panel/sections/transitions/transitions.tsx +++ b/apps/builder/app/builder/features/style-panel/sections/transitions/transitions.tsx @@ -93,6 +93,7 @@ export const Section = () => { selectedOrLastStyleSourceSelector && selectedOrLastStyleSourceSelector.state === undefined; const styles = useComputedStyles(transitionLongHandProperties); + const dots = getDots(styles); return ( { onOpenChange={setIsOpen} trigger={ { styleDecl.usedValue.type === "unparsed" || styleDecl.usedValue.type === "guaranteedInvalid" ) { - return; + return []; } const source = styleDecl.source.name; @@ -66,12 +66,16 @@ export const RepeatedStyleSection = (props: { label: string; description: string; properties: [StyleProperty, ...StyleProperty[]]; + collapsible?: boolean; onAdd: () => void; children: ReactNode; }) => { - const { label, description, children, properties, onAdd } = props; + const { label, description, children, properties, onAdd, collapsible } = + props; const [isOpen, setIsOpen] = useOpenState(props); const styles = useComputedStyles(properties); + const dots = getDots(styles); + return ( ({ @@ -77,6 +83,7 @@ const style = css({ overwritten: perColorStyle("overwritten"), remote: perColorStyle("remote"), code: perColorStyle("code"), + inactive: perColorStyle("inactive"), }, }, defaultVariants: { color: "default" }, diff --git a/packages/design-system/src/components/section-title.stories.tsx b/packages/design-system/src/components/section-title.stories.tsx index 844751dabd62..d4c610615953 100644 --- a/packages/design-system/src/components/section-title.stories.tsx +++ b/packages/design-system/src/components/section-title.stories.tsx @@ -17,12 +17,14 @@ const Wrap = ({ children }: { children: React.ReactNode }) => ( const Variants = ({ state, + inactive, }: { state: ComponentProps["data-state"]; + inactive?: ComponentProps["inactive"]; }) => ( <> - + Simplest @@ -30,6 +32,7 @@ const Variants = ({ } />} data-state={state} + inactive={inactive} > With button @@ -39,6 +42,7 @@ const Variants = ({ dots={["local", "remote"]} suffix={} />} data-state={state} + inactive={inactive} > With dots @@ -48,6 +52,7 @@ const Variants = ({ dots={["local"]} suffix={} />} data-state={state} + inactive={inactive} > With label @@ -57,6 +62,7 @@ const Variants = ({ dots={["local", "remote"]} suffix={} />} data-state={state} + inactive={inactive} > Some title so long that it cannot possibly fit @@ -64,7 +70,7 @@ const Variants = ({ - + Some title so long that it cannot possibly fit @@ -100,6 +106,12 @@ export const Demo = () => ( + + + + + + ); diff --git a/packages/design-system/src/components/section-title.tsx b/packages/design-system/src/components/section-title.tsx index ef297d2a54e9..ce4280347a85 100644 --- a/packages/design-system/src/components/section-title.tsx +++ b/packages/design-system/src/components/section-title.tsx @@ -75,11 +75,12 @@ const chevronStyle = css({ transition: "transform 150ms, opacity 200ms", color: theme.colors.backgroundIconSubtle, variants: { - state: { + openState: { open: { transform: "rotate(90deg)", }, closed: {}, + inactive: {}, }, }, }); @@ -100,8 +101,12 @@ const dotStyle = css({ }, }); -const context = createContext<{ state: "open" | "closed" }>({ - state: "closed", +const context = createContext<{ + openState: "open" | "closed"; + inactive: boolean; +}>({ + openState: "closed", + inactive: false, }); export const SectionTitle = forwardRef( @@ -112,8 +117,12 @@ export const SectionTitle = forwardRef( css, children, suffix, + inactive = false, + collapsible = true, ...props }: ComponentProps<"button"> & { + inactive?: boolean; + collapsible?: boolean; /** https://www.radix-ui.com/docs/primitives/components/collapsible#trigger */ "data-state"?: "open" | "closed"; dots?: Array<"local" | "overwritten" | "remote">; @@ -123,34 +132,39 @@ export const SectionTitle = forwardRef( }, ref: Ref ) => { - const state = props["data-state"] ?? "closed"; - const finalDots = state === "open" ? [] : (dots ?? []); + const openState = props["data-state"] ?? "closed"; + const finalDots = openState === "open" ? [] : (dots ?? []); return ( - + ( - - + {collapsible && ( + + )} {/* If the label is itself a button, we don't want to nest a button inside another button. Therefore, we render the label in a layer above the SectionTitle button */}
-
+
{children} {finalDots.length > 0 && ( @@ -187,11 +201,14 @@ export const SectionTitleLabel = forwardRef( }: Omit, "truncate" | "text">, ref: Ref ) => { - const { state } = useContext(context); + const { openState, inactive } = useContext(context); const commonCss = { flex: "0 1 auto" }; - - const color = state === "closed" ? undefined : props.color; + const color = inactive + ? "inactive" + : openState === "closed" + ? "default" + : props.color; const isButton = isLabelButton(color); @@ -202,9 +219,13 @@ export const SectionTitleLabel = forwardRef( {...props} color={color} css={{ - color: state === "closed" ? `var(${labelTextColor})` : undefined, + color: + openState === "closed" && inactive === false + ? `var(${labelTextColor})` + : undefined, ...commonCss, ...css, + // When we use a SectionTitle button, we can't directly render a label inside it. // Instead, we need to render the label using a div that has position:absolute and pointer-events:none // However, if the label itself is a button, we need to make sure that it remains clickable.