@flexiui/svelte-rich-text 0.0.32 → 0.0.34

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.
@@ -4,7 +4,7 @@
4
4
  import { TableKit } from "@tiptap/extension-table";
5
5
  import { CellSelection } from "prosemirror-tables";
6
6
 
7
- import { NodeLineHeight } from './extensions/NodeLineHeight';
7
+ import { NodeLineHeight } from "./extensions/NodeLineHeight";
8
8
  import { MediaGridExtension } from "./extensions/MediaGrid/MediaGrid";
9
9
  import { MediaGridItemExtension } from "./extensions/MediaGrid/MediaGridItem";
10
10
  import {
@@ -50,6 +50,21 @@
50
50
  onSelectionUpdate?: (params: any) => void;
51
51
  onPaste?: (params: any) => void;
52
52
  };
53
+ config?: {
54
+ editorBgColor?: string;
55
+ editorRadius?: string;
56
+ toolbarStickyPosition?: number;
57
+ toolbarZIndex?: number;
58
+ toolbarBgColor?: string;
59
+ toolbarPadding?: string;
60
+ toolbarGap?: string;
61
+ docMaxWidth?: string;
62
+ docPadding?: string;
63
+ docBg?: string;
64
+ docMarginInline?: string;
65
+ docMarginBlock?: string;
66
+ docRadius?: string;
67
+ };
53
68
  }
54
69
 
55
70
  let {
@@ -72,9 +87,30 @@
72
87
  onSelectionUpdate: () => {},
73
88
  onPaste: () => {},
74
89
  },
90
+ config,
75
91
  }: Props = $props();
76
92
 
77
93
  let editor = $state() as Readable<Editor>;
94
+ const defaultEditorConfig = {
95
+ editorBgColor: "transparent",
96
+ editorRadius: "12px",
97
+ toolbarStickyPosition: 0,
98
+ toolbarBgColor: "#242424",
99
+ toolbarZIndex: 10,
100
+ toolbarPadding: "8px",
101
+ toolbarGap: "5px",
102
+ docMaxWidth: "1024px",
103
+ docPadding: "2rem",
104
+ docBg: "transparent",
105
+ docMarginInline: "auto",
106
+ docMarginBlock: "2rem",
107
+ docRadius: "0",
108
+ };
109
+
110
+ let editorConfig = $state({
111
+ ...defaultEditorConfig,
112
+ ...(config ?? {}),
113
+ });
78
114
 
