Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/window settings #880

Merged
merged 6 commits into from
Dec 12, 2024
Merged
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
3 changes: 1 addition & 2 deletions apps/studio/electron/main/requirements/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ function checkNodeInstallation(): boolean {
`${process.env.VOLTA_HOME}/bin`, // Volta
`${process.env.HOME}/.volta/bin`, // Volta
`${process.env.HOME}/.asdf/installs/nodejs`, // ASDF
]
.filter(Boolean);
].filter(Boolean);

const existingPath = process.env.PATH || '';
const pathSeparator = process.platform === 'win32' ? ';' : ':';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Button } from '@onlook/ui/button';
import { Icons } from '@onlook/ui/icons/index';
import { useState } from 'react';

enum DeviceTheme {
Device = 'Device',
Dark = 'Dark',
Light = 'Light',
}

const DeviceSettings = () => {
const [deviceTheme, setDeviceTheme] = useState(DeviceTheme.Device);

return (
<div className="flex flex-col gap-2">
<p className="text-smallPlus text-foreground-primary">Device Settings</p>
<div className="flex flex-row justify-between items-center">
<span className="text-xs text-foreground-secondary">Theme</span>
<div className="flex flex-row p-0.5 w-3/5 bg-background-secondary rounded">
<Button
size={'icon'}
className={`h-full w-full px-0.5 py-1.5 bg-background-secondary rounded-sm ${
deviceTheme === DeviceTheme.Device
? 'bg-background-tertiary hover:bg-background-tertiary'
: 'hover:bg-background-tertiary/50 text-foreground-onlook'
}`}
variant={'ghost'}
onClick={() =>
deviceTheme !== DeviceTheme.Device && setDeviceTheme(DeviceTheme.Device)
}
>
<Icons.Laptop />
</Button>
<Button
size={'icon'}
className={`h-full w-full px-0.5 py-1.5 bg-background-secondary rounded-sm ${
deviceTheme === DeviceTheme.Dark
? 'bg-background-tertiary hover:bg-background-tertiary'
: 'hover:bg-background-tertiary/50 text-foreground-onlook'
}`}
variant={'ghost'}
onClick={() =>
deviceTheme !== DeviceTheme.Dark && setDeviceTheme(DeviceTheme.Dark)
}
>
<Icons.Moon />
</Button>
<Button
size={'icon'}
className={`h-full w-full px-0.5 py-1.5 bg-background-secondary rounded-sm ${
deviceTheme === DeviceTheme.Light
? 'bg-background-tertiary hover:bg-background-tertiary'
: 'hover:bg-background-tertiary/50 text-foreground-onlook'
}`}
variant={'ghost'}
onClick={() =>
deviceTheme !== DeviceTheme.Light && setDeviceTheme(DeviceTheme.Light)
}
>
<Icons.Sun />
</Button>
</div>
</div>
</div>
);
};

export default DeviceSettings;
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { Button } from '@onlook/ui/button';
import { Icons } from '@onlook/ui/icons/index';
import { Input } from '@onlook/ui/input';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@onlook/ui/select';
import { useState } from 'react';

const deviceOptions = [
'iPhone SE',
'iPhone 12',
'iPhone 12 Pro',
'iPhone 12 Pro Max',
'iPhone 11',
'iPhone 11 Pro',
'iPhone 11 Pro Max',
'Google Pixel 5',
'Google Pixel 4a',
'Samsung Galaxy S21',
'Samsung Galaxy S21+',
'Samsung Galaxy S21 Ultra',
'Samsung Galaxy Note 20',
'Samsung Galaxy Note 20 Ultra',
'OnePlus 9',
'OnePlus 9 Pro',
];

enum Orientation {
Potrait = 'Potrait',
Landscape = 'Landscape',
}

