Skip to content
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
69 changes: 43 additions & 26 deletions docs/editor/shortcuts/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ Last updated: Based on keybindings_sheet array and useHotkeys calls in hotkeys.t
<!-- | Command | ⌘ | — | ❌ No | ⌘ (macOS only) | -->
<!-- | Control | ⌃ | Ctrl | ❌ No | Ctrl (Windows), ⌃ (macOS) | -->
<!-- | Option / Alt | ⌥ | Alt | ❌ No | Alt (Windows), ⌥ (macOS) | -->
<!-- | Shift | ⇧ | | ✅ Yes | ⇧ (both) | -->
<!-- | Enter / Return | ⏎ / ↵ | | ⚠️ Mostly | ↵ or Enter | -->
<!-- | Shift | ⇧ | Shift | ❌ No | Shift (Windows), ⇧ (macOS) | -->
<!-- | Enter / Return | ⏎ / ↵ | Enter | ❌ No | Enter (Windows), ↵ (macOS) | -->
<!-- | Escape | ⎋ | Esc | ⚠️ Mixed | Esc | -->
<!-- | Backspace / Delete | ⌫ / ⌦ | Backspace / Del | ❌ No | Text label | -->

Expand All @@ -46,7 +46,7 @@ Last updated: Based on keybindings_sheet array and useHotkeys calls in hotkeys.t
| Eraser tool | `E` | `E` | Eraser tool |
| Paint bucket | `G` | `G` | Flood fill tool (bitmap mode only) |
| Variable width | `⇧ + W` | `⇧ + W` | Variable width tool (vector mode only) |
| Eye dropper | `I` | `I` | Pick color from screen |
| Eye dropper | `I` or `⌃ + C` | `I` | Pick color from screen |

## Selection & Navigation

Expand Down Expand Up @@ -88,6 +88,8 @@ Last updated: Based on keybindings_sheet array and useHotkeys calls in hotkeys.t
| Nudge resize (down, 10px) | `Ctrl + ⌥ + ⇧ + ↓` | `Ctrl + Alt + ⇧ + ↓` | Resize selection height by 10px |
| Move to front | `]` | `]` | Move selection to front (or increase brush size if brush tool active) |
| Move to back | `[` | `[` | Move selection to back (or decrease brush size if brush tool active) |
| Move forward | `⌘ + ]` | `Ctrl + ]` | Move selection forward one layer |
| Move backward | `⌘ + [` | `Ctrl + [` | Move selection backward one layer |

## Alignment & Distribution

Expand All @@ -113,32 +115,45 @@ Last updated: Based on keybindings_sheet array and useHotkeys calls in hotkeys.t

## Text Formatting

| Action | macOS | Windows/Linux | Description |
| ------------------- | ----------- | -------------- | ------------------------- |
| Toggle bold | `⌘ + B` | `Ctrl + B` | Toggle bold style |
| Toggle italic | `⌘ + I` | `Ctrl + I` | Toggle italic style |
| Toggle underline | `⌘ + U` | `Ctrl + U` | Toggle underline style |
| Toggle line-through | `⌘ + ⇧ + X` | `Ctrl + ⇧ + X` | Toggle line-through style |
| Increase font size | `⌘ + ⇧ + >` | `Ctrl + ⇧ + >` | Increase font size by 1px |
| Decrease font size | `⌘ + ⇧ + <` | `Ctrl + ⇧ + <` | Decrease font size by 1px |
| Action | macOS | Windows/Linux | Description |
| ----------------------- | ----------- | ---------------- | ------------------------- |
| Toggle bold | `⌘ + B` | `Ctrl + B` | Toggle bold style |
| Toggle italic | `⌘ + I` | `Ctrl + I` | Toggle italic style |
| Toggle underline | `⌘ + U` | `Ctrl + U` | Toggle underline style |
| Toggle line-through | `⌘ + ⇧ + X` | `Ctrl + ⇧ + X` | Toggle line-through style |
| Text align left | `⌘ + ⌥ + L` | `Ctrl + Alt + L` | Align text to the left |
| Text align center | `⌘ + ⌥ + T` | `Ctrl + Alt + T` | Center text horizontally |
| Text align right | `⌘ + ⌥ + R` | `Ctrl + Alt + R` | Align text to the right |
| Text align justify | `⌘ + ⌥ + J` | `Ctrl + Alt + J` | Justify text horizontally |
| Increase font size | `⌘ + ⇧ + >` | `Ctrl + ⇧ + >` | Increase font size by 1px |
| Decrease font size | `⌘ + ⇧ + <` | `Ctrl + ⇧ + <` | Decrease font size by 1px |
| Increase font weight | `⌘ + ⌥ + >` | `Ctrl + Alt + >` | Increase font weight |
| Decrease font weight | `⌘ + ⌥ + <` | `Ctrl + Alt + <` | Decrease font weight |
| Increase line height | `⌥ + ⇧ + >` | `Alt + ⇧ + >` | Increase line height |
| Decrease line height | `⌥ + ⇧ + <` | `Alt + ⇧ + <` | Decrease line height |
| Increase letter spacing | `⌥ + >` | `Alt + >` | Increase letter spacing |
| Decrease letter spacing | `⌥ + <` | `Alt + <` | Decrease letter spacing |

