@pixerate/editor-svelte 0.105.0 → 0.107.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/README.md +2 -0
- package/dist/color/ColorPicker.svelte +546 -0
- package/dist/color/ColorPicker.svelte.d.ts +36 -0
- package/dist/color/DualColorPicker.svelte +296 -0
- package/dist/color/DualColorPicker.svelte.d.ts +34 -0
- package/dist/color/colorScheme.svelte.d.ts +19 -0
- package/dist/color/colorScheme.svelte.js +76 -0
- package/dist/color/index.d.ts +9 -0
- package/dist/color/index.js +5 -0
- package/dist/effects/EffectAuthoringEditor.svelte +447 -0
- package/dist/effects/EffectAuthoringEditor.svelte.d.ts +18 -0
- package/dist/effects/EffectParameters.svelte +14 -4
- package/dist/effects/EffectParameters.svelte.d.ts +4 -0
- package/dist/effects/effects.svelte.d.ts +8 -1
- package/dist/effects/effects.svelte.js +26 -0
- package/dist/effects/index.d.ts +3 -0
- package/dist/effects/index.js +1 -0
- package/dist/gradient/FillPicker.svelte +236 -0
- package/dist/gradient/FillPicker.svelte.d.ts +32 -0
- package/dist/gradient/GradientEditor.svelte +472 -0
- package/dist/gradient/GradientEditor.svelte.d.ts +23 -0
- package/dist/gradient/gradientController.svelte.d.ts +13 -0
- package/dist/gradient/gradientController.svelte.js +21 -0
- package/dist/gradient/index.d.ts +10 -0
- package/dist/gradient/index.js +5 -0
- package/dist/gradient/types.d.ts +2 -0
- package/dist/gradient/types.js +1 -0
- package/dist/parameters/ParameterColor.svelte +63 -64
- package/package.json +18 -2
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import ColorPicker from './ColorPicker.svelte';
|
|
3
|
+
import {
|
|
4
|
+
type ColorScheme,
|
|
5
|
+
type ColorValue,
|
|
6
|
+
type DualColor,
|
|
7
|
+
DEFAULT_SCHEME_SURFACES,
|
|
8
|
+
normalizeDualColor,
|
|
9
|
+
resolveColor,
|
|
10
|
+
setDualColorSide,
|
|
11
|
+
deriveDualColor,
|
|
12
|
+
checkDualColorContrast,
|
|
13
|
+
isSameInBothSchemes
|
|
14
|
+
} from '@pixerate/editor/color';
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
/** Dual colour value (CSS string, light-dark(), or { light, dark }) */
|
|
18
|
+
value?: ColorValue;
|
|
19
|
+
/** Called when the dual colour value changes */
|
|
20
|
+
onchange?: (value: DualColor) => void;
|
|
21
|
+
onChange?: (value: DualColor) => void;
|
|
22
|
+
/** Controlled active scheme mode ('light' | 'dark') */
|
|
23
|
+
scheme?: ColorScheme;
|
|
24
|
+
/** Called when the user toggles active scheme tab */
|
|
25
|
+
onschemechange?: (scheme: ColorScheme) => void;
|
|
26
|
+
onSchemeChange?: (scheme: ColorScheme) => void;
|
|
27
|
+
/** Expected background surfaces for derivation and preview */
|
|
28
|
+
surfaces?: DualColor;
|
|
29
|
+
/** Optional background colour to evaluate contrast against in both modes */
|
|
30
|
+
contrastAgainst?: ColorValue;
|
|
31
|
+
/** Whether alpha slider is shown */
|
|
32
|
+
alpha?: boolean;
|
|
33
|
+
/** Preset swatches */
|
|
34
|
+
swatches?: readonly string[];
|
|
35
|
+
/** Whether picker is disabled */
|
|
36
|
+
disabled?: boolean;
|
|
37
|
+
/** Additional CSS class name */
|
|
38
|
+
class?: string;
|
|
39
|
+
className?: string;
|
|
40
|
+
/** Gesture callbacks */
|
|
41
|
+
ongesturestart?: () => void;
|
|
42
|
+
onGestureStart?: () => void;
|
|
43
|
+
ongestureend?: () => void;
|
|
44
|
+
onGestureEnd?: () => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let {
|
|
48
|
+
value = DEFAULT_SCHEME_SURFACES,
|
|
49
|
+
onchange,
|
|
50
|
+
onChange,
|
|
51
|
+
scheme: controlledScheme,
|
|
52
|
+
onschemechange,
|
|
53
|
+
onSchemeChange,
|
|
54
|
+
surfaces = DEFAULT_SCHEME_SURFACES,
|
|
55
|
+
contrastAgainst,
|
|
56
|
+
alpha = false,
|
|
57
|
+
swatches,
|
|
58
|
+
disabled = false,
|
|
59
|
+
class: classProp,
|
|
60
|
+
className: classNameProp,
|
|
61
|
+
ongesturestart,
|
|
62
|
+
onGestureStart,
|
|
63
|
+
ongestureend,
|
|
64
|
+
onGestureEnd
|
|
65
|
+
}: Props = $props();
|
|
66
|
+
|
|
67
|
+
const className = $derived(classProp || classNameProp || '');
|
|
68
|
+
|
|
69
|
+
const triggerChange = (val: DualColor) => {
|
|
70
|
+
onchange?.(val);
|
|
71
|
+
onChange?.(val);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const triggerSchemeChange = (s: ColorScheme) => {
|
|
75
|
+
onschemechange?.(s);
|
|
76
|
+
onSchemeChange?.(s);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const triggerGestureStart = () => {
|
|
80
|
+
ongesturestart?.();
|
|
81
|
+
onGestureStart?.();
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const triggerGestureEnd = () => {
|
|
85
|
+
ongestureend?.();
|
|
86
|
+
onGestureEnd?.();
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Internal active scheme state
|
|
90
|
+
let internalScheme = $state<ColorScheme>('light');
|
|
91
|
+
const activeScheme = $derived(controlledScheme ?? internalScheme);
|
|
92
|
+
|
|
93
|
+
// Normalised dual value
|
|
94
|
+
const dual = $derived<DualColor>(
|
|
95
|
+
normalizeDualColor(value) ?? DEFAULT_SCHEME_SURFACES
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// Same in both mode state
|
|
99
|
+
let sameInBoth = $state<boolean>(false);
|
|
100
|
+
let initializedSame = false;
|
|
101
|
+
|
|
102
|
+
$effect(() => {
|
|
103
|
+
if (!initializedSame) {
|
|
104
|
+
sameInBoth = isSameInBothSchemes(dual);
|
|
105
|
+
initializedSame = true;
|
|
106
|
+
} else if (isSameInBothSchemes(dual)) {
|
|
107
|
+
sameInBoth = true;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const handleSchemeSelect = (nextScheme: ColorScheme) => {
|
|
112
|
+
if (disabled) return;
|
|
113
|
+
internalScheme = nextScheme;
|
|
114
|
+
triggerSchemeChange(nextScheme);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const handleToggleSameInBoth = (checked: boolean) => {
|
|
118
|
+
if (disabled) return;
|
|
119
|
+
sameInBoth = checked;
|
|
120
|
+
if (checked) {
|
|
121
|
+
const activeColor = activeScheme === 'dark' ? dual.dark : dual.light;
|
|
122
|
+
triggerChange({ light: activeColor, dark: activeColor });
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const handleDerive = () => {
|
|
127
|
+
if (disabled) return;
|
|
128
|
+
triggerGestureStart();
|
|
129
|
+
const currentActive = activeScheme === 'dark' ? dual.dark : dual.light;
|
|
130
|
+
const derived = deriveDualColor(currentActive, {
|
|
131
|
+
from: activeScheme,
|
|
132
|
+
// An unreadable surfaces prop falls back to the defaults rather than throwing.
|
|
133
|
+
surfaces: normalizeDualColor(surfaces) ?? DEFAULT_SCHEME_SURFACES
|
|
134
|
+
});
|
|
135
|
+
triggerChange(derived);
|
|
136
|
+
triggerGestureEnd();
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const handleInnerChange = (hex: string) => {
|
|
140
|
+
if (sameInBoth) {
|
|
141
|
+
triggerChange({ light: hex, dark: hex });
|
|
142
|
+
} else {
|
|
143
|
+
const updated = setDualColorSide(dual, activeScheme, hex);
|
|
144
|
+
triggerChange(updated);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const activeColor = $derived(activeScheme === 'dark' ? dual.dark : dual.light);
|
|
149
|
+
const activeContrastTarget = $derived(
|
|
150
|
+
contrastAgainst ? resolveColor(contrastAgainst, activeScheme) ?? undefined : undefined
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const dualContrast = $derived(
|
|
154
|
+
// An unreadable contrastAgainst shows no badges rather than throwing during render.
|
|
155
|
+
contrastAgainst && normalizeDualColor(contrastAgainst)
|
|
156
|
+
? checkDualColorContrast(dual, contrastAgainst)
|
|
157
|
+
: null
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const CHECKERBOARD_BG =
|
|
161
|
+
'linear-gradient(45deg, #2a2e39 25%, transparent 25%), linear-gradient(-45deg, #2a2e39 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #2a2e39 75%), linear-gradient(-45deg, transparent 75%, #2a2e39 75%)';
|
|
162
|
+
</script>
|
|
163
|
+
|
|
164
|
+
<div
|
|
165
|
+
class="pixerate-dual-color-picker {className}"
|
|
166
|
+
style="display: flex; flex-direction: column; gap: 12px; padding: 12px; background: #1a1d24; color: #e2e8f0; border-radius: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); width: 260px; font-family: system-ui, -apple-system, sans-serif; font-size: 13px; box-sizing: border-box;"
|
|
167
|
+
>
|
|
168
|
+
<!-- Segmented Control -->
|
|
169
|
+
<div
|
|
170
|
+
role="tablist"
|
|
171
|
+
aria-label="Color scheme mode"
|
|
172
|
+
style="display: flex; background: #111318; border-radius: 6px; padding: 2px; gap: 2px;"
|
|
173
|
+
>
|
|
174
|
+
<button
|
|
175
|
+
type="button"
|
|
176
|
+
role="tab"
|
|
177
|
+
aria-selected={activeScheme === 'light'}
|
|
178
|
+
onclick={() => handleSchemeSelect('light')}
|
|
179
|
+
{disabled}
|
|
180
|
+
style="flex: 1; padding: 6px 8px; border: none; border-radius: 4px; background: {activeScheme === 'light' ? '#2d3342' : 'transparent'}; color: {activeScheme === 'light' ? '#ffffff' : '#8c95a6'}; font-weight: {activeScheme === 'light' ? 600 : 400}; cursor: {disabled ? 'not-allowed' : 'pointer'}; font-size: 12px; transition: all 0.15s ease;"
|
|
181
|
+
>
|
|
182
|
+
Light
|
|
183
|
+
</button>
|
|
184
|
+
<button
|
|
185
|
+
type="button"
|
|
186
|
+
role="tab"
|
|
187
|
+
aria-selected={activeScheme === 'dark'}
|
|
188
|
+
onclick={() => handleSchemeSelect('dark')}
|
|
189
|
+
{disabled}
|
|
190
|
+
style="flex: 1; padding: 6px 8px; border: none; border-radius: 4px; background: {activeScheme === 'dark' ? '#2d3342' : 'transparent'}; color: {activeScheme === 'dark' ? '#ffffff' : '#8c95a6'}; font-weight: {activeScheme === 'dark' ? 600 : 400}; cursor: {disabled ? 'not-allowed' : 'pointer'}; font-size: 12px; transition: all 0.15s ease;"
|
|
191
|
+
>
|
|
192
|
+
Dark
|
|
193
|
+
</button>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<!-- Dual Preview Swatches -->
|
|
197
|
+
<div style="display: flex; gap: 8px;">
|
|
198
|
+
<!-- Light preview button -->
|
|
199
|
+
<button
|
|
200
|
+
type="button"
|
|
201
|
+
onclick={() => handleSchemeSelect('light')}
|
|
202
|
+
{disabled}
|
|
203
|
+
title="Light scheme colour preview"
|
|
204
|
+
style="flex: 1; display: flex; flex-direction: column; gap: 4px; padding: 6px; background: {activeScheme === 'light' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.2)'}; border: 1px solid {activeScheme === 'light' ? '#4f46e5' : 'rgba(255,255,255,0.08)'}; border-radius: 6px; cursor: {disabled ? 'not-allowed' : 'pointer'}; text-align: left;"
|
|
205
|
+
>
|
|
206
|
+
<div style="display: flex; justify-content: space-between; align-items: center; font-size: 11px; color: #8c95a6;">
|
|
207
|
+
<span>Light</span>
|
|
208
|
+
<span style="font-family: monospace; font-size: 10px;">{dual.light}</span>
|
|
209
|
+
</div>
|
|
210
|
+
<div
|
|
211
|
+
style="height: 24px; border-radius: 4px; background-image: {CHECKERBOARD_BG}; background-size: 8px 8px; overflow: hidden; border: 1px solid rgba(0,0,0,0.15);"
|
|
212
|
+
>
|
|
213
|
+
<div style="width: 100%; height: 100%; background: {dual.light};"></div>
|
|
214
|
+
</div>
|
|
215
|
+
</button>
|
|
216
|
+
|
|
217
|
+
<!-- Dark preview button -->
|
|
218
|
+
<button
|
|
219
|
+
type="button"
|
|
220
|
+
onclick={() => handleSchemeSelect('dark')}
|
|
221
|
+
{disabled}
|
|
222
|
+
title="Dark scheme colour preview"
|
|
223
|
+
style="flex: 1; display: flex; flex-direction: column; gap: 4px; padding: 6px; background: {activeScheme === 'dark' ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.2)'}; border: 1px solid {activeScheme === 'dark' ? '#4f46e5' : 'rgba(255,255,255,0.08)'}; border-radius: 6px; cursor: {disabled ? 'not-allowed' : 'pointer'}; text-align: left;"
|
|
224
|
+
>
|
|
225
|
+
<div style="display: flex; justify-content: space-between; align-items: center; font-size: 11px; color: #8c95a6;">
|
|
226
|
+
<span>Dark</span>
|
|
227
|
+
<span style="font-family: monospace; font-size: 10px;">{dual.dark}</span>
|
|
228
|
+
</div>
|
|
229
|
+
<div
|
|
230
|
+
style="height: 24px; border-radius: 4px; background-image: {CHECKERBOARD_BG}; background-size: 8px 8px; overflow: hidden; border: 1px solid rgba(0,0,0,0.15);"
|
|
231
|
+
>
|
|
232
|
+
<div style="width: 100%; height: 100%; background: {dual.dark};"></div>
|
|
233
|
+
</div>
|
|
234
|
+
</button>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<!-- Dual Toolbar: Same in both toggle and Derive button -->
|
|
238
|
+
<div style="display: flex; justify-content: space-between; align-items: center; font-size: 11px; color: #cbd5e1;">
|
|
239
|
+
<label style="display: flex; align-items: center; gap: 6px; cursor: {disabled ? 'not-allowed' : 'pointer'}; user-select: none;">
|
|
240
|
+
<input
|
|
241
|
+
type="checkbox"
|
|
242
|
+
checked={sameInBoth}
|
|
243
|
+
onchange={(e) => handleToggleSameInBoth(e.currentTarget.checked)}
|
|
244
|
+
{disabled}
|
|
245
|
+
style="margin: 0; cursor: {disabled ? 'not-allowed' : 'pointer'};"
|
|
246
|
+
/>
|
|
247
|
+
Same in both
|
|
248
|
+
</label>
|
|
249
|
+
|
|
250
|
+
<button
|
|
251
|
+
type="button"
|
|
252
|
+
onclick={handleDerive}
|
|
253
|
+
{disabled}
|
|
254
|
+
title="Automatically derive the opposing scheme colour preserving contrast"
|
|
255
|
+
style="padding: 2px 8px; font-size: 11px; background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.15); border-radius: 4px; color: #cbd5e1; cursor: {disabled ? 'not-allowed' : 'pointer'};"
|
|
256
|
+
>
|
|
257
|
+
Derive {activeScheme === 'light' ? 'Dark' : 'Light'}
|
|
258
|
+
</button>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<!-- Embedded Headless ColorPicker for the active scheme side -->
|
|
262
|
+
<ColorPicker
|
|
263
|
+
value={activeColor}
|
|
264
|
+
onchange={handleInnerChange}
|
|
265
|
+
contrastAgainst={activeContrastTarget}
|
|
266
|
+
{alpha}
|
|
267
|
+
{swatches}
|
|
268
|
+
{disabled}
|
|
269
|
+
ongesturestart={triggerGestureStart}
|
|
270
|
+
ongestureend={triggerGestureEnd}
|
|
271
|
+
/>
|
|
272
|
+
|
|
273
|
+
<!-- Optional Dual Contrast Badge Summary -->
|
|
274
|
+
{#if dualContrast}
|
|
275
|
+
<div
|
|
276
|
+
style="display: flex; gap: 8px; padding-top: 4px; border-top: 1px solid rgba(255,255,255,0.08); font-size: 11px;"
|
|
277
|
+
>
|
|
278
|
+
<div style="flex: 1; display: flex; justify-content: space-between; align-items: center;">
|
|
279
|
+
<span style="color: #8c95a6;">Light:</span>
|
|
280
|
+
<span
|
|
281
|
+
style="padding: 1px 4px; border-radius: 3px; font-weight: 600; font-family: monospace; background: {dualContrast.light.level === 'fail' ? '#7f1d1d' : '#14532d'}; color: {dualContrast.light.level === 'fail' ? '#fca5a5' : '#86efac'};"
|
|
282
|
+
>
|
|
283
|
+
{dualContrast.light.ratio}:1 ({dualContrast.light.level.toUpperCase()})
|
|
284
|
+
</span>
|
|
285
|
+
</div>
|
|
286
|
+
<div style="flex: 1; display: flex; justify-content: space-between; align-items: center;">
|
|
287
|
+
<span style="color: #8c95a6;">Dark:</span>
|
|
288
|
+
<span
|
|
289
|
+
style="padding: 1px 4px; border-radius: 3px; font-weight: 600; font-family: monospace; background: {dualContrast.dark.level === 'fail' ? '#7f1d1d' : '#14532d'}; color: {dualContrast.dark.level === 'fail' ? '#fca5a5' : '#86efac'};"
|
|
290
|
+
>
|
|
291
|
+
{dualContrast.dark.ratio}:1 ({dualContrast.dark.level.toUpperCase()})
|
|
292
|
+
</span>
|
|
293
|
+
</div>
|
|
294
|
+
</div>
|
|
295
|
+
{/if}
|
|
296
|
+
</div>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type ColorScheme, type ColorValue, type DualColor } from '@pixerate/editor/color';
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Dual colour value (CSS string, light-dark(), or { light, dark }) */
|
|
4
|
+
value?: ColorValue;
|
|
5
|
+
/** Called when the dual colour value changes */
|
|
6
|
+
onchange?: (value: DualColor) => void;
|
|
7
|
+
onChange?: (value: DualColor) => void;
|
|
8
|
+
/** Controlled active scheme mode ('light' | 'dark') */
|
|
9
|
+
scheme?: ColorScheme;
|
|
10
|
+
/** Called when the user toggles active scheme tab */
|
|
11
|
+
onschemechange?: (scheme: ColorScheme) => void;
|
|
12
|
+
onSchemeChange?: (scheme: ColorScheme) => void;
|
|
13
|
+
/** Expected background surfaces for derivation and preview */
|
|
14
|
+
surfaces?: DualColor;
|
|
15
|
+
/** Optional background colour to evaluate contrast against in both modes */
|
|
16
|
+
contrastAgainst?: ColorValue;
|
|
17
|
+
/** Whether alpha slider is shown */
|
|
18
|
+
alpha?: boolean;
|
|
19
|
+
/** Preset swatches */
|
|
20
|
+
swatches?: readonly string[];
|
|
21
|
+
/** Whether picker is disabled */
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
/** Additional CSS class name */
|
|
24
|
+
class?: string;
|
|
25
|
+
className?: string;
|
|
26
|
+
/** Gesture callbacks */
|
|
27
|
+
ongesturestart?: () => void;
|
|
28
|
+
onGestureStart?: () => void;
|
|
29
|
+
ongestureend?: () => void;
|
|
30
|
+
onGestureEnd?: () => void;
|
|
31
|
+
}
|
|
32
|
+
declare const DualColorPicker: import("svelte").Component<Props, {}, "">;
|
|
33
|
+
type DualColorPicker = ReturnType<typeof DualColorPicker>;
|
|
34
|
+
export default DualColorPicker;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ColorScheme, ColorValue } from '@pixerate/editor/color';
|
|
2
|
+
export interface ColorSchemeOptions {
|
|
3
|
+
/** Explicit scheme override ('light' or 'dark') */
|
|
4
|
+
override?: ColorScheme | null;
|
|
5
|
+
/** Attribute on document.documentElement to monitor for theme changes (default: 'data-theme') */
|
|
6
|
+
attribute?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ColorSchemeStore {
|
|
9
|
+
readonly current: ColorScheme;
|
|
10
|
+
destroy(): void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Headless Svelte 5 rune tracking active colour scheme across media query and document attribute.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createColorScheme(options?: ColorSchemeOptions): ColorSchemeStore;
|
|
16
|
+
/**
|
|
17
|
+
* Pure helper resolving a ColorValue against a specific scheme.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolvedColor(value: ColorValue, scheme: ColorScheme): string | null;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { resolveColor } from '@pixerate/editor/color';
|
|
2
|
+
/**
|
|
3
|
+
* Headless Svelte 5 rune tracking active colour scheme across media query and document attribute.
|
|
4
|
+
*/
|
|
5
|
+
export function createColorScheme(options = {}) {
|
|
6
|
+
let system = $state('light');
|
|
7
|
+
let attrValue = $state(null);
|
|
8
|
+
const attributeName = options.attribute ?? 'data-theme';
|
|
9
|
+
function readSystem() {
|
|
10
|
+
if (typeof window === 'undefined' || !window.matchMedia)
|
|
11
|
+
return 'light';
|
|
12
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
13
|
+
}
|
|
14
|
+
function readAttribute() {
|
|
15
|
+
if (typeof document === 'undefined')
|
|
16
|
+
return null;
|
|
17
|
+
const val = document.documentElement.getAttribute(attributeName)?.toLowerCase();
|
|
18
|
+
if (val === 'dark' || val === 'light')
|
|
19
|
+
return val;
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
system = readSystem();
|
|
23
|
+
attrValue = readAttribute();
|
|
24
|
+
let mqlListener = null;
|
|
25
|
+
let observer = null;
|
|
26
|
+
if (typeof window !== 'undefined' && window.matchMedia) {
|
|
27
|
+
const mql = window.matchMedia('(prefers-color-scheme: dark)');
|
|
28
|
+
mqlListener = (e) => {
|
|
29
|
+
system = e.matches ? 'dark' : 'light';
|
|
30
|
+
};
|
|
31
|
+
if (mql.addEventListener) {
|
|
32
|
+
mql.addEventListener('change', mqlListener);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
mql.addListener(mqlListener);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (typeof document !== 'undefined' && typeof MutationObserver !== 'undefined') {
|
|
39
|
+
observer = new MutationObserver(() => {
|
|
40
|
+
attrValue = readAttribute();
|
|
41
|
+
});
|
|
42
|
+
observer.observe(document.documentElement, {
|
|
43
|
+
attributes: true,
|
|
44
|
+
attributeFilter: [attributeName]
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function destroy() {
|
|
48
|
+
if (mqlListener && typeof window !== 'undefined' && window.matchMedia) {
|
|
49
|
+
const mql = window.matchMedia('(prefers-color-scheme: dark)');
|
|
50
|
+
if (mql.removeEventListener) {
|
|
51
|
+
mql.removeEventListener('change', mqlListener);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
mql.removeListener(mqlListener);
|
|
55
|
+
}
|
|
56
|
+
mqlListener = null;
|
|
57
|
+
}
|
|
58
|
+
if (observer) {
|
|
59
|
+
observer.disconnect();
|
|
60
|
+
observer = null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const current = $derived(options.override ?? attrValue ?? system);
|
|
64
|
+
return {
|
|
65
|
+
get current() {
|
|
66
|
+
return current;
|
|
67
|
+
},
|
|
68
|
+
destroy
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Pure helper resolving a ColorValue against a specific scheme.
|
|
73
|
+
*/
|
|
74
|
+
export function resolvedColor(value, scheme) {
|
|
75
|
+
return resolveColor(value, scheme);
|
|
76
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ComponentProps } from 'svelte';
|
|
2
|
+
import type ColorPickerComponent from './ColorPicker.svelte';
|
|
3
|
+
import type DualColorPickerComponent from './DualColorPicker.svelte';
|
|
4
|
+
export { default as ColorPicker } from './ColorPicker.svelte';
|
|
5
|
+
export { default as DualColorPicker } from './DualColorPicker.svelte';
|
|
6
|
+
export type ColorPickerProps = ComponentProps<typeof ColorPickerComponent>;
|
|
7
|
+
export type DualColorPickerProps = ComponentProps<typeof DualColorPickerComponent>;
|
|
8
|
+
export { createColorScheme, resolvedColor, type ColorSchemeOptions, type ColorSchemeStore } from './colorScheme.svelte.js';
|
|
9
|
+
export * from '@pixerate/editor/color';
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as ColorPicker } from './ColorPicker.svelte';
|
|
2
|
+
export { default as DualColorPicker } from './DualColorPicker.svelte';
|
|
3
|
+
export { createColorScheme, resolvedColor } from './colorScheme.svelte.js';
|
|
4
|
+
// Re-export core colour module for consumer convenience
|
|
5
|
+
export * from '@pixerate/editor/color';
|