diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-03-02 13:13:07 +0100 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-03-02 13:13:07 +0100 |
| commit | e7dcb586c0408255a0bf788e355fdb6d50d9b923 (patch) | |
| tree | 74ff0fc0f1ba501a075342db9b4e454450926154 /apps/web/src/components/editor/panels/properties/effect-properties.tsx | |
| parent | 93bea01c9ed3fcbebc64c9cef382a51d24c925ed (diff) | |
| download | opencut-e7dcb586c0408255a0bf788e355fdb6d50d9b923.tar.gz | |
feat: Clip effects, asset sorting, and timeline improvements
Major features and improvements:
* **Clip Effects**:
* Added UI in Properties Panel to manage effects on video/image clips (add, remove, toggle, reorder).
* Implemented dynamic parameter fields for effects.
* Added support for keyframing effect parameters.
* **Assets Panel**:
* Added sorting options: Name, Type, Duration, and File Size.
* Persisted view preferences (grid/list mode, sort order) to local storage.
* Refactored media item rendering and drag interactions.
* **Timeline & Interaction**:
* **Keyframe Dragging**: Added ability to drag keyframes directly on the timeline element.
* **Resizing**: Improved resize logic to respect neighboring clips (prevents overlaps).
* **Visuals**: Implemented tiled background rendering for video/image clips on the timeline.
* **Shortcuts**: Added "Deselect All" action bound to the `Escape` key.
* **Fixes**: Corrected drag-and-drop coordinate calculations when the timeline track area is scrolled.
* **Text Elements**:
* Refactored text background storage to use an explicit `enabled` flag.
* Added `V8toV9` storage migration to update existing projects.
* **Architecture**:
* Moved export state management to `ProjectManager` for better lifecycle handling.
* Refactored `PropertiesPanel` sections to be more composable (custom headers, borders).
Diffstat (limited to 'apps/web/src/components/editor/panels/properties/effect-properties.tsx')
| -rw-r--r-- | apps/web/src/components/editor/panels/properties/effect-properties.tsx | 103 |
1 files changed, 29 insertions, 74 deletions
diff --git a/apps/web/src/components/editor/panels/properties/effect-properties.tsx b/apps/web/src/components/editor/panels/properties/effect-properties.tsx index bf137291..75fc30a1 100644 --- a/apps/web/src/components/editor/panels/properties/effect-properties.tsx +++ b/apps/web/src/components/editor/panels/properties/effect-properties.tsx @@ -1,78 +1,16 @@ "use client"; import type { EffectElement } from "@/types/timeline"; -import type { EffectParamDefinition } from "@/types/effects"; import { getEffect } from "@/lib/effects/registry"; import { useEditor } from "@/hooks/use-editor"; -import { clamp } from "@/utils/math"; -import { Section, SectionContent, SectionHeader, SectionField, SectionFields } from "./section"; -import { Slider } from "@/components/ui/slider"; -import { NumberField } from "@/components/ui/number-field"; -import { usePropertyDraft } from "./hooks/use-property-draft"; - -function EffectParamField({ - param, - element, - trackId, -}: { - param: EffectParamDefinition; - element: EffectElement; - trackId: string; -}) { - const editor = useEditor(); - - const currentValue = Number(element.params[param.key] ?? param.default); - const min = param.min ?? 0; - const max = param.max ?? 100; - const step = param.step ?? 1; - - const updateParam = (value: number) => - editor.timeline.previewElements({ - updates: [ - { - trackId, - elementId: element.id, - updates: { params: { ...element.params, [param.key]: value } }, - }, - ], - }); - - const commitParam = () => editor.timeline.commitPreview(); - - const draft = usePropertyDraft({ - displayValue: String(currentValue), - parse: (input) => { - const parsed = parseFloat(input); - if (Number.isNaN(parsed)) return null; - return clamp({ value: parsed, min, max }); - }, - onPreview: updateParam, - onCommit: commitParam, - }); - - return ( - <SectionField label={param.label}> - <div className="flex items-center gap-3"> - <Slider - className="flex-1" - min={min} - max={max} - step={step} - value={[currentValue]} - onValueChange={([value]) => updateParam(value)} - onValueCommit={commitParam} - /> - <NumberField - className="w-16 shrink-0" - value={draft.displayValue} - onFocus={draft.onFocus} - onChange={draft.onChange} - onBlur={draft.onBlur} - /> - </div> - </SectionField> - ); -} +import { + Section, + SectionContent, + SectionHeader, + SectionFields, + SectionTitle, +} from "./section"; +import { EffectParamField } from "./effect-param-field"; export function EffectProperties({ element, @@ -81,19 +19,36 @@ export function EffectProperties({ element: EffectElement; trackId: string; }) { + const editor = useEditor(); const definition = getEffect({ effectType: element.effectType }); + const previewParam = + ({ key }: { key: string }) => + (value: number | string | boolean) => + editor.timeline.previewElements({ + updates: [ + { + trackId, + elementId: element.id, + updates: { params: { ...element.params, [key]: value } }, + }, + ], + }); + return ( - <Section hasBorderTop={false}> - <SectionHeader title={definition.name} /> + <Section showTopBorder={false}> + <SectionHeader> + <SectionTitle>{definition.name}</SectionTitle> + </SectionHeader> <SectionContent> <SectionFields> {definition.params.map((param) => ( <EffectParamField key={param.key} param={param} - element={element} - trackId={trackId} + value={element.params[param.key] ?? param.default} + onPreview={previewParam({ key: param.key })} + onCommit={() => editor.timeline.commitPreview()} /> ))} </SectionFields> |
