@flexiui/svelte-rich-text 0.0.64 → 0.0.66

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.
@@ -1,15 +1,21 @@
1
1
  <script lang="ts" generics="T extends keyof SvelteHTMLElements = 'div'">
2
- import type { SvelteHTMLElements } from 'svelte/elements';
2
+ import type { SvelteHTMLElements } from "svelte/elements";
3
3
  import "./styles.css";
4
4
  import "katex/dist/katex.min.css";
5
5
  import { onMount, onDestroy } from "svelte";
6
6
  import type { Readable } from "svelte/store";
7
7
  import { computePosition, offset, autoUpdate } from "@floating-ui/dom";
8
+ import {
9
+ ColorPicker,
10
+ ColorPickerSwatch,
11
+ ColorPickerTrigger,
12
+ } from "@flexiui/svelte-color-picker";
13
+
8
14
  import {
9
15
  Mathematics,
10
16
  migrateMathStrings,
11
17
  } from "@tiptap/extension-mathematics";
12
- import { CharacterCount } from '@tiptap/extensions'
18
+ import { CharacterCount } from "@tiptap/extensions";
13
19
  import { CellSelection } from "prosemirror-tables";
14
20
  import {
15
21
  createEditor,
@@ -35,7 +41,7 @@
35
41
 
36
42
  import { Document } from "@tiptap/extension-document";
37
43
 
38
- const InlineDoc = Document.extend({ content: "inline*" })
44
+ const InlineDoc = Document.extend({ content: "inline*" });
39
45
 
40
46
  interface Props {
41
47
  id?: string;
@@ -91,10 +97,10 @@
91
97
  docTextColor?: string;
92
98
  buttonStyle?: "accent-soft" | "accent-solid";
93
99
  };
94
- contentWrapperAs?: T;
95
- inlineNodeMode?: boolean;
96
- trailingNode?: boolean;
97
- }
100
+ contentWrapperAs?: T;
101
+ inlineNodeMode?: boolean;
102
+ trailingNode?: boolean;
103
+ }
98
104
 
99
105
  type ToolbarButton =
100
106
  | string
@@ -148,12 +154,13 @@
148
154
  onPaste: () => {},
149
155
  },
150
156
  config,
151
- contentWrapperAs = "div" as T,
152
- inlineNodeMode = false,
153
- trailingNode = true,
154
- }: Props = $props();
157
+ contentWrapperAs = "div" as T,
158
+ inlineNodeMode = false,
159
+ trailingNode = true,
160
+ }: Props = $props();
155
161
 
156
162
  let editor = $state() as Readable<Editor>;
163
+ let color = $state("#3b82f6");
157
164
 
158
165
  const DEFAULT_TOOLBAR = [
159
166
  [{ type: "undo" }, "redo"],
@@ -164,7 +171,13 @@
164
171
  ["inlineMath"],
165
172
  ["image", "audio"],
166
173
  ["mediaGrid", "table"],
167
- ["textAlignLeft", "textAlignCenter", "textAlignRight", "clearFormatting", "clearNodes"],
174
+ [
175
+ "textAlignLeft",
176
+ "textAlignCenter",
177
+ "textAlignRight",
178
+ "clearFormatting",
179
+ "clearNodes",
180
+ ],
168
181
  // ...
169
182
  ];
170
183
 
@@ -209,7 +222,8 @@
209
222
  editorConfig.toolbarJustifyContent = "flex-end";
210
223
  }
211
224
 
212
- let bubbleOffset = $editor?.storage.tableCell.customTableSelection === "column" ? 18 : 8;
225
+ let bubbleOffset =
226
+ $editor?.storage.tableCell.customTableSelection === "column" ? 18 : 8;
213
227
 
214
228
  let tooltipVisible = $state(false);
215
229
  let tooltipX = $state(0);
@@ -233,7 +247,9 @@
233
247
 
234
248
  const isAccentSoft = editorConfig.buttonStyle === "accent-soft";
