diff options
Diffstat (limited to 'apps/web/src/components/editor/panels/properties/sections')
| -rw-r--r-- | apps/web/src/components/editor/panels/properties/sections/blending.tsx | 132 | ||||
| -rw-r--r-- | apps/web/src/components/editor/panels/properties/sections/transform.tsx | 186 |
2 files changed, 161 insertions, 157 deletions
diff --git a/apps/web/src/components/editor/panels/properties/sections/blending.tsx b/apps/web/src/components/editor/panels/properties/sections/blending.tsx index 430efa25..4343c692 100644 --- a/apps/web/src/components/editor/panels/properties/sections/blending.tsx +++ b/apps/web/src/components/editor/panels/properties/sections/blending.tsx @@ -8,7 +8,7 @@ import { } from "@/constants/timeline-constants"; import { OcCheckerboardIcon } from "@opencut/ui/icons"; import { Fragment, useRef } from "react"; -import { Section, SectionContent, SectionHeader } from "../section"; +import { Section, SectionContent, SectionField, SectionHeader } from "../section"; import { Select, SelectContent, @@ -124,73 +124,73 @@ export function BlendingSection({ return ( <Section collapsible sectionKey={`${element.type}:blending`}> <SectionHeader title="Blending" /> - <SectionContent> - <div className="flex items-start gap-2"> - <div className="w-1/2 space-y-1.5"> - <NumberField + <SectionContent> + <div className="flex items-start gap-2"> + <SectionField label="Opacity" className="w-1/2"> + <NumberField + className="w-full" + icon={ + <OcCheckerboardIcon className="size-3.5 text-muted-foreground" /> + } + value={opacity.displayValue} + min={0} + max={100} + onFocus={opacity.onFocus} + onChange={opacity.onChange} + onBlur={opacity.onBlur} + onScrub={opacity.scrubTo} + onScrubEnd={opacity.commitScrub} + onReset={() => + editor.timeline.updateElements({ + updates: [ + { + trackId, + elementId: element.id, + updates: { opacity: DEFAULT_OPACITY }, + }, + ], + }) + } + isDefault={element.opacity === DEFAULT_OPACITY} + dragSensitivity="slow" + /> + </SectionField> + <SectionField label="Blend mode" className="w-1/2"> + <Select + value={committedBlendModeRef.current} + onOpenChange={handleBlendModeOpenChange} + onValueChange={commitBlendMode} + > + <SelectTrigger + icon={<HugeiconsIcon icon={RainDropIcon} />} className="w-full" - icon={ - <OcCheckerboardIcon className="size-3.5 text-muted-foreground" /> - } - value={opacity.displayValue} - min={0} - max={100} - onFocus={opacity.onFocus} - onChange={opacity.onChange} - onBlur={opacity.onBlur} - onScrub={opacity.scrubTo} - onScrubEnd={opacity.commitScrub} - onReset={() => - editor.timeline.updateElements({ - updates: [ - { - trackId, - elementId: element.id, - updates: { opacity: DEFAULT_OPACITY }, - }, - ], - }) - } - isDefault={element.opacity === DEFAULT_OPACITY} - dragSensitivity="slow" - /> - </div> - <div className="w-1/2 space-y-1.5"> - <Select - value={committedBlendModeRef.current} - onOpenChange={handleBlendModeOpenChange} - onValueChange={commitBlendMode} > - <SelectTrigger - icon={<HugeiconsIcon icon={RainDropIcon} />} - className="w-full" - > - <SelectValue placeholder="Select blend mode" /> - </SelectTrigger> - <SelectContent className="w-36"> - {BLEND_MODE_GROUPS.map((group, groupIndex) => ( - <Fragment key={group[0]?.value ?? `group-${groupIndex}`}> - {group.map((option) => ( - <SelectItem - key={option.value} - value={option.value} - onPointerEnter={() => - previewBlendMode(option.value as BlendMode) - } - > - {option.label} - </SelectItem> - ))} - {groupIndex < BLEND_MODE_GROUPS.length - 1 ? ( - <SelectSeparator /> - ) : null} - </Fragment> - ))} - </SelectContent> - </Select> - </div> - </div> - </SectionContent> + <SelectValue placeholder="Select blend mode" /> + </SelectTrigger> + <SelectContent className="w-36"> + {BLEND_MODE_GROUPS.map((group, groupIndex) => ( + <Fragment key={group[0]?.value ?? `group-${groupIndex}`}> + {group.map((option) => ( + <SelectItem + key={option.value} + value={option.value} + onPointerEnter={() => + previewBlendMode(option.value as BlendMode) + } + > + {option.label} + </SelectItem> + ))} + {groupIndex < BLEND_MODE_GROUPS.length - 1 ? ( + <SelectSeparator /> + ) : null} + </Fragment> + ))} + </SelectContent> + </Select> + </SectionField> + </div> + </SectionContent> </Section> ); } diff --git a/apps/web/src/components/editor/panels/properties/sections/transform.tsx b/apps/web/src/components/editor/panels/properties/sections/transform.tsx index 17a54b4c..21da36e9 100644 --- a/apps/web/src/components/editor/panels/properties/sections/transform.tsx +++ b/apps/web/src/components/editor/panels/properties/sections/transform.tsx @@ -3,7 +3,7 @@ import { useEditor } from "@/hooks/use-editor"; import { usePropertyDraft } from "../hooks/use-property-draft"; import { clamp } from "@/utils/math"; import type { ElementType, Transform } from "@/types/timeline"; -import { Section, SectionContent, SectionHeader } from "../section"; +import { Section, SectionContent, SectionField, SectionFields, SectionHeader } from "../section"; import { Button } from "@/components/ui/button"; import { HugeiconsIcon } from "@hugeicons/react"; import { @@ -120,8 +120,9 @@ export function TransformSection({ return ( <Section collapsible sectionKey={`${element.type}:transform`}> <SectionHeader title="Transform" /> - <SectionContent> - <div className="flex flex-col gap-2"> + <SectionContent> + <SectionFields> + <SectionField label="Scale"> <div className="flex items-center gap-2"> {isScaleLocked ? ( <> @@ -144,105 +145,108 @@ export function TransformSection({ <HugeiconsIcon icon={Link05Icon} /> </Button> </div> + </SectionField> + <SectionField label="Position"> <div className="flex items-center gap-2"> - <NumberField - icon="X" - className="flex-1" - value={positionX.displayValue} - onFocus={positionX.onFocus} - onChange={positionX.onChange} - onBlur={positionX.onBlur} - onScrub={positionX.scrubTo} - onScrubEnd={positionX.commitScrub} - onReset={() => - editor.timeline.updateElements({ - updates: [ - { - trackId, - elementId: element.id, - updates: { - transform: { - ...element.transform, - position: { - ...element.transform.position, - x: DEFAULT_TRANSFORM.position.x, + <NumberField + icon="X" + className="flex-1" + value={positionX.displayValue} + onFocus={positionX.onFocus} + onChange={positionX.onChange} + onBlur={positionX.onBlur} + onScrub={positionX.scrubTo} + onScrubEnd={positionX.commitScrub} + onReset={() => + editor.timeline.updateElements({ + updates: [ + { + trackId, + elementId: element.id, + updates: { + transform: { + ...element.transform, + position: { + ...element.transform.position, + x: DEFAULT_TRANSFORM.position.x, + }, }, }, }, - }, - ], - }) - } - isDefault={ - element.transform.position.x === DEFAULT_TRANSFORM.position.x - } - /> - <NumberField - icon="Y" - className="flex-1" - value={positionY.displayValue} - onFocus={positionY.onFocus} - onChange={positionY.onChange} - onBlur={positionY.onBlur} - onScrub={positionY.scrubTo} - onScrubEnd={positionY.commitScrub} - onReset={() => - editor.timeline.updateElements({ - updates: [ - { - trackId, - elementId: element.id, - updates: { - transform: { - ...element.transform, - position: { - ...element.transform.position, - y: DEFAULT_TRANSFORM.position.y, + ], + }) + } + isDefault={ + element.transform.position.x === DEFAULT_TRANSFORM.position.x + } + /> + <NumberField + icon="Y" + className="flex-1" + value={positionY.displayValue} + onFocus={positionY.onFocus} + onChange={positionY.onChange} + onBlur={positionY.onBlur} + onScrub={positionY.scrubTo} + onScrubEnd={positionY.commitScrub} + onReset={() => + editor.timeline.updateElements({ + updates: [ + { + trackId, + elementId: element.id, + updates: { + transform: { + ...element.transform, + position: { + ...element.transform.position, + y: DEFAULT_TRANSFORM.position.y, + }, }, }, }, - }, - ], - }) - } - isDefault={ - element.transform.position.y === DEFAULT_TRANSFORM.position.y - } - /> + ], + }) + } + isDefault={ + element.transform.position.y === DEFAULT_TRANSFORM.position.y + } + /> </div> + </SectionField> - <div className="flex items-center gap-2"> - <NumberField - icon={<HugeiconsIcon icon={RotateClockwiseIcon} />} - className="flex-1" - value={rotation.displayValue} - onFocus={rotation.onFocus} - onChange={rotation.onChange} - onBlur={rotation.onBlur} - dragSensitivity="slow" - onScrub={rotation.scrubTo} - onScrubEnd={rotation.commitScrub} - onReset={() => - editor.timeline.updateElements({ - updates: [ - { - trackId, - elementId: element.id, - updates: { - transform: { - ...element.transform, - rotate: DEFAULT_TRANSFORM.rotate, - }, + <SectionField label="Rotation"> + <NumberField + icon={<HugeiconsIcon icon={RotateClockwiseIcon} />} + className="flex-none" + value={rotation.displayValue} + onFocus={rotation.onFocus} + onChange={rotation.onChange} + onBlur={rotation.onBlur} + dragSensitivity="slow" + onScrub={rotation.scrubTo} + onScrubEnd={rotation.commitScrub} + onReset={() => + editor.timeline.updateElements({ + updates: [ + { + trackId, + elementId: element.id, + updates: { + transform: { + ...element.transform, + rotate: DEFAULT_TRANSFORM.rotate, }, }, - ], - }) - } - isDefault={element.transform.rotate === DEFAULT_TRANSFORM.rotate} - /> - </div> - </div> - </SectionContent> + }, + ], + }) + } + isDefault={element.transform.rotate === DEFAULT_TRANSFORM.rotate} + /> + </SectionField> + </SectionFields> + </SectionContent> </Section> ); } |