## Object Properties

| Action | macOS | Windows/Linux | Description |
| ------------------- | ------------------ | ------------------ | ------------------------------------- |
| Toggle active | `⌘ + ⇧ + H` | `Ctrl + ⇧ + H` | Toggle active state for the selection |
| Toggle locked | `⌘ + ⇧ + L` | `Ctrl + ⇧ + L` | Toggle locked state for the selection |
| Set opacity to 0% | `0` (double press) | `0` (double press) | Set opacity to 0% |
| Set opacity to 10% | `1` | `1` | Set opacity to 10% |
| Set opacity to 20% | `2` | `2` | Set opacity to 20% |
| Set opacity to 30% | `3` | `3` | Set opacity to 30% |
| Set opacity to 40% | `4` | `4` | Set opacity to 40% |
| Set opacity to 50% | `5` | `5` | Set opacity to 50% |
| Set opacity to 60% | `6` | `6` | Set opacity to 60% |
| Set opacity to 70% | `7` | `7` | Set opacity to 70% |
| Set opacity to 80% | `8` | `8` | Set opacity to 80% |
| Set opacity to 90% | `9` | `9` | Set opacity to 90% |
| Set opacity to 100% | `0` (single press) | `0` (single press) | Set opacity to 100% |
| Action | macOS | Windows/Linux | Description |
| -------------------- | ------------------ | ------------------ | ---------------------------------------------- |
| Toggle active | `⌘ + ⇧ + H` | `Ctrl + ⇧ + H` | Toggle active state for the selection |
| Toggle locked | `⌘ + ⇧ + L` | `Ctrl + ⇧ + L` | Toggle locked state for the selection |
| Remove fill | `⌥ + /` | `Alt + /` | Remove fill from selection |
| Remove stroke | `⇧ + /` | `⇧ + /` | Remove stroke from selection (sets width to 0) |
| Swap fill and stroke | `⇧ + X` | `⇧ + X` | Swap fill paints and stroke paints |
| Set opacity to 0% | `0` (double press) | `0` (double press) | Set opacity to 0% |
| Set opacity to 10% | `1` | `1` | Set opacity to 10% |
| Set opacity to 20% | `2` | `2` | Set opacity to 20% |
| Set opacity to 30% | `3` | `3` | Set opacity to 30% |
| Set opacity to 40% | `4` | `4` | Set opacity to 40% |
| Set opacity to 50% | `5` | `5` | Set opacity to 50% |
| Set opacity to 60% | `6` | `6` | Set opacity to 60% |
| Set opacity to 70% | `7` | `7` | Set opacity to 70% |
| Set opacity to 80% | `8` | `8` | Set opacity to 80% |
| Set opacity to 90% | `9` | `9` | Set opacity to 90% |
| Set opacity to 100% | `0` (single press) | `0` (single press) | Set opacity to 100% |

## View & Zoom

Expand Down Expand Up @@ -179,3 +194,5 @@ The following shortcuts are defined but not yet implemented:
- `⇧ + V` - Flip vertical
- `⌥ + ⌘ + K` / `Alt + Ctrl + K` - Create component
- `⌥ + ⌘ + B` / `Alt + Ctrl + B` - Eject component
- `Tab` - Text range: Increase indentation
- `⇧ + Tab` - Text range: Decrease indentation
244 changes: 244 additions & 0 deletions editor/components/ui/field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
"use client"

