import {Op, html, css, TemplateResult} from "@benev/slate"
import {styles} from "./styles.js"
import {Tooltip} from "../../views/tooltip/view.js"
import {shadow_component} from "../../context/context.js"
import {tooltipStyles} from "../../views/tooltip/styles.js"
import {StateHandler} from "../../views/state-handler/view.js"
import {ImageEffect, VideoEffect} from "../../context/types.js"
import circleInfoSvg from "../../icons/gravity-ui/circle-info.svg.js"
import overlapSvg from "../../icons/material-design-icons/overlap.svg.js"
import {FilterPropertyConfig, FilterSchemas, FilterType} from "../../context/controllers/compositor/parts/filter-manager.js"
export const OmniFilters = shadow_component(use => {
use.watch(() => use.context.state)
use.styles([styles, tooltipStyles, css`
#icon-container {
position: relative;
top: -25px;
}
`])
const controllers = use.context.controllers
const filtersManager = controllers.compositor.managers.filtersManager
const [filterPreviews, setFilterPreviews, getFilterPreviews] = use.state<{type: FilterType, canvas: PIXI.ICanvas, uid: number}[]>([])
const selectedImageOrVideoEffect = use.context.state.selected_effect?.kind === "video" || use.context.state.selected_effect?.kind === "image"
? use.context.state.effects.find(effect => effect.id === use.context.state.selected_effect!.id)! as ImageEffect | VideoEffect
: null
use.mount(() => {
filtersManager.createFilterPreviews((preview) => setFilterPreviews([...getFilterPreviews(), preview]))
const dispose = filtersManager.onChange(() => use.rerender())
return () => dispose()
})
const imageAndVideoEffects = () => use.context.state.effects.filter(effect => effect.kind === "image" || effect.kind === "video") as VideoEffect[] | ImageEffect[]
const renderEffectSelectionDropdown = () => {
return html`
{
const target = event.target as HTMLSelectElement
const effectId = target.value
const effect = use.context.state.effects.find(effect => effect.id === effectId)
controllers.timeline.set_selected_effect(effect, use.context.state)
}}
id="clip"
name="clip"
>
none
${imageAndVideoEffects().map(effect => html`${effect.name}`)}
${renderDropdownInfo()}
`
}
const renderDropdownInfo = () => {
return Tooltip(
circleInfoSvg,
html`Select video or image either from dropdown menu here, timeline or scene
`
)
}
const renderControl = (
filterType: FilterType,
propertyPath: string[],
config: FilterPropertyConfig
): TemplateResult => {
const label = propertyPath.join(".");
switch (config.type) {
case "number":
return html`
filtersManager.updateEffectFilter(
selectedImageOrVideoEffect!,
filterType,
propertyPath,
+(e.target as HTMLInputElement).value
)}
type="range"
min="${config.min}"
max="${config.max}"
value="${config.default}"
step="0.1"
>
`
case "color":
return html`
`
case "boolean":
return html`
`
case "choice":
// Normalize options: if it's an array, map each string to { value, label }
// if it's an object, use Object.entries.
const normalizedOptions = Array.isArray(config.options)
? config.options.map(option => ({ value: option, label: option }))
: Object.entries(config.options).map(
([value, label]) => ({ value, label })
)
return html`
filtersManager.updateEffectFilter(
selectedImageOrVideoEffect!,
filterType,
propertyPath,
+(e.target as HTMLSelectElement).value
)}
>
${normalizedOptions.map(
({ value, label }) => html`
${label}
`
)}
`
case "object":
// For an object type, recursively render nested controls.
return html`
`
case "array":
return html`
`
default:
return html``
}
}
const renderFilterOptions = (type: FilterType): TemplateResult => {
const { ...params } = FilterSchemas[type] as typeof FilterSchemas[typeof type]
return html`
${Object.entries(params).map(([propertyName, config]) =>
renderControl(type, [propertyName], config)
)}
`
}
const renderFilters = () => {
return filterPreviews.map(({type, canvas}) => {
return html`
{
e.preventDefault()
filtersManager.addFilterToEffect(selectedImageOrVideoEffect!, type)
}}
>
${type}
Options
${renderFilterOptions(type)}
`})
}
return StateHandler(Op.all(
use.context.is_webcodecs_supported.value,
use.context.helpers.ffmpeg.is_loading.value), () => html`
${overlapSvg} Filters
${renderEffectSelectionDropdown()}
${renderFilters()}
`)
})