Skip to content

Commit 1592875

Browse files
fix: ui editor sanding (#2551)
* fix: prevent tooltips from glitching out * refactor: optimize scroll behavior in color picker component * refactor: improve styling and layout of tabs in color picker component * refactor: update settings modal and change order of screen sizes * removed border from color picker buttons to remain on-brand
1 parent 687901e commit 1592875

File tree

10 files changed

+125
-137
lines changed

10 files changed

+125
-137
lines changed

apps/web/client/src/app/project/[id]/_components/editor-bar/dropdowns/padding.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export const Padding = observer(() => {
124124

125125
return (
126126
<DropdownMenu open={isOpen} onOpenChange={onOpenChange} modal={false}>
127-
<HoverOnlyTooltip content="Padding" side="bottom" className="mt-1" hideArrow disabled={isOpen}>
127+
<HoverOnlyTooltip content="Padding" side="bottom">
128128
<DropdownMenuTrigger asChild>
129129
<ToolbarButton
130130
isOpen={isOpen}

apps/web/client/src/app/project/[id]/_components/editor-bar/hover-tooltip.tsx

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Tooltip, TooltipTrigger, TooltipContent } from "@onlook/ui/tooltip";
2-
import { useState } from "react";
32
import type { ReactNode } from "react";
43

54
interface HoverOnlyTooltipProps {
@@ -9,29 +8,26 @@ interface HoverOnlyTooltipProps {
98
className?: string;
109
hideArrow?: boolean;
1110
disabled?: boolean;
11+
sideOffset?: number;
1212
}
1313

1414
export function HoverOnlyTooltip({
1515
children,
1616
content,
1717
side = "bottom",
1818
className,
19-
hideArrow = false,
19+
hideArrow = true,
2020
disabled = false,
21+
sideOffset = 5,
2122
}: HoverOnlyTooltipProps) {
22-
const [hovered, setHovered] = useState(false);
23+
if (disabled) {
24+
return <>{children}</>;
25+
}
2326

2427
return (
25-
<Tooltip open={hovered && !disabled}>
26-
<TooltipTrigger
27-
asChild
28-
onMouseEnter={() => !disabled && setHovered(true)}
29-
onMouseLeave={() => setHovered(false)}
30-
onBlur={() => setHovered(false)}
31-
>
32-
{children}
33-
</TooltipTrigger>
34-
<TooltipContent side={side} className={className} hideArrow={hideArrow}>
28+
<Tooltip disableHoverableContent>
29+
<TooltipTrigger asChild>{children}</TooltipTrigger>
30+
<TooltipContent side={side} className={className} hideArrow={hideArrow} sideOffset={sideOffset}>
3531
{content}
3632
</TooltipContent>
3733
</Tooltip>

apps/web/client/src/app/project/[id]/_components/editor-bar/inputs/color-picker.tsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,10 @@ const ColorGroup = ({
4040
}, [isExpanded]);
4141

4242
useEffect(() => {
43-
setTimeout(() => {
44-
if (selectedRef.current) {
45-
selectedRef.current.scrollIntoView({ behavior: 'smooth', block: 'center' });
46-
}
47-
}, 100);
48-
}, [selectedColor, expanded]);
43+
if (selectedRef.current) {
44+
selectedRef.current.scrollIntoView({ block: 'center' });
45+
}
46+
}, [ expanded]);
4947

5048
return (
5149
<div className="w-full group">
@@ -732,23 +730,23 @@ export const ColorPickerContent: React.FC<ColorPickerProps> = ({
732730
>
733731
{!isCreatingNewColor && (
734732
<TabsList className="bg-transparent px-2 m-0 gap-2 justify-between w-full">
735-
<div className="flex gap-2">
733+
<div className="flex gap-1">
736734
<TabsTrigger
737735
value={TabValue.BRAND}
738-
className="bg-transparent text-xs p-1 hover:text-foreground-primary"
736+
className="flex items-center justify-center px-1.5 py-1 text-xs rounded-md bg-transparent hover:bg-background-secondary hover:text-foreground-primary transition-colors"
739737
>
740738
Brand
741739
</TabsTrigger>
742740

743741
<TabsTrigger
744742
value={TabValue.CUSTOM}
745-
className="bg-transparent text-xs p-1 hover:text-foreground-primary"
743+
className="flex items-center justify-center px-1.5 py-1 text-xs rounded-md bg-transparent hover:bg-background-secondary hover:text-foreground-primary transition-colors"
746744
>
747745
Custom
748746
</TabsTrigger>
749747
<TabsTrigger
750748
value={TabValue.GRADIENT}
751-
className="bg-transparent text-xs p-1 hover:text-foreground-primary"
749+
className="flex items-center justify-center px-1.5 py-1 text-xs rounded-md bg-transparent hover:bg-background-secondary hover:text-foreground-primary transition-colors"
752750
>
753751
Gradient
754752
</TabsTrigger>

apps/web/client/src/app/project/[id]/_components/editor-bar/window-selected/device-selector.tsx

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { DEVICE_OPTIONS, Orientation } from '@onlook/constants';
33
import type { WindowMetadata } from '@onlook/models';
44
import { Icons } from '@onlook/ui/icons/index';
55
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger } from '@onlook/ui/select';
6-
import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip';
76
import { computeWindowMetadata, getDeviceType } from '@onlook/utility';
87
import { observer } from 'mobx-react-lite';
98
import { useEffect, useMemo, useState } from 'react';
9+
import { HoverOnlyTooltip } from '../hover-tooltip';
1010

1111
const DeviceIcon = ({ deviceType, orientation }: { deviceType: string, orientation: Orientation }) => {
1212
switch (deviceType) {
@@ -82,15 +82,12 @@ export const DeviceSelector = observer(() => {
8282

8383
return (
8484
<Select value={device} onValueChange={handleDeviceChange}>
85-
<Tooltip>
86-
<TooltipTrigger asChild>
87-
<SelectTrigger className="flex items-center gap-2 text-muted-foreground dark:bg-transparent border border-border/0 cursor-pointer rounded-lg hover:bg-background-tertiary/20 hover:text-white hover:border hover:border-border focus-visible:ring-0 focus-visible:ring-offset-0 focus:outline-none focus-visible:outline-none">
88-
<DeviceIcon deviceType={deviceType} orientation={metadata.orientation} />
89-
<span className="font-medium">{deviceType}</span>
90-
</SelectTrigger>
91-
</TooltipTrigger>
92-
<TooltipContent side="bottom">Device</TooltipContent>
93-
</Tooltip>
85+
<HoverOnlyTooltip content="Device" side="bottom" sideOffset={10}>
86+
<SelectTrigger className="flex items-center gap-2 text-muted-foreground dark:bg-transparent border border-border/0 cursor-pointer rounded-lg hover:bg-background-tertiary/20 hover:text-white hover:border hover:border-border focus-visible:ring-0 focus-visible:ring-offset-0 focus:outline-none focus-visible:outline-none">
87+
<DeviceIcon deviceType={deviceType} orientation={metadata.orientation} />
88+
<span className="font-medium">{deviceType}</span>
89+
</SelectTrigger>
90+
</HoverOnlyTooltip>
9491
<SelectContent>
9592
{Object.entries(DEVICE_OPTIONS).map(([category, devices]) => (
9693
<SelectGroup key={category}>
Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
11
import React from 'react';
22
import { Button } from '@onlook/ui/button';
33
import { Icons } from '@onlook/ui/icons';
4-
import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip';
54
import { type FrameData } from '@/components/store/editor/frames';
5+
import { HoverOnlyTooltip } from '../hover-tooltip';
66

77
export function RotateGroup({ frameData }: { frameData: FrameData }) {
88
return (
9-
<Tooltip key="rotate">
10-
<TooltipTrigger asChild>
11-
<Button
12-
variant="ghost"
13-
size="icon"
14-
className="h-8 w-8"
15-
onClick={() => {
16-
const { width, height } = frameData.frame.dimension;
17-
frameData.frame.dimension.width = height;
18-
frameData.frame.dimension.height = width;
19-
}}
20-
>
21-
<Icons.CounterClockwiseClock className="h-4 w-4" />
22-
</Button>
23-
</TooltipTrigger>
24-
<TooltipContent side="bottom">Rotate Device</TooltipContent>
25-
</Tooltip>
9+
<HoverOnlyTooltip content="Rotate Device" side="bottom" sideOffset={10}>
10+
<Button
11+
variant="ghost"
12+
size="icon"
13+
className="h-8 w-8"
14+
onClick={() => {
15+
const { width, height } = frameData.frame.dimension;
16+
frameData.frame.dimension.width = height;
17+
frameData.frame.dimension.height = width;
18+
}}
19+
>
20+
<Icons.CounterClockwiseClock className="h-4 w-4" />
21+
</Button>
22+
</HoverOnlyTooltip>
2623
);
2724
}

apps/web/client/src/app/project/[id]/_components/editor-bar/window-selected/theme-group.tsx

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { SystemTheme } from '@onlook/models/assets';
22
import { Icons } from '@onlook/ui/icons';
33
import { toast } from '@onlook/ui/sonner';
4-
import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip';
54
import { useEffect, useState } from 'react';
5+
import { HoverOnlyTooltip } from '../hover-tooltip';
66
import { ToolbarButton } from '../toolbar-button';
77
import { type FrameData } from '@/components/store/editor/frames';
88

@@ -33,39 +33,30 @@ export function ThemeGroup({ frameData }: { frameData: FrameData }) {
3333

3434
return (
3535
<>
36-
<Tooltip key="system">
37-
<TooltipTrigger asChild>
36+
<HoverOnlyTooltip content="System Theme" side="bottom" sideOffset={10}>
3837
<ToolbarButton
3938
className={`w-10 ${theme === SystemTheme.SYSTEM ? 'bg-background-tertiary hover:bg-background-tertiary' : 'hover:bg-background-tertiary/50 text-foreground-onlook'}`}
4039
onClick={() => changeTheme(SystemTheme.SYSTEM)}
4140
>
4241
<Icons.Laptop className="h-4 w-4" />
4342
</ToolbarButton>
44-
</TooltipTrigger>
45-
<TooltipContent side="bottom">System Theme</TooltipContent>
46-
</Tooltip>
47-
<Tooltip key="dark">
48-
<TooltipTrigger asChild>
43+
</HoverOnlyTooltip>
44+
<HoverOnlyTooltip content="Dark Theme" side="bottom" sideOffset={10}>
4945
<ToolbarButton
5046
className={`w-10 ${theme === SystemTheme.DARK ? 'bg-background-tertiary hover:bg-background-tertiary' : 'hover:bg-background-tertiary/50 text-foreground-onlook'}`}
5147
onClick={() => changeTheme(SystemTheme.DARK)}
5248
>
5349
<Icons.Moon className="h-4 w-4" />
5450
</ToolbarButton>
55-
</TooltipTrigger>
56-
<TooltipContent side="bottom">Dark Theme</TooltipContent>
57-
</Tooltip>
58-
<Tooltip key="light">
59-
<TooltipTrigger asChild>
51+
</HoverOnlyTooltip>
52+
<HoverOnlyTooltip content="Light Theme" side="bottom" sideOffset={10}>
6053
<ToolbarButton
6154
className={`w-10 ${theme === SystemTheme.LIGHT ? 'bg-background-tertiary hover:bg-background-tertiary' : 'hover:bg-background-tertiary/50 text-foreground-onlook'}`}
6255
onClick={() => changeTheme(SystemTheme.LIGHT)}
6356
>
6457
<Icons.Sun className="h-4 w-4" />
6558
</ToolbarButton>
66-
</TooltipTrigger>
67-
<TooltipContent side="bottom">Light Theme</TooltipContent>
68-
</Tooltip>
59+
</HoverOnlyTooltip>
6960
</>
7061
);
7162
}

apps/web/client/src/app/project/[id]/_components/editor-bar/window-selected/window-actions-group.tsx

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { useEditorEngine } from '@/components/store/editor';
22
import { Button } from '@onlook/ui/button';
33
import { Icons } from '@onlook/ui/icons';
4-
import { Tooltip, TooltipContent, TooltipTrigger } from '@onlook/ui/tooltip';
54
import { useState } from 'react';
5+
import { HoverOnlyTooltip } from '../hover-tooltip';
66
import { ToolbarButton } from '../toolbar-button';
77
import type { FrameData } from '@/components/store/editor/frames';
88

@@ -39,40 +39,33 @@ export function WindowActionsGroup({ frameData }: { frameData: FrameData }) {
3939

4040
return (
4141
<>
42-
<Tooltip key="duplicate">
43-
<TooltipTrigger asChild>
42+
<HoverOnlyTooltip content="Duplicate Window" side="bottom" sideOffset={10}>
43+
<ToolbarButton
44+
className="flex items-center w-10"
45+
onClick={duplicateWindow}
46+
disabled={isDuplicating}
47+
>
48+
{isDuplicating ? (
49+
<Icons.LoadingSpinner className="size-4 min-size-4 animate-spin" />
50+
) : (
51+
<Icons.Copy className="size-4 min-size-4" />
52+
)}
53+
</ToolbarButton>
54+
</HoverOnlyTooltip>
55+
{editorEngine.frames.canDelete() && (
56+
<HoverOnlyTooltip content="Delete Window" side="bottom" sideOffset={10}>
4457
<ToolbarButton
4558
className="flex items-center w-10"
46-
onClick={duplicateWindow}
47-
disabled={isDuplicating}
59+
disabled={!editorEngine.frames.canDelete() || isDeleting}
60+
onClick={deleteWindow}
4861
>
49-
{isDuplicating ? (
62+
{isDeleting ? (
5063
<Icons.LoadingSpinner className="size-4 min-size-4 animate-spin" />
5164
) : (
52-
<Icons.Copy className="size-4 min-size-4" />
65+
<Icons.Trash className="size-4 min-size-4" />
5366
)}
5467
</ToolbarButton>
55-
</TooltipTrigger>
56-
<TooltipContent side="bottom">Duplicate Window</TooltipContent>
57-
</Tooltip>
58-
{editorEngine.frames.canDelete() && (
59-
<Tooltip key="delete">
60-
<TooltipTrigger asChild>
61-
<ToolbarButton
62-
className="flex items-center w-10"
63-
disabled={!editorEngine.frames.canDelete() || isDeleting}
64-
onClick={deleteWindow}
65-
>
66-
{isDeleting ? (
67-
<Icons.LoadingSpinner className="size-4 min-size-4 animate-spin" />
68-
) : (
69-
<Icons.Trash className="size-4 min-size-4" />
70-
)}
71-
</ToolbarButton>
72-
73-
</TooltipTrigger>
74-
<TooltipContent side="bottom">Delete Window</TooltipContent>
75-
</Tooltip>
68+
</HoverOnlyTooltip>
7669
)}
7770
</>
7871
);

apps/web/client/src/components/ui/settings-modal/site/metadata-form.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,8 +154,8 @@ export const MetadataForm = ({
154154
};
155155

156156
return (
157-
<div className="text-sm">
158-
<div className="flex flex-col gap-6 p-6">
157+
<div className="text-sm flex flex-col h-full">
158+
<div className="flex flex-col gap-6 p-6 pb-24 overflow-y-auto flex-1">
159159
{renderTitle()}
160160

161161
<Separator />
@@ -230,10 +230,14 @@ export const MetadataForm = ({
230230
</div>
231231
)}
232232
</div>
233+
</div>
234+
235+
{/* Pinned buttons at the bottom */}
236+
<div className="sticky bottom-0 bg-background border-t border-border/50 p-6" style={{ borderTopWidth: '0.5px' }}>
233237
<div className="flex justify-end gap-4">
234238
<Button
235-
variant="ghost"
236-
className="flex items-center gap-2 px-4 py-0"
239+
variant="outline"
240+
className="flex items-center gap-2 px-4 py-2 bg-background border border-border/50"
237241
type="button"
238242
onClick={handleDiscard}
239243
disabled={!isDirty || disabled}
@@ -242,7 +246,7 @@ export const MetadataForm = ({
242246
</Button>
243247
<Button
244248
variant="secondary"
245-
className="flex items-center gap-2 px-4 py-0 backdrop-blur-sm rounded border border-foreground-tertiary/20"
249+
className="flex items-center gap-2 px-4 py-2"
246250
type="button"
247251
onClick={onSave}
248252
disabled={!isDirty || disabled || isSaving}

0 commit comments

Comments
 (0)