import { useMemo } from "react"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/components/lib/utils/index"
import { Label } from "@/components/ui/label"
import { Separator } from "@/components/ui/separator"

function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
return (
<fieldset
data-slot="field-set"
className={cn(
"flex flex-col gap-6",
"has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
className
)}
{...props}
/>
)
}

function FieldLegend({
className,
variant = "legend",
...props
}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
return (
<legend
data-slot="field-legend"
data-variant={variant}
className={cn(
"mb-3 font-medium",
"data-[variant=legend]:text-base",
"data-[variant=label]:text-sm",
className
)}
{...props}
/>
)
}

function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="field-group"
className={cn(
"group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4",
className
)}
{...props}
/>
)
}

const fieldVariants = cva(
"group/field data-[invalid=true]:text-destructive flex w-full gap-3",
{
variants: {
orientation: {
vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"],
horizontal: [
"flex-row items-center",
"[&>[data-slot=field-label]]:flex-auto",
"has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px has-[>[data-slot=field-content]]:items-start",
],
responsive: [
"@md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto flex-col [&>*]:w-full [&>.sr-only]:w-auto",
"@md/field-group:[&>[data-slot=field-label]]:flex-auto",
"@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
],
},
},
defaultVariants: {
orientation: "vertical",
},
}
)

function Field({
className,
orientation = "vertical",
...props
}: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
return (
<div
role="group"
data-slot="field"
data-orientation={orientation}
className={cn(fieldVariants({ orientation }), className)}
{...props}
/>
)
}

function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="field-content"
className={cn(
"group/field-content flex flex-1 flex-col gap-1.5 leading-snug",
className
)}
{...props}
/>
)
}

function FieldLabel({
className,
...props
}: React.ComponentProps<typeof Label>) {
return (
<Label
data-slot="field-label"
className={cn(
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>[data-slot=field]]:p-4",
"has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10",
className
)}
{...props}
/>
)
}

function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="field-label"
className={cn(
"flex w-fit items-center gap-2 text-sm font-medium leading-snug group-data-[disabled=true]/field:opacity-50",
className
)}
{...props}
/>
)
}

function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
return (
<p
data-slot="field-description"
className={cn(
"text-muted-foreground text-sm font-normal leading-normal group-has-[[data-orientation=horizontal]]/field:text-balance",
"nth-last-2:-mt-1 last:mt-0 [[data-variant=legend]+&]:-mt-1.5",
"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
className
)}
{...props}
/>
)
}

function FieldSeparator({
children,
className,
...props
}: React.ComponentProps<"div"> & {
children?: React.ReactNode
}) {
return (
<div
data-slot="field-separator"
data-content={!!children}
className={cn(
"relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
className
)}
{...props}
>
<Separator className="absolute inset-0 top-1/2" />
{children && (
<span
className="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
data-slot="field-separator-content"
>
{children}
</span>
)}
</div>
)
}

function FieldError({
className,
children,
errors,
...props
}: React.ComponentProps<"div"> & {
errors?: Array<{ message?: string } | undefined>
}) {
const content = useMemo(() => {
if (children) {
return children
}

if (!errors) {
return null
}

if (errors?.length === 1 && errors[0]?.message) {
return errors[0].message
}

return (
<ul className="ml-4 flex list-disc flex-col gap-1">
{errors.map(
(error, index) =>
error?.message && <li key={index}>{error.message}</li>
)}
</ul>
)
}, [children, errors])

if (!content) {
return null
}

return (
<div
role="alert"
data-slot="field-error"
className={cn("text-destructive text-sm font-normal", className)}
{...props}
>
{content}
</div>
)
}

export {
Field,
FieldLabel,
FieldDescription,
FieldError,
FieldGroup,
FieldLegend,
FieldSeparator,
FieldSet,
FieldContent,
FieldTitle,
}
32 changes: 17 additions & 15 deletions editor/components/ui/label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,25 @@

import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/components/lib/utils/index"

function Label({
className,
...props
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
return (
<LabelPrimitive.Root
data-slot="label"
className={cn(
"flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
className
)}
{...props}
/>
)
}
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
)

const Label = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
VariantProps<typeof labelVariants>
>(({ className, ...props }, ref) => (
<LabelPrimitive.Root
ref={ref}
className={cn(labelVariants(), className)}
{...props}
/>
))
Label.displayName = LabelPrimitive.Root.displayName

export { Label }
Loading