Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the start of the multi-side / all side dropdown adjustment

Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,31 @@ export const Border = observer(() => {

const [activeTab, setActiveTab] = useState<BorderTab>(areAllBordersEqual ? BorderTab.ALL : BorderTab.INDIVIDUAL);

// Track if user is actively interacting with the input
const [isUserInteracting, setIsUserInteracting] = useState(false);

// Determine if we should show "Mixed" in the input when on "All sides" tab
const shouldShowMixed = activeTab === BorderTab.ALL && !areAllBordersEqual && !isUserInteracting;

// Custom onChange handler that tracks user interaction
const handleBorderChange = (value: number) => {
setIsUserInteracting(true);
handleBoxChange('borderWidth', value.toString());
};

// Reset interaction state when switching tabs or closing dropdown
const handleTabChange = (tab: BorderTab) => {
setActiveTab(tab);
setIsUserInteracting(false);
};

const handleOpenChange = (open: boolean) => {
onOpenChange(open);
if (!open) {
setIsUserInteracting(false);
}
};

const getBorderDisplay = () => {
const top = boxState.borderTopWidth.num ?? 0;
const right = boxState.borderRightWidth.num ?? 0;
Expand All @@ -64,7 +89,7 @@ export const Border = observer(() => {
const borderValue = getBorderDisplay()

return (
<DropdownMenu open={isOpen} onOpenChange={onOpenChange} modal={false}>
<DropdownMenu open={isOpen} onOpenChange={handleOpenChange} modal={false}>
<HoverOnlyTooltip
content="Border"
side="bottom"
Expand Down Expand Up @@ -93,7 +118,7 @@ export const Border = observer(() => {
>
<div className="flex items-center gap-2 mb-3">
<button
onClick={() => setActiveTab(BorderTab.ALL)}
onClick={() => handleTabChange(BorderTab.ALL)}
className={`flex-1 text-sm px-4 py-1.5 rounded-md transition-colors cursor-pointer ${activeTab === BorderTab.ALL
? 'text-foreground-primary bg-background-active/50'
: 'text-muted-foreground hover:bg-background-tertiary/20 hover:text-foreground-hover'
Expand All @@ -102,7 +127,7 @@ export const Border = observer(() => {
All sides
</button>
<button
onClick={() => setActiveTab(BorderTab.INDIVIDUAL)}
onClick={() => handleTabChange(BorderTab.INDIVIDUAL)}
className={`flex-1 text-sm px-4 py-1.5 rounded-md transition-colors cursor-pointer ${activeTab === BorderTab.INDIVIDUAL
? 'text-foreground-primary bg-background-active/50'
: 'text-muted-foreground hover:bg-background-tertiary/20 hover:text-foreground-hover'
Expand All @@ -113,10 +138,11 @@ export const Border = observer(() => {
</div>
{activeTab === BorderTab.ALL ? (
<InputRange
value={boxState.borderWidth.num ?? 0}
onChange={(value) => handleBoxChange('borderWidth', value.toString())}
value={shouldShowMixed ? 0 : (boxState.borderWidth.num ?? 0)}
onChange={handleBorderChange}
unit={boxState.borderWidth.unit}
onUnitChange={(unit) => handleUnitChange('borderWidth', unit)}
displayValue={shouldShowMixed ? 'Mixed' : undefined}
/>
) : (
<SpacingInputs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,31 @@ export const Margin = observer(() => {

const [activeTab, setActiveTab] = useState<MarginTab>(areAllMarginsEqual ? MarginTab.ALL : MarginTab.INDIVIDUAL);

// Track if user is actively interacting with the input
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the border component, the margin component duplicates isUserInteracting logic. Consider refactoring this into a shared hook.

const [isUserInteracting, setIsUserInteracting] = useState(false);

// Determine if we should show "Mixed" in the input when on "All sides" tab
const shouldShowMixed = activeTab === MarginTab.ALL && !areAllMarginsEqual && !isUserInteracting;

// Custom onChange handler that tracks user interaction
const handleMarginChange = (value: number) => {
setIsUserInteracting(true);
handleBoxChange('margin', value.toString());
};

// Reset interaction state when switching tabs or closing dropdown
const handleTabChange = (tab: MarginTab) => {
setActiveTab(tab);
setIsUserInteracting(false);
};

const handleOpenChange = (open: boolean) => {
onOpenChange(open);
if (!open) {
setIsUserInteracting(false);
}
};

const getMarginIcon = () => {
const margins = {
top: boxState.marginTop.num ?? 0,
Expand Down Expand Up @@ -148,7 +173,7 @@ export const Margin = observer(() => {
const marginValue = getMarginDisplay();

return (
<DropdownMenu open={isOpen} onOpenChange={onOpenChange} modal={false}>
<DropdownMenu open={isOpen} onOpenChange={handleOpenChange} modal={false}>
<HoverOnlyTooltip content="Margin" side="bottom" className="mt-1" hideArrow disabled={isOpen}>
<DropdownMenuTrigger asChild>
<ToolbarButton
Expand All @@ -168,16 +193,16 @@ export const Margin = observer(() => {
>
<div className="mb-3 flex items-center gap-2">
<button
onClick={() => setActiveTab(MarginTab.ALL)}
onClick={() => handleTabChange(MarginTab.ALL)}
className={`flex-1 cursor-pointer rounded-md px-4 py-1.5 text-sm transition-colors ${activeTab === MarginTab.ALL
? "bg-background-active/50 text-foreground-primary"
: "text-muted-foreground hover:bg-background-tertiary/20 hover:text-foreground-hover"
}`}
>
{areAllMarginsEqual ? "All sides" : "Mixed"}
All sides
</button>
<button
onClick={() => setActiveTab(MarginTab.INDIVIDUAL)}
onClick={() => handleTabChange(MarginTab.INDIVIDUAL)}
className={`flex-1 cursor-pointer rounded-md px-4 py-1.5 text-sm transition-colors ${activeTab === MarginTab.INDIVIDUAL
? "bg-background-active/50 text-foreground-primary"
: "text-muted-foreground hover:bg-background-tertiary/20 hover:text-foreground-hover"
Expand All @@ -188,10 +213,11 @@ export const Margin = observer(() => {
</div>
{activeTab === MarginTab.ALL ? (
<InputRange
value={boxState.margin.num ?? 0}
onChange={(value) => handleBoxChange('margin', value.toString())}
value={shouldShowMixed ? 0 : (boxState.margin.num ?? 0)}
onChange={handleMarginChange}
unit={boxState.margin.unit}
onUnitChange={(unit) => handleUnitChange('margin', unit)}
displayValue={shouldShowMixed ? 'Mixed' : undefined}
/>
) : (
<SpacingInputs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,31 @@ export const Padding = observer(() => {

const [activeTab, setActiveTab] = useState<PaddingTab>(areAllPaddingsEqual ? PaddingTab.ALL : PaddingTab.INDIVIDUAL);

// Track if user is actively interacting with the input
const [isUserInteracting, setIsUserInteracting] = useState(false);

// Determine if we should show "Mixed" in the input when on "All sides" tab
const shouldShowMixed = activeTab === PaddingTab.ALL && !areAllPaddingsEqual && !isUserInteracting;

// Custom onChange handler that tracks user interaction
const handlePaddingChange = (value: number) => {
setIsUserInteracting(true);
handleBoxChange('padding', value.toString());
};

// Reset interaction state when switching tabs or closing dropdown
const handleTabChange = (tab: PaddingTab) => {
setActiveTab(tab);
setIsUserInteracting(false);
};

const handleOpenChange = (open: boolean) => {
onOpenChange(open);
if (!open) {
setIsUserInteracting(false);
}
};

const getPaddingIcon = () => {
const paddings = {
top: boxState.paddingTop.num ?? 0,
Expand Down Expand Up @@ -123,7 +148,7 @@ export const Padding = observer(() => {
const paddingValue = getPaddingDisplay();

return (
<DropdownMenu open={isOpen} onOpenChange={onOpenChange} modal={false}>
<DropdownMenu open={isOpen} onOpenChange={handleOpenChange} modal={false}>
<HoverOnlyTooltip content="Padding" side="bottom">
<DropdownMenuTrigger asChild>
<ToolbarButton
Expand All @@ -140,16 +165,16 @@ export const Padding = observer(() => {
<DropdownMenuContent align="start" className="w-[280px] mt-1 p-3 rounded-lg">
<div className="flex items-center gap-2 mb-3">
<button
onClick={() => setActiveTab(PaddingTab.ALL)}
onClick={() => handleTabChange(PaddingTab.ALL)}
className={`flex-1 text-sm px-4 py-1.5 rounded-md transition-colors cursor-pointer ${activeTab === PaddingTab.ALL
? 'text-foreground-primary bg-background-active/50'
: 'text-muted-foreground hover:bg-background-tertiary/20 hover:text-foreground-hover'
}`}
>
{areAllPaddingsEqual ? "All sides" : "Mixed"}
All sides
</button>
<button
onClick={() => setActiveTab(PaddingTab.INDIVIDUAL)}
onClick={() => handleTabChange(PaddingTab.INDIVIDUAL)}
className={`flex-1 text-sm px-4 py-1.5 rounded-md transition-colors cursor-pointer ${activeTab === PaddingTab.INDIVIDUAL
? 'text-foreground-primary bg-background-active/50'
: 'text-muted-foreground hover:bg-background-tertiary/20 hover:text-foreground-hover'
Expand All @@ -160,10 +185,11 @@ export const Padding = observer(() => {
</div>
{activeTab === PaddingTab.ALL ? (
<InputRange
value={boxState.padding.num ?? 0}
onChange={(value) => handleBoxChange('padding', value.toString())}
value={shouldShowMixed ? 0 : (boxState.padding.num ?? 0)}
onChange={handlePaddingChange}
unit={boxState.padding.unit}
onUnitChange={(unit) => handleUnitChange('padding', unit)}
displayValue={shouldShowMixed ? 'Mixed' : undefined}
/>
) : (
<SpacingInputs
Expand Down
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-side / all side dropdown adjustment

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
import { Icons } from "@onlook/ui/icons";
import { cn } from "@onlook/ui/utils";
import { observer } from "mobx-react-lite";
import { useState } from "react";
import { useMemo, useState } from "react";
import { useBoxControl } from "../hooks/use-box-control";
import { useDropdownControl } from "../hooks/use-dropdown-manager";
import { HoverOnlyTooltip } from "../hover-tooltip";
Expand All @@ -17,13 +17,52 @@ import { SpacingInputs } from "../inputs/spacing-inputs";
import { ToolbarButton } from "../toolbar-button";

export const Radius = observer(() => {
const [activeTab, setActiveTab] = useState('all');
const { boxState, handleBoxChange, handleUnitChange, handleIndividualChange } = useBoxControl('radius');

const areAllCornersEqual = useMemo((): boolean => {
const corners = {
topLeft: boxState.borderTopLeftRadius.num ?? 0,
topRight: boxState.borderTopRightRadius.num ?? 0,
bottomRight: boxState.borderBottomRightRadius.num ?? 0,
bottomLeft: boxState.borderBottomLeftRadius.num ?? 0,
};

const values = Object.values(corners);

return values.every(val => val === values[0]);
}, [boxState.borderTopLeftRadius.num, boxState.borderTopRightRadius.num, boxState.borderBottomRightRadius.num, boxState.borderBottomLeftRadius.num]);

const [activeTab, setActiveTab] = useState(areAllCornersEqual ? 'all' : 'individual');

const { isOpen, onOpenChange } = useDropdownControl({
id: 'radius-dropdown'
});

// Track if user is actively interacting with the input
const [isUserInteracting, setIsUserInteracting] = useState(false);

// Determine if we should show "Mixed" in the input when on "All sides" tab
const shouldShowMixed = activeTab === 'all' && !areAllCornersEqual && !isUserInteracting;

// Custom onChange handler that tracks user interaction
const handleRadiusChange = (value: number) => {
setIsUserInteracting(true);
handleBoxChange('borderRadius', value.toString());
};

// Reset interaction state when switching tabs or closing dropdown
const handleTabChange = (tab: string) => {
setActiveTab(tab);
setIsUserInteracting(false);
};

const handleOpenChange = (open: boolean) => {
onOpenChange(open);
if (!open) {
setIsUserInteracting(false);
}
};

const getRadiusIcon = () => {
const topLeft = boxState.borderTopLeftRadius.num ?? 0;
const topRight = boxState.borderTopRightRadius.num ?? 0;
Expand Down Expand Up @@ -101,7 +140,7 @@ export const Radius = observer(() => {
const radiusValue = getRadiusDisplay();

return (
<DropdownMenu open={isOpen} onOpenChange={onOpenChange} modal={false}>
<DropdownMenu open={isOpen} onOpenChange={handleOpenChange} modal={false}>
<HoverOnlyTooltip content="Radius" side="bottom" className="mt-1" hideArrow disabled={isOpen}>
<DropdownMenuTrigger asChild>
<ToolbarButton
Expand All @@ -118,7 +157,7 @@ export const Radius = observer(() => {
<DropdownMenuContent align="start" className="w-[280px] mt-1 p-3 rounded-lg">
<div className="flex items-center gap-2 mb-3">
<button
onClick={() => setActiveTab('all')}
onClick={() => handleTabChange('all')}
className={cn(
'flex-1 text-sm px-4 py-1.5 rounded-md transition-colors cursor-pointer',
activeTab === 'all'
Expand All @@ -129,7 +168,7 @@ export const Radius = observer(() => {
All sides
</button>
<button
onClick={() => setActiveTab('individual')}
onClick={() => handleTabChange('individual')}
className={cn('flex-1 text-sm px-4 py-1.5 rounded-md transition-colors cursor-pointer',
activeTab === 'individual'
? 'text-foreground-primary bg-background-active/50'
Expand All @@ -141,10 +180,11 @@ export const Radius = observer(() => {
</div>
{activeTab === 'all' ? (
<InputRange
value={boxState.borderRadius.num ?? 0}
onChange={(value) => handleBoxChange('borderRadius', value.toString())}
value={shouldShowMixed ? 0 : (boxState.borderRadius.num ?? 0)}
onChange={handleRadiusChange}
unit={boxState.borderRadius.unit}
onUnitChange={(unit) => handleUnitChange('borderRadius', unit)}
displayValue={shouldShowMixed ? 'Mixed' : undefined}
/>
) : (
<SpacingInputs
Expand Down
Loading
Loading