edytor 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +225 -5
- package/dist/block/block.svelte.d.ts +123 -0
- package/dist/block/block.svelte.js +430 -0
- package/dist/block/block.utils.d.ts +82 -0
- package/dist/block/block.utils.js +499 -0
- package/dist/block/content.svelte.d.ts +14 -0
- package/dist/block/content.svelte.js +40 -0
- package/dist/block/inlineBlock.svelte.d.ts +30 -0
- package/dist/block/inlineBlock.svelte.js +53 -0
- package/dist/components/Block.svelte +27 -0
- package/dist/components/Block.svelte.d.ts +7 -0
- package/dist/components/Content.svelte +44 -0
- package/dist/components/Content.svelte.d.ts +7 -0
- package/dist/components/Edytor.svelte +165 -0
- package/dist/components/Edytor.svelte.d.ts +33 -0
- package/dist/components/InlineBlock.svelte +17 -0
- package/dist/components/InlineBlock.svelte.d.ts +7 -0
- package/dist/components/Mark.svelte +38 -0
- package/dist/components/Mark.svelte.d.ts +11 -0
- package/dist/components/ReadonlyEditor.svelte +46 -0
- package/dist/components/ReadonlyEditor.svelte.d.ts +4 -0
- package/dist/components/Text.svelte +37 -0
- package/dist/components/Text.svelte.d.ts +7 -0
- package/dist/components/readonlyElements.svelte.d.ts +82 -0
- package/dist/components/readonlyElements.svelte.js +144 -0
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +4 -0
- package/dist/dnd.svelte.d.ts +17 -0
- package/dist/dnd.svelte.js +125 -0
- package/dist/edytor.svelte.d.ts +94 -0
- package/dist/edytor.svelte.js +262 -0
- package/dist/edytor.utils.d.ts +3 -0
- package/dist/edytor.utils.js +78 -0
- package/dist/events/onBeforeInput.d.ts +2 -2
- package/dist/events/onBeforeInput.js +338 -0
- package/dist/events/onKeyDown.d.ts +2 -69
- package/dist/events/onKeyDown.js +18 -0
- package/dist/hotkeys.d.ts +27 -0
- package/dist/hotkeys.js +282 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.js +7 -8
- package/dist/localProvider.d.ts +82 -0
- package/dist/localProvider.js +267 -0
- package/dist/plugins/arrowMove/arrowMove.d.ts +2 -0
- package/dist/plugins/arrowMove/arrowMove.js +66 -0
- package/dist/plugins/code/CodePlugin.svelte +218 -0
- package/dist/plugins/code/CodePlugin.svelte.d.ts +23 -0
- package/dist/plugins/code/prism.css +141 -0
- package/dist/plugins/image/ImagePlugin.svelte +22 -0
- package/dist/plugins/image/ImagePlugin.svelte.d.ts +20 -0
- package/dist/plugins/index.d.ts +3 -0
- package/dist/plugins/index.js +3 -0
- package/dist/plugins/mention/MentionPlugin.svelte +40 -0
- package/dist/plugins/mention/MentionPlugin.svelte.d.ts +20 -0
- package/dist/plugins/richtext/RichTextPlugin.svelte +241 -0
- package/dist/plugins/richtext/RichTextPlugin.svelte.d.ts +26 -0
- package/dist/plugins.d.ts +202 -0
- package/dist/plugins.js +19 -0
- package/dist/selection/selection.svelte.d.ts +68 -0
- package/dist/selection/selection.svelte.js +391 -0
- package/dist/selection/selection.utils.d.ts +14 -0
- package/dist/selection/selection.utils.js +153 -0
- package/dist/text/deltas.d.ts +11 -0
- package/dist/text/deltas.js +109 -0
- package/dist/text/text.svelte.d.ts +70 -0
- package/dist/text/text.svelte.js +119 -0
- package/dist/text/text.utils.d.ts +40 -0
- package/dist/text/text.utils.js +174 -0
- package/dist/utils/json.d.ts +32 -0
- package/dist/utils/json.js +2 -0
- package/dist/utils/jsx.d.ts +25 -0
- package/dist/utils/jsx.js +66 -0
- package/dist/utils/serialize.d.ts +1 -0
- package/dist/utils/serialize.js +1 -0
- package/dist/utils.d.ts +6 -0
- package/dist/utils.js +19 -0
- package/package.json +81 -33
- package/dist/edytor.cjs.development.js +0 -1762
- package/dist/edytor.cjs.development.js.map +0 -1
- package/dist/edytor.cjs.production.min.js +0 -2
- package/dist/edytor.cjs.production.min.js.map +0 -1
- package/dist/edytor.esm.js +0 -1691
- package/dist/edytor.esm.js.map +0 -1
- package/dist/events/index.d.ts +0 -3
- package/dist/events/onDrop.d.ts +0 -3
- package/dist/operations/deleteText.d.ts +0 -7
- package/dist/operations/formatText.d.ts +0 -20
- package/dist/operations/index.d.ts +0 -9
- package/dist/operations/insertText.d.ts +0 -5
- package/dist/operations/merge.d.ts +0 -3
- package/dist/operations/moveNode.d.ts +0 -12
- package/dist/operations/nestNode.d.ts +0 -2
- package/dist/operations/removeEmptyText.d.ts +0 -2
- package/dist/operations/splitLeaf.d.ts +0 -2
- package/dist/operations/splitNode.d.ts +0 -9
- package/dist/types.d.ts +0 -140
- package/dist/utils/common.d.ts +0 -17
- package/dist/utils/dropper.d.ts +0 -26
- package/dist/utils/index.d.ts +0 -6
- package/dist/utils/leaves.d.ts +0 -34
- package/dist/utils/nodes.d.ts +0 -14
- package/dist/utils/selection.d.ts +0 -51
- package/dist/utils/yClasses/EdytorDoc.d.ts +0 -16
- package/dist/utils/yClasses/index.d.ts +0 -3
- package/dist/utils/yClasses/yLeaf.d.ts +0 -7
- package/dist/utils/yClasses/yNode.d.ts +0 -12
- package/dist/yjs/index.d.ts +0 -1
- package/dist/yjs/wsProvider.d.ts +0 -1
- package/index.ts +0 -1
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Block } from '../block/block.svelte.js';
|
|
3
|
+
import { Text } from '../text/text.svelte.js';
|
|
4
|
+
import RenderText from './Text.svelte';
|
|
5
|
+
import RenderInlineBlock from './InlineBlock.svelte';
|
|
6
|
+
import { InlineBlock } from '../block/inlineBlock.svelte.js';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
block
|
|
10
|
+
}: {
|
|
11
|
+
block: Block;
|
|
12
|
+
} = $props();
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
{#snippet renderContent(content: (Text | InlineBlock)[])}
|
|
16
|
+
{#each content as blockOrText (blockOrText.id)}
|
|
17
|
+
{#if 'children' in blockOrText}
|
|
18
|
+
<RenderText text={blockOrText} />
|
|
19
|
+
{:else}
|
|
20
|
+
<RenderInlineBlock block={blockOrText} />
|
|
21
|
+
{/if}
|
|
22
|
+
{/each}
|
|
23
|
+
{/snippet}
|
|
24
|
+
{@render renderContent(
|
|
25
|
+
block.content
|
|
26
|
+
)}<!--
|
|
27
|
+
-->{#if block?.content.length === 1 && (block.content[0] as Text)?.isEmpty && block.edytor.placeholder}
|
|
28
|
+
<span data-edytor-text-placeholder contentEditable="false" style="user-select: none;">
|
|
29
|
+
{#if typeof block.edytor.placeholder === 'string'}
|
|
30
|
+
{block.edytor.placeholder}
|
|
31
|
+
{:else}
|
|
32
|
+
{@render block.edytor.placeholder({ block })}
|
|
33
|
+
{/if}
|
|
34
|
+
</span>
|
|
35
|
+
{/if}<!--
|
|
36
|
+
-->{#if block?.suggestions}
|
|
37
|
+
<span
|
|
38
|
+
data-edytor-text-suggestion
|
|
39
|
+
contentEditable="false"
|
|
40
|
+
style="user-select: none; pointer-events: none"
|
|
41
|
+
>
|
|
42
|
+
{@render renderContent(block.suggestions)}
|
|
43
|
+
</span>
|
|
44
|
+
{/if}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { Edytor, useEdytor, type Snippets } from '../edytor.svelte.js';
|
|
3
|
+
import { Awareness } from 'y-protocols/awareness';
|
|
4
|
+
export { Edytor as EdytorContext, useEdytor };
|
|
5
|
+
import type { Plugin } from '../plugins.js';
|
|
6
|
+
import type { Block as BlockType } from '../block/block.svelte.js';
|
|
7
|
+
const defaultValue: JSONDoc = {
|
|
8
|
+
children: [
|
|
9
|
+
{
|
|
10
|
+
type: 'paragraph',
|
|
11
|
+
content: [{ text: 'One', marks: { bold: true } }]
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
{
|
|
15
|
+
type: 'image',
|
|
16
|
+
content: [{ text: 'Caption' }]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
type: 'paragraph',
|
|
20
|
+
content: [
|
|
21
|
+
{ text: 'One', marks: { bold: true } },
|
|
22
|
+
{ text: ' Two', marks: { italic: true } }
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
type: 'paragraph',
|
|
27
|
+
content: [{ text: '' }]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
type: 'paragraph',
|
|
31
|
+
content: [
|
|
32
|
+
{ text: 'Three', marks: { italic: true } },
|
|
33
|
+
{ text: ' Four', marks: { bold: true } },
|
|
34
|
+
{ text: '' }
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
{
|
|
39
|
+
type: 'paragraph',
|
|
40
|
+
content: [
|
|
41
|
+
{ text: 'Hello', marks: { bold: true } },
|
|
42
|
+
{ text: '', marks: { void: true } },
|
|
43
|
+
{ text: ' World', marks: { bold: true } }
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
type: 'paragraph',
|
|
48
|
+
content: [
|
|
49
|
+
{ text: 'hello', marks: { bold: true } },
|
|
50
|
+
{
|
|
51
|
+
type: 'mention'
|
|
52
|
+
},
|
|
53
|
+
{ text: 'World', marks: { bold: true } },
|
|
54
|
+
{
|
|
55
|
+
type: 'mention'
|
|
56
|
+
},
|
|
57
|
+
{ text: 'Prout', marks: { bold: true } }
|
|
58
|
+
],
|
|
59
|
+
|
|
60
|
+
children: [
|
|
61
|
+
{
|
|
62
|
+
type: 'paragraph',
|
|
63
|
+
content: [{ text: 'One', marks: { bold: true } }],
|
|
64
|
+
children: [
|
|
65
|
+
{
|
|
66
|
+
type: 'paragraph',
|
|
67
|
+
content: [{ text: 'Two', marks: { bold: true } }]
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: 'code',
|
|
75
|
+
content: [{ text: 'caption yo' }],
|
|
76
|
+
children: [{ type: 'codeLine', content: [{ text: '\t\tconsole.log("hello")' }] }]
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type EdytorProps = Snippets & {
|
|
82
|
+
plugins?: Plugin[];
|
|
83
|
+
class?: string;
|
|
84
|
+
edytor?: Edytor;
|
|
85
|
+
doc?: Y.Doc;
|
|
86
|
+
awareness?: Awareness;
|
|
87
|
+
readonly?: boolean;
|
|
88
|
+
hotKeys?: Record<string, HotKey>;
|
|
89
|
+
onChange?: (value: JSONBlock) => void;
|
|
90
|
+
onSelectionChange?: (selection: EdytorSelection) => void;
|
|
91
|
+
value?: JSONDoc;
|
|
92
|
+
placeholder?: string | Snippet<[{ block: BlockType }]>;
|
|
93
|
+
sync?: ({
|
|
94
|
+
doc,
|
|
95
|
+
awareness,
|
|
96
|
+
synced
|
|
97
|
+
}: {
|
|
98
|
+
doc: Y.Doc;
|
|
99
|
+
awareness: Awareness;
|
|
100
|
+
synced: (provider?: any) => void;
|
|
101
|
+
}) => void;
|
|
102
|
+
};
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
<script lang="ts">
|
|
106
|
+
import * as Y from 'yjs';
|
|
107
|
+
import type { JSONBlock, JSONDoc } from '../utils/json.js';
|
|
108
|
+
import { onMount, setContext, type Snippet } from 'svelte';
|
|
109
|
+
import type { HotKey } from '../hotkeys.js';
|
|
110
|
+
import type { EdytorSelection } from '../selection/selection.svelte.js';
|
|
111
|
+
import ReadonlyEditor from './ReadonlyEditor.svelte';
|
|
112
|
+
import Block from './Block.svelte';
|
|
113
|
+
|
|
114
|
+
let {
|
|
115
|
+
plugins,
|
|
116
|
+
class: className,
|
|
117
|
+
edytor = $bindable(),
|
|
118
|
+
doc,
|
|
119
|
+
readonly = false,
|
|
120
|
+
value = $bindable(defaultValue),
|
|
121
|
+
hotKeys,
|
|
122
|
+
sync,
|
|
123
|
+
awareness,
|
|
124
|
+
onChange,
|
|
125
|
+
onSelectionChange,
|
|
126
|
+
placeholder,
|
|
127
|
+
...snippets
|
|
128
|
+
}: EdytorProps = $props();
|
|
129
|
+
|
|
130
|
+
edytor = new Edytor({
|
|
131
|
+
snippets,
|
|
132
|
+
readonly,
|
|
133
|
+
plugins,
|
|
134
|
+
doc,
|
|
135
|
+
awareness,
|
|
136
|
+
hotKeys,
|
|
137
|
+
onSelectionChange,
|
|
138
|
+
onChange,
|
|
139
|
+
sync: !!sync,
|
|
140
|
+
value,
|
|
141
|
+
placeholder
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
onMount(() => {
|
|
145
|
+
if (!readonly && sync) {
|
|
146
|
+
sync({
|
|
147
|
+
doc: edytor.doc,
|
|
148
|
+
awareness: edytor.awareness,
|
|
149
|
+
synced: () => {
|
|
150
|
+
edytor.sync(value);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
setContext('edytor', edytor);
|
|
157
|
+
</script>
|
|
158
|
+
|
|
159
|
+
{#if edytor.synced || readonly}
|
|
160
|
+
<div class={className} use:edytor.attach data-edytor contenteditable={!readonly}>
|
|
161
|
+
{#each edytor.root?.children || [] as block (block.id)}
|
|
162
|
+
<Block {block} />
|
|
163
|
+
{/each}
|
|
164
|
+
</div>
|
|
165
|
+
{/if}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Edytor, useEdytor, type Snippets } from '../edytor.svelte.js';
|
|
2
|
+
import { Awareness } from 'y-protocols/awareness';
|
|
3
|
+
export { Edytor as EdytorContext, useEdytor };
|
|
4
|
+
import type { Plugin } from '../plugins.js';
|
|
5
|
+
import type { Block as BlockType } from '../block/block.svelte.js';
|
|
6
|
+
export type EdytorProps = Snippets & {
|
|
7
|
+
plugins?: Plugin[];
|
|
8
|
+
class?: string;
|
|
9
|
+
edytor?: Edytor;
|
|
10
|
+
doc?: Y.Doc;
|
|
11
|
+
awareness?: Awareness;
|
|
12
|
+
readonly?: boolean;
|
|
13
|
+
hotKeys?: Record<string, HotKey>;
|
|
14
|
+
onChange?: (value: JSONBlock) => void;
|
|
15
|
+
onSelectionChange?: (selection: EdytorSelection) => void;
|
|
16
|
+
value?: JSONDoc;
|
|
17
|
+
placeholder?: string | Snippet<[{
|
|
18
|
+
block: BlockType;
|
|
19
|
+
}]>;
|
|
20
|
+
sync?: ({ doc, awareness, synced }: {
|
|
21
|
+
doc: Y.Doc;
|
|
22
|
+
awareness: Awareness;
|
|
23
|
+
synced: (provider?: any) => void;
|
|
24
|
+
}) => void;
|
|
25
|
+
};
|
|
26
|
+
import * as Y from 'yjs';
|
|
27
|
+
import type { JSONBlock, JSONDoc } from '../utils/json.js';
|
|
28
|
+
import { type Snippet } from 'svelte';
|
|
29
|
+
import type { HotKey } from '../hotkeys.js';
|
|
30
|
+
import type { EdytorSelection } from '../selection/selection.svelte.js';
|
|
31
|
+
declare const Edytor: import("svelte").Component<EdytorProps, {}, "edytor" | "value">;
|
|
32
|
+
type Edytor = ReturnType<typeof Edytor>;
|
|
33
|
+
export default Edytor;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { InlineBlock } from '../block/inlineBlock.svelte.js';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
block
|
|
6
|
+
}: {
|
|
7
|
+
block: InlineBlock;
|
|
8
|
+
} = $props();
|
|
9
|
+
|
|
10
|
+
const snippet = $derived(block.definition.snippet);
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
{#if snippet}
|
|
14
|
+
<span data-edytor-inline-block use:block.attach>
|
|
15
|
+
{@render snippet({ block })}
|
|
16
|
+
</span>
|
|
17
|
+
{/if}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { InlineBlock } from '../block/inlineBlock.svelte.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
block: InlineBlock;
|
|
4
|
+
};
|
|
5
|
+
declare const InlineBlock: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
6
|
+
type InlineBlock = ReturnType<typeof InlineBlock>;
|
|
7
|
+
export default InlineBlock;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Text } from '../text/text.svelte.js';
|
|
3
|
+
import Mark from './Mark.svelte';
|
|
4
|
+
import type { JSONDelta } from '../text/deltas.js';
|
|
5
|
+
|
|
6
|
+
let {
|
|
7
|
+
delta,
|
|
8
|
+
text,
|
|
9
|
+
index
|
|
10
|
+
}: {
|
|
11
|
+
text: Text;
|
|
12
|
+
delta: JSONDelta;
|
|
13
|
+
index: number;
|
|
14
|
+
} = $props();
|
|
15
|
+
|
|
16
|
+
const mark = $derived(delta.marks[index]);
|
|
17
|
+
const definition = $derived(text.edytor.marks.get(mark?.[0]));
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
{#snippet content()}
|
|
21
|
+
{#if delta.marks[index + 1]}
|
|
22
|
+
<Mark index={index + 1} {delta} {text} />
|
|
23
|
+
{:else}
|
|
24
|
+
{delta.text}
|
|
25
|
+
{/if}
|
|
26
|
+
{/snippet}
|
|
27
|
+
|
|
28
|
+
{#if definition?.snippet}
|
|
29
|
+
{#if definition.void}
|
|
30
|
+
<span data-edytor-mark-void contenteditable="false">
|
|
31
|
+
{@render definition.snippet({ content, mark: mark?.[1], text })}
|
|
32
|
+
</span>
|
|
33
|
+
{:else}
|
|
34
|
+
{@render definition.snippet({ content, mark: mark?.[1], text })}
|
|
35
|
+
{/if}
|
|
36
|
+
{:else}
|
|
37
|
+
{@render content()}
|
|
38
|
+
{/if}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Text } from '../text/text.svelte.js';
|
|
2
|
+
import Mark from './Mark.svelte';
|
|
3
|
+
import type { JSONDelta } from '../text/deltas.js';
|
|
4
|
+
type $$ComponentProps = {
|
|
5
|
+
text: Text;
|
|
6
|
+
delta: JSONDelta;
|
|
7
|
+
index: number;
|
|
8
|
+
};
|
|
9
|
+
declare const Mark: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
10
|
+
type Mark = ReturnType<typeof Mark>;
|
|
11
|
+
export default Mark;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { JSONBlock } from '../utils/json.js';
|
|
3
|
+
import { type EdytorProps } from './Edytor.svelte';
|
|
4
|
+
import { createReadonlyBlock } from './readonlyElements.svelte.js';
|
|
5
|
+
import Block from './Block.svelte';
|
|
6
|
+
import { Edytor } from '../edytor.svelte.js';
|
|
7
|
+
let {
|
|
8
|
+
value,
|
|
9
|
+
class: className,
|
|
10
|
+
plugins,
|
|
11
|
+
...snippets
|
|
12
|
+
}: Omit<
|
|
13
|
+
EdytorProps,
|
|
14
|
+
| 'awareness'
|
|
15
|
+
| 'readonly'
|
|
16
|
+
| 'hotKeys'
|
|
17
|
+
| 'onChange'
|
|
18
|
+
| 'onSelectionChange'
|
|
19
|
+
| 'sync'
|
|
20
|
+
| 'edytor'
|
|
21
|
+
| 'doc'
|
|
22
|
+
> = $props();
|
|
23
|
+
|
|
24
|
+
const generateReadonlyBlock = (block: JSONBlock) => {
|
|
25
|
+
return createReadonlyBlock({
|
|
26
|
+
block,
|
|
27
|
+
edytor,
|
|
28
|
+
parent: edytor
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const children = $derived(value!.children.map(generateReadonlyBlock));
|
|
33
|
+
|
|
34
|
+
const edytor = new Edytor({
|
|
35
|
+
snippets,
|
|
36
|
+
readonly: true,
|
|
37
|
+
plugins,
|
|
38
|
+
value
|
|
39
|
+
});
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<div class={className} data-edytor contenteditable="false">
|
|
43
|
+
{#each children || [] as block (block.id)}
|
|
44
|
+
<Block {block} />
|
|
45
|
+
{/each}
|
|
46
|
+
</div>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type EdytorProps } from './Edytor.svelte';
|
|
2
|
+
declare const ReadonlyEditor: import("svelte").Component<Omit<EdytorProps, "edytor" | "hotKeys" | "readonly" | "doc" | "awareness" | "sync" | "onSelectionChange" | "onChange">, {}, "">;
|
|
3
|
+
type ReadonlyEditor = ReturnType<typeof ReadonlyEditor>;
|
|
4
|
+
export default ReadonlyEditor;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Text } from '../text/text.svelte.js';
|
|
3
|
+
import Mark from './Mark.svelte';
|
|
4
|
+
let {
|
|
5
|
+
text
|
|
6
|
+
}: {
|
|
7
|
+
text: Text;
|
|
8
|
+
} = $props();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<!-- The comment blocks are needed to prevent unwanted text nodes with whitespace. -->
|
|
12
|
+
<!-- thanks for the tip: https://github.com/michael/svedit/blob/main/src/lib/Text.svelte -->
|
|
13
|
+
|
|
14
|
+
<span use:text.attach style:white-space="break-spaces"
|
|
15
|
+
><!--
|
|
16
|
+
-->{#if text.isEmpty}<!--
|
|
17
|
+
-->​<!--
|
|
18
|
+
-->{:else}<!--
|
|
19
|
+
-->{#each text.children as delta (delta.id)}<!--
|
|
20
|
+
-->{#if delta.marks.length}<!--
|
|
21
|
+
--><Mark
|
|
22
|
+
{delta}
|
|
23
|
+
index={0}
|
|
24
|
+
{text}
|
|
25
|
+
/><!--
|
|
26
|
+
-->{:else}<!--
|
|
27
|
+
-->{delta.text}<!--
|
|
28
|
+
-->{/if}<!--
|
|
29
|
+
-->{/each}<!--
|
|
30
|
+
-->{/if}<!--
|
|
31
|
+
-->{#if text.endsWithNewline}<!--
|
|
32
|
+
--><span
|
|
33
|
+
class="newline">​</span
|
|
34
|
+
><!--
|
|
35
|
+
-->{/if}<!--
|
|
36
|
+
--></span
|
|
37
|
+
>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { InlineBlock } from '../block/inlineBlock.svelte.js';
|
|
2
|
+
import type { BlockDefinition, InlineBlockDefinition } from '../plugins.js';
|
|
3
|
+
import { type JSONDelta } from '../text/deltas.js';
|
|
4
|
+
import type { JSONBlock, JSONInlineBlock, JSONText } from '../utils/json.js';
|
|
5
|
+
import type { Block } from '../block/block.svelte.js';
|
|
6
|
+
import type { Edytor } from '../edytor.svelte.js';
|
|
7
|
+
import type { Text } from '../text/text.svelte.js';
|
|
8
|
+
declare class ReadonlyBlock {
|
|
9
|
+
readonly: boolean;
|
|
10
|
+
edytor: Edytor;
|
|
11
|
+
parent: ReadonlyBlock | Block | Edytor;
|
|
12
|
+
type: string;
|
|
13
|
+
value: JSONBlock;
|
|
14
|
+
children: ReadonlyBlock[];
|
|
15
|
+
content: (ReadonlyText | ReadonlyInlineBlock)[];
|
|
16
|
+
id: string;
|
|
17
|
+
definition: BlockDefinition;
|
|
18
|
+
selected: boolean;
|
|
19
|
+
focused: boolean;
|
|
20
|
+
insideIsland: boolean;
|
|
21
|
+
isEmpty: boolean;
|
|
22
|
+
hasChildren: boolean;
|
|
23
|
+
hasContent: boolean;
|
|
24
|
+
node: HTMLElement | undefined;
|
|
25
|
+
suggestions: undefined;
|
|
26
|
+
constructor({ block, edytor, parent }: {
|
|
27
|
+
edytor: Edytor;
|
|
28
|
+
parent: ReadonlyBlock | Block | Edytor;
|
|
29
|
+
block: JSONBlock;
|
|
30
|
+
});
|
|
31
|
+
attach(node: HTMLElement): void;
|
|
32
|
+
void: (node: HTMLElement) => void;
|
|
33
|
+
}
|
|
34
|
+
export declare const createReadonlyBlock: ({ edytor, parent, block }: {
|
|
35
|
+
edytor: Edytor;
|
|
36
|
+
parent: ReadonlyBlock | Block | Edytor;
|
|
37
|
+
block: JSONBlock;
|
|
38
|
+
}) => Block;
|
|
39
|
+
export declare const createReadonlyInlineBlock: ({ edytor, parent, block }: {
|
|
40
|
+
edytor: Edytor;
|
|
41
|
+
parent: ReadonlyBlock | Block | Edytor;
|
|
42
|
+
block: JSONInlineBlock;
|
|
43
|
+
}) => InlineBlock;
|
|
44
|
+
export declare const createReadonlyText: ({ edytor, parent, value }: {
|
|
45
|
+
edytor: Edytor;
|
|
46
|
+
parent: ReadonlyBlock | Block;
|
|
47
|
+
value: JSONText[];
|
|
48
|
+
}) => Text;
|
|
49
|
+
export declare class ReadonlyText {
|
|
50
|
+
#private;
|
|
51
|
+
readonly: boolean;
|
|
52
|
+
edytor: Edytor;
|
|
53
|
+
parent: ReadonlyBlock | Block;
|
|
54
|
+
stringContent: string;
|
|
55
|
+
node: HTMLElement | undefined;
|
|
56
|
+
isEmpty: boolean;
|
|
57
|
+
endsWithNewline: boolean;
|
|
58
|
+
id: string;
|
|
59
|
+
length: number;
|
|
60
|
+
get value(): JSONText[];
|
|
61
|
+
get children(): JSONDelta[];
|
|
62
|
+
constructor({ value, parent, edytor }: {
|
|
63
|
+
value: JSONText[];
|
|
64
|
+
parent: ReadonlyBlock | Block;
|
|
65
|
+
edytor: Edytor;
|
|
66
|
+
});
|
|
67
|
+
attach(node: HTMLElement): void;
|
|
68
|
+
}
|
|
69
|
+
declare class ReadonlyInlineBlock {
|
|
70
|
+
readonly: boolean;
|
|
71
|
+
edytor: Edytor;
|
|
72
|
+
parent: ReadonlyBlock | Block | Edytor;
|
|
73
|
+
type: string;
|
|
74
|
+
data: {};
|
|
75
|
+
definition: InlineBlockDefinition;
|
|
76
|
+
constructor({ parent, block, edytor }: {
|
|
77
|
+
parent: ReadonlyBlock | Block | Edytor;
|
|
78
|
+
block: JSONInlineBlock;
|
|
79
|
+
edytor: Edytor;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
export {};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { deltaToJson, jsonToDelta, toDeltas } from '../text/deltas.js';
|
|
2
|
+
import { id } from '../utils.js';
|
|
3
|
+
import { DEV } from 'esm-env';
|
|
4
|
+
class ReadonlyBlock {
|
|
5
|
+
readonly = true;
|
|
6
|
+
edytor;
|
|
7
|
+
parent;
|
|
8
|
+
type;
|
|
9
|
+
value;
|
|
10
|
+
children;
|
|
11
|
+
content;
|
|
12
|
+
id;
|
|
13
|
+
definition;
|
|
14
|
+
selected = false;
|
|
15
|
+
focused = false;
|
|
16
|
+
insideIsland = false;
|
|
17
|
+
isEmpty;
|
|
18
|
+
hasChildren;
|
|
19
|
+
hasContent;
|
|
20
|
+
node;
|
|
21
|
+
suggestions = undefined;
|
|
22
|
+
constructor({ block, edytor, parent }) {
|
|
23
|
+
this.definition = edytor.getBlockDefinition('block', block.type);
|
|
24
|
+
this.id = block.id || id('b');
|
|
25
|
+
this.type = block.type;
|
|
26
|
+
this.edytor = edytor;
|
|
27
|
+
this.parent = parent;
|
|
28
|
+
this.value = block;
|
|
29
|
+
this.children =
|
|
30
|
+
block.children?.map((block) => new ReadonlyBlock({ block, edytor, parent })) || [];
|
|
31
|
+
this.content =
|
|
32
|
+
block.content?.map((child) => {
|
|
33
|
+
if ('type' in child) {
|
|
34
|
+
return new ReadonlyInlineBlock({
|
|
35
|
+
block: child,
|
|
36
|
+
parent,
|
|
37
|
+
edytor
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return new ReadonlyText({ value: [child], parent: this, edytor });
|
|
42
|
+
}
|
|
43
|
+
}) || [];
|
|
44
|
+
this.hasChildren = this.children.length > 0;
|
|
45
|
+
this.hasContent =
|
|
46
|
+
this.content.length > 0 &&
|
|
47
|
+
this.content.some((part) => part instanceof ReadonlyText && !part.isEmpty);
|
|
48
|
+
this.isEmpty = !this.hasChildren && !this.hasContent;
|
|
49
|
+
}
|
|
50
|
+
attach(node) {
|
|
51
|
+
node.contentEditable = 'false';
|
|
52
|
+
this.node = node;
|
|
53
|
+
}
|
|
54
|
+
void = (node) => {
|
|
55
|
+
// node.contentEditable = 'false';
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const createProxy = (target) => {
|
|
59
|
+
return new Proxy(target, {
|
|
60
|
+
get(target, prop) {
|
|
61
|
+
if (typeof prop === 'string' && prop in target) {
|
|
62
|
+
const value = target[prop];
|
|
63
|
+
console.log({ value });
|
|
64
|
+
// Handle functions by binding them to the target
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
if (DEV) {
|
|
68
|
+
console.warn(`[ReadonlyBlock] Access to property "${String(prop)}" is forbidden in readonly mode`);
|
|
69
|
+
}
|
|
70
|
+
// Return a proxy for undefined to allow for safe chaining
|
|
71
|
+
return createProxy({});
|
|
72
|
+
},
|
|
73
|
+
apply(target, thisArg, args) {
|
|
74
|
+
if (typeof target === 'function') {
|
|
75
|
+
return target.apply(thisArg, args);
|
|
76
|
+
}
|
|
77
|
+
if (DEV) {
|
|
78
|
+
console.warn(`[ReadonlyBlock] Attempted to call a non-function value in readonly mode`, {
|
|
79
|
+
target
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return createProxy({});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
export const createReadonlyBlock = ({ edytor, parent, block }) => {
|
|
87
|
+
return createProxy(new ReadonlyBlock({ block, edytor, parent }));
|
|
88
|
+
};
|
|
89
|
+
export const createReadonlyInlineBlock = ({ edytor, parent, block }) => {
|
|
90
|
+
return createProxy(new ReadonlyInlineBlock({ block, edytor, parent }));
|
|
91
|
+
};
|
|
92
|
+
export const createReadonlyText = ({ edytor, parent, value }) => {
|
|
93
|
+
return createProxy(new ReadonlyText({ value, parent, edytor }));
|
|
94
|
+
};
|
|
95
|
+
export class ReadonlyText {
|
|
96
|
+
readonly = true;
|
|
97
|
+
edytor;
|
|
98
|
+
parent;
|
|
99
|
+
#children;
|
|
100
|
+
stringContent;
|
|
101
|
+
node;
|
|
102
|
+
isEmpty;
|
|
103
|
+
endsWithNewline;
|
|
104
|
+
id;
|
|
105
|
+
length;
|
|
106
|
+
get value() {
|
|
107
|
+
return deltaToJson(this.#children);
|
|
108
|
+
}
|
|
109
|
+
get children() {
|
|
110
|
+
const transformer = this.parent.definition?.transformText;
|
|
111
|
+
return transformer
|
|
112
|
+
? jsonToDelta(
|
|
113
|
+
// @ts-expect-error
|
|
114
|
+
transformer({ text: this, block: this.parent, content: deltaToJson(this.#children) }))
|
|
115
|
+
: this.#children;
|
|
116
|
+
}
|
|
117
|
+
constructor({ value, parent, edytor }) {
|
|
118
|
+
this.#children = jsonToDelta(value);
|
|
119
|
+
this.stringContent = value.map((child) => child.text).join('');
|
|
120
|
+
this.isEmpty = this.stringContent.length === 0;
|
|
121
|
+
this.parent = parent;
|
|
122
|
+
this.endsWithNewline = this.stringContent.endsWith('\n');
|
|
123
|
+
this.id = id('t');
|
|
124
|
+
this.edytor = edytor;
|
|
125
|
+
this.length = this.children.reduce((acc, child) => acc + child.text.length, 0);
|
|
126
|
+
}
|
|
127
|
+
attach(node) {
|
|
128
|
+
this.node = node;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
class ReadonlyInlineBlock {
|
|
132
|
+
readonly = true;
|
|
133
|
+
edytor;
|
|
134
|
+
parent;
|
|
135
|
+
type;
|
|
136
|
+
data = {};
|
|
137
|
+
definition;
|
|
138
|
+
constructor({ parent, block, edytor }) {
|
|
139
|
+
this.type = block.type;
|
|
140
|
+
this.definition = edytor.getBlockDefinition('inline', this.type);
|
|
141
|
+
this.parent = parent;
|
|
142
|
+
this.edytor = parent.edytor;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Block } from './block/block.svelte.js';
|
|
2
|
+
type DnDOptions = {
|
|
3
|
+
onDrop?: (source: Block, target: Block, position: 'before' | 'after' | 'inside') => void;
|
|
4
|
+
canDrop?: (source: Block, target: Block) => boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare class DnD {
|
|
7
|
+
private state;
|
|
8
|
+
private options;
|
|
9
|
+
private uniqueContextId;
|
|
10
|
+
constructor(options?: DnDOptions);
|
|
11
|
+
private calculateDropPosition;
|
|
12
|
+
private updateDropIndicator;
|
|
13
|
+
attach: (element: HTMLElement, block: Block) => {
|
|
14
|
+
destroy(): void;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export {};
|