79
115
  const extensions = [
80
116
  // Color.configure({ types: [TextStyle.name, ListItem.name] }),
@@ -244,61 +280,67 @@
244
280
  onMount(() => {
245
281
  editor = createEditor({
246
282
  extensions,
247
- content,
283
+ content:
284
+ '<h2 style="line-height: 1;"><span style="font-size: 22px;">Aquí tens algunes expressions bàsiques en català. Llegeix-les, escolta-les i repeteix-les.</span></h2><table class="fl-table-editable" style="min-width: 75px;"><colgroup><col style="min-width: 25px;"><col style="min-width: 25px;"><col style="min-width: 25px;"></colgroup><tbody><tr><th class="fl-cell-editable" colspan="1" rowspan="1"><p></p></th><th class="fl-cell-editable" colspan="1" rowspan="1"><p></p></th><th class="fl-cell-editable" colspan="1" rowspan="1"><p></p></th></tr><tr><td class="fl-cell-editable" colspan="1" rowspan="1"><p></p></td><td class="fl-cell-editable" colspan="1" rowspan="1"><p></p></td><td class="fl-cell-editable" colspan="1" rowspan="1"><p></p></td></tr><tr><td class="fl-cell-editable" colspan="1" rowspan="1"><p></p></td><td class="fl-cell-editable" colspan="1" rowspan="1"><p></p></td><td class="fl-cell-editable" colspan="1" rowspan="1"><p></p></td></tr></tbody></table><ul><li><p style="line-height: 1.5;">Hola!</p></li><li><p style="line-height: 1.5;">Com estàs?</p></li><li><p style="line-height: 1.5;">Com et dius?</p></li><li><p style="line-height: 1.5;">Com es diu agua en català?</p></li><li><p style="line-height: 1.5;">Què vol dir rentadora?</p></li><li><p style="line-height: 1.5;">Com s\'escriu bolígraf?</p></li><li><p style="line-height: 1.5;">Com es pronuncia aquesta paraula?</p></li><li><p style="line-height: 1.5;">M\'ho pots repetir, si us plau?</p></li><li><p style="line-height: 1.5;">A poc a poc, si us plau.</p></li><li><p style="line-height: 1.5;">Com?</p></li><li><p style="line-height: 1.5;">No t\'entenc.</p></li><li><p style="line-height: 1.5;">Gràcies.</p></li><li><p style="line-height: 1.5;">De res.</p></li><li><p style="line-height: 1.5;">Perdona.</p></li><li><p style="line-height: 1.5;">I tant!</p></li><li><p style="line-height: 1.5;">Edited</p></li></ul><pre><code>const name = "Alex";\nconsole.log(`Hello ${name}`)</code></pre><media-grid-component class="fl-media-grid" data-cols="3" data-gap="1.5rem" data-show-indicator="false" data-indicator-type="numeric"><div data-type="grid-item" class="fl-grid-item"><img src="https://img2.rtve.es/n/16848600?w=1600"></div><div data-type="grid-item" class="fl-grid-item"><img src="https://img2.rtve.es/n/16848600?w=1600"></div><div data-type="grid-item" class="fl-grid-item"><img src="https://img2.rtve.es/n/16848600?w=1600"></div></media-grid-component><audio-player class="audio-player" src="https://pub-503cb197f1134814ac02f257b9cde1c1.r2.dev/uploads/audio-festa-major.mp3" controls="true" data-color-play="#333" data-color-bar="#888" data-max-width="100%" data-id="fl-audio-4i336em9v43"></audio-player><blockquote><p>- Hola! Com estàs?<br>- Molt bé, i tu?</p></blockquote><audio-player class="audio-player" src="https://pub-503cb197f1134814ac02f257b9cde1c1.r2.dev/uploads/dictat-buscar-habitatge.mp3" controls="true" data-color-play="#333" data-color-bar="#888" data-max-width="100%" data-id="fl-audio-4i336em9v43"></audio-player><blockquote><p>- Com et dius?<br>- Em dic Sara, i tu?</p></blockquote><audio-player class="audio-player" src="https://pub-503cb197f1134814ac02f257b9cde1c1.r2.dev/uploads/dictat-buscar-habitatge.mp3" controls="true" data-color-play="#333" data-color-bar="#888" data-max-width="100%" data-id="fl-audio-4i336em9v43"></audio-player><blockquote><p>Com es diu mesa en català?</p></blockquote><audio-player class="audio-player" src="https://pub-503cb197f1134814ac02f257b9cde1c1.r2.dev/uploads/dictat-buscar-habitatge.mp3" controls="true" data-color-play="#333" data-color-bar="#888" data-max-width="100%" data-id="fl-audio-4i336em9v43"></audio-player><blockquote><p>Què vol dir rentadora?</p></blockquote><audio-player class="audio-player" src="https://pub-503cb197f1134814ac02f257b9cde1c1.r2.dev/uploads/dictat-buscar-habitatge.mp3" controls="true" data-color-play="#333" data-color-bar="#888" data-max-width="100%" data-id="fl-audio-4i336em9v43"></audio-player><blockquote><p>Com s\'escriu &nbsp;bolígraf?</p></blockquote><audio-player class="audio-player" src="https://pub-503cb197f1134814ac02f257b9cde1c1.r2.dev/uploads/dictat-buscar-habitatge.mp3" controls="true" data-color-play="#333" data-color-bar="#888" data-max-width="100%" data-id="fl-audio-4i336em9v43"></audio-player><blockquote><p>Com es pronuncia això?</p></blockquote><audio-player class="audio-player" src="https://pub-503cb197f1134814ac02f257b9cde1c1.r2.dev/uploads/dictat-buscar-habitatge.mp3" controls="true" data-color-play="#333" data-color-bar="#888" data-max-width="100%" data-id="fl-audio-4i336em9v43"></audio-player><blockquote><p>- No t\'entenc. A poc a poc, si us plau.<br>- Ostres, perdona.</p></blockquote><audio-player class="audio-player" src="https://pub-503cb197f1134814ac02f257b9cde1c1.r2.dev/uploads/dictat-buscar-habitatge.mp3" controls="true" data-color-play="#333" data-color-bar="#888" data-max-width="100%" data-id="fl-audio-4i336em9v43"></audio-player><blockquote><p>- Com? M\'ho pots repetir, si us plau?<br>- Sí, i tant!</p></blockquote><audio-player class="audio-player" src="https://pub-503cb197f1134814ac02f257b9cde1c1.r2.dev/uploads/dictat-buscar-habitatge.mp3" controls="true" data-color-play="#333" data-color-bar="#888" data-max-width="100%" data-id="fl-audio-4i336em9v43"></audio-player><blockquote><p>- Gràcies.<br>- De res!</p></blockquote>',
248
285
  editorProps: {
249
286
  attributes: {
250
- class: "fl-rich-text-content-eee",
287
+ class: "fl-rich-text-content-doc",
251
288
  },
252
289
  handleKeyDown: (view, event) => {
253
- if (event.key === "Enter" && !event.ctrlKey) {
254
- enterPressed = true;
255
-
256
- setTimeout(() => {
257
- enterPressed = false;
258
- const { from } = view.state.selection;
259
-
260
- // Obtener el nodo de ProseMirror en la posición actual
261
- const pos = view.state.doc.resolve(from);
262
- const nodeBefore = pos.node(pos.depth);
263
- const parentNode = pos.node(pos.depth - 1);
264
-
265
- // console.log("Node type:", nodeBefore.type.name);
266
- // console.log("Parent node type:", parentNode?.type.name);
267
-
268
- // Solo ejecutar si estamos en un párrafo Y el padre no es una lista
269
- const isInList = parentNode?.type.name === "listItem" ||
270
- parentNode?.type.name === "bulletList" ||
271
- parentNode?.type.name === "orderedList";
272
-
273
- if (nodeBefore.type.name === "paragraph" && !isInList) {
274
- const domAtPos = view.domAtPos(from);
275
- let element = domAtPos.node;
276
-
277
- if (element.nodeType === Node.TEXT_NODE) {
278
- element = element.parentElement;
279
- }
280
-
281
- if (element instanceof HTMLElement) {
282
- const computedSize = window.getComputedStyle(element).fontSize;
283
- const computedLineHeight = window.getComputedStyle(element).lineHeight;
284
- // console.log({ computedSize, computedLineHeight });
285
-
286
- const lineHeightPx = parseFloat(computedLineHeight.replace("px", ""))
287
- const fontSizePx = parseFloat(computedSize.replace("px", ""))
288
-
289
- const lineHeightUnitless = lineHeightPx / fontSizePx;
290
-
291
- // console.log(lineHeightUnitless.toFixed(2)); // ej: "x.xx"
292
-
293
- fontSize = Math.round(Number(computedSize.replace("px", "")));
294
- $editor.chain().focus().unsetFontSize().run();
295
-
296
- $editor.chain().focus().unsetNodeLineHeight().run();
297
- }
298
- }
299
- }, 200);
300
- }
301
- },
290
+ if (event.key === "Enter" && !event.ctrlKey) {
291
+ enterPressed = true;
292
+
293
+ setTimeout(() => {
294
+ enterPressed = false;
295
+ const { from } = view.state.selection;
296
+
297
+ // Obtener el nodo de ProseMirror en la posición actual
298
+ const pos = view.state.doc.resolve(from);
299
+ const nodeBefore = pos.node(pos.depth);
300
+ const parentNode = pos.node(pos.depth - 1);
301
+
302
+ // console.log("Node type:", nodeBefore.type.name);
303
+ // console.log("Parent node type:", parentNode?.type.name);
304
+
305
+ // Solo ejecutar si estamos en un párrafo Y el padre no es una lista
306
+ const isInList =
307
+ parentNode?.type.name === "listItem" ||
308
+ parentNode?.type.name === "bulletList" ||
309
+ parentNode?.type.name === "orderedList";
310
+
311
+ if (nodeBefore.type.name === "paragraph" && !isInList) {
312
+ const domAtPos = view.domAtPos(from);
313
+ let element = domAtPos.node;
314
+
315
+ if (element.nodeType === Node.TEXT_NODE) {
316
+ element = element.parentElement;
317
+ }
318
+
319
+ if (element instanceof HTMLElement) {
320
+ const computedSize =
321
+ window.getComputedStyle(element).fontSize;
322
+ const computedLineHeight =
323
+ window.getComputedStyle(element).lineHeight;
324
+ // console.log({ computedSize, computedLineHeight });
325
+
326
+ const lineHeightPx = parseFloat(
327
+ computedLineHeight.replace("px", "")
328
+ );
329
+ const fontSizePx = parseFloat(computedSize.replace("px", ""));
330
+
331
+ const lineHeightUnitless = lineHeightPx / fontSizePx;
332
+
333
+ // console.log(lineHeightUnitless.toFixed(2)); // ej: "x.xx"
334
+
335
+ fontSize = Math.round(Number(computedSize.replace("px", "")));
336
+ $editor.chain().focus().unsetFontSize().run();
337
+
338
+ $editor.chain().focus().unsetNodeLineHeight().run();
339
+ }
340
+ }
341
+ }, 200);
342
+ }
343
+ },
302
344
  },
303
345
  onTransaction: ({ editor, transaction }) => {
304
346
  editorEvents.onTransaction({ editor, transaction });
@@ -323,11 +365,12 @@
323
365
  // Obtener el font-size computado
324
366
  if (element instanceof HTMLElement) {
325
367
  const computedSize = window.getComputedStyle(element).fontSize;
326
- const computedLineHeight = window.getComputedStyle(element).lineHeight;
368
+ const computedLineHeight =
369
+ window.getComputedStyle(element).lineHeight;
327
370
  // console.log("Get element font size:", computedSize);
328
371
  // console.log("Get element line height:", computedLineHeight);
329
- const lineHeightPx = parseFloat(computedLineHeight.replace("px", ""))
330
- const fontSizePx = parseFloat(computedSize.replace("px", ""))
372
+ const lineHeightPx = parseFloat(computedLineHeight.replace("px", ""));
373
+ const fontSizePx = parseFloat(computedSize.replace("px", ""));
331
374
  const lineHeightUnitless = lineHeightPx / fontSizePx;
332
375
 
333
376
  // console.log(lineHeightUnitless.toFixed(2)); // ej: "x.xx"
@@ -349,8 +392,6 @@
349
392
  } else {
350
393
  lineHeight = Number(lineHeightUnitless.toFixed(2));
351
394
  }
352
-
353
-
354
395
  }
355
396
  },
356
397
 
@@ -499,9 +540,7 @@
499
540
  }
500
541
 
501
542
  function handleRangeInput(e: any) {
502
-
503
- $editor.commands.setNodeLineHeight(lineHeight.toString())
504
-
543
+ $editor.commands.setNodeLineHeight(lineHeight.toString());
505
544
  }
506
545
 
507
546
  function addAudio() {
@@ -511,37 +550,54 @@
511
550
  alert("Please enter a valid URL");
512
551
  return;
513
552
  }
514
-
515
- $editor.chain().focus().setAudio({ src, controls: true }).run()
516
-
553
+
554
+ $editor.chain().focus().setAudio({ src, controls: true }).run();
517
555
  }
518
556
 
519
557
  function addImage() {
520
- const previousSrc = $editor.getAttributes("image").src;
558
+ const previousSrc = $editor.getAttributes("image").src;
521
559
  const src = window.prompt("Enter the URL of the image:", previousSrc);
522
560
 
523
561
  if (!src) {
524
562
  alert("Please enter a valid URL");
525
563
  return;
526
564
  }
527
-
528
- $editor.chain().focus().setImage({ src }).run()
529
-
565
+
566
+ $editor.chain().focus().setImage({ src }).run();
530
567
  }
531
568
 
532
569
  function addMediaGrid() {
533
-
534
- $editor.chain().focus().insertGrid({ cols: 2 }).run()
535
-
570
+ $editor.chain().focus().insertGrid({ cols: 2 }).run();
536
571
  }
537
572
 
538
- function addTable(){
539
- $editor.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()
573
+ function addTable() {
574
+ $editor
575
+ .chain()
576
+ .focus()
577
+ .insertTable({ rows: 3, cols: 3, withHeaderRow: true })
578
+ .run();
540
579
  }
541
580
  </script>
542
581
 
543
- <div class="fl-rich-text {className}" class:editable>
544
-
582
+ <div
583
+ class="fl-rich-text {className}"
584
+ class:editable
585
+ style="
586
+ --fl-editor-radius: {editorConfig.editorRadius};
587
+ --fl-editor-bg: {editorConfig.editorBgColor};
588
+ --fl-toolbar-sticky-position: {editorConfig.toolbarStickyPosition}px;
589
+ --fl-toolbar-z-index: {editorConfig.toolbarZIndex};
590
+ --fl-toolbar-padding: {editorConfig.toolbarPadding};
591
+ --fl-toolbar-gap: {editorConfig.toolbarGap};
592
+ --fl-toolbar-bg: {editorConfig.toolbarBgColor};
593
+ --fl-doc-max-width: {editorConfig.docMaxWidth};
594
+ --fl-doc-padding: {editorConfig.docPadding};
595
+ --fl-doc-bg: {editorConfig.docBg};
596
+ --fl-doc-margin-inline: {editorConfig.docMarginInline};
597
+ --fl-doc-margin-block: {editorConfig.docMarginBlock};
598
+ --fl-doc-radius: {editorConfig.docRadius};
599
+ "
600
+ >
545
601
  {#if editor}
546
602
  <header class="fl-rich-text-toolbar">
547
603
  <!-- Undo/Redo -->
@@ -871,35 +927,80 @@
871
927
  <!-- Font size editor -->
872
928
  <div role="group" class="fl-rich-text-toolbar-group">
873
929
  <div class="fl-font-size-editor">
874
- <button
875
- type="button"
876
- aria-label="Decrease font size"
877
- onclick={decrementFontSize}
878
- class="fl-font-size-editor-button">
879
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-minus"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5 12l14 0" /></svg>
880
- </button
930
+ <button
931
+ type="button"
932
+ aria-label="Decrease font size"
933
+ onclick={decrementFontSize}
934
+ class="fl-font-size-editor-button"
935
+ >
936
+ <svg
937
+ xmlns="http://www.w3.org/2000/svg"
938
+ width="24"
939
+ height="24"
940
+ viewBox="0 0 24 24"
941
+ fill="none"
942
+ stroke="currentColor"
943
+ stroke-width="2"
944
+ stroke-linecap="round"
945
+ stroke-linejoin="round"
946
+ class="icon icon-tabler icons-tabler-outline icon-tabler-minus"
947
+ ><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path
948
+ d="M5 12l14 0"
949
+ /></svg
881
950
  >
882
- <input type="text" bind:value={fontSize} />
883
- <button
884
- type="button"
885
- aria-label="Increase font size"
886
- onclick={incrementFontSize}
887
- class="fl-font-size-editor-button">
888
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-plus"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 5l0 14" /><path d="M5 12l14 0" /></svg>
889
- </button
951
+ </button>
952
+ <input type="text" bind:value={fontSize} />
953
+ <button
954
+ type="button"
955
+ aria-label="Increase font size"
956
+ onclick={incrementFontSize}
957
+ class="fl-font-size-editor-button"
958
+ >
959
+ <svg
960
+ xmlns="http://www.w3.org/2000/svg"
961
+ width="24"
962
+ height="24"
963
+ viewBox="0 0 24 24"
964
+ fill="none"
965
+ stroke="currentColor"
966
+ stroke-width="2"
967
+ stroke-linecap="round"
968
+ stroke-linejoin="round"
969
+ class="icon icon-tabler icons-tabler-outline icon-tabler-plus"
970
+ ><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path
971
+ d="M12 5l0 14"
972
+ /><path d="M5 12l14 0" /></svg
890
973
  >
974
+ </button>
891
975
  </div>
892
976
  </div>
893
977
 
894
978
  <!-- Line height -->
895
979
  <div role="group" class="fl-rich-text-toolbar-group">
896
- <button
897
- class="fl-font-size-button"
898
- aria-label="Line height"
899
- type="button"
900
- onclick={(e) => toogleDropdown(e.currentTarget, "line-height-dropdown")}
980
+ <button
981
+ class="fl-font-size-button"
982
+ aria-label="Line height"
983
+ type="button"
984
+ onclick={(e) =>
985
+ toogleDropdown(e.currentTarget, "line-height-dropdown")}
901
986
  >
902
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-line-height"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3 8l3 -3l3 3" /><path d="M3 16l3 3l3 -3" /><path d="M6 5l0 14" /><path d="M13 6l7 0" /><path d="M13 12l7 0" /><path d="M13 18l7 0" /></svg>
987
+ <svg
988
+ xmlns="http://www.w3.org/2000/svg"
989
+ width="16"
990
+ height="16"
991
+ viewBox="0 0 24 24"
992
+ fill="none"
993
+ stroke="currentColor"
994
+ stroke-width="2"
995
+ stroke-linecap="round"
996
+ stroke-linejoin="round"
997
+ class="icon icon-tabler icons-tabler-outline icon-tabler-line-height"
998
+ ><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path
999
+ d="M3 8l3 -3l3 3"
1000
+ /><path d="M3 16l3 3l3 -3" /><path d="M6 5l0 14" /><path
1001
+ d="M13 6l7 0"
1002
+ /><path d="M13 12l7 0" /><path d="M13 18l7 0" /></svg
1003
+ >
903
1004
 
904
1005
  <svg
905
1006
  class="toogle-dropdown-button-icon"
@@ -1224,7 +1325,17 @@
1224
1325
  aria-label="Image"
1225
1326
  class:is-active={$editor.isActive("image")}
1226
1327
  >
1227
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-photo"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M8.813 11.612c.457 -.38 .918 -.38 1.386 .011l.108 .098l4.986 4.986l.094 .083a1 1 0 0 0 1.403 -1.403l-.083 -.094l-1.292 -1.293l.292 -.293l.106 -.095c.457 -.38 .918 -.38 1.386 .011l.108 .098l4.674 4.675a4 4 0 0 1 -3.775 3.599l-.206 .005h-12a4 4 0 0 1 -3.98 -3.603l6.687 -6.69l.106 -.095zm9.187 -9.612a4 4 0 0 1 3.995 3.8l.005 .2v9.585l-3.293 -3.292l-.15 -.137c-1.256 -1.095 -2.85 -1.097 -4.096 -.017l-.154 .14l-.307 .306l-2.293 -2.292l-.15 -.137c-1.256 -1.095 -2.85 -1.097 -4.096 -.017l-.154 .14l-5.307 5.306v-9.585a4 4 0 0 1 3.8 -3.995l.2 -.005h12zm-2.99 5l-.127 .007a1 1 0 0 0 0 1.986l.117 .007l.127 -.007a1 1 0 0 0 0 -1.986l-.117 -.007z" /></svg>
1328
+ <svg
1329
+ xmlns="http://www.w3.org/2000/svg"
1330
+ width="24"
1331
+ height="24"
1332
+ viewBox="0 0 24 24"
1333
+ fill="currentColor"
1334
+ class="icon icon-tabler icons-tabler-filled icon-tabler-photo"
1335
+ ><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path
1336
+ d="M8.813 11.612c.457 -.38 .918 -.38 1.386 .011l.108 .098l4.986 4.986l.094 .083a1 1 0 0 0 1.403 -1.403l-.083 -.094l-1.292 -1.293l.292 -.293l.106 -.095c.457 -.38 .918 -.38 1.386 .011l.108 .098l4.674 4.675a4 4 0 0 1 -3.775 3.599l-.206 .005h-12a4 4 0 0 1 -3.98 -3.603l6.687 -6.69l.106 -.095zm9.187 -9.612a4 4 0 0 1 3.995 3.8l.005 .2v9.585l-3.293 -3.292l-.15 -.137c-1.256 -1.095 -2.85 -1.097 -4.096 -.017l-.154 .14l-.307 .306l-2.293 -2.292l-.15 -.137c-1.256 -1.095 -2.85 -1.097 -4.096 -.017l-.154 .14l-5.307 5.306v-9.585a4 4 0 0 1 3.8 -3.995l.2 -.005h12zm-2.99 5l-.127 .007a1 1 0 0 0 0 1.986l.117 .007l.127 -.007a1 1 0 0 0 0 -1.986l-.117 -.007z"
1337
+ /></svg
1338
+ >
1228
1339
  </button>
1229
1340
  <!-- Audio -->
1230
1341
  <button
@@ -1233,7 +1344,7 @@
1233
1344
  aria-label="Audio"
1234
1345
  class:is-active={$editor.isActive("audio")}
1235
1346
  >
1236
- Audio
1347
+ Audio
1237
1348
  </button>
1238
1349
  </div>
1239
1350
 
@@ -1246,7 +1357,23 @@
1246
1357
  aria-label="Media grid"
1247
1358
  class:is-active={$editor.isActive("MediaGridComponent")}
1248
1359
  >
1249
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="currentColor" class="icon icon-tabler icons-tabler-filled icon-tabler-layout-grid"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 3a2 2 0 0 1 2 2v4a2 2 0 0 1 -2 2h-4a2 2 0 0 1 -2 -2v-4a2 2 0 0 1 2 -2z" /><path d="M19 3a2 2 0 0 1 2 2v4a2 2 0 0 1 -2 2h-4a2 2 0 0 1 -2 -2v-4a2 2 0 0 1 2 -2z" /><path d="M9 13a2 2 0 0 1 2 2v4a2 2 0 0 1 -2 2h-4a2 2 0 0 1 -2 -2v-4a2 2 0 0 1 2 -2z" /><path d="M19 13a2 2 0 0 1 2 2v4a2 2 0 0 1 -2 2h-4a2 2 0 0 1 -2 -2v-4a2 2 0 0 1 2 -2z" /></svg>
1360
+ <svg
1361
+ xmlns="http://www.w3.org/2000/svg"
1362
+ width="24"
1363
+ height="24"
1364
+ viewBox="0 0 24 24"
1365
+ fill="currentColor"
1366
+ class="icon icon-tabler icons-tabler-filled icon-tabler-layout-grid"
1367
+ ><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path
1368
+ d="M9 3a2 2 0 0 1 2 2v4a2 2 0 0 1 -2 2h-4a2 2 0 0 1 -2 -2v-4a2 2 0 0 1 2 -2z"
1369
+ /><path
1370
+ d="M19 3a2 2 0 0 1 2 2v4a2 2 0 0 1 -2 2h-4a2 2 0 0 1 -2 -2v-4a2 2 0 0 1 2 -2z"
1371
+ /><path
1372
+ d="M9 13a2 2 0 0 1 2 2v4a2 2 0 0 1 -2 2h-4a2 2 0 0 1 -2 -2v-4a2 2 0 0 1 2 -2z"
1373
+ /><path
1374
+ d="M19 13a2 2 0 0 1 2 2v4a2 2 0 0 1 -2 2h-4a2 2 0 0 1 -2 -2v-4a2 2 0 0 1 2 -2z"
1375
+ /></svg
1376
+ >
1250
1377
  </button>
1251
1378
  <!-- Table -->
1252
1379
  <button
@@ -1255,7 +1382,21 @@
1255
1382
  aria-label="Table"
1256
1383
  class:is-active={$editor.isActive("table")}
1257
1384
  >
1258
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-table"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3 5a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-14" /><path d="M3 10h18" /><path d="M10 3v18" /></svg>
1385
+ <svg
1386
+ xmlns="http://www.w3.org/2000/svg"
1387
+ width="24"
1388
+ height="24"
1389
+ viewBox="0 0 24 24"
1390
+ fill="none"
1391
+ stroke="currentColor"
1392
+ stroke-width="2"
1393
+ stroke-linecap="round"
1394
+ stroke-linejoin="round"
1395
+ class="icon icon-tabler icons-tabler-outline icon-tabler-table"
1396
+ ><path stroke="none" d="M0 0h24v24H0z" fill="none" /><path
1397
+ d="M3 5a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v14a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-14"
1398
+ /><path d="M3 10h18" /><path d="M10 3v18" /></svg
1399
+ >
1259
1400
  </button>
1260
1401
  </div>
1261
1402
 
@@ -1906,12 +2047,18 @@
1906
2047
  {/if}
1907
2048
  </div>
1908
2049
  {:else if activeDropdownType === "line-height-dropdown"}
1909
- <div class="fl-range-element">
1910
- <span class="fl-range-element-value">
1911
- {lineHeight.toFixed(2)}
1912
- </span>
1913
- <input oninput={handleRangeInput} type="range" min="0.5" max="4" step="0.05" bind:value={lineHeight}>
1914
- </div>
1915
-
2050
+ <div class="fl-range-element">
2051
+ <span class="fl-range-element-value">
2052
+ {lineHeight.toFixed(2)}
2053
+ </span>
2054
+ <input
2055
+ oninput={handleRangeInput}
2056
+ type="range"
2057
+ min="0.5"
2058
+ max="4"
2059
+ step="0.05"
2060
+ bind:value={lineHeight}
2061
+ />
2062
+ </div>
1916
2063
  {/if}
1917
2064
  </div>
@@ -1,2 +1,19 @@
1
1
  import { Node } from '@tiptap/core';
2
+ interface InsertGridOptions {
3
+ cols?: number;
4
+ }
5
+ declare module '@tiptap/core' {
6
+ interface Commands<ReturnType> {
7
+ MediaGridComponent: {
8
+ /**
9
+ * Añade un elemento de audio personalizado
10
+ * @example
11
+ * editor.commands.setAudio({ src: '/audio.mp3', controls: true })
12
+ */
13
+ insertGrid: (options?: InsertGridOptions) => ReturnType;
14
+ addGridItem: () => ReturnType;
15
+ };
16
+ }
17
+ }
2
18
  export declare const MediaGridExtension: Node<any, any>;
19
+ export {};
@@ -19,43 +19,43 @@ export const MediaGridExtension = Node.create({
19
19
  },
20
20
  cols: {
21
21
  default: 2,
22
- parseHTML: element => parseInt(element.getAttribute('data-cols') || '2', 10),
22
+ parseHTML: element => {
23
+ return element.dataset.cols;
24
+ },
23
25
  renderHTML: attrs => ({ 'data-cols': attrs.cols }),
24
26
  },
25
27
  gap: {
26
28
  default: '1rem',
27
- parseHTML: element => element.getAttribute('data-gap') || '1rem',
29
+ parseHTML: element => element.dataset.gap,
28
30
  renderHTML: attrs => ({ 'data-gap': attrs.gap }),
29
31
  },
30
32
  showIndicator: {
31
33
  default: false,
32
- parseHTML: element => element.getAttribute('data-show-indicator') || false,
34
+ parseHTML: element => element.dataset.showIndicator,
33
35
  renderHTML: attrs => ({ 'data-show-indicator': attrs.showIndicator }),
34
36
  },
35
37
  indicatorType: {
36
38
  default: 'numeric', // 'numeric' | 'alphabetic'
37
- parseHTML: element => element.getAttribute('data-indicator-type') || 'numeric',
39
+ parseHTML: element => element.dataset.indicatorType,
38
40
  renderHTML: attrs => ({ 'data-indicator-type': attrs.indicatorType }),
39
41
  }
40
42
  };
41
43
  },
42
44
  parseHTML() {
43
- return [{ tag: 'media-grid-component' }];
45
+ return [
46
+ {
47
+ tag: 'media-grid-component',
48
+ }
49
+ ];
44
50
  },
45
51
  renderHTML({ HTMLAttributes }) {
46
- return ['div', mergeAttributes(HTMLAttributes), 0];
52
+ return ['media-grid-component', mergeAttributes(HTMLAttributes), 0];
47
53
  },
48
54
  addCommands() {
49
55
  return {
50
56
  insertGrid: (options) => ({ tr, state, dispatch }) => {
51
57
  const { schema } = state;
52
58
  const cols = options?.cols || 2;
53
- // const items = Array.from({ length: cols }, () =>
54
- // schema.nodes.gridItem.create(
55
- // null,
56
- // schema.nodes.image.create({ src: 'https://placehold.co/800x400' })
57
- // )
58
- // )
59
59
  const items = Array.from({ length: cols }, () => schema.nodes.gridItem.create() // 👈 sin contenido
60
60
  );
61
61
  const grid = this.type.create({ cols }, items);
@@ -5,6 +5,7 @@
5
5
 
6
6
  const { node, updateAttributes, selected, getPos, editor }: NodeViewProps = $props();
7
7
 
8
+ console.log(node);
8
9
  let cols = $state(node.attrs.cols || 2);
9
10
  let gap = $state(node.attrs.gap || 1);
10
11
  let showIndicator = $state(node.attrs.showIndicator || false);
@@ -101,7 +102,7 @@
101
102
  oninput={handleShowIndicatorChange}
102
103
  type="checkbox"
103
104
  id="show-indicator"
104
- value={showIndicator}
105
+ checked={showIndicator}
105
106
  />
106
107
  Show indicators
107
108
  </label>
package/dist/styles.css CHANGED
@@ -6,9 +6,21 @@
6
6
  --gray-1: #f0f0f0;
7
7
  --gray-2: #e0e0e0;
8
8
  --gray-3: #c0c0c0;
9
- --toolbar-gap: 5px;
10
- --toolbar-padding: 6px;
9
+
11
10
  --fl-editor-radius: 12px;
11
+ --fl-editor-bg: #242424;
12
+ --fl-toolbar-sticky-position: 0;
13
+ --fl-toolbar-z-index: 10;
14
+ --fl-toolbar-padding: 6px;
15
+ --fl-toolbar-gap: 5px;
16
+ --fl-toolbar-bg: #242424;
17
+
18
+ --fl-doc-max-width: 1024px;
19
+ --fl-doc-padding: 2rem;
20
+ --fl-doc-bg: transparent;
21
+ --fl-doc-margin-inline: auto;
22
+ --fl-doc-margin-block: 2rem;
23
+ --fl-doc-radius: 0;
12
24
  }
13
25
 
14
26
  /* Basic editor styles */
@@ -252,9 +264,18 @@
252
264
  flex-direction: column;
253
265
  text-align: left;
254
266
  min-height: 56px;
255
- padding: 2rem;
256
- background: #242424;
257
- border-radius: 0 0 var(--fl-editor-radius) var(--fl-editor-radius);
267
+ box-sizing: border-box;
268
+ width: 100%;
269
+ }
270
+
271
+ .fl-rich-text-content-doc {
272
+ padding: var(--fl-doc-padding, 2rem);
273
+ background: var(--fl-doc-bg, transparent);
274
+ border-radius: var(--fl-doc-radius, 12px);
275
+ width: 100%;
276
+ max-width: var(--fl-doc-max-width, 1024px);
277
+ margin-inline: var(--fl-doc-margin-inline, auto);
278
+ margin-block: var(--fl-doc-margin-block, 2rem);
258
279
  }
259
280
 
260
281
  .fl-toolbar-dropdown-panel {
@@ -265,7 +286,7 @@
265
286
  backdrop-filter: blur(42px);
266
287
  border-radius: 14px;
267
288
  min-width: auto;
268
- z-index: 10;
289
+ z-index: 1000;
269
290
  box-sizing: border-box;
270
291
 
271
292
  &.fl-toolbar-dropdown-panel--table {
@@ -296,7 +317,7 @@
296
317
  width: 100%;
297
318
  height: 100%;
298
319
  min-height: 56px;
299
- background-color: var(--fl-bg-color, #242424);
320
+ background-color: var(--fl-editor-bg, #242424);
300
321
  color: var(--text-color);
301
322
  box-sizing: border-box;
302
323
  border-radius: var(--fl-editor-radius);
@@ -307,10 +328,10 @@
307
328
  flex-wrap: nowrap;
308
329
  overflow: auto;
309
330
  align-items: center;
310
- gap: var(--toolbar-gap);
311
- padding: var(--toolbar-padding);
331
+ gap: var(--fl-toolbar-gap);
332
+ padding: var(--fl-toolbar-padding);
312
333
  position: sticky;
313
- top: var(--sticky-position, 0);
334
+ top: var(--fl-toolbar-sticky-position, 0);
314
335
  z-index: var(--fl-toolbar-z-index, 10);
315
336
  background: var(--fl-toolbar-bg, #242424);
316
337
  border-radius: var(--fl-editor-radius);
@@ -319,7 +340,7 @@
319
340
  .fl-rich-text-toolbar-group {
320
341
  display: flex;
321
342
  flex-wrap: nowrap;
322
- gap: var(--toolbar-gap);
343
+ gap: var(--fl-toolbar-gap);
323
344
 
324
345
  button {
325
346
  padding: 8px 8px;
@@ -374,7 +395,7 @@
374
395
  font-weight: 500;
375
396
  border: none;
376
397
  padding: 0px;
377
- font-size: 12px;
398
+ font-size: 11px;
378
399
  border-radius: 3px;
379
400
  outline: 1px dashed #818181;
380
401
  scale: 1.1;
@@ -571,8 +592,8 @@
571
592
  position: absolute;
572
593
  left: 10px;
573
594
  top: 10px;
574
- width: 24px;
575
- height: 24px;
595
+ width: 26px;
596
+ height: 26px;
576
597
  display: flex;
577
598
  align-items: center;
578
599
  justify-content: center;
@@ -580,7 +601,10 @@
580
601
  border-radius: 100%;
581
602
  color: #fff;
582
603
  font-weight: 600;
583
- font-size: 14px;
604
+ font-size: 16px;
605
+ text-align: center;
606
+ font-family: monospace;
607
+ text-transform: uppercase;
584
608
  }
585
609
  }
586
610
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flexiui/svelte-rich-text",
3
- "version": "0.0.32",
3
+ "version": "0.0.34",
4
4
  "description": "A lightweight and flexible rich text editor component for Svelte",
5
5
  "keywords": [
6
6
  "svelte",