@rkosafo/cai.components 0.0.82 → 0.0.84
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.
|
@@ -199,9 +199,6 @@
|
|
|
199
199
|
type={form ? 'submit' : 'button'}
|
|
200
200
|
formnovalidate
|
|
201
201
|
class={clsx(styling.close)}
|
|
202
|
-
onclick={() => {
|
|
203
|
-
if (!form) close();
|
|
204
|
-
}}
|
|
205
202
|
/>
|
|
206
203
|
{/if}
|
|
207
204
|
{:else if header}
|
|
@@ -222,9 +219,6 @@
|
|
|
222
219
|
type={form ? 'submit' : 'button'}
|
|
223
220
|
formnovalidate
|
|
224
221
|
class={closeCls({ class: clsx(theme?.close, styling.close) })}
|
|
225
|
-
onclick={() => {
|
|
226
|
-
if (!form) close();
|
|
227
|
-
}}
|
|
228
222
|
/>
|
|
229
223
|
{/if}
|
|
230
224
|
{/snippet}
|
|
@@ -233,7 +227,7 @@
|
|
|
233
227
|
<div
|
|
234
228
|
bind:this={ref}
|
|
235
229
|
use:focusTrap
|
|
236
|
-
class="fixed inset-0 z-40
|
|
230
|
+
class="fixed inset-0 z-40 overflow-y-auto p-4"
|
|
237
231
|
onclick={_onclick}
|
|
238
232
|
onkeydown={(ev) => {
|
|
239
233
|
if (ev.key === 'Escape' && !permanent) {
|
|
@@ -253,22 +247,22 @@
|
|
|
253
247
|
class: clsx(
|
|
254
248
|
theme?.base,
|
|
255
249
|
className,
|
|
256
|
-
'
|
|
250
|
+
fullscreen ? 'z-10' : 'absolute z-10',
|
|
257
251
|
!modal && 'shadow-xl'
|
|
258
252
|
)
|
|
259
253
|
})}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
254
|
+
tabindex="-1"
|
|
255
|
+
onsubmit={_onsubmit}
|
|
256
|
+
transition:transition|global={paramsOptions as ParamsType}
|
|
257
|
+
{...restProps}
|
|
258
|
+
>
|
|
259
|
+
{#if form}
|
|
260
|
+
<form method="dialog" class={formCls({ class: clsx(theme?.form) })}>
|
|
261
|
+
{@render content()}
|
|
262
|
+
</form>
|
|
263
|
+
{:else}
|
|
267
264
|
{@render content()}
|
|
268
|
-
|
|
269
|
-
{:else}
|
|
270
|
-
{@render content()}
|
|
271
|
-
{/if}
|
|
265
|
+
{/if}
|
|
272
266
|
</div>
|
|
273
267
|
</div>
|
|
274
268
|
{/if}
|
|
@@ -34,6 +34,15 @@
|
|
|
34
34
|
export type CustomToastOptions = ToastOptions;
|
|
35
35
|
export type ToastTheme = Record<string, never>;
|
|
36
36
|
|
|
37
|
+
export type ToastSoundPreset =
|
|
38
|
+
| 'chime'
|
|
39
|
+
| 'ding'
|
|
40
|
+
| 'bell'
|
|
41
|
+
| 'soft'
|
|
42
|
+
| 'pop'
|
|
43
|
+
| 'pulse'
|
|
44
|
+
| 'airy';
|
|
45
|
+
|
|
37
46
|
export interface ToasterProps {
|
|
38
47
|
position?: ToastPosition;
|
|
39
48
|
duration?: number;
|
|
@@ -43,6 +52,8 @@
|
|
|
43
52
|
containerStyle?: string;
|
|
44
53
|
containerClassName?: string;
|
|
45
54
|
fireWithSound?: boolean;
|
|
55
|
+
/** Web Audio preset when `fireWithSound` is true. Default `chime`. */
|
|
56
|
+
soundPreset?: ToastSoundPreset;
|
|
46
57
|
// Kept for backward compatibility (no-op in wrapper mode)
|
|
47
58
|
className?: string;
|
|
48
59
|
theme?: ToastTheme;
|
|
@@ -58,58 +69,188 @@
|
|
|
58
69
|
|
|
59
70
|
let currentDuration = 4000;
|
|
60
71
|
let currentFireWithSound = false;
|
|
72
|
+
let currentSoundPreset: ToastSoundPreset = 'chime';
|
|
61
73
|
|
|
62
|
-
|
|
63
|
-
if (!currentFireWithSound || typeof window === 'undefined') return;
|
|
74
|
+
let sharedAudioContext: AudioContext | null = null;
|
|
64
75
|
|
|
76
|
+
function getSharedAudioContext(): AudioContext | null {
|
|
77
|
+
if (typeof window === 'undefined') return null;
|
|
65
78
|
try {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
gain.gain.exponentialRampToValueAtTime(0.06, context.currentTime + 0.01);
|
|
76
|
-
gain.gain.exponentialRampToValueAtTime(0.0001, context.currentTime + 0.18);
|
|
77
|
-
oscillator.connect(gain);
|
|
78
|
-
gain.connect(context.destination);
|
|
79
|
-
oscillator.start();
|
|
80
|
-
oscillator.stop(context.currentTime + 0.2);
|
|
79
|
+
if (sharedAudioContext && sharedAudioContext.state !== 'closed') {
|
|
80
|
+
return sharedAudioContext;
|
|
81
|
+
}
|
|
82
|
+
sharedAudioContext = null;
|
|
83
|
+
const AudioContextClass =
|
|
84
|
+
window.AudioContext || (window as Window & { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;
|
|
85
|
+
if (!AudioContextClass) return null;
|
|
86
|
+
sharedAudioContext = new AudioContextClass();
|
|
87
|
+
return sharedAudioContext;
|
|
81
88
|
} catch {
|
|
82
|
-
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function envelopeAttackRelease(
|
|
94
|
+
gain: GainNode,
|
|
95
|
+
ctx: AudioContext,
|
|
96
|
+
t: number,
|
|
97
|
+
peak: number,
|
|
98
|
+
attack: number,
|
|
99
|
+
release: number
|
|
100
|
+
) {
|
|
101
|
+
gain.gain.setValueAtTime(0.0001, t);
|
|
102
|
+
gain.gain.exponentialRampToValueAtTime(peak, t + attack);
|
|
103
|
+
gain.gain.exponentialRampToValueAtTime(0.0001, t + attack + release);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function playPresetSound(ctx: AudioContext, preset: ToastSoundPreset) {
|
|
107
|
+
const t0 = ctx.currentTime;
|
|
108
|
+
|
|
109
|
+
switch (preset) {
|
|
110
|
+
case 'chime': {
|
|
111
|
+
const freqs = [523.25, 659.25];
|
|
112
|
+
for (let i = 0; i < freqs.length; i++) {
|
|
113
|
+
const start = t0 + i * 0.065;
|
|
114
|
+
const osc = ctx.createOscillator();
|
|
115
|
+
const g = ctx.createGain();
|
|
116
|
+
osc.type = 'sine';
|
|
117
|
+
osc.frequency.setValueAtTime(freqs[i], start);
|
|
118
|
+
envelopeAttackRelease(g, ctx, start, 0.07, 0.012, 0.11);
|
|
119
|
+
osc.connect(g);
|
|
120
|
+
g.connect(ctx.destination);
|
|
121
|
+
osc.start(start);
|
|
122
|
+
osc.stop(start + 0.2);
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case 'ding': {
|
|
127
|
+
const osc = ctx.createOscillator();
|
|
128
|
+
const g = ctx.createGain();
|
|
129
|
+
osc.type = 'sine';
|
|
130
|
+
osc.frequency.setValueAtTime(1046.5, t0);
|
|
131
|
+
envelopeAttackRelease(g, ctx, t0, 0.08, 0.004, 0.14);
|
|
132
|
+
osc.connect(g);
|
|
133
|
+
g.connect(ctx.destination);
|
|
134
|
+
osc.start(t0);
|
|
135
|
+
osc.stop(t0 + 0.2);
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
case 'bell': {
|
|
139
|
+
const osc = ctx.createOscillator();
|
|
140
|
+
const g = ctx.createGain();
|
|
141
|
+
osc.type = 'triangle';
|
|
142
|
+
osc.frequency.setValueAtTime(740, t0);
|
|
143
|
+
envelopeAttackRelease(g, ctx, t0, 0.06, 0.006, 0.22);
|
|
144
|
+
osc.connect(g);
|
|
145
|
+
g.connect(ctx.destination);
|
|
146
|
+
osc.start(t0);
|
|
147
|
+
osc.stop(t0 + 0.28);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
case 'soft': {
|
|
151
|
+
const osc = ctx.createOscillator();
|
|
152
|
+
const g = ctx.createGain();
|
|
153
|
+
osc.type = 'sine';
|
|
154
|
+
osc.frequency.setValueAtTime(392, t0);
|
|
155
|
+
envelopeAttackRelease(g, ctx, t0, 0.05, 0.02, 0.2);
|
|
156
|
+
osc.connect(g);
|
|
157
|
+
g.connect(ctx.destination);
|
|
158
|
+
osc.start(t0);
|
|
159
|
+
osc.stop(t0 + 0.26);
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
case 'pop': {
|
|
163
|
+
const osc = ctx.createOscillator();
|
|
164
|
+
const g = ctx.createGain();
|
|
165
|
+
osc.type = 'sine';
|
|
166
|
+
osc.frequency.setValueAtTime(320, t0);
|
|
167
|
+
envelopeAttackRelease(g, ctx, t0, 0.06, 0.002, 0.045);
|
|
168
|
+
osc.connect(g);
|
|
169
|
+
g.connect(ctx.destination);
|
|
170
|
+
osc.start(t0);
|
|
171
|
+
osc.stop(t0 + 0.08);
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
case 'pulse': {
|
|
175
|
+
const osc = ctx.createOscillator();
|
|
176
|
+
const g = ctx.createGain();
|
|
177
|
+
osc.type = 'sine';
|
|
178
|
+
osc.frequency.setValueAtTime(620, t0);
|
|
179
|
+
osc.frequency.exponentialRampToValueAtTime(920, t0 + 0.07);
|
|
180
|
+
envelopeAttackRelease(g, ctx, t0, 0.07, 0.003, 0.08);
|
|
181
|
+
osc.connect(g);
|
|
182
|
+
g.connect(ctx.destination);
|
|
183
|
+
osc.start(t0);
|
|
184
|
+
osc.stop(t0 + 0.12);
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
case 'airy':
|
|
188
|
+
default: {
|
|
189
|
+
const osc = ctx.createOscillator();
|
|
190
|
+
const g = ctx.createGain();
|
|
191
|
+
osc.type = 'sine';
|
|
192
|
+
osc.frequency.setValueAtTime(1760, t0);
|
|
193
|
+
envelopeAttackRelease(g, ctx, t0, 0.025, 0.002, 0.07);
|
|
194
|
+
osc.connect(g);
|
|
195
|
+
g.connect(ctx.destination);
|
|
196
|
+
osc.start(t0);
|
|
197
|
+
osc.stop(t0 + 0.1);
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
83
200
|
}
|
|
84
201
|
}
|
|
85
202
|
|
|
203
|
+
/** Schedules sound after the current frame so the toast paints first; handles suspended AudioContext. */
|
|
204
|
+
function scheduleToastSound() {
|
|
205
|
+
if (!currentFireWithSound || typeof window === 'undefined') return;
|
|
206
|
+
|
|
207
|
+
queueMicrotask(() => {
|
|
208
|
+
void (async () => {
|
|
209
|
+
const ctx = getSharedAudioContext();
|
|
210
|
+
if (!ctx) return;
|
|
211
|
+
try {
|
|
212
|
+
if (ctx.state === 'suspended') await ctx.resume();
|
|
213
|
+
playPresetSound(ctx, currentSoundPreset);
|
|
214
|
+
} catch {
|
|
215
|
+
// Ignore audio errors.
|
|
216
|
+
}
|
|
217
|
+
})();
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
86
221
|
export function updateToastConfig(config: {
|
|
87
222
|
duration?: number;
|
|
88
223
|
fireWithSound?: boolean;
|
|
224
|
+
soundPreset?: ToastSoundPreset;
|
|
89
225
|
theme?: ToastTheme;
|
|
90
226
|
richColors?: boolean;
|
|
91
227
|
}) {
|
|
92
228
|
if (config.duration !== undefined) currentDuration = config.duration;
|
|
93
229
|
if (config.fireWithSound !== undefined) currentFireWithSound = config.fireWithSound;
|
|
230
|
+
if (config.soundPreset !== undefined) currentSoundPreset = config.soundPreset;
|
|
94
231
|
}
|
|
95
232
|
|
|
96
233
|
export const toast = Object.assign(
|
|
97
234
|
(message: Renderable, options?: ToastOptions) => {
|
|
98
|
-
|
|
99
|
-
|
|
235
|
+
const id = originalToast(message, { duration: currentDuration, ...options });
|
|
236
|
+
scheduleToastSound();
|
|
237
|
+
return id;
|
|
100
238
|
},
|
|
101
239
|
{
|
|
102
240
|
success: (message: Renderable, options?: ToastOptions) => {
|
|
103
|
-
|
|
104
|
-
|
|
241
|
+
const id = originalToast.success(message, { duration: currentDuration, ...options });
|
|
242
|
+
scheduleToastSound();
|
|
243
|
+
return id;
|
|
105
244
|
},
|
|
106
245
|
error: (message: Renderable, options?: ToastOptions) => {
|
|
107
|
-
|
|
108
|
-
|
|
246
|
+
const id = originalToast.error(message, { duration: currentDuration, ...options });
|
|
247
|
+
scheduleToastSound();
|
|
248
|
+
return id;
|
|
109
249
|
},
|
|
110
250
|
loading: (message: Renderable, options?: ToastOptions) => {
|
|
111
|
-
|
|
112
|
-
|
|
251
|
+
const id = originalToast.loading(message, { duration: currentDuration, ...options });
|
|
252
|
+
scheduleToastSound();
|
|
253
|
+
return id;
|
|
113
254
|
},
|
|
114
255
|
promise: <T,>(
|
|
115
256
|
promise: Promise<T>,
|
|
@@ -120,8 +261,9 @@
|
|
|
120
261
|
},
|
|
121
262
|
opts?: DefaultToastOptions
|
|
122
263
|
) => {
|
|
123
|
-
|
|
124
|
-
|
|
264
|
+
const out = originalToast.promise(promise, msgs, opts);
|
|
265
|
+
scheduleToastSound();
|
|
266
|
+
return out;
|
|
125
267
|
},
|
|
126
268
|
dismiss: originalToast.dismiss.bind(originalToast),
|
|
127
269
|
remove: originalToast.remove.bind(originalToast),
|
|
@@ -154,7 +296,8 @@
|
|
|
154
296
|
expand = false,
|
|
155
297
|
gap = 8,
|
|
156
298
|
offset = '32px',
|
|
157
|
-
fireWithSound = false
|
|
299
|
+
fireWithSound = false,
|
|
300
|
+
soundPreset = 'chime'
|
|
158
301
|
}: ToasterProps = $props();
|
|
159
302
|
|
|
160
303
|
$effect(() => {
|
|
@@ -162,7 +305,7 @@
|
|
|
162
305
|
void theme;
|
|
163
306
|
void richColors;
|
|
164
307
|
void customIcons;
|
|
165
|
-
updateToastConfig({ duration, fireWithSound });
|
|
308
|
+
updateToastConfig({ duration, fireWithSound, soundPreset });
|
|
166
309
|
});
|
|
167
310
|
</script>
|
|
168
311
|
|
|
@@ -4,6 +4,7 @@ export { resolveValue };
|
|
|
4
4
|
export type { DefaultToastOptions, IconTheme, Toast, ToastOptions, ToastPosition, ToastType, Renderable, ValueFunction, ValueOrFunction } from 'svelte-french-toast';
|
|
5
5
|
export type CustomToastOptions = ToastOptions;
|
|
6
6
|
export type ToastTheme = Record<string, never>;
|
|
7
|
+
export type ToastSoundPreset = 'chime' | 'ding' | 'bell' | 'soft' | 'pop' | 'pulse' | 'airy';
|
|
7
8
|
export interface ToasterProps {
|
|
8
9
|
position?: ToastPosition;
|
|
9
10
|
duration?: number;
|
|
@@ -13,6 +14,8 @@ export interface ToasterProps {
|
|
|
13
14
|
containerStyle?: string;
|
|
14
15
|
containerClassName?: string;
|
|
15
16
|
fireWithSound?: boolean;
|
|
17
|
+
/** Web Audio preset when `fireWithSound` is true. Default `chime`. */
|
|
18
|
+
soundPreset?: ToastSoundPreset;
|
|
16
19
|
className?: string;
|
|
17
20
|
theme?: ToastTheme;
|
|
18
21
|
richColors?: boolean;
|
|
@@ -27,6 +30,7 @@ export interface ToasterProps {
|
|
|
27
30
|
export declare function updateToastConfig(config: {
|
|
28
31
|
duration?: number;
|
|
29
32
|
fireWithSound?: boolean;
|
|
33
|
+
soundPreset?: ToastSoundPreset;
|
|
30
34
|
theme?: ToastTheme;
|
|
31
35
|
richColors?: boolean;
|
|
32
36
|
}): void;
|
package/dist/ui/toast/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default as Toaster, toast, originalToast, updateToastConfig } from './Toast.svelte';
|
|
2
|
-
export type { ToasterProps, ToastType, CustomToastOptions, ToastTheme, ToastOptions } from './Toast.svelte';
|
|
2
|
+
export type { ToasterProps, ToastSoundPreset, ToastType, CustomToastOptions, ToastTheme, ToastOptions } from './Toast.svelte';
|