235
249
  let percentage = $derived.by(() => {
236
- return $editor ? (100 / charactersLimit) * $editor.storage.characterCount.characters() : 0
250
+ return $editor
251
+ ? (100 / charactersLimit) * $editor.storage.characterCount.characters()
252
+ : 0;
237
253
  });
238
254
 
239
255
  let toolbarGroups = $derived(
@@ -278,10 +294,10 @@
278
294
  "rgb(255, 102, 142)",
279
295
  ];
280
296
 
281
- const extensions = getRichTextExtensions({
282
- editable: true,
283
- trailingNode,
284
- customExtensions: [
297
+ const extensions = getRichTextExtensions({
298
+ editable: true,
299
+ trailingNode,
300
+ customExtensions: [
285
301
  Mathematics.configure({
286
302
  inlineOptions: {
287
303
  onClick: (node, pos) => {
@@ -577,7 +593,6 @@
577
593
  },
578
594
  });
579
595
  });
580
-
581
596
 
582
597
  onDestroy(() => {
583
598
  if (editor) {
@@ -641,6 +656,73 @@
641
656
  editor.view.dispatch(tr);
642
657
  }
643
658
  }
659
+
660
+ let defaultColor = "#fafafa"
661
+ let colorValue = $derived(defaultColor);
662
+
663
+ function onFormatChange(e) {
664
+
665
+
666
+ }
667
+
668
+ let colorValueRgb = $state(colorValue);
669
+
670
+ function onChange(value: any) {
671
+ colorValue = value.hex;
672
+ colorValueRgb = value.rgb;
673
+ }
674
+
675
+ let prevSelection: { from: number; to: number } | null = null;
676
+ let prevColorValueRgb: string | null = null;
677
+
678
+ function onOpenChange(open: boolean) {
679
+
680
+ if (open) {
681
+ const { from, to } = $editor.state.selection;
682
+ prevSelection = { from, to };
683
+ prevColorValueRgb = $editor?.getAttributes("textStyle")?.color;
684
+ return;
685
+ }
686
+
687
+ if (!open) {
688
+
689
+ if(colorValue === defaultColor) return;
690
+
691
+ if(colorValueRgb === prevColorValueRgb) {
692
+ return;
693
+ }
694
+
695
+ // Guardar color reciente
696
+ const included = recentCustomColors.includes(colorValueRgb);
697
+ if (!included) {
698
+ recentCustomColors = [
699
+ ...recentCustomColors,
700
+ colorValueRgb,
701
+ ];
702
+ }
703
+
704
+ // Aplicar color al rango previo
705
+ if (prevSelection) {
706
+ const { from, to } = prevSelection;
707
+ const { state, view } = $editor;
708
+
709
+ const textStyle = state.schema.marks.textStyle;
710
+
711
+ const tr = state.tr.addMark(
712
+ from,
713
+ to,
714
+ textStyle.create({ color: colorValueRgb })
715
+ );
716
+
717
+ view.dispatch(tr);
718
+
719
+
720
+ colorValue = defaultColor;
721
+ colorValueRgb = defaultColor;
722
+
723
+ }
724
+ }
725
+ }
644
726
  </script>
645
727
 
646
728
  <div
@@ -660,7 +742,8 @@
660
742
  --fl-toolbar-btn-padding: {editorConfig.toolbarBtnPadding};
661
743
  --fl-toolbar-btn-radius: {editorConfig.toolbarBtnRadius};
662
744
  --fl-toolbar-btn-min-height: {editorConfig.toolbarBtnMinHeight};
663
- --fl-toolbar-btn-min-width: {editorConfig.toolbarBtnMinWidth || editorConfig.toolbarBtnMinHeight};
745
+ --fl-toolbar-btn-min-width: {editorConfig.toolbarBtnMinWidth ||
746
+ editorConfig.toolbarBtnMinHeight};
664
747
  --fl-doc-max-width: {editorConfig.docMaxWidth};
665
748
  --fl-doc-padding: {editorConfig.docPadding};
666
749
  --fl-doc-bg: {editorConfig.docBg};
@@ -682,29 +765,35 @@
682
765
  <p>Array anidado (no debería pasar)</p>
683
766
  {:else if typeof toolbarItem === "string"}
684
767
  <RenderToolbarButton
685
- type={toolbarItem}
686
- {editor}
687
- {nodeCounters}
688
- {nodesLimit}
689
- {currentNodeCount}
690
- accentSoft={isAccentSoft}
691
- {fontSize}
692
- onToggleDropdown={(e: MouseEvent, dropdownName: string) => {
693
- toogleDropdown(e.currentTarget as HTMLElement, dropdownName);
694
- }}
768
+ type={toolbarItem}
769
+ {editor}
770
+ {nodeCounters}
771
+ {nodesLimit}
772
+ {currentNodeCount}
773
+ accentSoft={isAccentSoft}
774
+ {fontSize}
775
+ onToggleDropdown={(e: MouseEvent, dropdownName: string) => {
776
+ toogleDropdown(
777
+ e.currentTarget as HTMLElement,
778
+ dropdownName,
779
+ );
780
+ }}
695
781
  />
696
782
  {:else if isButton(toolbarItem)}
697
783
  <RenderToolbarButton
698
- type={toolbarItem.type}
699
- {editor}
700
- {nodeCounters}
701
- {nodesLimit}
702
- {currentNodeCount}
703
- accentSoft={isAccentSoft}
704
- {fontSize}
705
- onToggleDropdown={(e: MouseEvent, dropdownName: string) => {
706
- toogleDropdown(e.currentTarget as HTMLElement, dropdownName);
707
- }}
784
+ type={toolbarItem.type}
785
+ {editor}
786
+ {nodeCounters}
787
+ {nodesLimit}
788
+ {currentNodeCount}
789
+ accentSoft={isAccentSoft}
790
+ {fontSize}
791
+ onToggleDropdown={(e: MouseEvent, dropdownName: string) => {
792
+ toogleDropdown(
793
+ e.currentTarget as HTMLElement,
794
+ dropdownName,
795
+ );
796
+ }}
708
797
  />
709
798
  {/if}
710
799
  {/each}
@@ -715,7 +804,12 @@
715
804
  </header>
716
805
  {/if}
717
806
 
718
- <EditorContent as={contentWrapperAs} editor={$editor} class="fl-rich-text-content" data-fl-editable="true" />
807
+ <EditorContent
808
+ as={contentWrapperAs}
809
+ editor={$editor}
810
+ class="fl-rich-text-content"
811
+ data-fl-editable="true"
812
+ />
719
813
 
720
814
  <!-- Warning message for node limit -->
721
815
  {#if showLimitWarning && nodesLimit}
@@ -728,27 +822,31 @@
728
822
  <!-- Bottom bar showing node count -->
729
823
  {#if showCountersBar || percentage >= 90}
730
824
  <div class="fl-counters-bar">
731
- <div class="fl-character-count" class:fl-character-count--warning={percentage >= 100}>
825
+ <div
826
+ class="fl-character-count"
827
+ class:fl-character-count--warning={percentage >= 100}
828
+ >
732
829
  {#if charactersLimit}
733
830
  <svg height="20" width="20" viewBox="0 0 20 20">
734
- <circle r="10" cx="10" cy="10" fill="#ffffff30" />
735
- <circle
736
- r="5"
737
- cx="10"
738
- cy="10"
739
- fill="transparent"
740
- stroke="currentColor"
741
- stroke-width="10"
742
- stroke-dasharray={`calc(${percentage} * 31.4 / 100) 31.4`}
743
- transform="rotate(-90) translate(-20)"
744
- />
745
- <circle r="6" cx="10" cy="10" fill="var(--fl-editor-bg)" />
831
+ <circle r="10" cx="10" cy="10" fill="#ffffff30" />
832
+ <circle
833
+ r="5"
834
+ cx="10"
835
+ cy="10"
836
+ fill="transparent"
837
+ stroke="currentColor"
838
+ stroke-width="10"
839
+ stroke-dasharray={`calc(${percentage} * 31.4 / 100) 31.4`}
840
+ transform="rotate(-90) translate(-20)"
841
+ />
842
+ <circle r="6" cx="10" cy="10" fill="var(--fl-editor-bg)" />
746
843
  </svg>
747
844
  {/if}
748
845
 
749
846
  <span>
750
- Characters: {$editor?.storage?.characterCount?.characters()}
751
- {#if charactersLimit} / {charactersLimit}{/if}
847
+ Characters: {$editor?.storage?.characterCount?.characters()}
848
+ {#if charactersLimit}
849
+ / {charactersLimit}{/if}
752
850
  </span>
753
851
  </div>
754
852
 
@@ -805,40 +903,26 @@
805
903
  </div>
806
904
  {:else if activeDropdownType === "text-color-dropdown"}
807
905
  <div class="fl-editor-color-palette">
808
- <button
809
- class="fl-color-swatch fl-color-picker-btn"
810
- aria-label="Text color picker"
811
- type="button"
906
+ <div class="color-picker-wrapper">
907
+ <ColorPicker
908
+ value={$editor?.getAttributes("textStyle")?.color || colorValue}
909
+ defaultFormat="rgb"
910
+ onFormatChange={onFormatChange}
911
+ onChange={onChange}
912
+ onOpenChange={onOpenChange}
913
+ portalElement={".color-picker-wrapper"}
812
914
  >
813
- <input
814
- type="color"
815
- onblur={(event: any) => {
816
- const inclued = recentCustomColors.includes(event?.target?.value);
817
- if (!inclued) {
818
- recentCustomColors = [
819
- ...recentCustomColors,
820
- event?.target?.value,
821
- ];
822
- }
823
- $editor.chain().focus().setColor(event?.target?.value).run();
824
- hideDropdown();
825
- }}
826
- onchange={(event: any) => {
827
- const inclued = recentCustomColors.includes(event?.target?.value);
828
- if (!inclued) {
829
- recentCustomColors = [
830
- ...recentCustomColors,
831
- event?.target?.value,
832
- ];
833
- }
834
- $editor.chain().focus().setColor(event?.target?.value).run();
835
- hideDropdown();
836
- }}
837
- value={rgbToHex($editor?.getAttributes("textStyle")?.color)}
838
- data-testid="setColor"
839
- id="colorPicker"
840
- />
841
- </button>
915
+ <ColorPickerTrigger class="font-mono">
916
+ <!-- <ColorPickerSwatch class="w-6 h-6 rounded-md" showAlpha={true} value={colorValueRgb} /> -->
917
+ <!-- {colorValue} -->
918
+ <button
919
+ class="fl-color-swatch fl-color-picker-btn"
920
+ aria-label="Text color picker"
921
+ type="button"
922
+ ></button>
923
+ </ColorPickerTrigger>
924
+ </ColorPicker>
925
+ </div>
842
926
 
843
927
  {#each TEXT_COLOR_PALETTE as color}
844
928
  <button
@@ -1131,29 +1215,35 @@
1131
1215
  <p>Array anidado (no debería pasar)</p>
1132
1216
  {:else if typeof bubbleMenuItem === "string"}
1133
1217
  <RenderToolbarButton
1134
- type={bubbleMenuItem}
1135
- {editor}
1136
- {nodeCounters}
1137
- {nodesLimit}
1138
- {currentNodeCount}
1139
- accentSoft={isAccentSoft}
1140
- {fontSize}
1141
- onToggleDropdown={(e: MouseEvent, dropdownName: string) => {
1142
- toogleDropdown(e.currentTarget as HTMLElement, dropdownName);
1143
- }}
1218
+ type={bubbleMenuItem}
1219
+ {editor}
1220
+ {nodeCounters}
1221
+ {nodesLimit}
1222
+ {currentNodeCount}
1223
+ accentSoft={isAccentSoft}
1224
+ {fontSize}
1225
+ onToggleDropdown={(e: MouseEvent, dropdownName: string) => {
1226
+ toogleDropdown(
1227
+ e.currentTarget as HTMLElement,
1228
+ dropdownName,
1229
+ );
1230
+ }}
1144
1231
  />
1145
1232
  {:else if isButton(bubbleMenuItem)}
1146
1233
  <RenderToolbarButton
1147
- type={bubbleMenuItem.type}
1148
- {editor}
1149
- {nodeCounters}
1150
- {nodesLimit}
1151
- {currentNodeCount}
1152
- accentSoft={isAccentSoft}
1153
- {fontSize}
1154
- onToggleDropdown={(e: MouseEvent, dropdownName: string) => {
1155
- toogleDropdown(e.currentTarget as HTMLElement, dropdownName);
1156
- }}
1234
+ type={bubbleMenuItem.type}
1235
+ {editor}
1236
+ {nodeCounters}
1237
+ {nodesLimit}
1238
+ {currentNodeCount}
1239
+ accentSoft={isAccentSoft}
1240
+ {fontSize}
1241
+ onToggleDropdown={(e: MouseEvent, dropdownName: string) => {
1242
+ toogleDropdown(
1243
+ e.currentTarget as HTMLElement,
1244
+ dropdownName,
1245
+ );
1246
+ }}
1157
1247
  />
1158
1248
  {/if}
1159
1249
  {/each}
@@ -1184,4 +1274,4 @@
1184
1274
  margin-right: 4px;
1185
1275
  }
1186
1276
  }
1187
- </style>
1277
+ </style>
@@ -1,5 +1,5 @@
1
1
  import { SvelteComponentTyped } from "svelte";
2
- import type { SvelteHTMLElements } from 'svelte/elements';
2
+ import type { SvelteHTMLElements } from "svelte/elements";
3
3
  import "./styles.css";
4
4
  import "katex/dist/katex.min.css";
5
5
  declare class __sveltets_Render<T extends keyof SvelteHTMLElements = 'div'> {
@@ -3,4 +3,4 @@ export interface RenderOptions {
3
3
  customExtensions?: any[];
4
4
  customNodeMapping?: any;
5
5
  }
6
- export declare function renderHTMLFromJSON({ json, customExtensions, customNodeMapping }: RenderOptions): string;
6
+ export declare function renderHTMLFromJSON({ json, customExtensions, customNodeMapping }: RenderOptions): any;
@@ -1,4 +1,4 @@
1
- import { renderToHTMLString, serializeChildrenToHTMLString } from "@tiptap/static-renderer";
1
+ import { renderToHTMLString, serializeChildrenToHTMLString, } from '@tiptap/static-renderer/pm/html-string';
2
2
  import katex from "katex";
3
3
  import { getRichTextExtensions } from "./getExtensions";
4
4
  const nodeMapping = {
package/dist/styles.css CHANGED
@@ -71,7 +71,7 @@
71
71
  backdrop-filter: blur(42px);
72
72
  border-radius: 14px;
73
73
  min-width: auto;
74
- z-index: 1000;
74
+ z-index: 49;
75
75
  box-sizing: border-box;
76
76
 
77
77
  &.fl-toolbar-dropdown-panel--table {
@@ -127,7 +127,7 @@
127
127
  display: flex;
128
128
  gap: 5px;
129
129
  position: relative;
130
- z-index: var(--fl-toolbar-sticky-position, 10);
130
+ z-index: var(--fl-toolbar-z-index, 10);
131
131
 
132
132
 
133
133
  .fl-bubble-menu-mark-button {
package/package.json CHANGED
@@ -1,60 +1,66 @@
1
- {
2
- "name": "@flexiui/svelte-rich-text",
3
- "version": "0.0.64",
4
- "description": "A lightweight and flexible rich text editor component for Svelte",
5
- "keywords": [
6
- "svelte",
7
- "rich text",
8
- "editor",
9
- "wysiwyg",
10
- "flexiui",
11
- "text editor",
12
- "tiptap",
13
- "prosemirror"
14
- ],
15
- "type": "module",
16
- "scripts": {
17
- "dev": "vite",
18
- "build": "vite build && svelte-package",
19
- "preview": "vite preview"
20
- },
21
- "files": [
22
- "dist",
23
- "!dist/**/*.test.*",
24
- "!dist/**/*.spec.*"
25
- ],
26
- "sideEffects": [
27
- "**/*.css"
28
- ],
29
- "main": "./dist/index.js",
30
- "svelte": "./dist/index.js",
31
- "types": "./dist/index.d.ts",
32
- "exports": {
33
- ".": {
34
- "types": "./dist/index.d.ts",
35
- "svelte": "./dist/index.js",
36
- "default": "./dist/index.js"
37
- }
38
- },
39
- "author": "alexgipi <alexgp895@gmail.com>",
40
- "license": "MIT",
41
- "dependencies": {
42
- "@flexiui/svelte-dropdown": "^0.4.0",
43
- "@floating-ui/dom": "^1.7.4",
44
- "@tiptap/core": "^3.0.9",
45
- "@tiptap/extension-highlight": "^3.6.6",
46
- "@tiptap/extension-image": "^3.15.3",
47
- "@tiptap/extension-list": "^3.15.3",
48
- "@tiptap/extension-mathematics": "^3.14.0",
49
- "@tiptap/extension-table": "^3.15.3",
50
- "@tiptap/extension-text-align": "^3.6.6",
51
- "@tiptap/extension-text-style": "^3.0.9",
52
- "@tiptap/html": "^3.14.0",
53
- "@tiptap/pm": "^3.0.9",
54
- "@tiptap/starter-kit": "^3.0.9",
55
- "@tiptap/static-renderer": "^3.14.0",
56
- "katex": "^0.16.27",
57
- "svelte-tiptap": "^3.0.0",
58
- "wavesurfer.js": "^7.12.1"
59
- }
60
- }
1
+ {
2
+ "name": "@flexiui/svelte-rich-text",
3
+ "version": "0.0.66",
4
+ "description": "A lightweight and flexible rich text editor component for Svelte",
5
+ "keywords": [
6
+ "svelte",
7
+ "rich text",
8
+ "editor",
9
+ "wysiwyg",
10
+ "flexiui",
11
+ "text editor",
12
+ "tiptap",
13
+ "prosemirror"
14
+ ],
15
+ "type": "module",
16
+ "scripts": {
17
+ "dev": "vite",
18
+ "build": "vite build && svelte-package",
19
+ "preview": "vite preview"
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "!dist/**/*.test.*",
24
+ "!dist/**/*.spec.*"
25
+ ],
26
+ "sideEffects": [
27
+ "**/*.css"
28
+ ],
29
+ "main": "./dist/index.js",
30
+ "svelte": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "svelte": "./dist/index.js",
36
+ "default": "./dist/index.js"
37
+ }
38
+ },
39
+ "author": "alexgipi <alexgp895@gmail.com>",
40
+ "license": "MIT",
41
+ "dependencies": {
42
+ "@flexiui/svelte-color-picker": "^0.0.5",
43
+ "@flexiui/svelte-dropdown": "^0.4.0",
44
+ "@floating-ui/dom": "^1.7.4",
45
+ "@tiptap/core": "^3.0.9",
46
+ "@tiptap/extension-highlight": "^3.6.6",
47
+ "@tiptap/extension-image": "^3.15.3",
48
+ "@tiptap/extension-list": "^3.15.3",
49
+ "@tiptap/extension-mathematics": "^3.14.0",
50
+ "@tiptap/extension-table": "^3.15.3",
51
+ "@tiptap/extension-text-align": "^3.6.6",
52
+ "@tiptap/extension-text-style": "^3.0.9",
53
+ "@tiptap/html": "^3.14.0",
54
+ "@tiptap/pm": "^3.0.9",
55
+ "@tiptap/starter-kit": "^3.0.9",
56
+ "@tiptap/static-renderer": "^3.14.0",
57
+ "bits-ui": "^2.16.3",
58
+ "katex": "^0.16.27",
59
+ "svelte-tiptap": "^3.0.1",
60
+ "tailwindcss": "^4.2.1",
61
+ "wavesurfer.js": "^7.12.1"
62
+ },
63
+ "devDependencies": {
64
+ "@tailwindcss/vite": "^4.2.1"
65
+ }
66
+ }