@shelamkoff/rector 1.0.0 → 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 -23
- 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/inline-plugins/mention/index.js +1 -2
- 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 -921
- 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 -859
- 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/checklist/index.js +1 -2
- package/dist/plugins/code/index.js +1 -2
- package/dist/plugins/columns/index.js +1 -2
- package/dist/plugins/delimiter/index.js +1 -2
- package/dist/plugins/embed/index.js +1 -2
- package/dist/plugins/gallery/gallery.css +6 -1
- package/dist/plugins/gallery/index.js +375 -355
- 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/heading/index.js +1 -2
- package/dist/plugins/image/image.css +7 -2
- package/dist/plugins/image/index.js +365 -346
- 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/link-preview/index.js +1 -2
- package/dist/plugins/list/index.js +1 -2
- package/dist/plugins/paragraph/index.js +1 -2
- package/dist/plugins/person/index.js +1 -2
- package/dist/plugins/poll/index.js +1 -2
- package/dist/plugins/quote/index.js +1 -2
- package/dist/plugins/raw/index.js +1 -2
- 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/plugins/spoiler/index.js +1 -2
- package/dist/plugins/table/index.js +1 -2
- package/dist/plugins/toggle/index.js +1 -2
- package/dist/plugins/warning/index.js +1 -2
- package/dist/renderer/index.js +1 -2
- package/dist/renderer/renderers/attaches/index.js +1 -2
- package/dist/renderer/renderers/carousel/index.js +24 -24
- package/dist/renderer/renderers/carousel/styles.css +8 -2
- package/dist/renderer/renderers/checklist/index.js +1 -2
- package/dist/renderer/renderers/code/index.js +1 -2
- package/dist/renderer/renderers/columns/index.js +1 -2
- package/dist/renderer/renderers/delimiter/index.js +1 -2
- package/dist/renderer/renderers/embed/index.js +1 -2
- package/dist/renderer/renderers/gallery/index.js +1 -2
- package/dist/renderer/renderers/heading/index.js +1 -2
- package/dist/renderer/renderers/image/index.js +1 -2
- package/dist/renderer/renderers/link-preview/index.js +1 -2
- package/dist/renderer/renderers/list/index.js +1 -2
- package/dist/renderer/renderers/paragraph/index.js +1 -2
- package/dist/renderer/renderers/person/index.js +1 -2
- package/dist/renderer/renderers/poll/index.js +1 -2
- package/dist/renderer/renderers/quote/index.js +1 -2
- package/dist/renderer/renderers/spoiler/index.js +1 -2
- package/dist/renderer/renderers/table/index.js +1 -2
- package/dist/renderer/renderers/toggle/index.js +1 -2
- package/dist/renderer/renderers/warning/index.js +1 -2
- package/package.json +27 -29
- package/dist/shared/resolvePath.d.ts +0 -8
- package/dist/shared/resolvePath.js +0 -10
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keep a plugin surface above neighbouring editor blocks while it is open.
|
|
3
|
+
* Calls are reference-counted so a source editor can coexist with a settings
|
|
4
|
+
* panel owned by the same block.
|
|
5
|
+
*
|
|
6
|
+
* @param {HTMLElement} owner Element inside the plugin block.
|
|
7
|
+
* @param {AbortSignal} signal Plugin render lifecycle signal.
|
|
8
|
+
* @returns {{ open: () => void, close: () => void }}
|
|
9
|
+
*/
|
|
10
|
+
export function createPluginLayer(owner: HTMLElement, signal: AbortSignal): {
|
|
11
|
+
open: () => void;
|
|
12
|
+
close: () => void;
|
|
13
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const openLayers = new WeakMap()
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Keep a plugin surface above neighbouring editor blocks while it is open.
|
|
5
|
+
* Calls are reference-counted so a source editor can coexist with a settings
|
|
6
|
+
* panel owned by the same block.
|
|
7
|
+
*
|
|
8
|
+
* @param {HTMLElement} owner Element inside the plugin block.
|
|
9
|
+
* @param {AbortSignal} signal Plugin render lifecycle signal.
|
|
10
|
+
* @returns {{ open: () => void, close: () => void }}
|
|
11
|
+
*/
|
|
12
|
+
export function createPluginLayer(owner, signal) {
|
|
13
|
+
/** @type {HTMLElement | null} */
|
|
14
|
+
let block = null
|
|
15
|
+
let opened = false
|
|
16
|
+
|
|
17
|
+
const close = () => {
|
|
18
|
+
if (!opened || !block) return
|
|
19
|
+
const currentBlock = block
|
|
20
|
+
opened = false
|
|
21
|
+
block = null
|
|
22
|
+
const next = Math.max(0, (openLayers.get(currentBlock) || 1) - 1)
|
|
23
|
+
if (next === 0) {
|
|
24
|
+
openLayers.delete(currentBlock)
|
|
25
|
+
currentBlock.removeAttribute('data-oe-layer-open')
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
openLayers.set(currentBlock, next)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
signal.addEventListener('abort', close, { once: true })
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
open() {
|
|
35
|
+
if (opened || signal.aborted) return
|
|
36
|
+
const candidate = owner.closest('.oe-block')
|
|
37
|
+
if (!(candidate instanceof HTMLElement)) return
|
|
38
|
+
block = candidate
|
|
39
|
+
opened = true
|
|
40
|
+
openLayers.set(block, (openLayers.get(block) || 0) + 1)
|
|
41
|
+
block.setAttribute('data-oe-layer-open', 'true')
|
|
42
|
+
},
|
|
43
|
+
close,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
.oe-source-editor {
|
|
2
|
+
position: absolute;
|
|
3
|
+
inset: 0;
|
|
4
|
+
z-index: var(--oe-plugin-dialog-z-index, 1200);
|
|
5
|
+
box-sizing: border-box;
|
|
6
|
+
display: grid;
|
|
7
|
+
place-items: center;
|
|
8
|
+
min-height: 220px;
|
|
9
|
+
padding: 16px;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.oe-source-editor--preloaded {
|
|
13
|
+
visibility: hidden;
|
|
14
|
+
opacity: 0;
|
|
15
|
+
pointer-events: none;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.oe-source-editor[data-oe-source-editor='html'] {
|
|
19
|
+
min-height: min(26rem, calc(100vh - 2rem));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.oe-source-editor__backdrop {
|
|
23
|
+
position: absolute;
|
|
24
|
+
inset: 0;
|
|
25
|
+
width: 100%;
|
|
26
|
+
height: 100%;
|
|
27
|
+
padding: 0;
|
|
28
|
+
background: var(--oe-overlay-dark, rgba(0, 0, 0, 0.7));
|
|
29
|
+
border: 0;
|
|
30
|
+
border-radius: inherit;
|
|
31
|
+
cursor: default;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.oe-source-editor__panel {
|
|
35
|
+
position: relative;
|
|
36
|
+
display: grid;
|
|
37
|
+
gap: 12px;
|
|
38
|
+
box-sizing: border-box;
|
|
39
|
+
width: min(100%, 32rem);
|
|
40
|
+
max-height: calc(100vh - 2rem);
|
|
41
|
+
padding: 16px;
|
|
42
|
+
overflow-y: auto;
|
|
43
|
+
background: var(--oe-surface-elevated, #1e1e24);
|
|
44
|
+
border: 1px solid var(--oe-border-hover, #2a2a2a);
|
|
45
|
+
border-radius: var(--oe-radius, 8px);
|
|
46
|
+
box-shadow: var(--oe-toolbar-shadow, 0 8px 24px rgba(0, 0, 0, 0.4));
|
|
47
|
+
text-align: left;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.oe-source-editor[data-oe-source-editor='url'] .oe-source-editor__panel {
|
|
51
|
+
width: min(100%, 28rem);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.oe-source-editor__title {
|
|
55
|
+
margin: 0;
|
|
56
|
+
color: var(--oe-text-1, #f5f5f5);
|
|
57
|
+
font: inherit;
|
|
58
|
+
font-size: 15px;
|
|
59
|
+
font-weight: 650;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.oe-source-editor__label {
|
|
63
|
+
display: grid;
|
|
64
|
+
gap: 8px;
|
|
65
|
+
color: var(--oe-text-2, #c4c4c4);
|
|
66
|
+
font-size: 13px;
|
|
67
|
+
font-weight: 600;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.oe-source-editor__field {
|
|
71
|
+
box-sizing: border-box;
|
|
72
|
+
width: 100%;
|
|
73
|
+
min-height: 42px;
|
|
74
|
+
padding: 10px 12px;
|
|
75
|
+
resize: vertical;
|
|
76
|
+
color: var(--oe-text-1, #f5f5f5);
|
|
77
|
+
background: var(--oe-input-bg, #2c2c33);
|
|
78
|
+
border: 1px solid var(--oe-border-hover, #2a2a2a);
|
|
79
|
+
border-radius: var(--oe-radius-sm, 4px);
|
|
80
|
+
outline: none;
|
|
81
|
+
font: inherit;
|
|
82
|
+
font-size: 14px;
|
|
83
|
+
line-height: 1.5;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.oe-source-editor textarea.oe-source-editor__field {
|
|
87
|
+
min-height: 9rem;
|
|
88
|
+
max-height: min(40vh, 20rem);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.oe-source-editor__field:hover {
|
|
92
|
+
background: var(--oe-input-bg-hover, #353540);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.oe-source-editor .oe-source-editor__field:focus,
|
|
96
|
+
.oe-source-editor .oe-source-editor__field:focus-visible {
|
|
97
|
+
border-color: var(--oe-border-hover, #2a2a2a);
|
|
98
|
+
outline: none;
|
|
99
|
+
outline-offset: 0;
|
|
100
|
+
box-shadow: none;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.oe-source-editor__field[aria-invalid='true'] {
|
|
104
|
+
border-color: var(--oe-danger, #f87171);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.oe-source-editor__error {
|
|
108
|
+
color: var(--oe-danger-light, #fca5a5);
|
|
109
|
+
font-size: 13px;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.oe-source-editor__actions {
|
|
113
|
+
display: flex;
|
|
114
|
+
justify-content: flex-end;
|
|
115
|
+
gap: 8px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.oe-source-editor__button {
|
|
119
|
+
min-height: 36px;
|
|
120
|
+
padding: 7px 14px;
|
|
121
|
+
border: 1px solid transparent;
|
|
122
|
+
border-radius: var(--oe-radius-sm, 4px);
|
|
123
|
+
font: inherit;
|
|
124
|
+
font-size: 13px;
|
|
125
|
+
font-weight: 650;
|
|
126
|
+
cursor: pointer;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.oe-source-editor__button--secondary {
|
|
130
|
+
color: var(--oe-text-1, #f5f5f5);
|
|
131
|
+
background: var(--oe-card, #111);
|
|
132
|
+
border-color: var(--oe-border-hover, #2a2a2a);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.oe-source-editor__button--secondary:hover {
|
|
136
|
+
background: var(--oe-card-hover, #191919);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.oe-source-editor__button--primary {
|
|
140
|
+
color: var(--oe-text-on-accent, #fff);
|
|
141
|
+
background: var(--oe-accent, #4357b4);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.oe-source-editor__button--primary:hover {
|
|
145
|
+
filter: brightness(1.08);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@media (max-width: 520px) {
|
|
149
|
+
.oe-source-editor {
|
|
150
|
+
padding: 10px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.oe-source-editor__panel {
|
|
154
|
+
max-height: calc(100vh - 1.25rem);
|
|
155
|
+
padding: 14px;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.oe-source-editor__actions {
|
|
159
|
+
flex-direction: column-reverse;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.oe-source-editor__button {
|
|
163
|
+
width: 100%;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} SourceEditorConfig
|
|
3
|
+
* @property {HTMLElement} wrapper Plugin wrapper that owns the editor.
|
|
4
|
+
* @property {AbortSignal} signal Plugin render lifecycle signal.
|
|
5
|
+
* @property {'url' | 'html'} kind Source value kind.
|
|
6
|
+
* @property {string} title Dialog title.
|
|
7
|
+
* @property {string} label Accessible field label.
|
|
8
|
+
* @property {string} placeholder Field placeholder.
|
|
9
|
+
* @property {string} submitText Submit button label.
|
|
10
|
+
* @property {string} cancelText Cancel button label.
|
|
11
|
+
* @property {string} invalidText Validation error.
|
|
12
|
+
* @property {(value: string) => string} normalize Sanitizes and validates input.
|
|
13
|
+
* @property {(value: string) => void} onSubmit Receives the normalized value.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Build URL/HTML forms while the owning plugin view is rendered so their
|
|
17
|
+
* first style and layout pass does not happen in the user's click handler.
|
|
18
|
+
*
|
|
19
|
+
* @param {HTMLElement} wrapper Plugin wrapper that owns the editor.
|
|
20
|
+
* @param {AbortSignal} signal Plugin render lifecycle signal.
|
|
21
|
+
* @param {Array<'url' | 'html'>} kinds Forms used by the current plugin view.
|
|
22
|
+
* @returns {void}
|
|
23
|
+
*/
|
|
24
|
+
export function preloadSourceEditor(wrapper: HTMLElement, signal: AbortSignal, kinds: Array<"url" | "html">): void;
|
|
25
|
+
/**
|
|
26
|
+
* Open an editor-owned URL or HTML input surface without relying on native
|
|
27
|
+
* browser prompts.
|
|
28
|
+
*
|
|
29
|
+
* @param {SourceEditorConfig} config Source editor configuration.
|
|
30
|
+
* @returns {{ close: () => void }}
|
|
31
|
+
*/
|
|
32
|
+
export function openSourceEditor(config: SourceEditorConfig): {
|
|
33
|
+
close: () => void;
|
|
34
|
+
};
|
|
35
|
+
export type SourceEditorConfig = {
|
|
36
|
+
/**
|
|
37
|
+
* Plugin wrapper that owns the editor.
|
|
38
|
+
*/
|
|
39
|
+
wrapper: HTMLElement;
|
|
40
|
+
/**
|
|
41
|
+
* Plugin render lifecycle signal.
|
|
42
|
+
*/
|
|
43
|
+
signal: AbortSignal;
|
|
44
|
+
/**
|
|
45
|
+
* Source value kind.
|
|
46
|
+
*/
|
|
47
|
+
kind: "url" | "html";
|
|
48
|
+
/**
|
|
49
|
+
* Dialog title.
|
|
50
|
+
*/
|
|
51
|
+
title: string;
|
|
52
|
+
/**
|
|
53
|
+
* Accessible field label.
|
|
54
|
+
*/
|
|
55
|
+
label: string;
|
|
56
|
+
/**
|
|
57
|
+
* Field placeholder.
|
|
58
|
+
*/
|
|
59
|
+
placeholder: string;
|
|
60
|
+
/**
|
|
61
|
+
* Submit button label.
|
|
62
|
+
*/
|
|
63
|
+
submitText: string;
|
|
64
|
+
/**
|
|
65
|
+
* Cancel button label.
|
|
66
|
+
*/
|
|
67
|
+
cancelText: string;
|
|
68
|
+
/**
|
|
69
|
+
* Validation error.
|
|
70
|
+
*/
|
|
71
|
+
invalidText: string;
|
|
72
|
+
/**
|
|
73
|
+
* Sanitizes and validates input.
|
|
74
|
+
*/
|
|
75
|
+
normalize: (value: string) => string;
|
|
76
|
+
/**
|
|
77
|
+
* Receives the normalized value.
|
|
78
|
+
*/
|
|
79
|
+
onSubmit: (value: string) => void;
|
|
80
|
+
};
|
|
81
|
+
export type SourceEditorSurface = {
|
|
82
|
+
root: HTMLElement;
|
|
83
|
+
backdrop: HTMLButtonElement;
|
|
84
|
+
panel: HTMLFormElement;
|
|
85
|
+
title: HTMLHeadingElement;
|
|
86
|
+
labelText: HTMLSpanElement;
|
|
87
|
+
field: HTMLInputElement | HTMLTextAreaElement;
|
|
88
|
+
error: HTMLDivElement;
|
|
89
|
+
cancel: HTMLButtonElement;
|
|
90
|
+
submit: HTMLButtonElement;
|
|
91
|
+
signal: AbortSignal;
|
|
92
|
+
layer: {
|
|
93
|
+
open: () => void;
|
|
94
|
+
close: () => void;
|
|
95
|
+
};
|
|
96
|
+
destroy: () => void;
|
|
97
|
+
};
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { createPluginLayer } from './layer.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} SourceEditorSurface
|
|
5
|
+
* @property {HTMLElement} root
|
|
6
|
+
* @property {HTMLButtonElement} backdrop
|
|
7
|
+
* @property {HTMLFormElement} panel
|
|
8
|
+
* @property {HTMLHeadingElement} title
|
|
9
|
+
* @property {HTMLSpanElement} labelText
|
|
10
|
+
* @property {HTMLInputElement | HTMLTextAreaElement} field
|
|
11
|
+
* @property {HTMLDivElement} error
|
|
12
|
+
* @property {HTMLButtonElement} cancel
|
|
13
|
+
* @property {HTMLButtonElement} submit
|
|
14
|
+
* @property {AbortSignal} signal
|
|
15
|
+
* @property {{ open: () => void, close: () => void }} layer
|
|
16
|
+
* @property {() => void} destroy
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** @type {WeakMap<HTMLElement, { close: () => void, surface: SourceEditorSurface }>} */
|
|
20
|
+
const activeEditors = new WeakMap()
|
|
21
|
+
|
|
22
|
+
/** @type {WeakMap<HTMLElement, Map<'url' | 'html', SourceEditorSurface>>} */
|
|
23
|
+
const surfaces = new WeakMap()
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {Object} SourceEditorConfig
|
|
27
|
+
* @property {HTMLElement} wrapper Plugin wrapper that owns the editor.
|
|
28
|
+
* @property {AbortSignal} signal Plugin render lifecycle signal.
|
|
29
|
+
* @property {'url' | 'html'} kind Source value kind.
|
|
30
|
+
* @property {string} title Dialog title.
|
|
31
|
+
* @property {string} label Accessible field label.
|
|
32
|
+
* @property {string} placeholder Field placeholder.
|
|
33
|
+
* @property {string} submitText Submit button label.
|
|
34
|
+
* @property {string} cancelText Cancel button label.
|
|
35
|
+
* @property {string} invalidText Validation error.
|
|
36
|
+
* @property {(value: string) => string} normalize Sanitizes and validates input.
|
|
37
|
+
* @property {(value: string) => void} onSubmit Receives the normalized value.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Build URL/HTML forms while the owning plugin view is rendered so their
|
|
42
|
+
* first style and layout pass does not happen in the user's click handler.
|
|
43
|
+
*
|
|
44
|
+
* @param {HTMLElement} wrapper Plugin wrapper that owns the editor.
|
|
45
|
+
* @param {AbortSignal} signal Plugin render lifecycle signal.
|
|
46
|
+
* @param {Array<'url' | 'html'>} kinds Forms used by the current plugin view.
|
|
47
|
+
* @returns {void}
|
|
48
|
+
*/
|
|
49
|
+
export function preloadSourceEditor(wrapper, signal, kinds) {
|
|
50
|
+
if (signal.aborted) return
|
|
51
|
+
for (const kind of new Set(kinds)) ensureSurface(wrapper, signal, kind)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Open an editor-owned URL or HTML input surface without relying on native
|
|
56
|
+
* browser prompts.
|
|
57
|
+
*
|
|
58
|
+
* @param {SourceEditorConfig} config Source editor configuration.
|
|
59
|
+
* @returns {{ close: () => void }}
|
|
60
|
+
*/
|
|
61
|
+
export function openSourceEditor(config) {
|
|
62
|
+
if (config.signal.aborted) return { close() {} }
|
|
63
|
+
activeEditors.get(config.wrapper)?.close()
|
|
64
|
+
|
|
65
|
+
const surface = ensureSurface(config.wrapper, config.signal, config.kind)
|
|
66
|
+
if (!surface) return { close() {} }
|
|
67
|
+
const controller = new AbortController()
|
|
68
|
+
const { root, backdrop, panel, title, labelText, field, error, cancel, submit } = surface
|
|
69
|
+
backdrop.setAttribute('aria-label', config.cancelText)
|
|
70
|
+
panel.setAttribute('aria-label', config.title)
|
|
71
|
+
title.textContent = config.title
|
|
72
|
+
labelText.textContent = config.label
|
|
73
|
+
field.value = ''
|
|
74
|
+
field.placeholder = config.placeholder
|
|
75
|
+
field.removeAttribute('aria-invalid')
|
|
76
|
+
error.textContent = ''
|
|
77
|
+
error.hidden = true
|
|
78
|
+
cancel.textContent = config.cancelText
|
|
79
|
+
submit.textContent = config.submitText
|
|
80
|
+
setSurfaceVisible(surface, true)
|
|
81
|
+
surface.layer.open()
|
|
82
|
+
|
|
83
|
+
let closed = false
|
|
84
|
+
const close = () => {
|
|
85
|
+
if (closed) return
|
|
86
|
+
closed = true
|
|
87
|
+
if (activeEditors.get(config.wrapper)?.close === close) activeEditors.delete(config.wrapper)
|
|
88
|
+
controller.abort()
|
|
89
|
+
surface.layer.close()
|
|
90
|
+
setSurfaceVisible(surface, false)
|
|
91
|
+
}
|
|
92
|
+
activeEditors.set(config.wrapper, { close, surface })
|
|
93
|
+
|
|
94
|
+
config.signal.addEventListener('abort', close, { once: true, signal: controller.signal })
|
|
95
|
+
backdrop.addEventListener('click', close, { signal: controller.signal })
|
|
96
|
+
cancel.addEventListener('click', close, { signal: controller.signal })
|
|
97
|
+
root.addEventListener('keydown', (event) => {
|
|
98
|
+
if (event.key === 'Escape') {
|
|
99
|
+
event.preventDefault()
|
|
100
|
+
event.stopPropagation()
|
|
101
|
+
close()
|
|
102
|
+
}
|
|
103
|
+
}, { signal: controller.signal })
|
|
104
|
+
panel.addEventListener('submit', (event) => {
|
|
105
|
+
event.preventDefault()
|
|
106
|
+
let value = ''
|
|
107
|
+
try {
|
|
108
|
+
value = config.normalize(field.value)
|
|
109
|
+
} catch {
|
|
110
|
+
value = ''
|
|
111
|
+
}
|
|
112
|
+
if (!value) {
|
|
113
|
+
error.textContent = config.invalidText
|
|
114
|
+
error.hidden = false
|
|
115
|
+
field.setAttribute('aria-invalid', 'true')
|
|
116
|
+
field.focus()
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
close()
|
|
120
|
+
config.onSubmit(value)
|
|
121
|
+
}, { signal: controller.signal })
|
|
122
|
+
field.addEventListener('input', () => {
|
|
123
|
+
error.hidden = true
|
|
124
|
+
field.removeAttribute('aria-invalid')
|
|
125
|
+
}, { signal: controller.signal })
|
|
126
|
+
|
|
127
|
+
queueMicrotask(() => {
|
|
128
|
+
if (!closed) field.focus({ preventScroll: true })
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
return { close }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* @param {HTMLElement} wrapper
|
|
136
|
+
* @param {AbortSignal} signal
|
|
137
|
+
* @param {'url' | 'html'} kind
|
|
138
|
+
* @returns {SourceEditorSurface | null}
|
|
139
|
+
*/
|
|
140
|
+
function ensureSurface(wrapper, signal, kind) {
|
|
141
|
+
if (signal.aborted) return null
|
|
142
|
+
|
|
143
|
+
let owned = surfaces.get(wrapper)
|
|
144
|
+
if (!owned) {
|
|
145
|
+
owned = new Map()
|
|
146
|
+
surfaces.set(wrapper, owned)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const cached = owned.get(kind)
|
|
150
|
+
if (cached?.signal === signal && cached.root.isConnected) return cached
|
|
151
|
+
cached?.destroy()
|
|
152
|
+
|
|
153
|
+
const root = document.createElement('div')
|
|
154
|
+
root.className = 'oe-source-editor oe-source-editor--preloaded'
|
|
155
|
+
root.dataset.oeSourceEditor = kind
|
|
156
|
+
root.setAttribute('aria-hidden', 'true')
|
|
157
|
+
root.inert = true
|
|
158
|
+
|
|
159
|
+
const backdrop = document.createElement('button')
|
|
160
|
+
backdrop.type = 'button'
|
|
161
|
+
backdrop.className = 'oe-source-editor__backdrop'
|
|
162
|
+
backdrop.setAttribute('aria-label', 'Cancel')
|
|
163
|
+
backdrop.tabIndex = -1
|
|
164
|
+
|
|
165
|
+
const panel = document.createElement('form')
|
|
166
|
+
panel.className = 'oe-source-editor__panel'
|
|
167
|
+
panel.noValidate = true
|
|
168
|
+
panel.setAttribute('role', 'dialog')
|
|
169
|
+
panel.setAttribute('aria-modal', 'true')
|
|
170
|
+
panel.setAttribute('aria-label', 'Source')
|
|
171
|
+
|
|
172
|
+
const title = document.createElement('h3')
|
|
173
|
+
title.className = 'oe-source-editor__title'
|
|
174
|
+
title.textContent = 'Source'
|
|
175
|
+
|
|
176
|
+
const label = document.createElement('label')
|
|
177
|
+
label.className = 'oe-source-editor__label'
|
|
178
|
+
const labelText = document.createElement('span')
|
|
179
|
+
labelText.textContent = 'Source'
|
|
180
|
+
|
|
181
|
+
/** @type {HTMLInputElement | HTMLTextAreaElement} */
|
|
182
|
+
let field
|
|
183
|
+
if (kind === 'html') {
|
|
184
|
+
const textarea = document.createElement('textarea')
|
|
185
|
+
textarea.rows = 6
|
|
186
|
+
textarea.spellcheck = false
|
|
187
|
+
field = textarea
|
|
188
|
+
} else {
|
|
189
|
+
const input = document.createElement('input')
|
|
190
|
+
input.type = 'url'
|
|
191
|
+
input.inputMode = 'url'
|
|
192
|
+
input.setAttribute('autocomplete', 'url')
|
|
193
|
+
field = input
|
|
194
|
+
}
|
|
195
|
+
field.className = 'oe-source-editor__field'
|
|
196
|
+
field.required = true
|
|
197
|
+
label.append(labelText, field)
|
|
198
|
+
|
|
199
|
+
const error = document.createElement('div')
|
|
200
|
+
error.className = 'oe-source-editor__error'
|
|
201
|
+
error.setAttribute('role', 'alert')
|
|
202
|
+
error.hidden = true
|
|
203
|
+
|
|
204
|
+
const actions = document.createElement('div')
|
|
205
|
+
actions.className = 'oe-source-editor__actions'
|
|
206
|
+
|
|
207
|
+
const cancel = document.createElement('button')
|
|
208
|
+
cancel.type = 'button'
|
|
209
|
+
cancel.className = 'oe-source-editor__button oe-source-editor__button--secondary'
|
|
210
|
+
cancel.textContent = 'Cancel'
|
|
211
|
+
|
|
212
|
+
const submit = document.createElement('button')
|
|
213
|
+
submit.type = 'submit'
|
|
214
|
+
submit.className = 'oe-source-editor__button oe-source-editor__button--primary'
|
|
215
|
+
submit.textContent = 'Insert'
|
|
216
|
+
|
|
217
|
+
actions.append(cancel, submit)
|
|
218
|
+
panel.append(title, label, error, actions)
|
|
219
|
+
root.append(backdrop, panel)
|
|
220
|
+
wrapper.appendChild(root)
|
|
221
|
+
|
|
222
|
+
const layer = createPluginLayer(wrapper, signal)
|
|
223
|
+
/** @type {SourceEditorSurface} */
|
|
224
|
+
let surface
|
|
225
|
+
const destroy = () => {
|
|
226
|
+
const active = activeEditors.get(wrapper)
|
|
227
|
+
if (active?.surface === surface) active.close()
|
|
228
|
+
root.remove()
|
|
229
|
+
if (owned?.get(kind) === surface) owned.delete(kind)
|
|
230
|
+
if (owned?.size === 0) surfaces.delete(wrapper)
|
|
231
|
+
}
|
|
232
|
+
surface = {
|
|
233
|
+
root,
|
|
234
|
+
backdrop,
|
|
235
|
+
panel,
|
|
236
|
+
title,
|
|
237
|
+
labelText,
|
|
238
|
+
field,
|
|
239
|
+
error,
|
|
240
|
+
cancel,
|
|
241
|
+
submit,
|
|
242
|
+
signal,
|
|
243
|
+
layer,
|
|
244
|
+
destroy,
|
|
245
|
+
}
|
|
246
|
+
owned.set(kind, surface)
|
|
247
|
+
signal.addEventListener('abort', destroy, { once: true })
|
|
248
|
+
return surface
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* @param {SourceEditorSurface} surface
|
|
253
|
+
* @param {boolean} visible
|
|
254
|
+
* @returns {void}
|
|
255
|
+
*/
|
|
256
|
+
function setSurfaceVisible(surface, visible) {
|
|
257
|
+
surface.root.classList.toggle('oe-source-editor--preloaded', !visible)
|
|
258
|
+
surface.root.setAttribute('aria-hidden', String(!visible))
|
|
259
|
+
surface.root.inert = !visible
|
|
260
|
+
}
|
|
@@ -5,14 +5,13 @@
|
|
|
5
5
|
// =============================================================================
|
|
6
6
|
|
|
7
7
|
import { sanitizeHtml } from '../../core/sanitize.js'
|
|
8
|
-
import { resolvePath } from '../../shared/resolvePath.js'
|
|
9
8
|
import { BlockPluginAbstract } from '../BlockPluginAbstract.js'
|
|
10
9
|
import { validateSpoilerData } from '../../shared/blockDataValidators.js'
|
|
11
10
|
import { mapSpoilerTextFields } from '../../shared/mapTextFields.js'
|
|
12
11
|
import { normalizeTextValue } from '../../shared/textFormat.js'
|
|
13
12
|
import { READ_ONLY_INTERACTIVE_ATTRIBUTE } from '../../core/constants.js'
|
|
14
13
|
|
|
15
|
-
const editorStyles =
|
|
14
|
+
const editorStyles = new URL('./spoiler.css', import.meta.url).href
|
|
16
15
|
|
|
17
16
|
// Tabler icon: eye-off
|
|
18
17
|
const ICON = '<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"><path d="M10.585 10.587a2 2 0 0 0 2.829 2.828"/><path d="M16.681 16.673a8.717 8.717 0 0 1-4.681 1.327c-3.6 0-6.6-2-9-6 1.272-2.12 2.712-3.678 4.32-4.674m2.86-1.146a9.055 9.055 0 0 1 1.82-.18c3.6 0 6.6 2 9 6-.666 1.11-1.379 2.067-2.138 2.87"/><path d="M3 3l18 18"/></svg>'
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { sanitizeHtml } from '../../core/sanitize.js'
|
|
2
|
-
import { resolvePath } from '../../shared/resolvePath.js'
|
|
3
2
|
import { BlockPluginAbstract } from '../BlockPluginAbstract.js'
|
|
4
3
|
import { validateTableData } from '../../shared/blockDataValidators.js'
|
|
5
4
|
import { mapTableTextFields } from '../../shared/mapTextFields.js'
|
|
6
5
|
|
|
7
|
-
const editorStyles =
|
|
6
|
+
const editorStyles = new URL('./table.css', import.meta.url).href
|
|
8
7
|
|
|
9
8
|
// Tabler icon: table
|
|
10
9
|
const ICON = '<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"><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-14z"/><path d="M3 10h18"/><path d="M10 3v18"/></svg>'
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { sanitizeHtml } from '../../core/sanitize.js'
|
|
2
|
-
import { resolvePath } from '../../shared/resolvePath.js'
|
|
3
2
|
import { BlockPluginAbstract } from '../BlockPluginAbstract.js'
|
|
4
3
|
import { validateToggleData } from '../../shared/blockDataValidators.js'
|
|
5
4
|
import { mapToggleTextFields } from '../../shared/mapTextFields.js'
|
|
6
5
|
import { normalizeTextValue } from '../../shared/textFormat.js'
|
|
7
6
|
import { READ_ONLY_INTERACTIVE_ATTRIBUTE } from '../../core/constants.js'
|
|
8
7
|
|
|
9
|
-
const editorStyles =
|
|
8
|
+
const editorStyles = new URL('./toggle.css', import.meta.url).href
|
|
10
9
|
|
|
11
10
|
let toggleSequence = 0
|
|
12
11
|
|
|
@@ -5,13 +5,12 @@
|
|
|
5
5
|
// =============================================================================
|
|
6
6
|
|
|
7
7
|
import { sanitizeHtml } from '../../core/sanitize.js'
|
|
8
|
-
import { resolvePath } from '../../shared/resolvePath.js'
|
|
9
8
|
import { BlockPluginAbstract } from '../BlockPluginAbstract.js'
|
|
10
9
|
import { validateWarningData } from '../../shared/blockDataValidators.js'
|
|
11
10
|
import { normalizeTextValue } from '../../shared/textFormat.js'
|
|
12
11
|
import { mapWarningTextFields } from '../../shared/mapTextFields.js'
|
|
13
12
|
|
|
14
|
-
const editorStyles =
|
|
13
|
+
const editorStyles = new URL('./warning.css', import.meta.url).href
|
|
15
14
|
|
|
16
15
|
// Tabler icon: alert-triangle
|
|
17
16
|
const ICON = '<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"><path d="M12 9v4"/><path d="M10.363 3.591l-8.106 13.534a1.914 1.914 0 0 0 1.636 2.871h16.214a1.914 1.914 0 0 0 1.636-2.87l-8.106-13.536a1.914 1.914 0 0 0-3.274 0z"/><path d="M12 16h.01"/></svg>'
|
package/dist/renderer/index.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { InvalidBlockDataError, UnknownBlockTypeError } from './errors.js'
|
|
3
3
|
import { createInlineParser } from './inline.js'
|
|
4
4
|
import { createDefaultRenderers, getSupportedBlockTypes } from './renderers/index.js'
|
|
5
|
-
import { resolvePath } from '../shared/resolvePath.js'
|
|
6
5
|
import { deserializeInlineHtml } from '../shared/inlineMarshal.js'
|
|
7
6
|
import { cloneEditorData } from '../shared/cloneEditorData.js'
|
|
8
7
|
import defaultLocale from './locale/en.js'
|
|
@@ -11,7 +10,7 @@ import { validateKnownBlockData } from '../shared/blockDataValidators.js'
|
|
|
11
10
|
import { normalizeKnownBlockData } from '../shared/blockDataNormalizers.js'
|
|
12
11
|
import { normalizeTextAlign } from '../shared/textFormat.js'
|
|
13
12
|
|
|
14
|
-
const baseCssUrl =
|
|
13
|
+
const baseCssUrl = new URL('./styles/base.css', import.meta.url).href
|
|
15
14
|
|
|
16
15
|
/**
|
|
17
16
|
* Renders Rector document blocks to DOM elements.
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
import { resolvePath } from '../../../shared/resolvePath.js'
|
|
3
2
|
import { getFileIcon, getExtension, formatSize, EXT_COLORS } from '../../../shared/fileUtils.js'
|
|
4
3
|
import { sanitizeDownloadUrl, setSafeUrlAttribute } from '../../../shared/sanitize/sanitizeUrl.js'
|
|
5
4
|
import { loadZipRuntime } from '../../../shared/zipRuntime.js'
|
|
6
5
|
|
|
7
6
|
let groupSequence = 0
|
|
8
7
|
|
|
9
|
-
const styles =
|
|
8
|
+
const styles = new URL('./styles.css', import.meta.url).href
|
|
10
9
|
|
|
11
10
|
const ICON_CHEVRON = '<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"><path d="M6 9l6 6 6-6"/></svg>'
|
|
12
11
|
const ICON_FILE_DEFAULT = '<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"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M14 3v4a1 1 0 0 0 1 1h4"/><path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2"/></svg>'
|