const FrameDimensions = () => {
const [device, setDevice] = useState('iPhone SE');
const [orientation, setOrientation] = useState(Orientation.Potrait);
const [width, setWidth] = useState('375px');
const [height, setHeight] = useState('667px');
const [responsive, setResponsive] = useState('Closest Size');

return (
<div className="flex flex-col gap-2">
<p className="text-smallPlus text-foreground-primary">Frame Dimensions</p>
<div className="flex flex-row justify-between items-center">
<span className="text-xs text-foreground-secondary">Device</span>
<Select value={device} onValueChange={setDevice}>
<SelectTrigger className="w-3/5 bg-background-secondary border-background-secondary py-1.5 px-2 h-fit text-xs rounded focus:outline-none focus:ring-0">
<SelectValue placeholder="Select device" />
</SelectTrigger>
<SelectContent className="rounded-md bg-background-secondary">
{deviceOptions.map((deviceName) => (
<SelectItem
key={deviceName}
value={deviceName}
className="focus:bg-background-tertiary rounded-md text-xs cursor-pointer"
>
{deviceName}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex flex-row justify-between items-center">
<span className="text-xs text-foreground-secondary">Orientation</span>
<div className="flex flex-row p-0.5 w-3/5 bg-background-secondary rounded">
<Button
size={'icon'}
className={`h-full w-full px-0.5 py-1.5 bg-background-secondary rounded-sm ${orientation === Orientation.Potrait ? 'bg-background-tertiary hover:bg-background-tertiary' : 'hover:bg-background-tertiary/50'}`}
variant={'ghost'}
onClick={() =>
orientation === Orientation.Landscape &&
setOrientation(Orientation.Potrait)
}
>
<Icons.Potrait
className={`h-4 w-4 ${orientation !== Orientation.Potrait ? 'text-foreground-secondary hover:text-foreground-onlook' : ''}`}
/>
</Button>
<Button
size={'icon'}
className={`h-full w-full px-0.5 py-1.5 bg-background-secondary rounded-sm ${orientation === 'Landscape' ? 'bg-background-tertiary hover:bg-background-tertiary' : 'hover:bg-background-tertiary/50'}`}
variant={'ghost'}
onClick={() =>
orientation === Orientation.Potrait &&
setOrientation(Orientation.Landscape)
}
>
<Icons.Landscape
className={`h-4 w-4 ${orientation !== Orientation.Landscape ? 'text-foreground-secondary hover:text-foreground-onlook' : ''}`}
/>
</Button>
</div>
</div>
<div className="flex flex-row justify-between items-center">
<span className="text-xs text-foreground-secondary">Width</span>
<div className="relative w-3/5">
<Input
className="w-full px-2 h-8 text-xs rounded border-none text-foreground-active bg-background-secondary text-start focus:outline-none focus:ring-0 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
value={width}
onChange={(event) => setWidth(event.target.value)}
/>
<Button
size={'icon'}
className="p-0 h-fit w-fit absolute right-2 top-1/2 transform -translate-y-1/2"
variant={'ghost'}
>
<Icons.Link className="text-foreground-secondary" />
</Button>
</div>
</div>
<div className="flex flex-row justify-between items-center">
<span className="text-xs text-foreground-secondary">Height</span>
<div className="relative w-3/5">
<Input
className="w-full px-2 h-8 text-xs rounded border-none text-foreground-active bg-background-secondary text-start focus:outline-none focus:ring-0 [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
value={height}
onChange={(event) => setHeight(event.target.value)}
/>
<Button
size={'icon'}
className="p-0 h-fit w-fit absolute right-2 top-1/2 transform -translate-y-1/2"
variant={'ghost'}
>
<Icons.Link className="text-foreground-secondary" />
</Button>
</div>
</div>
<div className="flex flex-row justify-between items-center">
<span className="text-xs text-foreground-secondary">Responsive</span>
<Select value={responsive} onValueChange={setResponsive}>
<SelectTrigger className="w-3/5 rounded bg-background-secondary border-background-secondary px-2 h-8 text-xs">
<SelectValue placeholder="Select size" defaultValue={'Closest Size'} />
</SelectTrigger>
<SelectContent className="rounded-md bg-background-secondary">
<SelectItem
value="Closest Size"
className="focus:bg-background-tertiary rounded-md text-xs cursor-pointer"
>
Closest Size
</SelectItem>
</SelectContent>
</Select>
</div>
</div>
);
};

export default FrameDimensions;
49 changes: 49 additions & 0 deletions apps/studio/src/routes/editor/EditPanel/WindowSettings/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { Button } from '@onlook/ui/button';
import { Icons } from '@onlook/ui/icons/index';
import { Separator } from '@onlook/ui/separator';
import FrameDimensions from './FrameDimensions';
import DeviceSettings from './DeviceSettings';

const WindowSettings = ({ setIsOpen }: { setIsOpen: (isOpen: boolean) => void }) => {
return (
<div className="flex flex-col">
<div className="rounded-lg p-1 text-muted-foreground bg-transparent w-full gap-2 select-none justify-between items-center h-full px-2">
<div className="flex flex-row items-center gap-2">
<button
className="text-default rounded-lg p-2 bg-transparent hover:text-foreground-hover"
onClick={() => setIsOpen(false)}
>
<Icons.PinRight />
</button>
<div className="bg-transparent py-2 px-1 text-xs text-foreground-primary">
Window
</div>
</div>
</div>
<Separator />
<div className="flex flex-col gap-3 px-3 py-2">
<div className="flex flex-row gap-1">
<Button
variant={'outline'}
className="h-fit py-1.5 px-2.5 text-foreground-tertiary w-full items-center"
>
<Icons.Copy className="mr-2" />
<span className="text-xs">Duplicate</span>
</Button>
<Button
variant={'outline'}
className="h-fit py-1.5 px-2.5 text-foreground-tertiary w-full items-center"
>
<Icons.Trash className="mr-2" />
<span className="text-xs">Delete</span>
</Button>
</div>
<FrameDimensions />
<Separator />
<DeviceSettings />
</div>
</div>
);
};

export default WindowSettings;
7 changes: 4 additions & 3 deletions apps/studio/src/routes/editor/EditPanel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEditorEngine } from '@/components/Context';
import { EditorMode } from '@/lib/models';
import { EditorMode, EditorTabValue } from '@/lib/models';
import { Icons } from '@onlook/ui/icons';
import { Separator } from '@onlook/ui/separator';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@onlook/ui/tabs';
Expand All @@ -9,12 +9,13 @@ import { useEffect, useState } from 'react';
import ChatTab from './ChatTab';
import ChatControls from './ChatTab/ChatControls';
import ManualTab from './StylesTab';
import { EditorTabValue } from '@/lib/models';
import WindowSettings from './WindowSettings';

const EditPanel = observer(() => {
const editorEngine = useEditorEngine();
const [isOpen, setIsOpen] = useState(true);
const [selectedTab, setSelectedTab] = useState<EditorTabValue>(editorEngine.editPanelTab);
const [windowSettingsOpen, setWindowSettingsOpen] = useState(false);

useEffect(() => {
tabChange(editorEngine.editPanelTab);
Expand Down Expand Up @@ -104,7 +105,7 @@ const EditPanel = observer(() => {
isOpen ? 'opacity-100 visible' : 'opacity-0 invisible',
)}
>
{renderTabs()}
{windowSettingsOpen ? <WindowSettings setIsOpen={setIsOpen} /> : renderTabs()}
</div>
</div>
);
Expand Down
Binary file modified bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
"@radix-ui/react-popover": "^1.1.1",
"@radix-ui/react-progress": "^1.1.0",
"@radix-ui/react-scroll-area": "^1.2.0",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
Expand Down
34 changes: 34 additions & 0 deletions packages/ui/src/components/icons/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,40 @@ export const Icons = {
{...props}
/>
),
Landscape: ({ className, ...props }: IconProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
className={className}
{...props}
>
<rect width="20" height="12" x="2" y="6" rx="2" />
</svg>
),
Potrait: ({ className, ...props }: IconProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
className={className}
{...props}
>
<rect width="12" height="20" x="6" y="2" rx="2" />
</svg>
),
ArrowDown: ArrowDownIcon,
ArrowLeft: ArrowLeftIcon,
ArrowRight: ArrowRightIcon,
Expand Down
15 changes: 10 additions & 5 deletions packages/ui/src/components/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,20 @@ const SelectTrigger = React.forwardRef<
<SelectPrimitive.Trigger
ref={ref}
className={cn(
'flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
'flex h-9 w-full items-center whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 relative',
className,
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<CaretSortIcon className="h-4 w-4 opacity-50" />
</SelectPrimitive.Icon>
<div className="relative flex items-center w-full">
<div className="w-full overflow-hidden">
<div className="text-left overflow-hidden">{children}</div>
</div>
<div className="absolute right-[0px] top-0 h-full w-12 bg-gradient-to-r from-transparent to-background-secondary pointer-events-none" />
<SelectPrimitive.Icon asChild className="absolute right-[-6px]">
<CaretSortIcon className="h-4 w-4 flex-shrink-0 z-4" />
</SelectPrimitive.Icon>
</div>
</SelectPrimitive.Trigger>
));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
Expand Down