mirror of
https://github.com/OpenCut-app/OpenCut.git
synced 2026-07-13 21:52:53 +02:00
fix a ton of issues + improvement
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user