@widgetic/editor 4.0.1 → 4.0.2
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/components/WidgetPreview.svelte +2 -2
- package/dist/components/editors/PropsEditor.svelte +140 -83
- package/dist/components/ic/BrowserInputController.svelte +12 -5
- package/dist/components/ic/FontInputController.svelte +97 -61
- package/dist/components/ic/FontInputController.svelte.d.ts +1 -1
- package/dist/components/ic/TextAreaInputController.svelte +190 -62
- package/dist/components/ic/TextAreaInputController.svelte.d.ts +1 -0
- package/dist/components/ic/TextAreaPreviewInputController.svelte +80 -78
- package/dist/config.d.ts +6 -0
- package/dist/config.js +19 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/utils/FontManager.d.ts +2 -0
- package/dist/utils/FontManager.js +21 -2
- package/package.json +1 -1
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import { IconArrowsDiagonal, IconArrowsMaximize, IconX, IconBrush, IconBold, IconItalic, IconUnderline, IconLink } from '@widgetic/design-system/icons';
|
|
2
|
+
import { Portal } from '@widgetic/design-system/components';
|
|
3
|
+
import { IconArrowsDiagonal } from '@widgetic/design-system/icons';
|
|
5
4
|
import { onMount, onDestroy } from 'svelte';
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
import { InputControllerHeader, TextAreaInputController } from './index';
|
|
6
|
+
import { TextAreaInputController } from './index';
|
|
9
7
|
import type { InputControllerConfig } from './index';
|
|
10
8
|
|
|
11
9
|
/**
|
|
@@ -50,8 +48,15 @@
|
|
|
50
48
|
function createPreviewText(text: string) {
|
|
51
49
|
if (!text) return '';
|
|
52
50
|
|
|
51
|
+
let html = text;
|
|
52
|
+
if (!html.includes('<') && html.includes('<')) {
|
|
53
|
+
const decoded = document.createElement('textarea');
|
|
54
|
+
decoded.innerHTML = html;
|
|
55
|
+
html = decoded.value;
|
|
56
|
+
}
|
|
57
|
+
|
|
53
58
|
const parser = new DOMParser();
|
|
54
|
-
const doc = parser.parseFromString(
|
|
59
|
+
const doc = parser.parseFromString(html, 'text/html');
|
|
55
60
|
|
|
56
61
|
function processNode(node: Node): string {
|
|
57
62
|
if (node.nodeType === Node.TEXT_NODE) {
|
|
@@ -60,59 +65,59 @@
|
|
|
60
65
|
|
|
61
66
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
62
67
|
const element = node as Element;
|
|
63
|
-
|
|
68
|
+
let tag = element.tagName.toLowerCase();
|
|
64
69
|
const attributes: Record<string, string> = {};
|
|
70
|
+
const children = Array.from(node.childNodes)
|
|
71
|
+
.map(child => processNode(child))
|
|
72
|
+
.join('');
|
|
73
|
+
|
|
74
|
+
// Flatten block wrappers so marks render inside the compact clamp box
|
|
75
|
+
if (tag === 'p' || tag === 'div' || tag === 'br') {
|
|
76
|
+
return tag === 'br' ? ' ' : `${children} `;
|
|
77
|
+
}
|
|
65
78
|
|
|
66
|
-
// Preserve all style attributes
|
|
67
79
|
const style = element.getAttribute('style');
|
|
68
80
|
if (style) {
|
|
69
|
-
// Extract color value from style
|
|
70
81
|
const colorMatch = style.match(/color:\s*([^;]+)/);
|
|
71
82
|
if (colorMatch) {
|
|
72
|
-
|
|
73
|
-
// Set the color as a CSS variable
|
|
74
|
-
attributes.style = `--color: ${color}; ${style}`;
|
|
83
|
+
attributes.style = `--color: ${colorMatch[1]}; ${style}`;
|
|
75
84
|
} else {
|
|
76
85
|
attributes.style = style;
|
|
77
86
|
}
|
|
78
87
|
}
|
|
79
88
|
|
|
80
|
-
// Preserve data-text-style attribute
|
|
81
89
|
const textStyle = element.getAttribute('data-text-style');
|
|
82
90
|
if (textStyle) {
|
|
83
91
|
attributes['data-text-style'] = textStyle;
|
|
84
|
-
// Add preserve-color class
|
|
85
92
|
attributes.class = (attributes.class || '') + ' preserve-color';
|
|
86
93
|
}
|
|
87
94
|
|
|
88
|
-
// Preserve heading classes
|
|
89
95
|
if (tag.match(/^h[1-4]$/)) {
|
|
96
|
+
tag = 'span';
|
|
90
97
|
attributes.class = (attributes.class || '') + ' tiptap-heading';
|
|
91
98
|
}
|
|
92
99
|
|
|
93
|
-
// Build start tag with attributes
|
|
94
100
|
let startTag = `<${tag}`;
|
|
95
|
-
for (const [key,
|
|
96
|
-
startTag += ` ${key}="${
|
|
101
|
+
for (const [key, attrValue] of Object.entries(attributes)) {
|
|
102
|
+
startTag += ` ${key}="${attrValue.trim()}"`;
|
|
97
103
|
}
|
|
98
104
|
startTag += '>';
|
|
99
105
|
|
|
100
|
-
// Process child nodes
|
|
101
|
-
const children = Array.from(node.childNodes)
|
|
102
|
-
.map(child => processNode(child))
|
|
103
|
-
.join('');
|
|
104
|
-
|
|
105
106
|
return `${startTag}${children}</${tag}>`;
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
return '';
|
|
109
110
|
}
|
|
110
111
|
|
|
111
|
-
|
|
112
|
+
return Array.from(doc.body.childNodes)
|
|
112
113
|
.map(node => processNode(node))
|
|
113
|
-
.join('')
|
|
114
|
+
.join('')
|
|
115
|
+
.trim();
|
|
116
|
+
}
|
|
114
117
|
|
|
115
|
-
|
|
118
|
+
function handleFullEditorChange(html: string) {
|
|
119
|
+
value = html;
|
|
120
|
+
if (typeof onChange === 'function') onChange(html);
|
|
116
121
|
}
|
|
117
122
|
|
|
118
123
|
function handleClick() {
|
|
@@ -139,6 +144,10 @@
|
|
|
139
144
|
}
|
|
140
145
|
}
|
|
141
146
|
|
|
147
|
+
$: if (value) {
|
|
148
|
+
console.log('[TextAreaPreview] compact preview html', value.slice(0, 180));
|
|
149
|
+
}
|
|
150
|
+
|
|
142
151
|
function handlePortalMouseEnter() {
|
|
143
152
|
// console.log('Portal mouse entered');
|
|
144
153
|
isPortalHovered = true;
|
|
@@ -211,42 +220,31 @@
|
|
|
211
220
|
}
|
|
212
221
|
</script>
|
|
213
222
|
|
|
214
|
-
<div class="text-area-preview-input-controller
|
|
215
|
-
<div class="header-container flex-1 min-w-0">
|
|
216
|
-
<InputControllerHeader {label} />
|
|
217
|
-
</div>
|
|
218
|
-
|
|
223
|
+
<div class="text-area-preview-input-controller w-full min-w-0 overflow-hidden widgetic-colors">
|
|
219
224
|
<div
|
|
220
|
-
class="text-area-preview-main-container w-
|
|
225
|
+
class="text-area-preview-main-container w-full min-w-0 overflow-hidden"
|
|
221
226
|
>
|
|
222
227
|
<div
|
|
223
|
-
class="h-[92px] w-full border border-grey2
|
|
228
|
+
class="textarea-ic-preview-box relative h-[92px] w-full min-w-0 border border-grey2 bg-grey1 transition-colors duration-200 hover:border-forest-green cursor-pointer group overflow-hidden"
|
|
224
229
|
style="border-radius: var(--radius-large);"
|
|
225
230
|
on:click={handleClick}
|
|
226
231
|
on:keydown={(e) => e.key === 'Enter' && handleClick()}
|
|
227
232
|
role="button"
|
|
228
233
|
tabindex="0"
|
|
229
234
|
>
|
|
230
|
-
<div class="
|
|
235
|
+
<div class="textarea-ic-preview-copy min-w-0 overflow-hidden pr-10 pt-[13px] pb-4 pl-4">
|
|
231
236
|
<div
|
|
232
|
-
class="
|
|
237
|
+
class="textarea-ic-preview-html break-words text-grey7 text-sm leading-5"
|
|
238
|
+
class:text-grey3={isShowingPlaceholder}
|
|
233
239
|
>
|
|
234
|
-
|
|
235
|
-
<div
|
|
236
|
-
class="subtext line-clamp-3 break-words text-ellipsis prose-sm w-[129px] text-grey7"
|
|
237
|
-
class:text-grey3={isShowingPlaceholder}
|
|
238
|
-
style="display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden;"
|
|
239
|
-
>
|
|
240
|
-
{@html previewText}
|
|
241
|
-
</div>
|
|
242
|
-
</div>
|
|
243
|
-
<div class="absolute top-[14px] right-[12px]">
|
|
244
|
-
<IconArrowsDiagonal
|
|
245
|
-
class="w-5 h-5 text-grey5 group-hover:text-grey7 transition-colors duration-200"
|
|
246
|
-
/>
|
|
247
|
-
</div>
|
|
240
|
+
{@html previewText}
|
|
248
241
|
</div>
|
|
249
242
|
</div>
|
|
243
|
+
<div class="textarea-ic-expand-icon absolute top-[14px] right-[12px]">
|
|
244
|
+
<IconArrowsDiagonal
|
|
245
|
+
class="w-5 h-5 text-grey5 group-hover:text-grey7 transition-colors duration-200"
|
|
246
|
+
/>
|
|
247
|
+
</div>
|
|
250
248
|
</div>
|
|
251
249
|
</div>
|
|
252
250
|
|
|
@@ -280,24 +278,14 @@
|
|
|
280
278
|
style="border-radius: var(--radius-large);"
|
|
281
279
|
></div>
|
|
282
280
|
|
|
283
|
-
<Button
|
|
284
|
-
variant="ghost"
|
|
285
|
-
class="absolute z-[999] p-0 hover:bg-transparent h-[32px] w-[32px] top-[10px] right-[10px]"
|
|
286
|
-
onclick={handleClose}
|
|
287
|
-
>
|
|
288
|
-
<IconX
|
|
289
|
-
class="w-[24px] h-[24px] text-grey5 hover:text-grey7 transition-colors duration-200"
|
|
290
|
-
style="min-width: 24px; min-height: 24px;"
|
|
291
|
-
/>
|
|
292
|
-
</Button>
|
|
293
|
-
|
|
294
281
|
<TextAreaInputController
|
|
295
282
|
class="text-area w-full h-full transition-colors duration-200 {isPortalHovered ? 'border-solid !border-forest-green' : ''}"
|
|
296
283
|
label={label}
|
|
297
|
-
value
|
|
298
|
-
onChange={
|
|
284
|
+
bind:value
|
|
285
|
+
onChange={handleFullEditorChange}
|
|
299
286
|
config={config}
|
|
300
287
|
description={description}
|
|
288
|
+
onClose={handleClose}
|
|
301
289
|
/>
|
|
302
290
|
</div>
|
|
303
291
|
</Portal>
|
|
@@ -318,7 +306,7 @@
|
|
|
318
306
|
|
|
319
307
|
|
|
320
308
|
/* Heading styles matching tiptap editor */
|
|
321
|
-
:global(.
|
|
309
|
+
:global(.textarea-ic-preview-html h1.tiptap-heading) {
|
|
322
310
|
font-size: 2em !important;
|
|
323
311
|
font-weight: 600 !important;
|
|
324
312
|
line-height: 1.2 !important;
|
|
@@ -327,7 +315,7 @@
|
|
|
327
315
|
color: inherit !important;
|
|
328
316
|
}
|
|
329
317
|
|
|
330
|
-
:global(.
|
|
318
|
+
:global(.textarea-ic-preview-html h2.tiptap-heading) {
|
|
331
319
|
font-size: 1.5em !important;
|
|
332
320
|
font-weight: 600 !important;
|
|
333
321
|
line-height: 1.3 !important;
|
|
@@ -336,7 +324,7 @@
|
|
|
336
324
|
color: inherit !important;
|
|
337
325
|
}
|
|
338
326
|
|
|
339
|
-
:global(.
|
|
327
|
+
:global(.textarea-ic-preview-html h3.tiptap-heading) {
|
|
340
328
|
font-size: 1.17em !important;
|
|
341
329
|
font-weight: 600 !important;
|
|
342
330
|
line-height: 1.3 !important;
|
|
@@ -345,7 +333,7 @@
|
|
|
345
333
|
color: inherit !important;
|
|
346
334
|
}
|
|
347
335
|
|
|
348
|
-
:global(.
|
|
336
|
+
:global(.textarea-ic-preview-html h4.tiptap-heading) {
|
|
349
337
|
font-size: 1em !important;
|
|
350
338
|
font-weight: 600 !important;
|
|
351
339
|
line-height: 1.4 !important;
|
|
@@ -354,36 +342,50 @@
|
|
|
354
342
|
color: inherit !important;
|
|
355
343
|
}
|
|
356
344
|
|
|
345
|
+
:global(.textarea-ic-preview-html) {
|
|
346
|
+
display: -webkit-box;
|
|
347
|
+
-webkit-box-orient: vertical;
|
|
348
|
+
-webkit-line-clamp: 3;
|
|
349
|
+
line-clamp: 3;
|
|
350
|
+
overflow: hidden;
|
|
351
|
+
}
|
|
352
|
+
|
|
357
353
|
/* Color preservation */
|
|
358
|
-
:global(.
|
|
354
|
+
:global(.textarea-ic-preview-html [style*="color"]) {
|
|
359
355
|
color: var(--color) !important;
|
|
360
356
|
}
|
|
361
357
|
|
|
362
|
-
/* Text formatting */
|
|
363
|
-
:global(.
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
358
|
+
/* Text formatting — {@html} children need :global; include b/i as well as strong/em/u */
|
|
359
|
+
:global(.textarea-ic-preview-html :is(strong, b, [style*="font-weight"])) {
|
|
360
|
+
font-weight: 700 !important;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
:global(.textarea-ic-preview-html :is(em, i)) {
|
|
364
|
+
font-style: italic !important;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
:global(.textarea-ic-preview-html :is(u, [style*="text-decoration: underline"])) {
|
|
368
|
+
text-decoration: underline !important;
|
|
367
369
|
}
|
|
368
370
|
|
|
369
|
-
:global(.
|
|
370
|
-
:global(.
|
|
371
|
-
:global(.
|
|
371
|
+
:global(.textarea-ic-preview-html strong[style*="color"]),
|
|
372
|
+
:global(.textarea-ic-preview-html em[style*="color"]),
|
|
373
|
+
:global(.textarea-ic-preview-html u[style*="color"]) {
|
|
372
374
|
color: var(--color) !important;
|
|
373
375
|
}
|
|
374
376
|
|
|
375
377
|
/* Add specific color inheritance for data-text-style */
|
|
376
|
-
:global(.
|
|
378
|
+
:global(.textarea-ic-preview-html [data-text-style*="color"]) {
|
|
377
379
|
color: var(--color) !important;
|
|
378
380
|
}
|
|
379
381
|
|
|
380
382
|
/* Ensure all text elements can inherit colors */
|
|
381
|
-
:global(.
|
|
383
|
+
:global(.textarea-ic-preview-html *) {
|
|
382
384
|
color: inherit;
|
|
383
385
|
}
|
|
384
386
|
|
|
385
387
|
/* Ensure paragraphs have no margins */
|
|
386
|
-
:global(.
|
|
388
|
+
:global(.textarea-ic-preview-html p) {
|
|
387
389
|
margin-top: 0 !important;
|
|
388
390
|
margin-bottom: 0 !important;
|
|
389
391
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
interface Config {
|
|
2
2
|
GOOGLE_FONTS_API_KEY: string;
|
|
3
3
|
}
|
|
4
|
+
export declare function setGoogleFontsApiKey(key: string): void;
|
|
5
|
+
/**
|
|
6
|
+
* Google Fonts Web Fonts API key.
|
|
7
|
+
* Order: runtime setter → PUBLIC_GOOGLE_FONTS_API_KEY → VITE_GOOGLE_FONTS_API_KEY.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getGoogleFontsKey(): string;
|
|
4
10
|
export declare const config: Config;
|
|
5
11
|
export {};
|
package/dist/config.js
CHANGED
|
@@ -1,19 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Set by the consuming app (widget-creator / site) so the published
|
|
3
|
+
* `@widgetic/editor` package never bakes a key into dist at pack time.
|
|
4
|
+
* Vite still inlines PUBLIC_/VITE_ keys when the consumer bundles this source.
|
|
5
|
+
*/
|
|
6
|
+
let runtimeGoogleFontsApiKey = '';
|
|
7
|
+
export function setGoogleFontsApiKey(key) {
|
|
8
|
+
runtimeGoogleFontsApiKey = String(key || '').trim();
|
|
9
|
+
}
|
|
1
10
|
/**
|
|
2
11
|
* Google Fonts Web Fonts API key.
|
|
3
|
-
*
|
|
4
|
-
* Avoids $env/static/public import so the library works when embedded in site/creator.
|
|
12
|
+
* Order: runtime setter → PUBLIC_GOOGLE_FONTS_API_KEY → VITE_GOOGLE_FONTS_API_KEY.
|
|
5
13
|
*/
|
|
6
|
-
function getGoogleFontsKey() {
|
|
14
|
+
export function getGoogleFontsKey() {
|
|
15
|
+
if (runtimeGoogleFontsApiKey)
|
|
16
|
+
return runtimeGoogleFontsApiKey;
|
|
7
17
|
if (typeof import.meta !== 'undefined' && import.meta.env) {
|
|
8
|
-
const fromVite = import.meta.env.VITE_GOOGLE_FONTS_API_KEY;
|
|
9
|
-
if (fromVite && String(fromVite).trim())
|
|
10
|
-
return String(fromVite).trim();
|
|
11
18
|
const fromPublicMeta = import.meta.env.PUBLIC_GOOGLE_FONTS_API_KEY;
|
|
12
19
|
if (fromPublicMeta && String(fromPublicMeta).trim())
|
|
13
20
|
return String(fromPublicMeta).trim();
|
|
21
|
+
const fromVite = import.meta.env.VITE_GOOGLE_FONTS_API_KEY;
|
|
22
|
+
if (fromVite && String(fromVite).trim())
|
|
23
|
+
return String(fromVite).trim();
|
|
14
24
|
}
|
|
15
25
|
return '';
|
|
16
26
|
}
|
|
17
27
|
export const config = {
|
|
18
|
-
GOOGLE_FONTS_API_KEY
|
|
28
|
+
get GOOGLE_FONTS_API_KEY() {
|
|
29
|
+
return getGoogleFontsKey();
|
|
30
|
+
},
|
|
19
31
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { default as PropsEditor } from './components/editors/PropsEditor.svelte';
|
|
2
2
|
export { default as WidgetPreview } from './components/WidgetPreview.svelte';
|
|
3
3
|
export { setFileBrowserUploadConfig } from '@widgetic/file-browser';
|
|
4
|
+
export { setGoogleFontsApiKey, getGoogleFontsKey } from './config';
|
|
4
5
|
export { default as EmbedEditor } from './components/editors/EmbedEditor.svelte';
|
package/dist/index.js
CHANGED
|
@@ -4,5 +4,6 @@
|
|
|
4
4
|
export { default as PropsEditor } from './components/editors/PropsEditor.svelte';
|
|
5
5
|
export { default as WidgetPreview } from './components/WidgetPreview.svelte';
|
|
6
6
|
export { setFileBrowserUploadConfig } from '@widgetic/file-browser';
|
|
7
|
+
export { setGoogleFontsApiKey, getGoogleFontsKey } from './config';
|
|
7
8
|
// Embed Editor
|
|
8
9
|
export { default as EmbedEditor } from './components/editors/EmbedEditor.svelte';
|
|
@@ -70,6 +70,8 @@ declare class FontManager {
|
|
|
70
70
|
private static forbiddenApiKeyWarned;
|
|
71
71
|
private static badRequestApiKeyWarned;
|
|
72
72
|
private catalogFetchPromise;
|
|
73
|
+
/** Popular Google families shown when the Web Fonts API key is missing or blocked. */
|
|
74
|
+
private static getFallbackCatalog;
|
|
73
75
|
private hasGoogleFontsApiKey;
|
|
74
76
|
private warnMissingApiKey;
|
|
75
77
|
private warnForbiddenApiKey;
|
|
@@ -112,6 +112,25 @@ class FontManager {
|
|
|
112
112
|
static forbiddenApiKeyWarned = false;
|
|
113
113
|
static badRequestApiKeyWarned = false;
|
|
114
114
|
catalogFetchPromise = null;
|
|
115
|
+
/** Popular Google families shown when the Web Fonts API key is missing or blocked. */
|
|
116
|
+
static getFallbackCatalog() {
|
|
117
|
+
const sans = ['Inter', 'Roboto', 'Open Sans', 'Poppins', 'Lato', 'Montserrat', 'Nunito', 'Source Sans 3', 'Work Sans', 'DM Sans'];
|
|
118
|
+
const serif = ['Playfair Display', 'Merriweather', 'Libre Baskerville', 'Lora', 'Cormorant Garamond'];
|
|
119
|
+
const script = ['Dancing Script', 'Pacifico', 'Great Vibes'];
|
|
120
|
+
const other = ['Oswald', 'Raleway', 'Space Grotesk'];
|
|
121
|
+
const pack = (families, category) => families.map((family) => ({
|
|
122
|
+
family,
|
|
123
|
+
category,
|
|
124
|
+
isCustom: false,
|
|
125
|
+
variants: ['regular', '500', '600', '700'],
|
|
126
|
+
}));
|
|
127
|
+
return [
|
|
128
|
+
...pack(sans, 'Sans Serif'),
|
|
129
|
+
...pack(serif, 'Serif'),
|
|
130
|
+
...pack(script, 'Script'),
|
|
131
|
+
...pack(other, 'Other'),
|
|
132
|
+
];
|
|
133
|
+
}
|
|
115
134
|
hasGoogleFontsApiKey() {
|
|
116
135
|
return Boolean(this.apiKey?.trim());
|
|
117
136
|
}
|
|
@@ -621,7 +640,7 @@ class FontManager {
|
|
|
621
640
|
// Add custom fonts from fonts.json
|
|
622
641
|
const customFonts = await this.loadCustomFonts();
|
|
623
642
|
const catalog = await this.fetchGoogleFontsCatalog();
|
|
624
|
-
const googleFonts = catalog
|
|
643
|
+
const googleFonts = (catalog && catalog.length > 0) ? catalog : FontManager.getFallbackCatalog();
|
|
625
644
|
// Combine all fonts. A full Google catalog means the list is already
|
|
626
645
|
// complete; otherwise keep the 'initial' stage so a later fetch can
|
|
627
646
|
// still upgrade it.
|
|
@@ -638,7 +657,7 @@ class FontManager {
|
|
|
638
657
|
}
|
|
639
658
|
catch (error) {
|
|
640
659
|
console.error('Error getting initial fonts:', error);
|
|
641
|
-
return
|
|
660
|
+
return FontManager.getFallbackCatalog();
|
|
642
661
|
}
|
|
643
662
|
}
|
|
644
663
|
/**
|