@flexiui/svelte-rich-text 0.0.40 → 0.0.42

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.
@@ -3,6 +3,7 @@
3
3
  Mathematics,
4
4
  migrateMathStrings,
5
5
  } from "@tiptap/extension-mathematics";
6
+
6
7
  import { HEADINGS, rgbToHex } from "./utils";
7
8
  import "./styles.css";
8
9
  import "katex/dist/katex.min.css";
@@ -97,90 +98,9 @@
97
98
  ...(config ?? {}),
98
99
  });
99
100
 
100
- // const extensions = [
101
- // // Color.configure({ types: [TextStyle.name, ListItem.name] }),
102
- // Highlight.configure({ multicolor: true }),
103
- // TextStyleKit,
104
- // StarterKit.configure({
105
- // // Disable an included extension
106
- // trailingNode: false,
107
- // link: false,
108
- // bulletList: false,
109
- // listItem: false,
110
- // orderedList: false,
111
- // listKeymap: false,
112
- // }),
113
- // EnhancedLink,
114
- // Audio.configure({
115
- // HTMLAttributes: { class: "audio-player" },
116
- // }),
117
- // Image.configure({
118
- // inline: true,
119
- // }),
120
- // ListKit,
121
- // TextAlign.configure({
122
- // types: [
123
- // "heading",
124
- // "paragraph",
125
- // "bulletList",
126
- // "taskList",
127
- // "listItem",
128
- // "blockquote",
129
- // ],
130
- // }),
131
- // Mathematics.configure({
132
- // inlineOptions: {
133
- // onClick: (node, pos) => {
134
- // // you can do anything on click, e.g. open a dialog to edit the math node
135
- // // or just a prompt to edit the LaTeX code for a quick prototype
136
- // const katex = prompt(
137
- // "Update math LaTeX expression:",
138
- // node.attrs.latex
139
- // );
140
- // if (katex) {
141
- // $editor
142
- // .chain()
143
- // .setNodeSelection(pos)
144
- // .updateInlineMath({ latex: katex })
145
- // .focus()
146
- // .run();
147
- // }
148
- // },
149
- // },
150
- // blockOptions: {
151
- // // optional options for the block math node
152
- // },
153
- // katexOptions: {
154
- // displayMode: false,
155
- // throwOnError: false,
156
- // macros: {
157
- // "\\RR": "\\mathbb{R}",
158
- // "\\ZZ": "\\mathbb{Z}",
159
- // },
160
- // },
161
- // }),
162
- // NodeLineHeight,
163
- // MediaGridExtension,
164
- // MediaGridItemExtension,
165
- // TableKit.configure({
166
- // table: {
167
- // HTMLAttributes: { class: "fl-table-editable" },
168
- // resizable: true,
169
- // },
170
- // }),
171
- // CustomTableCell.configure({
172
- // HTMLAttributes: { class: "fl-cell-editable" },
173
- // }),
174
- // CustomTableHeader.configure({
175
- // HTMLAttributes: { class: "fl-cell-editable" },
176
- // }),
177
- // ...customExtensions,
178
- // ];
179
-
180
101
  const extensions = getRichTextExtensions({
181
102
  editable: true,
182
103
  customExtensions: [
183
- ...customExtensions,
184
104
  Mathematics.configure({
185
105
  inlineOptions: {
186
106
  onClick: (node, pos) => {
@@ -211,7 +131,8 @@
211
131
  "\\ZZ": "\\mathbb{Z}",
212
132
  },
213
133
  },
214
- })
134
+ }),
135
+ ...customExtensions,
215
136
  ]
216
137
  });
217
138
 
@@ -674,16 +595,22 @@
674
595
  <button
675
596
  type="button"
676
597
  onclick={(e) => toogleDropdown(e.currentTarget, "headings-dropdown")}
677
- class:is-active={$editor.isActive("heading")}
598
+ class:is-active={$editor.isActive("heading") || $editor.isActive("h1")}
678
599
  aria-label="Heading"
679
600
  >
