@pixerate/editor-svelte 0.128.4 → 0.129.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/effects/EffectParameters.svelte +48 -5
- package/dist/effects/EffectParameters.svelte.d.ts +3 -0
- package/dist/image-editor/AdjustmentLayersPanel.svelte +3 -14
- package/dist/image-editor/BrushOptionsBar.svelte +4 -15
- package/dist/image-editor/ImageEditor.svelte +2 -5
- package/dist/image-editor/ImageEffectControls.svelte +9 -2
- package/dist/image-editor/ImageEffectControls.svelte.d.ts +3 -0
- package/dist/image-editor/ImageProviderEffectsPanel.svelte +10 -5
- package/dist/image-editor/ImageProviderEffectsPanel.svelte.d.ts +3 -0
- package/dist/image-editor/LayerEffectsPanel.svelte +2 -1
- package/dist/image-editor/ShapeOptionsBar.svelte +4 -17
- package/dist/image-editor/TypeOptionsBar.svelte +3 -2
- package/dist/image-editor/controls/ImageColor.svelte +21 -0
- package/dist/image-editor/controls/ImageColor.svelte.d.ts +4 -0
- package/dist/image-editor/controls/ImageParameterColor.svelte +9 -0
- package/dist/image-editor/controls/ImageParameterColor.svelte.d.ts +4 -0
- package/dist/image-editor/controls/ImageParameterSelect.svelte +11 -0
- package/dist/image-editor/controls/ImageParameterSelect.svelte.d.ts +4 -0
- package/dist/image-editor/controls/ImageParameterSlider.svelte +9 -0
- package/dist/image-editor/controls/ImageParameterSlider.svelte.d.ts +4 -0
- package/dist/image-editor/controls/controls.d.ts +17 -0
- package/dist/image-editor/controls/parameterControls.d.ts +4 -0
- package/dist/image-editor/controls/parameterControls.js +11 -0
- package/dist/image-editor/index.d.ts +2 -1
- package/dist/image-editor/index.js +1 -0
- package/dist/parameters/ParameterColor.svelte +59 -51
- package/dist/parameters/ParameterColor.svelte.d.ts +2 -0
- package/dist/parameters/ParameterCommitInput.svelte +33 -4
- package/dist/parameters/ParameterFieldControl.svelte +7 -5
- package/dist/parameters/ParameterFieldControl.svelte.d.ts +2 -0
- package/dist/parameters/ParameterNumberInput.svelte +41 -17
- package/dist/parameters/ParameterNumberInput.svelte.d.ts +2 -0
- package/dist/parameters/ParameterSelect.svelte +25 -6
- package/dist/parameters/ParameterSelect.svelte.d.ts +2 -0
- package/dist/parameters/ParameterSlider.svelte +48 -20
- package/dist/parameters/ParameterSlider.svelte.d.ts +2 -0
- package/dist/parameters/ParameterVector.svelte +6 -3
- package/dist/parameters/ParameterVector.svelte.d.ts +2 -0
- package/dist/parameters/ParametersPanel.svelte +54 -18
- package/dist/parameters/ParametersPanel.svelte.d.ts +3 -0
- package/dist/parameters/controls.d.ts +89 -0
- package/dist/parameters/controls.js +19 -0
- package/dist/parameters/index.d.ts +1 -0
- package/dist/parameters/index.js +1 -0
- package/dist/parameters/styles.d.ts +6 -0
- package/dist/parameters/styles.js +7 -0
- package/package.json +2 -2
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import ColorPicker from '../color/ColorPicker.svelte';
|
|
4
4
|
import ParameterCommitInput from './ParameterCommitInput.svelte';
|
|
5
5
|
import { parameterInputClass } from './styles.js';
|
|
6
|
+
import { getParameterComponents, resolveParameterComponents, type ParameterComponents } from './controls.js';
|
|
6
7
|
|
|
7
8
|
interface Props {
|
|
8
9
|
field: ColorParameterField;
|
|
@@ -13,9 +14,10 @@
|
|
|
13
14
|
ongestureend?: () => void;
|
|
14
15
|
disabled?: boolean;
|
|
15
16
|
id?: string;
|
|
17
|
+
components?: ParameterComponents;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
|
-
let { field, value, onchange, ongesturestart, ongestureend, disabled = false, id }: Props = $props();
|
|
20
|
+
let { field, value, onchange, ongesturestart, ongestureend, disabled = false, id, components }: Props = $props();
|
|
19
21
|
|
|
20
22
|
const alpha = $derived(!!field.alpha);
|
|
21
23
|
const hex = $derived(normalizeHexColor(value, true) ?? '#000000ff');
|
|
@@ -23,6 +25,8 @@
|
|
|
23
25
|
const shown = $derived(alpha ? hex : rgb);
|
|
24
26
|
const off = $derived(disabled || !!field.readOnly);
|
|
25
27
|
const name = $derived(parameterLabel(field));
|
|
28
|
+
const context = getParameterComponents();
|
|
29
|
+
const CustomColor = $derived(resolveParameterComponents(components, context).color);
|
|
26
30
|
|
|
27
31
|
let open = $state(false);
|
|
28
32
|
let containerEl = $state<HTMLDivElement | null>(null);
|
|
@@ -52,57 +56,61 @@
|
|
|
52
56
|
});
|
|
53
57
|
</script>
|
|
54
58
|
|
|
55
|
-
<!-- A colour picker with a hex box and a popup ColorPicker. -->
|
|
56
|
-
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
class="{parameterInputClass} font-mono"
|
|
77
|
-
oncommit={(text) => {
|
|
78
|
-
const next = normalizeHexColor(text, alpha);
|
|
79
|
-
if (next) onchange(next);
|
|
80
|
-
}}
|
|
81
|
-
/>
|
|
82
|
-
</div>
|
|
83
|
-
{#if open}
|
|
84
|
-
<div
|
|
85
|
-
bind:this={popoverEl}
|
|
86
|
-
role="dialog"
|
|
87
|
-
aria-label={name}
|
|
88
|
-
tabindex="-1"
|
|
89
|
-
onfocusout={(e) => {
|
|
90
|
-
if (popoverEl && !popoverEl.contains(e.relatedTarget as Node)) {
|
|
91
|
-
open = false;
|
|
92
|
-
}
|
|
93
|
-
}}
|
|
94
|
-
class="absolute left-0 top-full z-50 mt-1 w-64 rounded-lg border border-border bg-popover p-2 text-popover-foreground shadow-xl"
|
|
95
|
-
style="background: var(--popover, var(--background, #fff));"
|
|
96
|
-
>
|
|
97
|
-
<ColorPicker
|
|
59
|
+
<!-- A colour picker with a hex box and a popup ColorPicker, or the host's own colour control. -->
|
|
60
|
+
{#if CustomColor}
|
|
61
|
+
<CustomColor {id} value={shown} {alpha} swatches={field.swatches} disabled={off} label={name} {onchange} {ongesturestart} {ongestureend} />
|
|
62
|
+
{:else}
|
|
63
|
+
<div bind:this={containerEl} class="relative space-y-1.5">
|
|
64
|
+
<div class="flex items-center gap-2">
|
|
65
|
+
<button
|
|
66
|
+
bind:this={buttonEl}
|
|
67
|
+
type="button"
|
|
68
|
+
aria-label={name}
|
|
69
|
+
aria-expanded={open}
|
|
70
|
+
aria-haspopup="dialog"
|
|
71
|
+
disabled={off}
|
|
72
|
+
onclick={() => (open = !open)}
|
|
73
|
+
class="relative h-7 w-9 shrink-0 overflow-hidden rounded-md border border-input bg-[repeating-conic-gradient(#d4d4d8_0_25%,#fff_0_50%)] bg-[length:8px_8px] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary disabled:cursor-not-allowed disabled:opacity-50"
|
|
74
|
+
>
|
|
75
|
+
<span class="absolute inset-0" style:background={shown}></span>
|
|
76
|
+
</button>
|
|
77
|
+
<ParameterCommitInput
|
|
78
|
+
{id}
|
|
79
|
+
aria-label="{name} hex"
|
|
98
80
|
value={shown}
|
|
99
|
-
{alpha}
|
|
100
|
-
swatches={field.swatches}
|
|
101
81
|
disabled={off}
|
|
102
|
-
{
|
|
103
|
-
{
|
|
104
|
-
{
|
|
82
|
+
spellcheck={false}
|
|
83
|
+
class="{parameterInputClass} font-mono"
|
|
84
|
+
oncommit={(text) => {
|
|
85
|
+
const next = normalizeHexColor(text, alpha);
|
|
86
|
+
if (next) onchange(next);
|
|
87
|
+
}}
|
|
105
88
|
/>
|
|
106
89
|
</div>
|
|
107
|
-
|
|
108
|
-
|
|
90
|
+
{#if open}
|
|
91
|
+
<div
|
|
92
|
+
bind:this={popoverEl}
|
|
93
|
+
role="dialog"
|
|
94
|
+
aria-label={name}
|
|
95
|
+
tabindex="-1"
|
|
96
|
+
onfocusout={(e) => {
|
|
97
|
+
if (popoverEl && !popoverEl.contains(e.relatedTarget as Node)) {
|
|
98
|
+
open = false;
|
|
99
|
+
}
|
|
100
|
+
}}
|
|
101
|
+
class="absolute left-0 top-full z-50 mt-1 w-64 rounded-lg border border-border bg-popover p-2 text-popover-foreground shadow-xl"
|
|
102
|
+
style="background: var(--popover, var(--background, #fff));"
|
|
103
|
+
>
|
|
104
|
+
<ColorPicker
|
|
105
|
+
value={shown}
|
|
106
|
+
{alpha}
|
|
107
|
+
swatches={field.swatches}
|
|
108
|
+
disabled={off}
|
|
109
|
+
{onchange}
|
|
110
|
+
{ongesturestart}
|
|
111
|
+
{ongestureend}
|
|
112
|
+
/>
|
|
113
|
+
</div>
|
|
114
|
+
{/if}
|
|
115
|
+
</div>
|
|
116
|
+
{/if}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ColorParameterField } from '@pixerate/editor/parameters';
|
|
2
|
+
import { type ParameterComponents } from './controls.js';
|
|
2
3
|
interface Props {
|
|
3
4
|
field: ColorParameterField;
|
|
4
5
|
value: string;
|
|
@@ -8,6 +9,7 @@ interface Props {
|
|
|
8
9
|
ongestureend?: () => void;
|
|
9
10
|
disabled?: boolean;
|
|
10
11
|
id?: string;
|
|
12
|
+
components?: ParameterComponents;
|
|
11
13
|
}
|
|
12
14
|
declare const ParameterColor: import("svelte").Component<Props, {}, "">;
|
|
13
15
|
type ParameterColor = ReturnType<typeof ParameterColor>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
3
|
-
import { parameterInputClass } from './styles.js';
|
|
3
|
+
import { parameterInputClass, parameterInputStyle } from './styles.js';
|
|
4
4
|
|
|
5
5
|
interface Props extends Omit<HTMLInputAttributes, 'value' | 'class'> {
|
|
6
6
|
value: string;
|
|
@@ -11,15 +11,27 @@
|
|
|
11
11
|
class?: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
let { value, oncommit, multiline = false, class: className = parameterInputClass, onkeydown, ...rest }: Props = $props();
|
|
14
|
+
let { value, oncommit, multiline = false, class: className = parameterInputClass, onkeydown, onchange, style, ...rest }: Props = $props();
|
|
15
|
+
|
|
16
|
+
/** The text last handed to `oncommit` (null once `value` changes), so the blur after Tab or `change` is a no-op. */
|
|
17
|
+
let lastCommitted: string | null = null;
|
|
18
|
+
$effect.pre(() => {
|
|
19
|
+
void value;
|
|
20
|
+
lastCommitted = null;
|
|
21
|
+
});
|
|
15
22
|
|
|
16
23
|
function commit(el: HTMLInputElement | HTMLTextAreaElement) {
|
|
17
|
-
|
|
24
|
+
const text = el.value;
|
|
25
|
+
if (text === value || text === lastCommitted) return;
|
|
26
|
+
oncommit(text, el);
|
|
27
|
+
// A refused edit puts the stored text back: then the same text is tried (and refused) again.
|
|
28
|
+
lastCommitted = el.value === text ? text : null;
|
|
18
29
|
}
|
|
19
30
|
|
|
20
31
|
function keydown(e: KeyboardEvent & { currentTarget: HTMLInputElement | HTMLTextAreaElement }) {
|
|
21
32
|
(onkeydown as ((e: KeyboardEvent) => void) | undefined)?.(e);
|
|
22
33
|
if (e.defaultPrevented) return;
|
|
34
|
+
if (e.key === 'Tab') commit(e.currentTarget);
|
|
23
35
|
if (e.key === 'Enter' && (!multiline || e.metaKey || e.ctrlKey)) {
|
|
24
36
|
e.preventDefault();
|
|
25
37
|
commit(e.currentTarget);
|
|
@@ -32,10 +44,27 @@
|
|
|
32
44
|
<textarea
|
|
33
45
|
{...rest as any}
|
|
34
46
|
class="{className} h-auto min-h-[3.5rem] py-1"
|
|
47
|
+
style="{parameterInputStyle} {style ?? ''}"
|
|
35
48
|
{value}
|
|
36
49
|
onblur={(e) => commit(e.currentTarget)}
|
|
50
|
+
onchange={(e) => {
|
|
51
|
+
(onchange as ((e: Event) => void) | undefined)?.(e);
|
|
52
|
+
commit(e.currentTarget);
|
|
53
|
+
}}
|
|
37
54
|
onkeydown={keydown}
|
|
38
55
|
></textarea>
|
|
39
56
|
{:else}
|
|
40
|
-
<input
|
|
57
|
+
<input
|
|
58
|
+
type="text"
|
|
59
|
+
{...rest}
|
|
60
|
+
class={className}
|
|
61
|
+
style="{parameterInputStyle} {style ?? ''}"
|
|
62
|
+
{value}
|
|
63
|
+
onblur={(e) => commit(e.currentTarget)}
|
|
64
|
+
onchange={(e) => {
|
|
65
|
+
(onchange as ((e: Event) => void) | undefined)?.(e);
|
|
66
|
+
commit(e.currentTarget);
|
|
67
|
+
}}
|
|
68
|
+
onkeydown={keydown}
|
|
69
|
+
/>
|
|
41
70
|
{/if}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import ParameterText from './ParameterText.svelte';
|
|
8
8
|
import ParameterToggle from './ParameterToggle.svelte';
|
|
9
9
|
import ParameterVector from './ParameterVector.svelte';
|
|
10
|
+
import type { ParameterComponents } from './controls.js';
|
|
10
11
|
|
|
11
12
|
interface Props {
|
|
12
13
|
field: ParameterField;
|
|
@@ -17,23 +18,24 @@
|
|
|
17
18
|
resolveFile?: (file: File) => string | Promise<string>;
|
|
18
19
|
disabled?: boolean;
|
|
19
20
|
id?: string;
|
|
21
|
+
components?: ParameterComponents;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
let { field, value, onchange, ongesturestart, ongestureend, resolveFile, disabled = false, id }: Props = $props();
|
|
24
|
+
let { field, value, onchange, ongesturestart, ongestureend, resolveFile, disabled = false, id, components }: Props = $props();
|
|
23
25
|
const set = $derived(onchange as (value: any) => void);
|
|
24
26
|
</script>
|
|
25
27
|
|
|
26
28
|
<!-- The control for any field type. -->
|
|
27
29
|
{#if field.type === 'number' || field.type === 'int'}
|
|
28
|
-
<ParameterSlider {field} value={value as number} onchange={set} {ongesturestart} {ongestureend} {disabled} {id} />
|
|
30
|
+
<ParameterSlider {field} value={value as number} onchange={set} {ongesturestart} {ongestureend} {disabled} {id} {components} />
|
|
29
31
|
{:else if field.type === 'boolean'}
|
|
30
32
|
<ParameterToggle {field} value={value as boolean} onchange={set} {disabled} {id} />
|
|
31
33
|
{:else if field.type === 'choice'}
|
|
32
|
-
<ParameterSelect {field} value={value as string | number} onchange={set} {disabled} {id} />
|
|
34
|
+
<ParameterSelect {field} value={value as string | number} onchange={set} {disabled} {id} {components} />
|
|
33
35
|
{:else if field.type === 'color'}
|
|
34
|
-
<ParameterColor {field} value={value as string} onchange={set} {ongesturestart} {ongestureend} {disabled} {id} />
|
|
36
|
+
<ParameterColor {field} value={value as string} onchange={set} {ongesturestart} {ongestureend} {disabled} {id} {components} />
|
|
35
37
|
{:else if field.type === 'vector'}
|
|
36
|
-
<ParameterVector {field} value={value as number[]} onchange={set} {disabled} {id} />
|
|
38
|
+
<ParameterVector {field} value={value as number[]} onchange={set} {disabled} {id} {components} />
|
|
37
39
|
{:else if field.type === 'image'}
|
|
38
40
|
<ParameterImage {field} value={value as string} onchange={set} {resolveFile} {disabled} {id} />
|
|
39
41
|
{:else if field.type === 'text'}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ParameterField, ParameterValue } from '@pixerate/editor/parameters';
|
|
2
|
+
import type { ParameterComponents } from './controls.js';
|
|
2
3
|
interface Props {
|
|
3
4
|
field: ParameterField;
|
|
4
5
|
value: ParameterValue;
|
|
@@ -8,6 +9,7 @@ interface Props {
|
|
|
8
9
|
resolveFile?: (file: File) => string | Promise<string>;
|
|
9
10
|
disabled?: boolean;
|
|
10
11
|
id?: string;
|
|
12
|
+
components?: ParameterComponents;
|
|
11
13
|
}
|
|
12
14
|
declare const ParameterFieldControl: import("svelte").Component<Props, {}, "">;
|
|
13
15
|
type ParameterFieldControl = ReturnType<typeof ParameterFieldControl>;
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
type NumberParameterField
|
|
9
9
|
} from '@pixerate/editor/parameters';
|
|
10
10
|
import ParameterCommitInput from './ParameterCommitInput.svelte';
|
|
11
|
-
import { parameterInputClass } from './styles.js';
|
|
11
|
+
import { parameterInputClass, parameterInputStyle } from './styles.js';
|
|
12
|
+
import { getParameterComponents, resolveParameterComponents, type ParameterComponents } from './controls.js';
|
|
12
13
|
|
|
13
14
|
interface Props {
|
|
14
15
|
field: NumberParameterField;
|
|
@@ -17,11 +18,14 @@
|
|
|
17
18
|
disabled?: boolean;
|
|
18
19
|
id?: string;
|
|
19
20
|
'aria-label'?: string;
|
|
21
|
+
components?: ParameterComponents;
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
let { field, value, onchange, disabled = false, id, 'aria-label': ariaLabel }: Props = $props();
|
|
24
|
+
let { field, value, onchange, disabled = false, id, 'aria-label': ariaLabel, components }: Props = $props();
|
|
23
25
|
|
|
24
26
|
const text = $derived(formatParameterNumber({ ...field, unit: undefined }, value));
|
|
27
|
+
const context = getParameterComponents();
|
|
28
|
+
const CustomInput = $derived(resolveParameterComponents(components, context).input);
|
|
25
29
|
|
|
26
30
|
function keydown(e: KeyboardEvent) {
|
|
27
31
|
if (e.key !== 'ArrowUp' && e.key !== 'ArrowDown') return;
|
|
@@ -29,25 +33,45 @@
|
|
|
29
33
|
const step = parameterStep(field) * (e.shiftKey ? 10 : 1);
|
|
30
34
|
onchange(clampParameterNumber(field, value + (e.key === 'ArrowUp' ? step : -step)));
|
|
31
35
|
}
|
|
36
|
+
|
|
37
|
+
function commitValue(t: string, input?: HTMLInputElement | HTMLTextAreaElement) {
|
|
38
|
+
const n = parseParameterNumberInput(field, t);
|
|
39
|
+
// Show the stored value again when the text was not a number or clamps to it.
|
|
40
|
+
if (n === null || n === value) {
|
|
41
|
+
if (input) input.value = text;
|
|
42
|
+
} else {
|
|
43
|
+
onchange(n);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
32
46
|
</script>
|
|
33
47
|
|
|
34
48
|
<!-- A number box: type a value (units allowed), or step with the arrow keys (Shift ×10). -->
|
|
35
49
|
<div class="relative min-w-0 flex-1">
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
{#if CustomInput}
|
|
51
|
+
<CustomInput
|
|
52
|
+
{id}
|
|
53
|
+
inputmode="decimal"
|
|
54
|
+
aria-label={ariaLabel ?? parameterLabel(field)}
|
|
55
|
+
label={parameterLabel(field)}
|
|
56
|
+
value={text}
|
|
57
|
+
disabled={disabled || field.readOnly}
|
|
58
|
+
class="{parameterInputClass} {field.unit ? 'pr-6' : ''}"
|
|
59
|
+
style={parameterInputStyle}
|
|
60
|
+
onkeydown={keydown}
|
|
61
|
+
oncommit={commitValue}
|
|
62
|
+
/>
|
|
63
|
+
{:else}
|
|
64
|
+
<ParameterCommitInput
|
|
65
|
+
{id}
|
|
66
|
+
inputmode="decimal"
|
|
67
|
+
aria-label={ariaLabel ?? parameterLabel(field)}
|
|
68
|
+
value={text}
|
|
69
|
+
disabled={disabled || field.readOnly}
|
|
70
|
+
class="{parameterInputClass} {field.unit ? 'pr-6' : ''}"
|
|
71
|
+
onkeydown={keydown}
|
|
72
|
+
oncommit={commitValue}
|
|
73
|
+
/>
|
|
74
|
+
{/if}
|
|
51
75
|
{#if field.unit}
|
|
52
76
|
<span class="pointer-events-none absolute right-2 top-1/2 -translate-y-1/2 text-[10px] text-muted-foreground">{field.unit}</span>
|
|
53
77
|
{/if}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type NumberParameterField } from '@pixerate/editor/parameters';
|
|
2
|
+
import { type ParameterComponents } from './controls.js';
|
|
2
3
|
interface Props {
|
|
3
4
|
field: NumberParameterField;
|
|
4
5
|
value: number;
|
|
@@ -6,6 +7,7 @@ interface Props {
|
|
|
6
7
|
disabled?: boolean;
|
|
7
8
|
id?: string;
|
|
8
9
|
'aria-label'?: string;
|
|
10
|
+
components?: ParameterComponents;
|
|
9
11
|
}
|
|
10
12
|
declare const ParameterNumberInput: import("svelte").Component<Props, {}, "">;
|
|
11
13
|
type ParameterNumberInput = ReturnType<typeof ParameterNumberInput>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { choiceOptionLabel, getChoiceOptions, parameterLabel, type ChoiceParameterField } from '@pixerate/editor/parameters';
|
|
3
|
-
import { parameterInputClass } from './styles.js';
|
|
2
|
+
import { choiceOptionLabel, getChoiceOptions, matchParameterOptionValue, parameterLabel, type ChoiceParameterField } from '@pixerate/editor/parameters';
|
|
3
|
+
import { parameterInputClass, parameterSelectStyle } from './styles.js';
|
|
4
|
+
import { getParameterComponents, resolveParameterComponents, type ParameterComponents } from './controls.js';
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
6
7
|
field: ChoiceParameterField;
|
|
@@ -8,17 +9,22 @@
|
|
|
8
9
|
onchange: (value: string | number) => void;
|
|
9
10
|
disabled?: boolean;
|
|
10
11
|
id?: string;
|
|
12
|
+
components?: ParameterComponents;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
let { field, value, onchange, disabled = false, id }: Props = $props();
|
|
15
|
+
let { field, value, onchange, disabled = false, id, components }: Props = $props();
|
|
14
16
|
|
|
15
17
|
const options = $derived(getChoiceOptions(field));
|
|
16
18
|
const off = $derived(disabled || !!field.readOnly);
|
|
19
|
+
const context = getParameterComponents();
|
|
20
|
+
const CustomSelect = $derived(resolveParameterComponents(components, context).select);
|
|
21
|
+
const label = $derived(parameterLabel(field));
|
|
22
|
+
const customOptions = $derived(options.map((o) => ({ value: o.value, label: choiceOptionLabel(o), description: o.description })));
|
|
17
23
|
</script>
|
|
18
24
|
|
|
19
25
|
<!-- A dropdown (or segmented buttons with `control: 'segmented'`) for `choice` fields. -->
|
|
20
26
|
{#if field.control === 'segmented'}
|
|
21
|
-
<div role="radiogroup" aria-label={
|
|
27
|
+
<div role="radiogroup" aria-label={label} class="inline-flex flex-wrap rounded-md border border-input bg-muted p-0.5">
|
|
22
28
|
{#each options as o (o.value)}
|
|
23
29
|
<button
|
|
24
30
|
type="button"
|
|
@@ -35,20 +41,33 @@
|
|
|
35
41
|
</button>
|
|
36
42
|
{/each}
|
|
37
43
|
</div>
|
|
44
|
+
{:else if CustomSelect}
|
|
45
|
+
<CustomSelect
|
|
46
|
+
{id}
|
|
47
|
+
aria-label={label}
|
|
48
|
+
{label}
|
|
49
|
+
{value}
|
|
50
|
+
options={customOptions}
|
|
51
|
+
disabled={off}
|
|
52
|
+
class={parameterInputClass}
|
|
53
|
+
style={parameterSelectStyle}
|
|
54
|
+
onchange={(v) => onchange(matchParameterOptionValue(options, v))}
|
|
55
|
+
/>
|
|
38
56
|
{:else}
|
|
39
57
|
<select
|
|
40
58
|
{id}
|
|
41
|
-
aria-label={
|
|
59
|
+
aria-label={label}
|
|
42
60
|
value={String(options.findIndex((o) => o.value === value))}
|
|
43
61
|
disabled={off}
|
|
44
62
|
class={parameterInputClass}
|
|
63
|
+
style={parameterSelectStyle}
|
|
45
64
|
onchange={(e) => {
|
|
46
65
|
const option = options[Number(e.currentTarget.value)];
|
|
47
66
|
if (option) onchange(option.value);
|
|
48
67
|
}}
|
|
49
68
|
>
|
|
50
69
|
{#each options as o, i (o.value)}
|
|
51
|
-
<option value={String(i)} title={o.description}>{choiceOptionLabel(o)}</option>
|
|
70
|
+
<option value={String(i)} title={o.description} style={parameterSelectStyle}>{choiceOptionLabel(o)}</option>
|
|
52
71
|
{/each}
|
|
53
72
|
</select>
|
|
54
73
|
{/if}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { type ChoiceParameterField } from '@pixerate/editor/parameters';
|
|
2
|
+
import { type ParameterComponents } from './controls.js';
|
|
2
3
|
interface Props {
|
|
3
4
|
field: ChoiceParameterField;
|
|
4
5
|
value: string | number;
|
|
5
6
|
onchange: (value: string | number) => void;
|
|
6
7
|
disabled?: boolean;
|
|
7
8
|
id?: string;
|
|
9
|
+
components?: ParameterComponents;
|
|
8
10
|
}
|
|
9
11
|
declare const ParameterSelect: import("svelte").Component<Props, {}, "">;
|
|
10
12
|
type ParameterSelect = ReturnType<typeof ParameterSelect>;
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
type NumberParameterField
|
|
9
9
|
} from '@pixerate/editor/parameters';
|
|
10
10
|
import ParameterNumberInput from './ParameterNumberInput.svelte';
|
|
11
|
-
import { parameterRangeClass } from './styles.js';
|
|
11
|
+
import { parameterRangeClass, parameterRangeStyle } from './styles.js';
|
|
12
|
+
import { getParameterComponents, resolveParameterComponents, type ParameterComponents } from './controls.js';
|
|
12
13
|
|
|
13
14
|
interface Props {
|
|
14
15
|
field: NumberParameterField;
|
|
@@ -19,9 +20,10 @@
|
|
|
19
20
|
ongestureend?: () => void;
|
|
20
21
|
disabled?: boolean;
|
|
21
22
|
id?: string;
|
|
23
|
+
components?: ParameterComponents;
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
let { field, value, onchange, ongesturestart, ongestureend, disabled = false, id }: Props = $props();
|
|
26
|
+
let { field, value, onchange, ongesturestart, ongestureend, disabled = false, id, components }: Props = $props();
|
|
25
27
|
|
|
26
28
|
let dragging = false;
|
|
27
29
|
function start() {
|
|
@@ -34,31 +36,57 @@
|
|
|
34
36
|
dragging = false;
|
|
35
37
|
ongestureend?.();
|
|
36
38
|
}
|
|
39
|
+
|
|
40
|
+
const context = getParameterComponents();
|
|
41
|
+
const CustomSlider = $derived(resolveParameterComponents(components, context).slider);
|
|
42
|
+
const step = $derived(parameterStep(field));
|
|
43
|
+
const label = $derived(parameterLabel(field));
|
|
44
|
+
const valuetext = $derived(formatParameterNumber(field, value));
|
|
37
45
|
</script>
|
|
38
46
|
|
|
39
47
|
<!-- A slider plus number box for `number` / `int` fields (number box only for `control: 'input'` or unbounded fields). -->
|
|
40
48
|
{#if isSliderField(field)}
|
|
41
49
|
<div class="flex items-center gap-2">
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
{#if CustomSlider}
|
|
51
|
+
<CustomSlider
|
|
52
|
+
id={id ? `${id}-slider` : undefined}
|
|
53
|
+
aria-label={label}
|
|
54
|
+
{label}
|
|
55
|
+
aria-valuetext={valuetext}
|
|
56
|
+
min={field.min ?? 0}
|
|
57
|
+
max={field.max ?? 100}
|
|
58
|
+
{step}
|
|
59
|
+
{value}
|
|
60
|
+
disabled={disabled || field.readOnly}
|
|
61
|
+
class={parameterRangeClass}
|
|
62
|
+
style={parameterRangeStyle}
|
|
63
|
+
ongesturestart={start}
|
|
64
|
+
ongestureend={end}
|
|
65
|
+
oninput={(v) => onchange(clampParameterNumber(field, v))}
|
|
66
|
+
/>
|
|
67
|
+
{:else}
|
|
68
|
+
<input
|
|
69
|
+
type="range"
|
|
70
|
+
aria-label={label}
|
|
71
|
+
aria-valuetext={valuetext}
|
|
72
|
+
min={field.min ?? 0}
|
|
73
|
+
max={field.max ?? 100}
|
|
74
|
+
{step}
|
|
75
|
+
{value}
|
|
76
|
+
disabled={disabled || field.readOnly}
|
|
77
|
+
class={parameterRangeClass}
|
|
78
|
+
style={parameterRangeStyle}
|
|
79
|
+
onpointerdown={start}
|
|
80
|
+
onpointerup={end}
|
|
81
|
+
onpointercancel={end}
|
|
82
|
+
onblur={end}
|
|
83
|
+
oninput={(e) => onchange(clampParameterNumber(field, Number(e.currentTarget.value)))}
|
|
84
|
+
/>
|
|
85
|
+
{/if}
|
|
58
86
|
<div class="w-20 shrink-0">
|
|
59
|
-
<ParameterNumberInput {field} {value} {onchange} {disabled} {id} aria-label="{
|
|
87
|
+
<ParameterNumberInput {field} {value} {onchange} {disabled} {id} aria-label="{label} value" {components} />
|
|
60
88
|
</div>
|
|
61
89
|
</div>
|
|
62
90
|
{:else}
|
|
63
|
-
<ParameterNumberInput {field} {value} {onchange} {disabled} {id} />
|
|
91
|
+
<ParameterNumberInput {field} {value} {onchange} {disabled} {id} {components} />
|
|
64
92
|
{/if}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type NumberParameterField } from '@pixerate/editor/parameters';
|
|
2
|
+
import { type ParameterComponents } from './controls.js';
|
|
2
3
|
interface Props {
|
|
3
4
|
field: NumberParameterField;
|
|
4
5
|
value: number;
|
|
@@ -8,6 +9,7 @@ interface Props {
|
|
|
8
9
|
ongestureend?: () => void;
|
|
9
10
|
disabled?: boolean;
|
|
10
11
|
id?: string;
|
|
12
|
+
components?: ParameterComponents;
|
|
11
13
|
}
|
|
12
14
|
declare const ParameterSlider: import("svelte").Component<Props, {}, "">;
|
|
13
15
|
type ParameterSlider = ReturnType<typeof ParameterSlider>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { parameterLabel, vectorComponentField, vectorSize, type VectorParameterField } from '@pixerate/editor/parameters';
|
|
3
3
|
import ParameterNumberInput from './ParameterNumberInput.svelte';
|
|
4
|
+
import type { ParameterComponents } from './controls.js';
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
6
7
|
field: VectorParameterField;
|
|
@@ -8,17 +9,18 @@
|
|
|
8
9
|
onchange: (value: number[]) => void;
|
|
9
10
|
disabled?: boolean;
|
|
10
11
|
id?: string;
|
|
12
|
+
components?: ParameterComponents;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
|
-
let { field, value, onchange, disabled = false, id }: Props = $props();
|
|
15
|
+
let { field, value, onchange, disabled = false, id, components }: Props = $props();
|
|
14
16
|
|
|
15
17
|
const size = $derived(vectorSize(field));
|
|
16
|
-
const
|
|
18
|
+
const vectorFields = $derived(Array.from({ length: size }, (_, i) => vectorComponentField(field, i)));
|
|
17
19
|
</script>
|
|
18
20
|
|
|
19
21
|
<!-- One number box per component for `vector` fields. -->
|
|
20
22
|
<div class="grid gap-1.5" style:grid-template-columns="repeat({size}, minmax(0, 1fr))">
|
|
21
|
-
{#each
|
|
23
|
+
{#each vectorFields as component, i (i)}
|
|
22
24
|
<label class="flex min-w-0 items-center gap-1">
|
|
23
25
|
<span class="w-3 shrink-0 text-[10px] uppercase text-muted-foreground">{component.label}</span>
|
|
24
26
|
<ParameterNumberInput
|
|
@@ -27,6 +29,7 @@
|
|
|
27
29
|
id={i === 0 ? id : undefined}
|
|
28
30
|
aria-label="{parameterLabel(field)} {component.label}"
|
|
29
31
|
disabled={disabled || field.readOnly}
|
|
32
|
+
{components}
|
|
30
33
|
onchange={(n) => onchange(Array.from({ length: size }, (_, j) => (j === i ? n : (value[j] ?? 0))))}
|
|
31
34
|
/>
|
|
32
35
|
</label>
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { type VectorParameterField } from '@pixerate/editor/parameters';
|
|
2
|
+
import type { ParameterComponents } from './controls.js';
|
|
2
3
|
interface Props {
|
|
3
4
|
field: VectorParameterField;
|
|
4
5
|
value: number[];
|
|
5
6
|
onchange: (value: number[]) => void;
|
|
6
7
|
disabled?: boolean;
|
|
7
8
|
id?: string;
|
|
9
|
+
components?: ParameterComponents;
|
|
8
10
|
}
|
|
9
11
|
declare const ParameterVector: import("svelte").Component<Props, {}, "">;
|
|
10
12
|
type ParameterVector = ReturnType<typeof ParameterVector>;
|