svelora 2.2.0 → 3.0.1

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 CHANGED
@@ -16,8 +16,8 @@
16
16
  </p>
17
17
 
18
18
  <p align="center">
19
- <a href="https://svelora.vercel.app"><strong>Live Demo</strong></a> &middot;
20
- <a href="https://svelora.vercel.app/getting-started"><strong>Getting Started</strong></a> &middot;
19
+ <a href="https://svelora-ui.vercel.app/"><strong>Live Demo</strong></a> &middot;
20
+ <a href="https://svelora-ui.vercel.app//getting-started"><strong>Getting Started</strong></a> &middot;
21
21
  <a href="https://github.com/asphum/svelora/blob/main/CHANGELOG.md"><strong>Changelog</strong></a>
22
22
  </p>
23
23
 
@@ -0,0 +1,83 @@
1
+ <script lang="ts" module>
2
+ import type { CodeBlockProps } from './code-block.types.js'
3
+
4
+ export type Props = CodeBlockProps
5
+ </script>
6
+
7
+ <script lang="ts">
8
+ import { getComponentConfig } from '../config.js'
9
+ import { codeBlockDefaults, codeBlockVariants } from './code-block.variants.js'
10
+
11
+ const config = getComponentConfig('codeBlock', codeBlockDefaults)
12
+
13
+ let {
14
+ title = 'Code',
15
+ code = '',
16
+ copyText,
17
+ copyable = true,
18
+ html,
19
+ variant = config.defaultVariants.variant,
20
+ size = config.defaultVariants.size,
21
+ class: className,
22
+ ui,
23
+ children
24
+ }: Props = $props()
25
+
26
+ let copyLabel = $state('Copy')
27
+
28
+ const resolvedCopyText = $derived((copyText ?? code).trim())
29
+ const canCopy = $derived(copyable && resolvedCopyText.length > 0)
30
+
31
+ const classes = $derived.by(() => {
32
+ const slots = codeBlockVariants({ variant, size })
33
+ return {
34
+ root: slots.root({ class: [config.slots.root, className, ui?.root] }),
35
+ header: slots.header({ class: [config.slots.header, ui?.header] }),
36
+ title: slots.title({ class: [config.slots.title, ui?.title] }),
37
+ button: slots.button({ class: [config.slots.button, ui?.button] }),
38
+ body: slots.body({ class: [config.slots.body, ui?.body] })
39
+ }
40
+ })
41
+
42
+ function scheduleLabelReset(resetLabel: string): void {
43
+ window.setTimeout(() => {
44
+ copyLabel = resetLabel
45
+ }, 1200)
46
+ }
47
+
48
+ async function handleCopy(): Promise<void> {
49
+ if (!canCopy) return
50
+ if (typeof navigator === 'undefined' || !navigator.clipboard) return
51
+
52
+ const resetLabel = copyLabel
53
+ try {
54
+ await navigator.clipboard.writeText(resolvedCopyText)
55
+ copyLabel = 'Copied'
56
+ } catch {
57
+ copyLabel = 'Failed'
58
+ }
59
+
60
+ scheduleLabelReset(resetLabel)
61
+ }
62
+ </script>
63
+
64
+ <div class={classes.root}>
65
+ <div class={classes.header}>
66
+ <p class={classes.title}>{title}</p>
67
+ <button type="button" class={classes.button} disabled={!canCopy} onclick={handleCopy}>
68
+ {copyLabel}
69
+ </button>
70
+ </div>
71
+
72
+ <div class={classes.body}>
73
+ {#if html}
74
+ {@html html}
75
+ {:else if children}
76
+ {@render children()}
77
+ {:else}
78
+ <pre class="shiki">
79
+ <code>{code}</code>
80
+ </pre>
81
+ {/if}
82
+ </div>
83
+ </div>
@@ -0,0 +1,5 @@
1
+ import type { CodeBlockProps } from './code-block.types.js';
2
+ export type Props = CodeBlockProps;
3
+ declare const CodeBlock: import("svelte").Component<CodeBlockProps, {}, "">;
4
+ type CodeBlock = ReturnType<typeof CodeBlock>;
5
+ export default CodeBlock;
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { ClassNameValue } from 'tailwind-merge';
3
+ import type { CodeBlockVariantProps, CodeBlockSlots } from './code-block.variants.js';
4
+ export type CodeBlockProps = {
5
+ title?: string;
6
+ code?: string;
7
+ copyText?: string;
8
+ copyable?: boolean;
9
+ html?: string;
10
+ variant?: NonNullable<CodeBlockVariantProps['variant']>;
11
+ size?: NonNullable<CodeBlockVariantProps['size']>;
12
+ class?: ClassNameValue;
13
+ ui?: Partial<Record<CodeBlockSlots, ClassNameValue>>;
14
+ children?: Snippet;
15
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,288 @@
1
+ import { type VariantProps } from 'tailwind-variants';
2
+ export declare const codeBlockVariants: import("tailwind-variants").TVReturnType<{
3
+ variant: {
4
+ outline: {
5
+ root: string;
6
+ header: string;
7
+ button: string;
8
+ };
9
+ soft: {
10
+ root: string;
11
+ header: string;
12
+ button: string;
13
+ };
14
+ subtle: {
15
+ root: string;
16
+ header: string;
17
+ button: string;
18
+ };
19
+ solid: {
20
+ root: string;
21
+ header: string;
22
+ button: string;
23
+ };
24
+ ghost: {
25
+ root: string;
26
+ header: string;
27
+ button: string;
28
+ };
29
+ none: {
30
+ root: string;
31
+ header: string;
32
+ button: string;
33
+ };
34
+ };
35
+ size: {
36
+ sm: {
37
+ header: string;
38
+ button: string;
39
+ body: string;
40
+ };
41
+ md: {
42
+ header: string;
43
+ button: string;
44
+ body: string;
45
+ };
46
+ lg: {
47
+ header: string;
48
+ button: string;
49
+ body: string;
50
+ };
51
+ };
52
+ }, {
53
+ root: string;
54
+ header: string;
55
+ title: string;
56
+ button: string;
57
+ body: string;
58
+ }, undefined, {
59
+ variant: {
60
+ outline: {
61
+ root: string;
62
+ header: string;
63
+ button: string;
64
+ };
65
+ soft: {
66
+ root: string;
67
+ header: string;
68
+ button: string;
69
+ };
70
+ subtle: {
71
+ root: string;
72
+ header: string;
73
+ button: string;
74
+ };
75
+ solid: {
76
+ root: string;
77
+ header: string;
78
+ button: string;
79
+ };
80
+ ghost: {
81
+ root: string;
82
+ header: string;
83
+ button: string;
84
+ };
85
+ none: {
86
+ root: string;
87
+ header: string;
88
+ button: string;
89
+ };
90
+ };
91
+ size: {
92
+ sm: {
93
+ header: string;
94
+ button: string;
95
+ body: string;
96
+ };
97
+ md: {
98
+ header: string;
99
+ button: string;
100
+ body: string;
101
+ };
102
+ lg: {
103
+ header: string;
104
+ button: string;
105
+ body: string;
106
+ };
107
+ };
108
+ }, {
109
+ root: string;
110
+ header: string;
111
+ title: string;
112
+ button: string;
113
+ body: string;
114
+ }, import("tailwind-variants").TVReturnType<{
115
+ variant: {
116
+ outline: {
117
+ root: string;
118
+ header: string;
119
+ button: string;
120
+ };
121
+ soft: {
122
+ root: string;
123
+ header: string;
124
+ button: string;
125
+ };
126
+ subtle: {
127
+ root: string;
128
+ header: string;
129
+ button: string;
130
+ };
131
+ solid: {
132
+ root: string;
133
+ header: string;
134
+ button: string;
135
+ };
136
+ ghost: {
137
+ root: string;
138
+ header: string;
139
+ button: string;
140
+ };
141
+ none: {
142
+ root: string;
143
+ header: string;
144
+ button: string;
145
+ };
146
+ };
147
+ size: {
148
+ sm: {
149
+ header: string;
150
+ button: string;
151
+ body: string;
152
+ };
153
+ md: {
154
+ header: string;
155
+ button: string;
156
+ body: string;
157
+ };
158
+ lg: {
159
+ header: string;
160
+ button: string;
161
+ body: string;
162
+ };
163
+ };
164
+ }, {
165
+ root: string;
166
+ header: string;
167
+ title: string;
168
+ button: string;
169
+ body: string;
170
+ }, undefined, unknown, unknown, undefined>>;
171
+ export type CodeBlockVariantProps = VariantProps<typeof codeBlockVariants>;
172
+ export type CodeBlockSlots = keyof ReturnType<typeof codeBlockVariants>;
173
+ export declare const codeBlockDefaults: {
174
+ defaultVariants: import("tailwind-variants").TVDefaultVariants<{
175
+ variant: {
176
+ outline: {
177
+ root: string;
178
+ header: string;
179
+ button: string;
180
+ };
181
+ soft: {
182
+ root: string;
183
+ header: string;
184
+ button: string;
185
+ };
186
+ subtle: {
187
+ root: string;
188
+ header: string;
189
+ button: string;
190
+ };
191
+ solid: {
192
+ root: string;
193
+ header: string;
194
+ button: string;
195
+ };
196
+ ghost: {
197
+ root: string;
198
+ header: string;
199
+ button: string;
200
+ };
201
+ none: {
202
+ root: string;
203
+ header: string;
204
+ button: string;
205
+ };
206
+ };
207
+ size: {
208
+ sm: {
209
+ header: string;
210
+ button: string;
211
+ body: string;
212
+ };
213
+ md: {
214
+ header: string;
215
+ button: string;
216
+ body: string;
217
+ };
218
+ lg: {
219
+ header: string;
220
+ button: string;
221
+ body: string;
222
+ };
223
+ };
224
+ }, {
225
+ root: string;
226
+ header: string;
227
+ title: string;
228
+ button: string;
229
+ body: string;
230
+ }, {
231
+ variant: {
232
+ outline: {
233
+ root: string;
234
+ header: string;
235
+ button: string;
236
+ };
237
+ soft: {
238
+ root: string;
239
+ header: string;
240
+ button: string;
241
+ };
242
+ subtle: {
243
+ root: string;
244
+ header: string;
245
+ button: string;
246
+ };
247
+ solid: {
248
+ root: string;
249
+ header: string;
250
+ button: string;
251
+ };
252
+ ghost: {
253
+ root: string;
254
+ header: string;
255
+ button: string;
256
+ };
257
+ none: {
258
+ root: string;
259
+ header: string;
260
+ button: string;
261
+ };
262
+ };
263
+ size: {
264
+ sm: {
265
+ header: string;
266
+ button: string;
267
+ body: string;
268
+ };
269
+ md: {
270
+ header: string;
271
+ button: string;
272
+ body: string;
273
+ };
274
+ lg: {
275
+ header: string;
276
+ button: string;
277
+ body: string;
278
+ };
279
+ };
280
+ }, {
281
+ root: string;
282
+ header: string;
283
+ title: string;
284
+ button: string;
285
+ body: string;
286
+ }>;
287
+ slots: Partial<Record<CodeBlockSlots, string>>;
288
+ };
@@ -0,0 +1,69 @@
1
+ import { tv } from 'tailwind-variants';
2
+ export const codeBlockVariants = tv({
3
+ slots: {
4
+ root: 'mt-4 overflow-hidden rounded-2xl border border-outline-variant bg-surface-container',
5
+ header: 'flex items-center justify-between border-b border-outline-variant',
6
+ title: 'text-sm font-medium text-on-surface-variant',
7
+ button: 'rounded-md border border-outline-variant text-xs font-medium text-on-surface-variant transition-colors hover:bg-surface-container-highest disabled:cursor-not-allowed disabled:opacity-50',
8
+ body: 'bg-surface-container-highest [&_.shiki]:rounded-none [&_.shiki]:overflow-x-auto [&_.shiki]:m-0'
9
+ },
10
+ variants: {
11
+ variant: {
12
+ outline: {
13
+ root: 'border border-outline-variant bg-surface-container',
14
+ header: 'border-outline-variant',
15
+ button: 'border-outline-variant'
16
+ },
17
+ soft: {
18
+ root: 'border border-outline-variant/60 bg-surface-container/40',
19
+ header: 'border-outline-variant/60',
20
+ button: 'border-outline-variant/60'
21
+ },
22
+ subtle: {
23
+ root: 'border border-outline-variant/40 bg-surface-container/20',
24
+ header: 'border-outline-variant/40',
25
+ button: 'border-outline-variant/40'
26
+ },
27
+ solid: {
28
+ root: 'border border-outline-variant/60 bg-surface-container-high',
29
+ header: 'border-outline-variant/60',
30
+ button: 'border-outline-variant/60'
31
+ },
32
+ ghost: {
33
+ root: 'border border-transparent bg-transparent',
34
+ header: 'border-outline-variant/30',
35
+ button: 'border-outline-variant/30'
36
+ },
37
+ none: {
38
+ root: 'border-0 bg-transparent rounded-none',
39
+ header: 'border-0 px-0',
40
+ button: 'border-outline-variant/40'
41
+ }
42
+ },
43
+ size: {
44
+ sm: {
45
+ header: 'px-3 py-2',
46
+ button: 'px-2 py-1',
47
+ body: '[&_.shiki]:p-3 [&_.shiki]:text-xs [&_.shiki]:leading-5'
48
+ },
49
+ md: {
50
+ header: 'px-4 py-3',
51
+ button: 'px-2.5 py-1',
52
+ body: '[&_.shiki]:p-4 [&_.shiki]:text-sm [&_.shiki]:leading-6'
53
+ },
54
+ lg: {
55
+ header: 'px-5 py-4',
56
+ button: 'px-3 py-1.5',
57
+ body: '[&_.shiki]:p-5 [&_.shiki]:text-sm [&_.shiki]:leading-6'
58
+ }
59
+ }
60
+ },
61
+ defaultVariants: {
62
+ variant: 'outline',
63
+ size: 'md'
64
+ }
65
+ });
66
+ export const codeBlockDefaults = {
67
+ defaultVariants: codeBlockVariants.defaultVariants,
68
+ slots: {}
69
+ };
@@ -0,0 +1,2 @@
1
+ export { default as CodeBlock } from './CodeBlock.svelte';
2
+ export type { CodeBlockProps } from './code-block.types.js';
@@ -0,0 +1 @@
1
+ export { default as CodeBlock } from './CodeBlock.svelte';
@@ -81,7 +81,7 @@
81
81
  }
82
82
 
83
83
  const rootProps = $derived.by(() => {
84
- const base = {
84
+ const base: Record<string, unknown> = {
85
85
  ...rest,
86
86
  open,
87
87
  onOpenChange: handleOpenChange,
@@ -99,6 +99,9 @@
99
99
  onRelease,
100
100
  onClose
101
101
  }
102
+ if (rest.container) {
103
+ base.container = rest.container
104
+ }
102
105
  if (snapPoints && fadeFromIndex !== null) {
103
106
  return { ...base, snapPoints, fadeFromIndex }
104
107
  }
@@ -182,9 +185,16 @@
182
185
  bits-ui v0 internally, which is incompatible with Svelte 5 snippets
183
186
  and yields an empty <button> in the DOM. We pass an `onclick` handler
184
187
  to the user-supplied trigger snippet so the user's element opens the
185
- drawer when activated.
188
+ drawer when activated, and we manually expose `data-state` so callers
189
+ can style/assert the trigger element.
186
190
  -->
187
- {@render children({ props: { type: 'button', onclick: () => (open = true) } })}
191
+ {@render children({
192
+ props: {
193
+ type: 'button',
194
+ onclick: () => (open = true),
195
+ 'data-state': open ? 'open' : 'closed'
196
+ }
197
+ })}
188
198
  {/if}
189
199
 
190
200
  {#if portal}
@@ -467,6 +467,9 @@ export type Props = EditorProps;
467
467
  try {
468
468
  const url = await onImageUpload(file)
469
469
  if (!isSafeImageSrc(url)) {
470
+ console.warn(
471
+ `[svelora] Blocked unsafe image src returned by onImageUpload: ${url}`
472
+ )
470
473
  return
471
474
  }
472
475
  editor.chain().focus().setImage({ src: url }).run()
@@ -0,0 +1,54 @@
1
+ <script lang="ts" module>
2
+ import type { FontsProps } from './fonts.types.js'
3
+
4
+ export type Props = FontsProps
5
+ </script>
6
+
7
+ <script lang="ts">
8
+ import { getConfig } from '../config.js'
9
+ import {
10
+ buildFontVariablesCss,
11
+ buildGoogleFontsUrl,
12
+ buildLocalFontFaceCss,
13
+ fontsDefaults,
14
+ resolveFontsOptions
15
+ } from './fonts.js'
16
+
17
+ const globalConfig = getConfig()
18
+ const config = resolveFontsOptions(globalConfig.fonts)
19
+
20
+ let {
21
+ fonts,
22
+ families,
23
+ display,
24
+ preconnect
25
+ }: Props = $props()
26
+
27
+ const resolvedFamilies = $derived(families ?? fonts ?? (config === false ? [] : config.families ?? []))
28
+ const resolvedDisplay = $derived(display ?? (config === false ? fontsDefaults.display : config.display))
29
+ const resolvedPreconnect = $derived(
30
+ preconnect ?? (config === false ? fontsDefaults.preconnect : config.preconnect)
31
+ )
32
+
33
+ const href = $derived(buildGoogleFontsUrl(resolvedFamilies, resolvedDisplay))
34
+ const localFontFaceCss = $derived(buildLocalFontFaceCss(resolvedFamilies, resolvedDisplay))
35
+ const variableCss = $derived(buildFontVariablesCss(resolvedFamilies))
36
+ const inlineCss = $derived([localFontFaceCss, variableCss].filter((value) => value.length > 0).join('\n\n'))
37
+ const hasFonts = $derived(href.length > 0 || inlineCss.length > 0)
38
+ </script>
39
+
40
+ <svelte:head>
41
+ {#if hasFonts}
42
+ {#if resolvedPreconnect && href}
43
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
44
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
45
+ {/if}
46
+ {#if href}
47
+ <link rel="stylesheet" href={href} />
48
+ {/if}
49
+ {#if inlineCss}
50
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
51
+ {@html `<style>${inlineCss}</style>`}
52
+ {/if}
53
+ {/if}
54
+ </svelte:head>
@@ -0,0 +1,5 @@
1
+ import type { FontsProps } from './fonts.types.js';
2
+ export type Props = FontsProps;
3
+ declare const Fonts: import("svelte").Component<FontsProps, {}, "">;
4
+ type Fonts = ReturnType<typeof Fonts>;
5
+ export default Fonts;
@@ -0,0 +1,12 @@
1
+ import type { FontDefinition, GoogleFontDisplay, FontsConfig, FontsOptions } from './fonts.types.js';
2
+ export declare const defaultFontFamilies: FontDefinition[];
3
+ export declare const fontsDefaults: {
4
+ families: FontDefinition[];
5
+ display: GoogleFontDisplay;
6
+ preconnect: boolean;
7
+ };
8
+ export declare function buildGoogleFontsUrl(fonts: FontDefinition[], display?: GoogleFontDisplay): string;
9
+ export declare function buildFontFamily(font: FontDefinition): string;
10
+ export declare function buildLocalFontFaceCss(fonts: FontDefinition[], defaultDisplay?: GoogleFontDisplay): string;
11
+ export declare function buildFontVariablesCss(fonts: FontDefinition[]): string;
12
+ export declare function resolveFontsOptions(config?: FontsConfig): FontsOptions | false;