680
- {#each HEADINGS as heading}
681
- {#if $editor.isActive("heading", { level: Number(heading.level) })}
682
- {@html heading.icon}
683
- {/if}
684
- {/each}
685
601
 
686
- {#if !$editor.isActive("heading")}
602
+
603
+ {#if $editor.isActive("heading")}
604
+ {#each HEADINGS as heading}
605
+ {#if $editor.isActive("heading", { level: Number(heading.level) })}
606
+ {@html heading.icon}
607
+ {/if}
608
+ {/each}
609
+ {:else if $editor.isActive("h1")}
610
+ {@html HEADINGS[0].icon}
611
+ {/if}
612
+
613
+ {#if !$editor.isActive("heading") && !$editor.isActive("h1")}
687
614
  <svg
688
615
  width="24"
689
616
  height="24"
@@ -1365,7 +1292,7 @@
1365
1292
  aria-label="Audio"
1366
1293
  class:is-active={$editor.isActive("audio")}
1367
1294
  >
1368
- Audio
1295
+ <svg style="transform: scale(1.1);" fill="#e8eaed" width="24px" viewBox="0 -960 960 960" height="24px" xmlns="http://www.w3.org/2000/svg"><path d="M400-120q-66 0-113-47t-47-113q0-66 47-113t113-47q23 0 42.5 5.5T480-418v-422h240v160H560v400q0 66-47 113t-113 47Z"></path></svg>
1369
1296
  </button>
1370
1297
  </div>
1371
1298
 
@@ -1542,8 +1469,9 @@
1542
1469
  type="button"
1543
1470
  onclick={() =>
1544
1471
  $editor.chain().focus().toggleHeading({ level: 1 }).run()}
1545
- class={$editor.isActive("heading", { level: 1 }) ? "is-active" : ""}
1472
+ class={($editor.isActive("heading", { level: 1 }) || $editor.isActive("h1")) ? "is-active" : ""}
1546
1473
  aria-label="H1"
1474
+ disabled={!$editor.isActive("h1")}
1547
1475
  >
1548
1476
  <svg
1549
1477
  width="24"
@@ -1563,6 +1491,7 @@
1563
1491
  $editor.chain().focus().toggleHeading({ level: 2 }).run()}
1564
1492
  class={$editor.isActive("heading", { level: 2 }) ? "is-active" : ""}
1565
1493
  aria-label="H2"
1494
+ disabled={$editor.isActive("h1")}
1566
1495
  >
1567
1496
  <svg
1568
1497
  width="16"
@@ -1582,6 +1511,7 @@
1582
1511
  $editor.chain().focus().toggleHeading({ level: 3 }).run()}
1583
1512
  class={$editor.isActive("heading", { level: 3 }) ? "is-active" : ""}
1584
1513
  aria-label="H3"
1514
+ disabled={$editor.isActive("h1")}
1585
1515
  >
1586
1516
  <svg
1587
1517
  width="16"
@@ -1601,6 +1531,7 @@
1601
1531
  $editor.chain().focus().toggleHeading({ level: 4 }).run()}
1602
1532
  class={$editor.isActive("heading", { level: 4 }) ? "is-active" : ""}
1603
1533
  aria-label="H4"
1534
+ disabled={$editor.isActive("h1")}
1604
1535
  >
1605
1536
  <svg
1606
1537
  width="16"
@@ -1620,6 +1551,7 @@
1620
1551
  $editor.chain().focus().toggleHeading({ level: 5 }).run()}
1621
1552
  class={$editor.isActive("heading", { level: 5 }) ? "is-active" : ""}
1622
1553
  aria-label="H5"
1554
+ disabled={$editor.isActive("h1")}
1623
1555
  >
1624
1556
  <svg
1625
1557
  width="16"
@@ -1639,6 +1571,7 @@
1639
1571
  $editor.chain().focus().toggleHeading({ level: 6 }).run()}
1640
1572
  class={$editor.isActive("heading", { level: 6 }) ? "is-active" : ""}
1641
1573
  aria-label="H6"
1574
+ disabled={$editor.isActive("h1")}
1642
1575
  >
1643
1576
  <svg
1644
1577
  width="16"
@@ -3,27 +3,42 @@ export interface AudioOptions {
3
3
  inline: boolean;
4
4
  allowBase64: boolean;
5
5
  HTMLAttributes: Record<string, any>;
6
+ bgColor: string;
7
+ textColor: string;
8
+ borderRadius: string;
9
+ accentColor: string;
10
+ accentColorPaused: string;
11
+ seekBarBgColor: string;
12
+ playBtnBgColor: string;
13
+ playBtnTextColor: string;
14
+ colorPlay: string;
15
+ maxWidth: string;
6
16
  }
7
17
  export interface SetAudioOptions {
8
18
  src: string;
9
19
  controls?: boolean;
10
20
  autoplay?: boolean;
11
21
  loop?: boolean;
22
+ bgColor?: string;
23
+ textColor?: string;
24
+ borderRadius?: string;
25
+ accentColor?: string;
26
+ accentColorPaused?: string;
27
+ seekBarBgColor?: string;
28
+ playBtnBgColor?: string;
29
+ playBtnTextColor?: string;
12
30
  colorPlay?: string;
13
- colorBar?: string;
14
31
  maxWidth?: string;
15
32
  }
16
33
  declare module '@tiptap/core' {
17
34
  interface Commands<ReturnType> {
18
35
  audio: {
19
- /**
20
- * Añade un elemento de audio personalizado
21
- * @example
22
- * editor.commands.setAudio({ src: '/audio.mp3', controls: true })
23
- */
24
36
  setAudio: (options: SetAudioOptions) => ReturnType;
25
37
  };
26
38
  }
27
39
  }
28
- export declare const inputRegex: RegExp;
40
+ /**
41
+ * Solo acepta: ![audio](url)
42
+ */
43
+ export declare const audioInputRegex: RegExp;
29
44
  export declare const Audio: Node<AudioOptions, any>;
@@ -1,7 +1,11 @@
1
- import { Node, mergeAttributes, nodeInputRule } from '@tiptap/core';
1
+ import { Node, mergeAttributes, nodeInputRule, } from '@tiptap/core';
2
+ import { Plugin } from '@tiptap/pm/state';
2
3
  import { SvelteNodeViewRenderer } from 'svelte-tiptap';
3
4
  import AudioPlayer from './AudioPlayerWrapper.svelte';
4
- export const inputRegex = /(?:^|\s)(!\[(.+|:?)]\((\S+)(?:(?:\s+)["'](\S+)["'])?\))$/;
5
+ /**
6
+ * Solo acepta: ![audio](url)
7
+ */
8
+ export const audioInputRegex = /(?:^|\s)(!\[audio\]\((\S+?)\))$/;
5
9
  export const Audio = Node.create({
6
10
  name: 'audio',
7
11
  addOptions() {
@@ -9,6 +13,17 @@ export const Audio = Node.create({
9
13
  inline: false,
10
14
  allowBase64: false,
11
15
  HTMLAttributes: {},
16
+ // 🎨 Defaults configurables para configure()
17
+ bgColor: '#8989891f',
18
+ textColor: 'currentColor',
19
+ borderRadius: '18px',
20
+ accentColor: '#5e17eb',
21
+ accentColorPaused: '#fff',
22
+ seekBarBgColor: '#8d8d8d3a',
23
+ playBtnBgColor: '#8d8d8d26',
24
+ playBtnTextColor: 'currentColor',
25
+ colorPlay: '#5d5d5dc9',
26
+ maxWidth: '100%',
12
27
  };
13
28
  },
14
29
  inline() {
@@ -21,109 +36,58 @@ export const Audio = Node.create({
21
36
  draggable: true,
22
37
  addAttributes() {
23
38
  return {
24
- src: {
25
- default: null,
26
- },
27
- controls: {
28
- default: true,
29
- parseHTML: el => el.hasAttribute('controls'),
30
- renderHTML: attrs => (attrs.controls ? { controls: true } : {}),
31
- },
32
- autoplay: {
33
- default: false,
34
- parseHTML: el => el.hasAttribute('autoplay'),
35
- renderHTML: attrs => (attrs.autoplay ? { autoplay: true } : {}),
36
- },
37
- loop: {
38
- default: false,
39
- parseHTML: el => el.hasAttribute('loop'),
40
- renderHTML: attrs => (attrs.loop ? { loop: true } : {}),
41
- },
39
+ src: { default: null },
40
+ controls: { default: true },
41
+ autoplay: { default: false },
42
+ loop: { default: false },
42
43
  id: {
43
- default: `fl-audio-player-${Math.random().toString(36).substring(2, 15)}`,
44
- parseHTML: el => el.getAttribute('data-id') || null,
45
- renderHTML: attrs => ({
46
- 'data-id': attrs.id,
47
- }),
44
+ default: `fl-audio-player-${Math.random().toString(36).slice(2)}`,
45
+ renderHTML: attrs => ({ 'data-id': attrs.id }),
48
46
  },
49
47
  bgColor: {
50
- default: '#8c8c8c45',
51
- parseHTML: el => el.getAttribute('data-bg-color'),
52
- renderHTML: attrs => ({
53
- 'data-bg-color': attrs.bgColor,
54
- }),
48
+ default: this.options.bgColor,
49
+ renderHTML: a => ({ 'data-bg-color': a.bgColor }),
55
50
  },
56
51
  textColor: {
57
- default: 'currentColor',
58
- parseHTML: el => el.getAttribute('data-text-color'),
59
- renderHTML: attrs => ({
60
- 'data-text-color': attrs.textColor,
61
- }),
52
+ default: this.options.textColor,
53
+ renderHTML: a => ({ 'data-text-color': a.textColor }),
62
54
  },
63
55
  borderRadius: {
64
- default: '18px',
65
- parseHTML: el => el.getAttribute('data-border-radius'),
66
- renderHTML: attrs => ({
67
- 'data-border-radius': attrs.borderRadius,
68
- }),
56
+ default: this.options.borderRadius,
57
+ renderHTML: a => ({ 'data-border-radius': a.borderRadius }),
69
58
  },
70
59
  accentColor: {
71
- default: '#5e17eb',
72
- parseHTML: el => el.getAttribute('data-accent-color'),
73
- renderHTML: attrs => ({
74
- 'data-accent-color': attrs.accentColor,
75
- }),
60
+ default: this.options.accentColor,
61
+ renderHTML: a => ({ 'data-accent-color': a.accentColor }),
76
62
  },
77
63
  accentColorPaused: {
78
- default: '#fff',
79
- parseHTML: el => el.getAttribute('data-accent-color-paused'),
80
- renderHTML: attrs => ({
81
- 'data-accent-color-paused': attrs.accentColorPaused,
82
- }),
64
+ default: this.options.accentColorPaused,
65
+ renderHTML: a => ({ 'data-accent-color-paused': a.accentColorPaused }),
83
66
  },
84
67
  seekBarBgColor: {
85
- default: '#8d8d8d3a',
86
- parseHTML: el => el.getAttribute('data-seek-bar-bg-color'),
87
- renderHTML: attrs => ({
88
- 'data-seek-bar-bg-color': attrs.seekBarBgColor,
89
- }),
68
+ default: this.options.seekBarBgColor,
69
+ renderHTML: a => ({ 'data-seek-bar-bg-color': a.seekBarBgColor }),
90
70
  },
91
71
  playBtnBgColor: {
92
- default: '#8d8d8d26',
93
- parseHTML: el => el.getAttribute('data-play-btn-bg-color'),
94
- renderHTML: attrs => ({
95
- 'data-play-btn-bg-color': attrs.playBtnBgColor,
96
- }),
72
+ default: this.options.playBtnBgColor,
73
+ renderHTML: a => ({ 'data-play-btn-bg-color': a.playBtnBgColor }),
97
74
  },
98
75
  playBtnTextColor: {
99
- default: 'currentColor',
100
- parseHTML: el => el.getAttribute('data-play-btn-text-color'),
101
- renderHTML: attrs => ({
102
- 'data-play-btn-text-color': attrs.playBtnTextColor,
103
- }),
76
+ default: this.options.playBtnTextColor,
77
+ renderHTML: a => ({ 'data-play-btn-text-color': a.playBtnTextColor }),
104
78
  },
105
79
  colorPlay: {
106
- default: '#5d5d5dc9',
107
- parseHTML: el => el.getAttribute('data-color-play'),
108
- renderHTML: attrs => ({
109
- 'data-color-play': attrs.colorPlay,
110
- }),
80
+ default: this.options.colorPlay,
81
+ renderHTML: a => ({ 'data-color-play': a.colorPlay }),
111
82
  },
112
83
  maxWidth: {
113
- default: '100%',
114
- parseHTML: el => el.getAttribute('data-max-width'),
115
- renderHTML: attrs => ({
116
- 'data-max-width': attrs.maxWidth,
117
- }),
84
+ default: this.options.maxWidth,
85
+ renderHTML: a => ({ 'data-max-width': a.maxWidth }),
118
86
  },
119
87
  };
120
88
  },
121
89
  parseHTML() {
122
- return [
123
- {
124
- tag: 'fl-audio-player',
125
- },
126
- ];
90
+ return [{ tag: 'fl-audio-player' }];
127
91
  },
128
92
  renderHTML({ HTMLAttributes }) {
129
93
  return [
@@ -136,24 +100,52 @@ export const Audio = Node.create({
136
100
  },
137
101
  addCommands() {
138
102
  return {
139
- setAudio: options => ({ commands }) => {
140
- return commands.insertContent({
141
- type: this.name,
142
- attrs: options,
143
- });
144
- },
103
+ setAudio: options => ({ commands }) => commands.insertContent({
104
+ type: this.name,
105
+ attrs: options,
106
+ }),
145
107
  };
146
108
  },
109
+ /**
110
+ * ✍️ Al escribir: ![audio](url)
111
+ */
147
112
  addInputRules() {
148
113
  return [
149
114
  nodeInputRule({
150
- find: inputRegex,
115
+ find: audioInputRegex,
151
116
  type: this.type,
152
117
  getAttributes: match => {
153
- const [, , , src] = match;
118
+ const [, , src] = match;
154
119
  return { src, controls: true };
155
120
  },
156
121
  }),
157
122
  ];
158
123
  },
124
+ /**
125
+ * 📋 Al pegar: ![audio](url)
126
+ */
127
+ addProseMirrorPlugins() {
128
+ return [
129
+ new Plugin({
130
+ props: {
131
+ handlePaste: (view, event) => {
132
+ const text = event.clipboardData?.getData('text/plain');
133
+ if (!text)
134
+ return false;
135
+ const match = text.match(/!\[audio\]\((\S+?)\)/);
136
+ if (!match)
137
+ return false;
138
+ const [, src] = match;
139
+ const { state, dispatch } = view;
140
+ const { from, to } = state.selection;
141
+ dispatch(state.tr.replaceRangeWith(from, to, this.type.create({
142
+ src,
143
+ controls: true,
144
+ })));
145
+ return true;
146
+ },
147
+ },
148
+ }),
149
+ ];
150
+ },
159
151
  });
@@ -498,7 +498,7 @@ style={styleVars}
498
498
 
499
499
  .audio-player {
500
500
  --player-primary-color: #5e17eb;
501
- --player-bg-color: #8c8c8c45;
501
+ --player-bg-color: #8989891f;
502
502
  --player-border-radius: 18px;
503
503
  --player-seekbar-bg: #8d8d8d3a;
504
504
  --player-seekbar-height: 6px;
@@ -516,6 +516,7 @@ style={styleVars}
516
516
  width: 100%;
517
517
  box-sizing: border-box;
518
518
  color: var(--player-text-color);
519
+ margin-block: 1em;
519
520
 
520
521
  &.playing {
521
522
  .audio-player-wave {
@@ -106,6 +106,12 @@
106
106
  display: flex;
107
107
  align-items: center;
108
108
  position: relative;
109
+ margin-top: 1em;
110
+ margin-bottom: 1em;
111
+
112
+ :global(.audio-player) {
113
+ margin: 0
114
+ }
109
115
  }
110
116
 
111
117
  .resize-grab {
@@ -0,0 +1,27 @@
1
+ import type { Editor } from '@tiptap/core';
2
+ import { Extension } from '@tiptap/core';
3
+ import type { Node as ProsemirrorNode } from '@tiptap/pm/model';
4
+ export interface PlaceholderOptions {
5
+ emptyEditorClass: string;
6
+ emptyNodeClass: string;
7
+ placeholder: ((props: {
8
+ editor: Editor;
9
+ node: ProsemirrorNode;
10
+ pos: number;
11
+ hasAnchor: boolean;
12
+ }) => string) | string;
13
+ showOnlyWhenEditable: boolean;
14
+ showOnlyCurrent: boolean | ((props: {
15
+ editor: Editor;
16
+ node: ProsemirrorNode;
17
+ pos: number;
18
+ hasAnchor: boolean;
19
+ }) => boolean);
20
+ includeChildren: boolean | ((props: {
21
+ editor: Editor;
22
+ node: ProsemirrorNode;
23
+ pos: number;
24
+ hasAnchor: boolean;
25
+ }) => boolean);
26
+ }
27
+ export declare const Placeholder: Extension<PlaceholderOptions, any>;
@@ -0,0 +1,61 @@
1
+ import { Extension, isNodeEmpty } from '@tiptap/core';
2
+ import { Plugin, PluginKey } from '@tiptap/pm/state';
3
+ import { Decoration, DecorationSet } from '@tiptap/pm/view';
4
+ export const Placeholder = Extension.create({
5
+ name: 'placeholder',
6
+ addOptions() {
7
+ return {
8
+ emptyEditorClass: 'is-editor-empty',
9
+ emptyNodeClass: 'is-empty',
10
+ placeholder: 'Write something …',
11
+ showOnlyWhenEditable: true,
12
+ showOnlyCurrent: true,
13
+ includeChildren: false,
14
+ };
15
+ },
16
+ addProseMirrorPlugins() {
17
+ return [
18
+ new Plugin({
19
+ key: new PluginKey('placeholder'),
20
+ props: {
21
+ decorations: ({ doc, selection }) => {
22
+ const active = this.editor.isEditable || !this.options.showOnlyWhenEditable;
23
+ const { anchor } = selection;
24
+ const decorations = [];
25
+ if (!active) {
26
+ return null;
27
+ }
28
+ const isEmptyDoc = this.editor.isEmpty;
29
+ doc.descendants((node, pos) => {
30
+ const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize;
31
+ const isEmpty = !node.isLeaf && isNodeEmpty(node);
32
+ // Evaluamos showOnlyCurrent dinámico
33
+ const showCurrent = typeof this.options.showOnlyCurrent === 'function'
34
+ ? this.options.showOnlyCurrent({ editor: this.editor, node, pos, hasAnchor })
35
+ : this.options.showOnlyCurrent;
36
+ // Evaluamos includeChildren dinámico
37
+ const includeChildren = typeof this.options.includeChildren === 'function'
38
+ ? this.options.includeChildren({ editor: this.editor, node, pos, hasAnchor })
39
+ : this.options.includeChildren;
40
+ if ((hasAnchor || !showCurrent) && isEmpty) {
41
+ const classes = [this.options.emptyNodeClass];
42
+ if (isEmptyDoc) {
43
+ classes.push(this.options.emptyEditorClass);
44
+ }
45
+ const decoration = Decoration.node(pos, pos + node.nodeSize, {
46
+ class: classes.join(' '),
47
+ 'data-placeholder': typeof this.options.placeholder === 'function'
48
+ ? this.options.placeholder({ editor: this.editor, node, pos, hasAnchor })
49
+ : this.options.placeholder,
50
+ });
51
+ decorations.push(decoration);
52
+ }
53
+ return includeChildren;
54
+ });
55
+ return DecorationSet.create(doc, decorations);
56
+ },
57
+ },
58
+ }),
59
+ ];
60
+ },
61
+ });
@@ -47,14 +47,13 @@
47
47
  }
48
48
 
49
49
  editor.on("update", ({ transaction }) => {
50
- console.log({ NODETYPE: node.type.name });
51
- if (node.type.name !== "tableCell" && node.type.name !== "tableHeader")
50
+ if (node?.type?.name !== "tableCell" && node?.type?.name !== "tableHeader")
52
51
  return;
53
52
 
54
53
  const nodeEl = editor.view.nodeDOM(getPos());
55
- console.log(nodeEl);
54
+ // console.log(nodeEl);
56
55
 
57
- console.log("update", transaction);
56
+ // console.log("update", transaction);
58
57
 
59
58
  const attrs = node.attrs;
60
59
  const { colspan, rowspan, colwidth } = attrs;
@@ -510,7 +509,7 @@
510
509
  justify-content: center;
511
510
  padding: 0;
512
511
  color: #ffffff;
513
- background: #3c3c3c;
512
+ background: #8989891f;
514
513
  border: none;
515
514
  outline: 1px solid #6e6e6e;
516
515
  z-index: 5;
@@ -539,7 +538,7 @@
539
538
  letter-spacing: 2px;
540
539
  padding: 0;
541
540
  color: #ffffff;
542
- background: #3c3c3c;
541
+ background: #8989891f;
543
542
  border: none;
544
543
  outline: 1px solid #6e6e6e;
545
544
  z-index: 5;
@@ -14,9 +14,16 @@ import { MediaGridItemExtension } from "./extensions/MediaGrid/MediaGridItem";
14
14
  import { CustomTableCell } from "./extensions/Table/CustomTableCell";
15
15
  import { CustomTableHeader } from "./extensions/Table/CustomTableHeader";
16
16
  import { EnhancedLink } from "./extensions/EnhancedLink";
17
+ import { Heading } from "@tiptap/extension-heading";
18
+ const DocHeading = Heading.extend({
19
+ name: "h1",
20
+ group: "none", // <- prevent it from being considered as a `block` in the body of the document
21
+ }).configure({ levels: [1] });
17
22
  export function getRichTextExtensions(options) {
18
23
  const { editable = false, customExtensions = [] } = options ?? {};
19
24
  return [
25
+ DocHeading,
26
+ Heading.configure({ levels: [2, 3, 4, 5, 6] }),
20
27
  Highlight.configure({ multicolor: true }),
21
28
  TextStyleKit,
22
29
  StarterKit.configure({
@@ -35,6 +42,7 @@ export function getRichTextExtensions(options) {
35
42
  ListKit,
36
43
  TextAlign.configure({
37
44
  types: [
45
+ "h1",
38
46
  "heading",
39
47
  "paragraph",
40
48
  "bulletList",
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  import RichText from './RichText.svelte';
2
2
  import { renderHTMLFromJSON } from './renderRichText';
3
- export { RichText, renderHTMLFromJSON };
3
+ import { Placeholder as PlaceholderExt } from './extensions/Placeholder';
4
+ import { Audio as AudioExt } from './extensions/Audio';
5
+ export { RichText, PlaceholderExt, AudioExt, renderHTMLFromJSON };
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  import RichText from './RichText.svelte';
2
2
  import { renderHTMLFromJSON } from './renderRichText';
3
- export { RichText, renderHTMLFromJSON };
3
+ import { Placeholder as PlaceholderExt } from './extensions/Placeholder';
4
+ import { Audio as AudioExt } from './extensions/Audio';
5
+ export { RichText, PlaceholderExt, AudioExt, renderHTMLFromJSON };
package/dist/styles.css CHANGED
@@ -35,42 +35,6 @@
35
35
  margin-bottom: 0;
36
36
  }
37
37
 
38
- /* Heading styles */
39
- /* h1,
40
- h2,
41
- h3,
42
- h4,
43
- h5,
44
- h6 {
45
- line-height: 1.3;
46
- margin-top: 1.5rem;
47
- text-wrap: pretty;
48
- }
49
-
50
- h1,
51
- h2 {
52
- margin-top: 1.5rem;
53
- margin-bottom: 1.5rem;
54
- }
55
-
56
- h1 {
57
- font-size: 1.4rem;
58
- }
59
-
60
- h2 {
61
- font-size: 1.2rem;
62
- }
63
-
64
- h3 {
65
- font-size: 1.1rem;
66
- }
67
-
68
- h4,
69
- h5,
70
- h6 {
71
- font-size: 1rem;
72
- } */
73
-
74
38
  /* Code and preformatted text styles */
75
39
  code {
76
40
  background-color: var(--purple-light);
@@ -255,6 +219,14 @@
255
219
  cursor: ew-resize;
256
220
  cursor: col-resize;
257
221
  }
222
+
223
+ .is-empty::before {
224
+ color: #8a8a8aa8;
225
+ content: attr(data-placeholder);
226
+ float: left;
227
+ height: 0;
228
+ pointer-events: none;
229
+ }
258
230
  }
259
231
 
260
232
  .fl-rich-text-content {
@@ -276,6 +248,7 @@
276
248
  max-width: var(--fl-doc-max-width, 1024px);
277
249
  margin-inline: var(--fl-doc-margin-inline, auto);
278
250
  margin-block: var(--fl-doc-margin-block, 2rem);
251
+ box-sizing: border-box;
279
252
  }
280
253
 
281
254
  .fl-toolbar-dropdown-panel {
@@ -496,7 +469,6 @@
496
469
  min-height: 32px;
497
470
 
498
471
  & button {
499
- background: transparent;
500
472
  border: none;
501
473
  display: flex;
502
474
  align-items: center;
@@ -507,10 +479,10 @@
507
479
  min-height: 18px;
508
480
  min-width: 18px;
509
481
  border-radius: 100%;
510
- background: #474747;
482
+ background: #8989891f;
511
483
 
512
484
  &:hover {
513
- background: rgb(104, 104, 104);
485
+ background: #89898966;
514
486
  }
515
487
 
516
488
  & svg {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flexiui/svelte-rich-text",
3
- "version": "0.0.40",
3
+ "version": "0.0.42",
4
4
  "description": "A lightweight and flexible rich text editor component for Svelte",
5
5
  "keywords": [
6
6
  "svelte",