@pixerate/editor-svelte 0.118.0 → 0.119.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 +5 -3
- package/dist/effects/EffectParameters.svelte.d.ts +3 -1
- package/dist/typography/FontPicker.svelte +243 -0
- package/dist/typography/FontPicker.svelte.d.ts +36 -0
- package/dist/typography/TypographyControl.svelte +73 -0
- package/dist/typography/TypographyControl.svelte.d.ts +26 -0
- package/dist/typography/index.d.ts +9 -0
- package/dist/typography/index.js +5 -0
- package/dist/typography/picker.svelte.d.ts +13 -0
- package/dist/typography/picker.svelte.js +19 -0
- package/package.json +10 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { effectFieldValue, effectParameterFields, type EffectDescription } from '@pixerate/editor/effects';
|
|
2
|
+
import { effectFieldValue, effectParameterFields, type EffectDescription, type EffectFieldOptions } from '@pixerate/editor/effects';
|
|
3
3
|
import { parameterLabel, parameterValuesEqual, type ParameterValue } from '@pixerate/editor/parameters';
|
|
4
4
|
import ParameterFieldControl from '../parameters/ParameterFieldControl.svelte';
|
|
5
5
|
|
|
@@ -23,12 +23,14 @@
|
|
|
23
23
|
onpresetname?: (name: string) => void;
|
|
24
24
|
/** Optional addon rendered next to the control label (e.g. keyframe toggle button). */
|
|
25
25
|
addon?: import('svelte').Snippet<[string]>;
|
|
26
|
+
/** Families offered by font controls (`format: "font"`); default the system font catalog. */
|
|
27
|
+
fonts?: EffectFieldOptions['fonts'];
|
|
26
28
|
disabled?: boolean;
|
|
27
29
|
class?: string;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
let { description, values, onchange, onreset, keyframed, onpreset, onpresetname, addon, disabled = false, class: className = '' }: Props = $props();
|
|
31
|
-
const fields = $derived(effectParameterFields(description));
|
|
32
|
+
let { description, values, onchange, onreset, keyframed, onpreset, onpresetname, addon, fonts, disabled = false, class: className = '' }: Props = $props();
|
|
33
|
+
const fields = $derived(effectParameterFields(description, { fonts }));
|
|
32
34
|
const idBase = `fx-${Math.random().toString(36).slice(2, 8)}`;
|
|
33
35
|
let gesture: string | null = null;
|
|
34
36
|
let count = 0;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type EffectDescription } from '@pixerate/editor/effects';
|
|
1
|
+
import { type EffectDescription, type EffectFieldOptions } from '@pixerate/editor/effects';
|
|
2
2
|
import { type ParameterValue } from '@pixerate/editor/parameters';
|
|
3
3
|
interface Props {
|
|
4
4
|
/** The effect's description (`describe`). */
|
|
@@ -20,6 +20,8 @@ interface Props {
|
|
|
20
20
|
onpresetname?: (name: string) => void;
|
|
21
21
|
/** Optional addon rendered next to the control label (e.g. keyframe toggle button). */
|
|
22
22
|
addon?: import('svelte').Snippet<[string]>;
|
|
23
|
+
/** Families offered by font controls (`format: "font"`); default the system font catalog. */
|
|
24
|
+
fonts?: EffectFieldOptions['fonts'];
|
|
23
25
|
disabled?: boolean;
|
|
24
26
|
class?: string;
|
|
25
27
|
}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onDestroy, untrack, type Snippet } from 'svelte';
|
|
3
|
+
import {
|
|
4
|
+
FONT_CATEGORIES,
|
|
5
|
+
FONT_CATEGORY_LABELS,
|
|
6
|
+
FontPickerModel,
|
|
7
|
+
type FontCategory,
|
|
8
|
+
type FontProvider,
|
|
9
|
+
type FontSpec,
|
|
10
|
+
type FontStyle,
|
|
11
|
+
type FontSummary
|
|
12
|
+
} from '@pixerate/editor/typography';
|
|
13
|
+
import { fontPickerOf } from './picker.svelte.js';
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
/** The selected font (controlled). */
|
|
17
|
+
value?: Partial<FontSpec>;
|
|
18
|
+
/** Called when the user (or an agent through the model's tools) picks a different font, weight or style. */
|
|
19
|
+
onchange?: (value: FontSpec) => void;
|
|
20
|
+
onChange?: (value: FontSpec) => void;
|
|
21
|
+
/** An existing model; when given, `provider`, `fonts` and `previewText` are ignored. */
|
|
22
|
+
model?: FontPickerModel;
|
|
23
|
+
/** Where fonts come from (fetched on mount). */
|
|
24
|
+
provider?: FontProvider;
|
|
25
|
+
/** A fixed list of fonts. */
|
|
26
|
+
fonts?: readonly FontSummary[];
|
|
27
|
+
/** Text for the live preview. */
|
|
28
|
+
previewText?: string;
|
|
29
|
+
/** Show the live preview (default true). */
|
|
30
|
+
showPreview?: boolean;
|
|
31
|
+
/** Show the category filter (default true). */
|
|
32
|
+
showCategories?: boolean;
|
|
33
|
+
/** Show the weight and style selects (default true). */
|
|
34
|
+
showWeight?: boolean;
|
|
35
|
+
/** Most options rendered at once (default 200); search to reach the rest. */
|
|
36
|
+
maxResults?: number;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
class?: string;
|
|
39
|
+
className?: string;
|
|
40
|
+
id?: string;
|
|
41
|
+
/** Accessible label for the group (default "Font picker"). */
|
|
42
|
+
'aria-label'?: string;
|
|
43
|
+
/** Extra controls rendered above the preview (used by `TypographyControl`). */
|
|
44
|
+
children?: Snippet;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let {
|
|
48
|
+
value,
|
|
49
|
+
onchange,
|
|
50
|
+
onChange,
|
|
51
|
+
model: external,
|
|
52
|
+
provider,
|
|
53
|
+
fonts,
|
|
54
|
+
previewText,
|
|
55
|
+
showPreview = true,
|
|
56
|
+
showCategories = true,
|
|
57
|
+
showWeight = true,
|
|
58
|
+
maxResults = 200,
|
|
59
|
+
disabled = false,
|
|
60
|
+
class: klass,
|
|
61
|
+
className,
|
|
62
|
+
id,
|
|
63
|
+
'aria-label': ariaLabel = 'Font picker',
|
|
64
|
+
children
|
|
65
|
+
}: Props = $props();
|
|
66
|
+
|
|
67
|
+
const model = untrack(() => external ?? new FontPickerModel({ provider, fonts, value, previewText }));
|
|
68
|
+
const picker = fontPickerOf(model);
|
|
69
|
+
const offValue = model.onValueChange((v) => (onchange ?? onChange)?.(v));
|
|
70
|
+
onDestroy(() => {
|
|
71
|
+
picker.destroy();
|
|
72
|
+
offValue();
|
|
73
|
+
});
|
|
74
|
+
if (!untrack(() => external) && untrack(() => provider)) void model.refresh();
|
|
75
|
+
|
|
76
|
+
$effect(() => {
|
|
77
|
+
const v = value;
|
|
78
|
+
if (v) untrack(() => model.setValue(v));
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const s = $derived(picker.state);
|
|
82
|
+
const shown = $derived(s.results.slice(0, maxResults));
|
|
83
|
+
// The model's getters read its current state; reading `s` first makes them update with it.
|
|
84
|
+
const css = $derived.by(() => {
|
|
85
|
+
void s;
|
|
86
|
+
return model.css;
|
|
87
|
+
});
|
|
88
|
+
const weights = $derived.by(() => {
|
|
89
|
+
void s;
|
|
90
|
+
return model.availableWeights;
|
|
91
|
+
});
|
|
92
|
+
const styles = $derived.by(() => {
|
|
93
|
+
void s;
|
|
94
|
+
return model.availableStyles;
|
|
95
|
+
});
|
|
96
|
+
const status = $derived(
|
|
97
|
+
s.loading
|
|
98
|
+
? 'Loading fonts…'
|
|
99
|
+
: s.error
|
|
100
|
+
? `Could not load fonts: ${s.error}`
|
|
101
|
+
: s.loadStatus[s.value.family.toLowerCase()] === 'failed'
|
|
102
|
+
? `${s.value.family} could not be loaded; a fallback is shown.`
|
|
103
|
+
: ''
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const baseId = `pixerate-font-picker-${Math.random().toString(36).slice(2, 10)}`;
|
|
107
|
+
const listId = `${baseId}-list`;
|
|
108
|
+
const optionId = (i: number) => `${baseId}-opt-${i}`;
|
|
109
|
+
let listEl: HTMLUListElement | undefined = $state();
|
|
110
|
+
|
|
111
|
+
$effect(() => {
|
|
112
|
+
const i = s.highlighted;
|
|
113
|
+
if (i < 0) return;
|
|
114
|
+
untrack(() => listEl?.ownerDocument.getElementById(optionId(i))?.scrollIntoView?.({ block: 'nearest' }));
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
function onKeydown(e: KeyboardEvent) {
|
|
118
|
+
if (model.handleKey(e.key)) e.preventDefault();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const field =
|
|
122
|
+
'box-sizing:border-box;width:100%;padding:5px 8px;border-radius:6px;border:1px solid var(--pixerate-font-picker-border, #3a4152);background:var(--pixerate-font-picker-field-bg, #151922);color:inherit;font:inherit;';
|
|
123
|
+
</script>
|
|
124
|
+
|
|
125
|
+
<div
|
|
126
|
+
{id}
|
|
127
|
+
role="group"
|
|
128
|
+
aria-label={ariaLabel}
|
|
129
|
+
aria-disabled={disabled || undefined}
|
|
130
|
+
class={klass ?? className}
|
|
131
|
+
style="box-sizing:border-box;width:var(--pixerate-font-picker-width, 280px);border-radius:var(--pixerate-font-picker-radius, 8px);background:var(--pixerate-font-picker-bg, #1f2430);color:var(--pixerate-font-picker-fg, #f0f2f5);padding:12px;display:flex;flex-direction:column;gap:8px;font-family:system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:12px;opacity:{disabled
|
|
132
|
+
? 0.6
|
|
133
|
+
: 1};pointer-events:{disabled ? 'none' : 'auto'};"
|
|
134
|
+
>
|
|
135
|
+
<div style="display:flex;gap:6px;">
|
|
136
|
+
<input
|
|
137
|
+
type="text"
|
|
138
|
+
role="combobox"
|
|
139
|
+
aria-label="Search fonts"
|
|
140
|
+
aria-expanded="true"
|
|
141
|
+
aria-controls={listId}
|
|
142
|
+
aria-autocomplete="list"
|
|
143
|
+
aria-activedescendant={s.highlighted >= 0 && s.highlighted < shown.length ? optionId(s.highlighted) : undefined}
|
|
144
|
+
placeholder="Search fonts"
|
|
145
|
+
value={s.query}
|
|
146
|
+
{disabled}
|
|
147
|
+
oninput={(e) => model.setQuery((e.currentTarget as HTMLInputElement).value)}
|
|
148
|
+
onkeydown={onKeydown}
|
|
149
|
+
style={field}
|
|
150
|
+
/>
|
|
151
|
+
{#if showCategories}
|
|
152
|
+
<select
|
|
153
|
+
aria-label="Font category"
|
|
154
|
+
value={s.category}
|
|
155
|
+
{disabled}
|
|
156
|
+
onchange={(e) => model.setCategory((e.currentTarget as HTMLSelectElement).value as FontCategory | 'all')}
|
|
157
|
+
style="{field}width:auto;"
|
|
158
|
+
>
|
|
159
|
+
{#each ['all', ...FONT_CATEGORIES] as c (c)}
|
|
160
|
+
<option value={c}>{FONT_CATEGORY_LABELS[c as FontCategory | 'all']}</option>
|
|
161
|
+
{/each}
|
|
162
|
+
</select>
|
|
163
|
+
{/if}
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<ul
|
|
167
|
+
bind:this={listEl}
|
|
168
|
+
id={listId}
|
|
169
|
+
role="listbox"
|
|
170
|
+
aria-label="Fonts"
|
|
171
|
+
style="list-style:none;margin:0;padding:2px;max-height:var(--pixerate-font-picker-list-height, 180px);overflow-y:auto;border-radius:6px;border:1px solid var(--pixerate-font-picker-border, #3a4152);"
|
|
172
|
+
>
|
|
173
|
+
{#if shown.length === 0}
|
|
174
|
+
<li role="presentation" style="padding:6px 8px;opacity:0.7;">{s.loading ? 'Loading…' : 'No fonts match'}</li>
|
|
175
|
+
{/if}
|
|
176
|
+
{#each shown as font, i (font.family)}
|
|
177
|
+
{@const selected = font.family.toLowerCase() === s.value.family.toLowerCase()}
|
|
178
|
+
{@const highlighted = i === s.highlighted}
|
|
179
|
+
<li
|
|
180
|
+
id={optionId(i)}
|
|
181
|
+
role="option"
|
|
182
|
+
tabindex="-1"
|
|
183
|
+
aria-selected={selected}
|
|
184
|
+
data-highlighted={highlighted || undefined}
|
|
185
|
+
onmousedown={(e) => e.preventDefault()}
|
|
186
|
+
onmouseenter={() => model.setHighlight(i)}
|
|
187
|
+
onclick={() => model.selectFamily(font.family)}
|
|
188
|
+
onkeydown={() => {}}
|
|
189
|
+
style="display:flex;justify-content:space-between;align-items:baseline;gap:8px;padding:5px 8px;border-radius:4px;cursor:pointer;background:{selected
|
|
190
|
+
? 'var(--pixerate-font-picker-selected, #2f3b57)'
|
|
191
|
+
: highlighted
|
|
192
|
+
? 'var(--pixerate-font-picker-hover, #2a3040)'
|
|
193
|
+
: 'transparent'};"
|
|
194
|
+
>
|
|
195
|
+
<span style="font-family:{model.familyCss(font)};font-size:14px;">{font.family}</span>
|
|
196
|
+
<span style="opacity:0.6;font-size:10px;">{FONT_CATEGORY_LABELS[font.category]}</span>
|
|
197
|
+
</li>
|
|
198
|
+
{/each}
|
|
199
|
+
</ul>
|
|
200
|
+
|
|
201
|
+
{#if showWeight}
|
|
202
|
+
<div style="display:flex;gap:6px;">
|
|
203
|
+
<select
|
|
204
|
+
aria-label="Font weight"
|
|
205
|
+
value={s.value.weight}
|
|
206
|
+
{disabled}
|
|
207
|
+
onchange={(e) => model.setWeight(Number((e.currentTarget as HTMLSelectElement).value))}
|
|
208
|
+
style={field}
|
|
209
|
+
>
|
|
210
|
+
{#each weights as w (w)}
|
|
211
|
+
<option value={w}>{w}</option>
|
|
212
|
+
{/each}
|
|
213
|
+
</select>
|
|
214
|
+
<select
|
|
215
|
+
aria-label="Font style"
|
|
216
|
+
value={s.value.style}
|
|
217
|
+
{disabled}
|
|
218
|
+
onchange={(e) => model.setStyle((e.currentTarget as HTMLSelectElement).value as FontStyle)}
|
|
219
|
+
style={field}
|
|
220
|
+
>
|
|
221
|
+
{#each styles as st (st)}
|
|
222
|
+
<option value={st}>{st}</option>
|
|
223
|
+
{/each}
|
|
224
|
+
</select>
|
|
225
|
+
</div>
|
|
226
|
+
{/if}
|
|
227
|
+
|
|
228
|
+
{@render children?.()}
|
|
229
|
+
|
|
230
|
+
{#if showPreview}
|
|
231
|
+
<div
|
|
232
|
+
aria-label="Font preview"
|
|
233
|
+
style="font-family:{css.fontFamily};font-weight:{css.fontWeight};font-style:{css.fontStyle};font-size:{Math.min(
|
|
234
|
+
s.value.size,
|
|
235
|
+
48
|
|
236
|
+
)}px;line-height:{css.lineHeight};letter-spacing:{css.letterSpacing};padding:6px 4px;overflow-wrap:anywhere;"
|
|
237
|
+
>
|
|
238
|
+
{s.previewText}
|
|
239
|
+
</div>
|
|
240
|
+
{/if}
|
|
241
|
+
|
|
242
|
+
<div role="status" aria-live="polite" style="opacity:0.75;">{status}</div>
|
|
243
|
+
</div>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
import { FontPickerModel, type FontProvider, type FontSpec, type FontSummary } from '@pixerate/editor/typography';
|
|
3
|
+
interface Props {
|
|
4
|
+
/** The selected font (controlled). */
|
|
5
|
+
value?: Partial<FontSpec>;
|
|
6
|
+
/** Called when the user (or an agent through the model's tools) picks a different font, weight or style. */
|
|
7
|
+
onchange?: (value: FontSpec) => void;
|
|
8
|
+
onChange?: (value: FontSpec) => void;
|
|
9
|
+
/** An existing model; when given, `provider`, `fonts` and `previewText` are ignored. */
|
|
10
|
+
model?: FontPickerModel;
|
|
11
|
+
/** Where fonts come from (fetched on mount). */
|
|
12
|
+
provider?: FontProvider;
|
|
13
|
+
/** A fixed list of fonts. */
|
|
14
|
+
fonts?: readonly FontSummary[];
|
|
15
|
+
/** Text for the live preview. */
|
|
16
|
+
previewText?: string;
|
|
17
|
+
/** Show the live preview (default true). */
|
|
18
|
+
showPreview?: boolean;
|
|
19
|
+
/** Show the category filter (default true). */
|
|
20
|
+
showCategories?: boolean;
|
|
21
|
+
/** Show the weight and style selects (default true). */
|
|
22
|
+
showWeight?: boolean;
|
|
23
|
+
/** Most options rendered at once (default 200); search to reach the rest. */
|
|
24
|
+
maxResults?: number;
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
class?: string;
|
|
27
|
+
className?: string;
|
|
28
|
+
id?: string;
|
|
29
|
+
/** Accessible label for the group (default "Font picker"). */
|
|
30
|
+
'aria-label'?: string;
|
|
31
|
+
/** Extra controls rendered above the preview (used by `TypographyControl`). */
|
|
32
|
+
children?: Snippet;
|
|
33
|
+
}
|
|
34
|
+
declare const FontPicker: import("svelte").Component<Props, {}, "">;
|
|
35
|
+
type FontPicker = ReturnType<typeof FontPicker>;
|
|
36
|
+
export default FontPicker;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onDestroy, untrack } from 'svelte';
|
|
3
|
+
import { FontPickerModel, type FontProvider, type FontSpec, type FontSummary } from '@pixerate/editor/typography';
|
|
4
|
+
import FontPicker from './FontPicker.svelte';
|
|
5
|
+
import { fontPickerOf } from './picker.svelte.js';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
/** The selected font and metrics (controlled). */
|
|
9
|
+
value?: Partial<FontSpec>;
|
|
10
|
+
/** Called with the whole spec when anything changes in the control (or through its agent tools). */
|
|
11
|
+
onchange?: (value: FontSpec) => void;
|
|
12
|
+
onChange?: (value: FontSpec) => void;
|
|
13
|
+
/** An existing model; when given, `provider`, `fonts` and `previewText` are ignored. */
|
|
14
|
+
model?: FontPickerModel;
|
|
15
|
+
provider?: FontProvider;
|
|
16
|
+
fonts?: readonly FontSummary[];
|
|
17
|
+
previewText?: string;
|
|
18
|
+
showPreview?: boolean;
|
|
19
|
+
showCategories?: boolean;
|
|
20
|
+
showWeight?: boolean;
|
|
21
|
+
maxResults?: number;
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
class?: string;
|
|
24
|
+
className?: string;
|
|
25
|
+
id?: string;
|
|
26
|
+
/** Accessible label for the group (default "Typography"). */
|
|
27
|
+
'aria-label'?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
let {
|
|
31
|
+
value,
|
|
32
|
+
onchange,
|
|
33
|
+
onChange,
|
|
34
|
+
model: external,
|
|
35
|
+
provider,
|
|
36
|
+
fonts,
|
|
37
|
+
previewText,
|
|
38
|
+
disabled = false,
|
|
39
|
+
'aria-label': ariaLabel = 'Typography',
|
|
40
|
+
...rest
|
|
41
|
+
}: Props = $props();
|
|
42
|
+
|
|
43
|
+
const model = untrack(() => external ?? new FontPickerModel({ provider, fonts, value, previewText }));
|
|
44
|
+
const picker = fontPickerOf(model);
|
|
45
|
+
onDestroy(() => picker.destroy());
|
|
46
|
+
if (!untrack(() => external) && untrack(() => provider)) void model.refresh();
|
|
47
|
+
const v = $derived(picker.state.value);
|
|
48
|
+
|
|
49
|
+
const numberField =
|
|
50
|
+
'box-sizing:border-box;width:100%;padding:5px 6px;border-radius:6px;border:1px solid var(--pixerate-font-picker-border, #3a4152);background:var(--pixerate-font-picker-field-bg, #151922);color:inherit;font:inherit;';
|
|
51
|
+
|
|
52
|
+
function commit(e: Event, set: (n: number) => void) {
|
|
53
|
+
const n = (e.currentTarget as HTMLInputElement).valueAsNumber;
|
|
54
|
+
if (Number.isFinite(n)) set(n);
|
|
55
|
+
}
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<FontPicker {model} {value} onchange={(next) => (onchange ?? onChange)?.(next)} {disabled} aria-label={ariaLabel} {...rest}>
|
|
59
|
+
<div style="display:flex;gap:6px;">
|
|
60
|
+
<label style="display:flex;flex-direction:column;gap:2px;flex:1;min-width:0;">
|
|
61
|
+
<span style="opacity:0.75;">Font size <span style="opacity:0.6;">px</span></span>
|
|
62
|
+
<input type="number" aria-label="Font size" value={v.size} step="1" min="1" max="1000" {disabled} oninput={(e) => commit(e, (n) => model.setSize(n))} style={numberField} />
|
|
63
|
+
</label>
|
|
64
|
+
<label style="display:flex;flex-direction:column;gap:2px;flex:1;min-width:0;">
|
|
65
|
+
<span style="opacity:0.75;">Line height <span style="opacity:0.6;">×</span></span>
|
|
66
|
+
<input type="number" aria-label="Line height" value={v.lineHeight} step="0.05" min="0.5" max="4" {disabled} oninput={(e) => commit(e, (n) => model.setLineHeight(n))} style={numberField} />
|
|
67
|
+
</label>
|
|
68
|
+
<label style="display:flex;flex-direction:column;gap:2px;flex:1;min-width:0;">
|
|
69
|
+
<span style="opacity:0.75;">Letter spacing <span style="opacity:0.6;">em</span></span>
|
|
70
|
+
<input type="number" aria-label="Letter spacing" value={v.letterSpacing} step="0.01" min="-0.5" max="1" {disabled} oninput={(e) => commit(e, (n) => model.setLetterSpacing(n))} style={numberField} />
|
|
71
|
+
</label>
|
|
72
|
+
</div>
|
|
73
|
+
</FontPicker>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FontPickerModel, type FontProvider, type FontSpec, type FontSummary } from '@pixerate/editor/typography';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** The selected font and metrics (controlled). */
|
|
4
|
+
value?: Partial<FontSpec>;
|
|
5
|
+
/** Called with the whole spec when anything changes in the control (or through its agent tools). */
|
|
6
|
+
onchange?: (value: FontSpec) => void;
|
|
7
|
+
onChange?: (value: FontSpec) => void;
|
|
8
|
+
/** An existing model; when given, `provider`, `fonts` and `previewText` are ignored. */
|
|
9
|
+
model?: FontPickerModel;
|
|
10
|
+
provider?: FontProvider;
|
|
11
|
+
fonts?: readonly FontSummary[];
|
|
12
|
+
previewText?: string;
|
|
13
|
+
showPreview?: boolean;
|
|
14
|
+
showCategories?: boolean;
|
|
15
|
+
showWeight?: boolean;
|
|
16
|
+
maxResults?: number;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
class?: string;
|
|
19
|
+
className?: string;
|
|
20
|
+
id?: string;
|
|
21
|
+
/** Accessible label for the group (default "Typography"). */
|
|
22
|
+
'aria-label'?: string;
|
|
23
|
+
}
|
|
24
|
+
declare const TypographyControl: import("svelte").Component<Props, {}, "">;
|
|
25
|
+
type TypographyControl = ReturnType<typeof TypographyControl>;
|
|
26
|
+
export default TypographyControl;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ComponentProps } from 'svelte';
|
|
2
|
+
import type FontPickerComponent from './FontPicker.svelte';
|
|
3
|
+
import type TypographyControlComponent from './TypographyControl.svelte';
|
|
4
|
+
export { default as FontPicker } from './FontPicker.svelte';
|
|
5
|
+
export { default as TypographyControl } from './TypographyControl.svelte';
|
|
6
|
+
export type FontPickerProps = ComponentProps<typeof FontPickerComponent>;
|
|
7
|
+
export type TypographyControlProps = ComponentProps<typeof TypographyControlComponent>;
|
|
8
|
+
export { fontPickerOf, type FontPickerBinding } from './picker.svelte.js';
|
|
9
|
+
export * from '@pixerate/editor/typography';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as FontPicker } from './FontPicker.svelte';
|
|
2
|
+
export { default as TypographyControl } from './TypographyControl.svelte';
|
|
3
|
+
export { fontPickerOf } from './picker.svelte.js';
|
|
4
|
+
// Re-export the core typography module for consumer convenience
|
|
5
|
+
export * from '@pixerate/editor/typography';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { FontPickerModel, FontPickerState } from '@pixerate/editor/typography';
|
|
2
|
+
export interface FontPickerBinding {
|
|
3
|
+
/** The model's state, reactive. */
|
|
4
|
+
readonly state: FontPickerState;
|
|
5
|
+
readonly model: FontPickerModel;
|
|
6
|
+
/** Stops following the model. */
|
|
7
|
+
destroy(): void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Follows a {@link FontPickerModel} with Svelte 5 runes: `binding.state` updates whenever the model
|
|
11
|
+
* changes (from the UI or from its agent tools). Call `destroy()` when the component unmounts.
|
|
12
|
+
*/
|
|
13
|
+
export declare function fontPickerOf(model: FontPickerModel): FontPickerBinding;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Follows a {@link FontPickerModel} with Svelte 5 runes: `binding.state` updates whenever the model
|
|
3
|
+
* changes (from the UI or from its agent tools). Call `destroy()` when the component unmounts.
|
|
4
|
+
*/
|
|
5
|
+
export function fontPickerOf(model) {
|
|
6
|
+
let current = $state.raw(model.getState());
|
|
7
|
+
const unsubscribe = model.subscribe((next) => {
|
|
8
|
+
current = next;
|
|
9
|
+
});
|
|
10
|
+
return {
|
|
11
|
+
get state() {
|
|
12
|
+
return current;
|
|
13
|
+
},
|
|
14
|
+
model,
|
|
15
|
+
destroy: () => {
|
|
16
|
+
unsubscribe();
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/editor-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.119.0",
|
|
4
4
|
"description": "Svelte bindings and prompt/rich-text components for @pixerate/editor",
|
|
5
5
|
"author": "Pixerate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -93,6 +93,11 @@
|
|
|
93
93
|
"svelte": "./dist/color/index.js",
|
|
94
94
|
"default": "./dist/color/index.js"
|
|
95
95
|
},
|
|
96
|
+
"./typography": {
|
|
97
|
+
"types": "./dist/typography/index.d.ts",
|
|
98
|
+
"svelte": "./dist/typography/index.js",
|
|
99
|
+
"default": "./dist/typography/index.js"
|
|
100
|
+
},
|
|
96
101
|
"./gradient": {
|
|
97
102
|
"types": "./dist/gradient/index.d.ts",
|
|
98
103
|
"svelte": "./dist/gradient/index.js",
|
|
@@ -144,6 +149,9 @@
|
|
|
144
149
|
"color": [
|
|
145
150
|
"./dist/color/index.d.ts"
|
|
146
151
|
],
|
|
152
|
+
"typography": [
|
|
153
|
+
"./dist/typography/index.d.ts"
|
|
154
|
+
],
|
|
147
155
|
"gradient": [
|
|
148
156
|
"./dist/gradient/index.d.ts"
|
|
149
157
|
]
|
|
@@ -160,7 +168,7 @@
|
|
|
160
168
|
"@tiptap/extension-bubble-menu": "^2.27.3",
|
|
161
169
|
"@tiptap/extension-placeholder": "^2.27.3",
|
|
162
170
|
"@tiptap/starter-kit": "^2.27.3",
|
|
163
|
-
"@pixerate/editor": "0.
|
|
171
|
+
"@pixerate/editor": "0.162.0"
|
|
164
172
|
},
|
|
165
173
|
"peerDependencies": {
|
|
166
174
|
"@dagrejs/dagre": "^1.1.4",
|