@shelamkoff/rector 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -24
- package/dist/core/clipboard/Clipboard.d.ts +2 -0
- package/dist/core/clipboard/Clipboard.js +52 -24
- package/dist/core/index.js +195 -194
- package/dist/core/locale/en.d.ts +1 -0
- package/dist/core/locale/en.js +1 -0
- package/dist/core/locale/ru.d.ts +1 -0
- package/dist/core/locale/ru.js +1 -0
- package/dist/core/themes/light.css +1 -1
- package/dist/core/themes/variables.css +72 -10
- package/dist/locale/en.d.ts +41 -0
- package/dist/locale/ru.d.ts +41 -0
- package/dist/plugins/attaches/attaches.css +16 -14
- package/dist/plugins/attaches/index.js +997 -920
- package/dist/plugins/attaches/locale/en.d.ts +9 -0
- package/dist/plugins/attaches/locale/en.js +11 -2
- package/dist/plugins/attaches/locale/ru.d.ts +9 -0
- package/dist/plugins/attaches/locale/ru.js +11 -2
- package/dist/plugins/carousel/carousel.css +213 -49
- package/dist/plugins/carousel/index.js +918 -858
- package/dist/plugins/carousel/locale/en.d.ts +15 -0
- package/dist/plugins/carousel/locale/en.js +18 -3
- package/dist/plugins/carousel/locale/ru.d.ts +15 -0
- package/dist/plugins/carousel/locale/ru.js +18 -3
- package/dist/plugins/gallery/gallery.css +6 -1
- package/dist/plugins/gallery/index.js +375 -354
- package/dist/plugins/gallery/locale/en.d.ts +8 -0
- package/dist/plugins/gallery/locale/en.js +11 -3
- package/dist/plugins/gallery/locale/ru.d.ts +8 -0
- package/dist/plugins/gallery/locale/ru.js +11 -3
- package/dist/plugins/gallery/view-empty.d.ts +2 -0
- package/dist/plugins/gallery/view-empty.js +21 -15
- package/dist/plugins/gallery/view-filled.d.ts +2 -2
- package/dist/plugins/gallery/view-filled.js +61 -56
- package/dist/plugins/image/image.css +7 -2
- package/dist/plugins/image/index.js +365 -345
- package/dist/plugins/image/locale/en.d.ts +8 -0
- package/dist/plugins/image/locale/en.js +11 -3
- package/dist/plugins/image/locale/ru.d.ts +8 -0
- package/dist/plugins/image/locale/ru.js +11 -3
- package/dist/plugins/image/view-empty.d.ts +2 -0
- package/dist/plugins/image/view-empty.js +21 -15
- package/dist/plugins/image/view-filled.d.ts +2 -2
- package/dist/plugins/image/view-filled.js +77 -72
- package/dist/plugins/shared/dropzone.d.ts +6 -0
- package/dist/plugins/shared/dropzone.js +47 -36
- package/dist/plugins/shared/layer.d.ts +13 -0
- package/dist/plugins/shared/layer.js +45 -0
- package/dist/plugins/shared/sourceEditor.css +165 -0
- package/dist/plugins/shared/sourceEditor.d.ts +97 -0
- package/dist/plugins/shared/sourceEditor.js +260 -0
- package/dist/renderer/renderers/carousel/index.js +24 -23
- package/dist/renderer/renderers/carousel/styles.css +8 -2
- package/package.json +32 -32
package/README.md
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
# @shelamkoff/rector
|
|
2
|
-
|
|
3
|
-
Rector is an extensible browser-native block editor with atomic undo and redo, versioned JSON documents, inline formatting, block plugins, and document rendering.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- [
|
|
11
|
-
- [
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- [
|
|
1
|
+
# @shelamkoff/rector
|
|
2
|
+
|
|
3
|
+
Rector is an extensible browser-native block editor with atomic undo and redo, versioned JSON documents, inline formatting, block plugins, and document rendering.
|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
## Documentation
|
|
7
|
+
|
|
8
|
+
The complete guides, API reference, extension contracts, plugin catalog, and live demo are published with VitePress:
|
|
9
|
+
|
|
10
|
+
- [English documentation](https://shelamkoff.github.io/editor/)
|
|
11
|
+
- [Документация на русском](https://shelamkoff.github.io/editor/ru/)
|
|
12
|
+
- [Live demo](https://shelamkoff.github.io/editor/#demo)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @shelamkoff/rector
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
See the [getting started guide](https://shelamkoff.github.io/editor/guide/getting-started) for setup, required styles, plugin registration, document persistence, and lifecycle management.
|
|
21
|
+
|
|
22
|
+
## Project
|
|
23
|
+
|
|
24
|
+
- [Source code and issues](https://github.com/Shelamkoff/editor)
|
|
25
|
+
- [MIT License](./LICENSE)
|
|
@@ -22,6 +22,7 @@ export class Clipboard {
|
|
|
22
22
|
* @property {import('../types.js').InlinePluginContext} inlinePluginCtx
|
|
23
23
|
* @property {import('../Diagnostics.js').Diagnostics} [diagnostics]
|
|
24
24
|
* @property {(() => boolean)} [uiActivePredicate]
|
|
25
|
+
* @property {string} [pendingPasteLabel]
|
|
25
26
|
*/
|
|
26
27
|
/**
|
|
27
28
|
* @param {HTMLElement} rootEl
|
|
@@ -41,6 +42,7 @@ export class Clipboard {
|
|
|
41
42
|
inlinePluginCtx: import("../types.js").InlinePluginContext;
|
|
42
43
|
diagnostics?: import("../Diagnostics.js").Diagnostics;
|
|
43
44
|
uiActivePredicate?: (() => boolean);
|
|
45
|
+
pendingPasteLabel?: string;
|
|
44
46
|
});
|
|
45
47
|
destroy(): void;
|
|
46
48
|
#private;
|
|
@@ -8,6 +8,27 @@ import { cloneEditorData } from '../../shared/cloneEditorData.js'
|
|
|
8
8
|
|
|
9
9
|
const MIME_TYPE = 'application/x-rector-editor'
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Core-owned progress UI for asynchronous file pastes. The plugin's pending
|
|
13
|
+
* element stays mounted so its upload lifecycle can continue, but is hidden
|
|
14
|
+
* until the completed block is committed to the document.
|
|
15
|
+
*
|
|
16
|
+
* @param {string} label
|
|
17
|
+
* @returns {HTMLElement}
|
|
18
|
+
*/
|
|
19
|
+
function createPendingPasteIndicator(label) {
|
|
20
|
+
const indicator = document.createElement('div')
|
|
21
|
+
indicator.className = 'oe-pending-paste__indicator'
|
|
22
|
+
indicator.setAttribute('role', 'status')
|
|
23
|
+
indicator.setAttribute('aria-label', label)
|
|
24
|
+
|
|
25
|
+
const spinner = document.createElement('span')
|
|
26
|
+
spinner.className = 'oe-pending-paste__spinner'
|
|
27
|
+
spinner.setAttribute('aria-hidden', 'true')
|
|
28
|
+
indicator.appendChild(spinner)
|
|
29
|
+
return indicator
|
|
30
|
+
}
|
|
31
|
+
|
|
11
32
|
/**
|
|
12
33
|
* Extract plain text and HTML from a DOM Range.
|
|
13
34
|
* @param {Range} range
|
|
@@ -118,6 +139,9 @@ export class Clipboard {
|
|
|
118
139
|
/** @type {boolean} */
|
|
119
140
|
#destroyed = false
|
|
120
141
|
|
|
142
|
+
/** @type {string} */
|
|
143
|
+
#pendingPasteLabel = 'Processing file'
|
|
144
|
+
|
|
121
145
|
/**
|
|
122
146
|
* @typedef {Object} ClipboardConfig
|
|
123
147
|
* @property {import('../types').IBlockManager} blocks
|
|
@@ -133,6 +157,7 @@ export class Clipboard {
|
|
|
133
157
|
* @property {import('../types').InlinePluginContext} inlinePluginCtx
|
|
134
158
|
* @property {import('../Diagnostics').Diagnostics} [diagnostics]
|
|
135
159
|
* @property {(() => boolean)} [uiActivePredicate]
|
|
160
|
+
* @property {string} [pendingPasteLabel]
|
|
136
161
|
*/
|
|
137
162
|
|
|
138
163
|
/**
|
|
@@ -143,7 +168,7 @@ export class Clipboard {
|
|
|
143
168
|
const {
|
|
144
169
|
blocks, selection, plugins, defaultBlockType, crossBlockSelection,
|
|
145
170
|
events, commands, blockOps, captureSnapshot, inlinePluginRegistry,
|
|
146
|
-
inlinePluginCtx, diagnostics, uiActivePredicate,
|
|
171
|
+
inlinePluginCtx, diagnostics, uiActivePredicate, pendingPasteLabel,
|
|
147
172
|
} = config
|
|
148
173
|
this.#rootEl = rootEl
|
|
149
174
|
this.#blocks = blocks
|
|
@@ -158,6 +183,7 @@ export class Clipboard {
|
|
|
158
183
|
this.#inlinePluginRegistry = inlinePluginRegistry
|
|
159
184
|
this.#inlinePluginCtx = inlinePluginCtx
|
|
160
185
|
this.#isUIActive = uiActivePredicate ?? null
|
|
186
|
+
this.#pendingPasteLabel = pendingPasteLabel || this.#pendingPasteLabel
|
|
161
187
|
|
|
162
188
|
this.#blockOps = blockOps
|
|
163
189
|
this.#router = new PasteRouter(plugins.values())
|
|
@@ -248,10 +274,10 @@ export class Clipboard {
|
|
|
248
274
|
const blocksData = selectedBlocks.map(block => canonical.get(block.id) ?? block.save())
|
|
249
275
|
const html = selectedBlocks.map((b) => b.contentElement.innerHTML).join('\n')
|
|
250
276
|
const text = selectedBlocks.map((b) => b.contentElement.textContent).join('\n')
|
|
251
|
-
e.clipboardData?.setData('text/plain', text)
|
|
252
|
-
e.clipboardData?.setData('text/html', html)
|
|
253
|
-
e.clipboardData?.setData(MIME_TYPE, JSON.stringify(blocksData))
|
|
254
|
-
}
|
|
277
|
+
e.clipboardData?.setData('text/plain', text)
|
|
278
|
+
e.clipboardData?.setData('text/html', html)
|
|
279
|
+
e.clipboardData?.setData(MIME_TYPE, JSON.stringify(blocksData))
|
|
280
|
+
}
|
|
255
281
|
|
|
256
282
|
}
|
|
257
283
|
|
|
@@ -280,10 +306,10 @@ export class Clipboard {
|
|
|
280
306
|
this.#selection.setCaretToBlock(focusBlock.id, 'start')
|
|
281
307
|
}
|
|
282
308
|
}
|
|
283
|
-
} finally {
|
|
284
|
-
this.#events.emit(EditorEvent.UNDO_BATCH_END)
|
|
285
|
-
}
|
|
286
|
-
}
|
|
309
|
+
} finally {
|
|
310
|
+
this.#events.emit(EditorEvent.UNDO_BATCH_END)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
287
313
|
|
|
288
314
|
}
|
|
289
315
|
|
|
@@ -555,21 +581,23 @@ export class Clipboard {
|
|
|
555
581
|
const initialData = plugin.onPaste({ type: 'file', file })
|
|
556
582
|
if (!initialData) return null
|
|
557
583
|
|
|
558
|
-
element = plugin.render(initialData, {
|
|
559
|
-
mutate: (operation) => operation(),
|
|
560
|
-
splitBlock: () => {},
|
|
561
|
-
exitEmptyBlock: () => false,
|
|
562
|
-
readOnly: false,
|
|
563
|
-
})
|
|
564
|
-
if (!(element instanceof HTMLElement)) {
|
|
565
|
-
console.error(`[Clipboard] File paste plugin "${plugin.type}" did not return an HTMLElement`)
|
|
566
|
-
return null
|
|
584
|
+
element = plugin.render(initialData, {
|
|
585
|
+
mutate: (operation) => operation(),
|
|
586
|
+
splitBlock: () => {},
|
|
587
|
+
exitEmptyBlock: () => false,
|
|
588
|
+
readOnly: false,
|
|
589
|
+
})
|
|
590
|
+
if (!(element instanceof HTMLElement)) {
|
|
591
|
+
console.error(`[Clipboard] File paste plugin "${plugin.type}" did not return an HTMLElement`)
|
|
592
|
+
return null
|
|
567
593
|
}
|
|
568
594
|
|
|
569
595
|
shell = document.createElement('div')
|
|
570
596
|
shell.className = 'oe-block oe-block--pending-paste'
|
|
571
597
|
shell.dataset.blockType = plugin.type
|
|
572
|
-
shell.
|
|
598
|
+
shell.setAttribute('aria-busy', 'true')
|
|
599
|
+
element.hidden = true
|
|
600
|
+
shell.append(createPendingPasteIndicator(this.#pendingPasteLabel), element)
|
|
573
601
|
pendingHost.appendChild(shell)
|
|
574
602
|
|
|
575
603
|
let cleaned = false
|
|
@@ -590,11 +618,11 @@ export class Clipboard {
|
|
|
590
618
|
await plugin.waitForPaste?.(element)
|
|
591
619
|
if (this.#destroyed || pending.cancelled) return null
|
|
592
620
|
|
|
593
|
-
const saved = plugin.save(element)
|
|
594
|
-
if (!saved || typeof saved !== 'object' || Array.isArray(saved)) {
|
|
595
|
-
console.error(`[Clipboard] File paste plugin "${plugin.type}" returned invalid data`)
|
|
596
|
-
return null
|
|
597
|
-
}
|
|
621
|
+
const saved = plugin.save(element)
|
|
622
|
+
if (!saved || typeof saved !== 'object' || Array.isArray(saved)) {
|
|
623
|
+
console.error(`[Clipboard] File paste plugin "${plugin.type}" returned invalid data`)
|
|
624
|
+
return null
|
|
625
|
+
}
|
|
598
626
|
if (plugin.validate && !plugin.validate(saved)) return null
|
|
599
627
|
return { type: plugin.type, data: cloneEditorData(saved) }
|
|
600
628
|
} catch (error) {
|