@seorii/tiptap 0.4.1 → 0.4.3
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/dist/i18n/es/index.d.ts +51 -0
- package/dist/i18n/es/index.js +50 -0
- package/dist/i18n/index.d.ts +196 -0
- package/dist/i18n/index.js +19 -2
- package/dist/i18n/ja/index.d.ts +51 -0
- package/dist/i18n/ja/index.js +50 -0
- package/dist/i18n/zh-hans/index.d.ts +51 -0
- package/dist/i18n/zh-hans/index.js +50 -0
- package/dist/i18n/zh-hant/index.d.ts +51 -0
- package/dist/i18n/zh-hant/index.js +50 -0
- package/dist/plugin/resize/index.js +254 -40
- package/dist/tiptap/TipTap.svelte +101 -10
- package/package.json +1 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
target: string[];
|
|
3
|
+
lang: string;
|
|
4
|
+
country: string;
|
|
5
|
+
text: string;
|
|
6
|
+
block: string;
|
|
7
|
+
loading: string;
|
|
8
|
+
delete: string;
|
|
9
|
+
close: string;
|
|
10
|
+
cancel: string;
|
|
11
|
+
insert: string;
|
|
12
|
+
noResult: string;
|
|
13
|
+
default: string;
|
|
14
|
+
auto: string;
|
|
15
|
+
title: string;
|
|
16
|
+
paragraph: string;
|
|
17
|
+
link: string;
|
|
18
|
+
alignLeft: string;
|
|
19
|
+
alignCenter: string;
|
|
20
|
+
alignRight: string;
|
|
21
|
+
alignJustify: string;
|
|
22
|
+
unorderedList: string;
|
|
23
|
+
numberList: string;
|
|
24
|
+
codeBlock: string;
|
|
25
|
+
mathBlock: string;
|
|
26
|
+
table: string;
|
|
27
|
+
twoColumns: string;
|
|
28
|
+
threeColumns: string;
|
|
29
|
+
image: string;
|
|
30
|
+
iframe: string;
|
|
31
|
+
youtube: string;
|
|
32
|
+
blockquote: string;
|
|
33
|
+
title1Info: string;
|
|
34
|
+
title2Info: string;
|
|
35
|
+
title3Info: string;
|
|
36
|
+
unorderedListInfo: string;
|
|
37
|
+
numberListInfo: string;
|
|
38
|
+
codeBlockInfo: string;
|
|
39
|
+
mathBlockInfo: string;
|
|
40
|
+
tableInfo: string;
|
|
41
|
+
twoColumnsInfo: string;
|
|
42
|
+
threeColumnsInfo: string;
|
|
43
|
+
imageInfo: string;
|
|
44
|
+
iframeInfo: string;
|
|
45
|
+
youtubeInfo: string;
|
|
46
|
+
blockquoteInfo: string;
|
|
47
|
+
newLineInfo: string;
|
|
48
|
+
placeholder: string;
|
|
49
|
+
insertCode: string;
|
|
50
|
+
};
|
|
51
|
+
export default _default;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
target: ['es', 'es-ES', 'es-419', 'es-MX'],
|
|
3
|
+
lang: 'es',
|
|
4
|
+
country: 'ES',
|
|
5
|
+
text: 'Texto',
|
|
6
|
+
block: 'Bloque',
|
|
7
|
+
loading: 'Cargando...',
|
|
8
|
+
delete: 'Eliminar',
|
|
9
|
+
close: 'Cerrar',
|
|
10
|
+
cancel: 'Cancelar',
|
|
11
|
+
insert: 'Insertar',
|
|
12
|
+
noResult: 'Sin resultados',
|
|
13
|
+
default: 'predeterminado',
|
|
14
|
+
auto: 'auto',
|
|
15
|
+
title: 'Título',
|
|
16
|
+
paragraph: 'Párrafo',
|
|
17
|
+
link: 'Enlace',
|
|
18
|
+
alignLeft: 'Alinear a la izquierda',
|
|
19
|
+
alignCenter: 'Centrar',
|
|
20
|
+
alignRight: 'Alinear a la derecha',
|
|
21
|
+
alignJustify: 'Justificar',
|
|
22
|
+
unorderedList: 'Lista con viñetas',
|
|
23
|
+
numberList: 'Lista numerada',
|
|
24
|
+
codeBlock: 'Bloque de código',
|
|
25
|
+
mathBlock: 'Bloque matemático',
|
|
26
|
+
table: 'Tabla',
|
|
27
|
+
twoColumns: '2 columnas',
|
|
28
|
+
threeColumns: '3 columnas',
|
|
29
|
+
image: 'Imagen',
|
|
30
|
+
iframe: 'iframe',
|
|
31
|
+
youtube: 'YouTube',
|
|
32
|
+
blockquote: 'Cita',
|
|
33
|
+
title1Info: 'Título grande',
|
|
34
|
+
title2Info: 'Título más pequeño',
|
|
35
|
+
title3Info: 'Título mediano',
|
|
36
|
+
unorderedListInfo: 'Lista sin orden',
|
|
37
|
+
numberListInfo: '1, 2, 3, 4',
|
|
38
|
+
codeBlockInfo: 'Bloque de código con resaltado de sintaxis',
|
|
39
|
+
mathBlockInfo: 'Bloque matemático',
|
|
40
|
+
tableInfo: 'Tabla',
|
|
41
|
+
twoColumnsInfo: 'Diseño de dos columnas',
|
|
42
|
+
threeColumnsInfo: 'Diseño de tres columnas',
|
|
43
|
+
imageInfo: 'Imagen',
|
|
44
|
+
iframeInfo: 'Insertar otro sitio web',
|
|
45
|
+
youtubeInfo: 'Insertar video de YouTube',
|
|
46
|
+
blockquoteInfo: 'Cita',
|
|
47
|
+
newLineInfo: 'Pulsa / para abrir comandos. O',
|
|
48
|
+
placeholder: 'Escribe contenido aquí...',
|
|
49
|
+
insertCode: 'Insertar código'
|
|
50
|
+
};
|
package/dist/i18n/index.d.ts
CHANGED
|
@@ -96,6 +96,202 @@ declare const locales: readonly [{
|
|
|
96
96
|
newLineInfo: string;
|
|
97
97
|
placeholder: string;
|
|
98
98
|
insertCode: string;
|
|
99
|
+
}, {
|
|
100
|
+
target: string[];
|
|
101
|
+
lang: string;
|
|
102
|
+
country: string;
|
|
103
|
+
text: string;
|
|
104
|
+
block: string;
|
|
105
|
+
loading: string;
|
|
106
|
+
delete: string;
|
|
107
|
+
close: string;
|
|
108
|
+
cancel: string;
|
|
109
|
+
insert: string;
|
|
110
|
+
noResult: string;
|
|
111
|
+
default: string;
|
|
112
|
+
auto: string;
|
|
113
|
+
title: string;
|
|
114
|
+
paragraph: string;
|
|
115
|
+
link: string;
|
|
116
|
+
alignLeft: string;
|
|
117
|
+
alignCenter: string;
|
|
118
|
+
alignRight: string;
|
|
119
|
+
alignJustify: string;
|
|
120
|
+
unorderedList: string;
|
|
121
|
+
numberList: string;
|
|
122
|
+
codeBlock: string;
|
|
123
|
+
mathBlock: string;
|
|
124
|
+
table: string;
|
|
125
|
+
twoColumns: string;
|
|
126
|
+
threeColumns: string;
|
|
127
|
+
image: string;
|
|
128
|
+
iframe: string;
|
|
129
|
+
youtube: string;
|
|
130
|
+
blockquote: string;
|
|
131
|
+
title1Info: string;
|
|
132
|
+
title2Info: string;
|
|
133
|
+
title3Info: string;
|
|
134
|
+
unorderedListInfo: string;
|
|
135
|
+
numberListInfo: string;
|
|
136
|
+
codeBlockInfo: string;
|
|
137
|
+
mathBlockInfo: string;
|
|
138
|
+
tableInfo: string;
|
|
139
|
+
twoColumnsInfo: string;
|
|
140
|
+
threeColumnsInfo: string;
|
|
141
|
+
imageInfo: string;
|
|
142
|
+
iframeInfo: string;
|
|
143
|
+
youtubeInfo: string;
|
|
144
|
+
blockquoteInfo: string;
|
|
145
|
+
newLineInfo: string;
|
|
146
|
+
placeholder: string;
|
|
147
|
+
insertCode: string;
|
|
148
|
+
}, {
|
|
149
|
+
target: string[];
|
|
150
|
+
lang: string;
|
|
151
|
+
country: string;
|
|
152
|
+
text: string;
|
|
153
|
+
block: string;
|
|
154
|
+
loading: string;
|
|
155
|
+
delete: string;
|
|
156
|
+
close: string;
|
|
157
|
+
cancel: string;
|
|
158
|
+
insert: string;
|
|
159
|
+
noResult: string;
|
|
160
|
+
default: string;
|
|
161
|
+
auto: string;
|
|
162
|
+
title: string;
|
|
163
|
+
paragraph: string;
|
|
164
|
+
link: string;
|
|
165
|
+
alignLeft: string;
|
|
166
|
+
alignCenter: string;
|
|
167
|
+
alignRight: string;
|
|
168
|
+
alignJustify: string;
|
|
169
|
+
unorderedList: string;
|
|
170
|
+
numberList: string;
|
|
171
|
+
codeBlock: string;
|
|
172
|
+
mathBlock: string;
|
|
173
|
+
table: string;
|
|
174
|
+
twoColumns: string;
|
|
175
|
+
threeColumns: string;
|
|
176
|
+
image: string;
|
|
177
|
+
iframe: string;
|
|
178
|
+
youtube: string;
|
|
179
|
+
blockquote: string;
|
|
180
|
+
title1Info: string;
|
|
181
|
+
title2Info: string;
|
|
182
|
+
title3Info: string;
|
|
183
|
+
unorderedListInfo: string;
|
|
184
|
+
numberListInfo: string;
|
|
185
|
+
codeBlockInfo: string;
|
|
186
|
+
mathBlockInfo: string;
|
|
187
|
+
tableInfo: string;
|
|
188
|
+
twoColumnsInfo: string;
|
|
189
|
+
threeColumnsInfo: string;
|
|
190
|
+
imageInfo: string;
|
|
191
|
+
iframeInfo: string;
|
|
192
|
+
youtubeInfo: string;
|
|
193
|
+
blockquoteInfo: string;
|
|
194
|
+
newLineInfo: string;
|
|
195
|
+
placeholder: string;
|
|
196
|
+
insertCode: string;
|
|
197
|
+
}, {
|
|
198
|
+
target: string[];
|
|
199
|
+
lang: string;
|
|
200
|
+
country: string;
|
|
201
|
+
text: string;
|
|
202
|
+
block: string;
|
|
203
|
+
loading: string;
|
|
204
|
+
delete: string;
|
|
205
|
+
close: string;
|
|
206
|
+
cancel: string;
|
|
207
|
+
insert: string;
|
|
208
|
+
noResult: string;
|
|
209
|
+
default: string;
|
|
210
|
+
auto: string;
|
|
211
|
+
title: string;
|
|
212
|
+
paragraph: string;
|
|
213
|
+
link: string;
|
|
214
|
+
alignLeft: string;
|
|
215
|
+
alignCenter: string;
|
|
216
|
+
alignRight: string;
|
|
217
|
+
alignJustify: string;
|
|
218
|
+
unorderedList: string;
|
|
219
|
+
numberList: string;
|
|
220
|
+
codeBlock: string;
|
|
221
|
+
mathBlock: string;
|
|
222
|
+
table: string;
|
|
223
|
+
twoColumns: string;
|
|
224
|
+
threeColumns: string;
|
|
225
|
+
image: string;
|
|
226
|
+
iframe: string;
|
|
227
|
+
youtube: string;
|
|
228
|
+
blockquote: string;
|
|
229
|
+
title1Info: string;
|
|
230
|
+
title2Info: string;
|
|
231
|
+
title3Info: string;
|
|
232
|
+
unorderedListInfo: string;
|
|
233
|
+
numberListInfo: string;
|
|
234
|
+
codeBlockInfo: string;
|
|
235
|
+
mathBlockInfo: string;
|
|
236
|
+
tableInfo: string;
|
|
237
|
+
twoColumnsInfo: string;
|
|
238
|
+
threeColumnsInfo: string;
|
|
239
|
+
imageInfo: string;
|
|
240
|
+
iframeInfo: string;
|
|
241
|
+
youtubeInfo: string;
|
|
242
|
+
blockquoteInfo: string;
|
|
243
|
+
newLineInfo: string;
|
|
244
|
+
placeholder: string;
|
|
245
|
+
insertCode: string;
|
|
246
|
+
}, {
|
|
247
|
+
target: string[];
|
|
248
|
+
lang: string;
|
|
249
|
+
country: string;
|
|
250
|
+
text: string;
|
|
251
|
+
block: string;
|
|
252
|
+
loading: string;
|
|
253
|
+
delete: string;
|
|
254
|
+
close: string;
|
|
255
|
+
cancel: string;
|
|
256
|
+
insert: string;
|
|
257
|
+
noResult: string;
|
|
258
|
+
default: string;
|
|
259
|
+
auto: string;
|
|
260
|
+
title: string;
|
|
261
|
+
paragraph: string;
|
|
262
|
+
link: string;
|
|
263
|
+
alignLeft: string;
|
|
264
|
+
alignCenter: string;
|
|
265
|
+
alignRight: string;
|
|
266
|
+
alignJustify: string;
|
|
267
|
+
unorderedList: string;
|
|
268
|
+
numberList: string;
|
|
269
|
+
codeBlock: string;
|
|
270
|
+
mathBlock: string;
|
|
271
|
+
table: string;
|
|
272
|
+
twoColumns: string;
|
|
273
|
+
threeColumns: string;
|
|
274
|
+
image: string;
|
|
275
|
+
iframe: string;
|
|
276
|
+
youtube: string;
|
|
277
|
+
blockquote: string;
|
|
278
|
+
title1Info: string;
|
|
279
|
+
title2Info: string;
|
|
280
|
+
title3Info: string;
|
|
281
|
+
unorderedListInfo: string;
|
|
282
|
+
numberListInfo: string;
|
|
283
|
+
codeBlockInfo: string;
|
|
284
|
+
mathBlockInfo: string;
|
|
285
|
+
tableInfo: string;
|
|
286
|
+
twoColumnsInfo: string;
|
|
287
|
+
threeColumnsInfo: string;
|
|
288
|
+
imageInfo: string;
|
|
289
|
+
iframeInfo: string;
|
|
290
|
+
youtubeInfo: string;
|
|
291
|
+
blockquoteInfo: string;
|
|
292
|
+
newLineInfo: string;
|
|
293
|
+
placeholder: string;
|
|
294
|
+
insertCode: string;
|
|
99
295
|
}];
|
|
100
296
|
type Locale = (typeof locales)[number];
|
|
101
297
|
export type LocaleInput = string | Locale | null | undefined;
|
package/dist/i18n/index.js
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import enUs from './en-us/index';
|
|
2
2
|
import koKr from './ko-kr/index';
|
|
3
|
-
|
|
3
|
+
import es from './es/index';
|
|
4
|
+
import ja from './ja/index';
|
|
5
|
+
import zhHans from './zh-hans/index';
|
|
6
|
+
import zhHant from './zh-hant/index';
|
|
7
|
+
const locales = [enUs, koKr, es, ja, zhHans, zhHant];
|
|
4
8
|
export const I18N_CONTEXT = Symbol('tiptap-i18n');
|
|
5
|
-
const normalizeLocaleCode = (value) =>
|
|
9
|
+
const normalizeLocaleCode = (value) => {
|
|
10
|
+
const normalized = (value ?? '').trim().replace(/_/g, '-').toLowerCase();
|
|
11
|
+
if (!normalized)
|
|
12
|
+
return '';
|
|
13
|
+
if (!normalized.startsWith('zh'))
|
|
14
|
+
return normalized;
|
|
15
|
+
if (normalized.includes('hant') ||
|
|
16
|
+
normalized.startsWith('zh-tw') ||
|
|
17
|
+
normalized.startsWith('zh-hk') ||
|
|
18
|
+
normalized.startsWith('zh-mo')) {
|
|
19
|
+
return 'zh-hant';
|
|
20
|
+
}
|
|
21
|
+
return 'zh-hans';
|
|
22
|
+
};
|
|
6
23
|
const findLocale = (localeList, language) => {
|
|
7
24
|
const normalized = normalizeLocaleCode(language);
|
|
8
25
|
if (!normalized)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
target: string[];
|
|
3
|
+
lang: string;
|
|
4
|
+
country: string;
|
|
5
|
+
text: string;
|
|
6
|
+
block: string;
|
|
7
|
+
loading: string;
|
|
8
|
+
delete: string;
|
|
9
|
+
close: string;
|
|
10
|
+
cancel: string;
|
|
11
|
+
insert: string;
|
|
12
|
+
noResult: string;
|
|
13
|
+
default: string;
|
|
14
|
+
auto: string;
|
|
15
|
+
title: string;
|
|
16
|
+
paragraph: string;
|
|
17
|
+
link: string;
|
|
18
|
+
alignLeft: string;
|
|
19
|
+
alignCenter: string;
|
|
20
|
+
alignRight: string;
|
|
21
|
+
alignJustify: string;
|
|
22
|
+
unorderedList: string;
|
|
23
|
+
numberList: string;
|
|
24
|
+
codeBlock: string;
|
|
25
|
+
mathBlock: string;
|
|
26
|
+
table: string;
|
|
27
|
+
twoColumns: string;
|
|
28
|
+
threeColumns: string;
|
|
29
|
+
image: string;
|
|
30
|
+
iframe: string;
|
|
31
|
+
youtube: string;
|
|
32
|
+
blockquote: string;
|
|
33
|
+
title1Info: string;
|
|
34
|
+
title2Info: string;
|
|
35
|
+
title3Info: string;
|
|
36
|
+
unorderedListInfo: string;
|
|
37
|
+
numberListInfo: string;
|
|
38
|
+
codeBlockInfo: string;
|
|
39
|
+
mathBlockInfo: string;
|
|
40
|
+
tableInfo: string;
|
|
41
|
+
twoColumnsInfo: string;
|
|
42
|
+
threeColumnsInfo: string;
|
|
43
|
+
imageInfo: string;
|
|
44
|
+
iframeInfo: string;
|
|
45
|
+
youtubeInfo: string;
|
|
46
|
+
blockquoteInfo: string;
|
|
47
|
+
newLineInfo: string;
|
|
48
|
+
placeholder: string;
|
|
49
|
+
insertCode: string;
|
|
50
|
+
};
|
|
51
|
+
export default _default;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
target: ['ja', 'ja-JP'],
|
|
3
|
+
lang: 'ja',
|
|
4
|
+
country: 'JP',
|
|
5
|
+
text: 'テキスト',
|
|
6
|
+
block: 'ブロック',
|
|
7
|
+
loading: '読み込み中...',
|
|
8
|
+
delete: '削除',
|
|
9
|
+
close: '閉じる',
|
|
10
|
+
cancel: 'キャンセル',
|
|
11
|
+
insert: '挿入',
|
|
12
|
+
noResult: '結果なし',
|
|
13
|
+
default: 'デフォルト',
|
|
14
|
+
auto: '自動',
|
|
15
|
+
title: '見出し',
|
|
16
|
+
paragraph: '本文',
|
|
17
|
+
link: 'リンク',
|
|
18
|
+
alignLeft: '左揃え',
|
|
19
|
+
alignCenter: '中央揃え',
|
|
20
|
+
alignRight: '右揃え',
|
|
21
|
+
alignJustify: '両端揃え',
|
|
22
|
+
unorderedList: '箇条書き',
|
|
23
|
+
numberList: '番号付きリスト',
|
|
24
|
+
codeBlock: 'コードブロック',
|
|
25
|
+
mathBlock: '数式ブロック',
|
|
26
|
+
table: '表',
|
|
27
|
+
twoColumns: '2カラム',
|
|
28
|
+
threeColumns: '3カラム',
|
|
29
|
+
image: '画像',
|
|
30
|
+
iframe: 'iframe',
|
|
31
|
+
youtube: 'YouTube',
|
|
32
|
+
blockquote: '引用',
|
|
33
|
+
title1Info: '大きな見出し',
|
|
34
|
+
title2Info: 'やや小さい見出し',
|
|
35
|
+
title3Info: '中くらいの見出し',
|
|
36
|
+
unorderedListInfo: '順序なしリスト',
|
|
37
|
+
numberListInfo: '1, 2, 3, 4',
|
|
38
|
+
codeBlockInfo: 'シンタックスハイライト付きコードブロック',
|
|
39
|
+
mathBlockInfo: '数式ブロック',
|
|
40
|
+
tableInfo: '表',
|
|
41
|
+
twoColumnsInfo: '2カラムレイアウト',
|
|
42
|
+
threeColumnsInfo: '3カラムレイアウト',
|
|
43
|
+
imageInfo: '画像',
|
|
44
|
+
iframeInfo: '別のウェブサイトを埋め込む',
|
|
45
|
+
youtubeInfo: 'YouTube動画を埋め込む',
|
|
46
|
+
blockquoteInfo: '引用ブロック',
|
|
47
|
+
newLineInfo: '/ でコマンドを開く。または',
|
|
48
|
+
placeholder: 'ここに内容を入力...',
|
|
49
|
+
insertCode: 'コードを挿入'
|
|
50
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
target: string[];
|
|
3
|
+
lang: string;
|
|
4
|
+
country: string;
|
|
5
|
+
text: string;
|
|
6
|
+
block: string;
|
|
7
|
+
loading: string;
|
|
8
|
+
delete: string;
|
|
9
|
+
close: string;
|
|
10
|
+
cancel: string;
|
|
11
|
+
insert: string;
|
|
12
|
+
noResult: string;
|
|
13
|
+
default: string;
|
|
14
|
+
auto: string;
|
|
15
|
+
title: string;
|
|
16
|
+
paragraph: string;
|
|
17
|
+
link: string;
|
|
18
|
+
alignLeft: string;
|
|
19
|
+
alignCenter: string;
|
|
20
|
+
alignRight: string;
|
|
21
|
+
alignJustify: string;
|
|
22
|
+
unorderedList: string;
|
|
23
|
+
numberList: string;
|
|
24
|
+
codeBlock: string;
|
|
25
|
+
mathBlock: string;
|
|
26
|
+
table: string;
|
|
27
|
+
twoColumns: string;
|
|
28
|
+
threeColumns: string;
|
|
29
|
+
image: string;
|
|
30
|
+
iframe: string;
|
|
31
|
+
youtube: string;
|
|
32
|
+
blockquote: string;
|
|
33
|
+
title1Info: string;
|
|
34
|
+
title2Info: string;
|
|
35
|
+
title3Info: string;
|
|
36
|
+
unorderedListInfo: string;
|
|
37
|
+
numberListInfo: string;
|
|
38
|
+
codeBlockInfo: string;
|
|
39
|
+
mathBlockInfo: string;
|
|
40
|
+
tableInfo: string;
|
|
41
|
+
twoColumnsInfo: string;
|
|
42
|
+
threeColumnsInfo: string;
|
|
43
|
+
imageInfo: string;
|
|
44
|
+
iframeInfo: string;
|
|
45
|
+
youtubeInfo: string;
|
|
46
|
+
blockquoteInfo: string;
|
|
47
|
+
newLineInfo: string;
|
|
48
|
+
placeholder: string;
|
|
49
|
+
insertCode: string;
|
|
50
|
+
};
|
|
51
|
+
export default _default;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
target: ['zh', 'zh-CN', 'zh-SG', 'zh-Hans'],
|
|
3
|
+
lang: 'zh',
|
|
4
|
+
country: 'CN',
|
|
5
|
+
text: '文本',
|
|
6
|
+
block: '区块',
|
|
7
|
+
loading: '加载中...',
|
|
8
|
+
delete: '删除',
|
|
9
|
+
close: '关闭',
|
|
10
|
+
cancel: '取消',
|
|
11
|
+
insert: '插入',
|
|
12
|
+
noResult: '无结果',
|
|
13
|
+
default: '默认',
|
|
14
|
+
auto: '自动',
|
|
15
|
+
title: '标题',
|
|
16
|
+
paragraph: '正文',
|
|
17
|
+
link: '链接',
|
|
18
|
+
alignLeft: '左对齐',
|
|
19
|
+
alignCenter: '居中对齐',
|
|
20
|
+
alignRight: '右对齐',
|
|
21
|
+
alignJustify: '两端对齐',
|
|
22
|
+
unorderedList: '无序列表',
|
|
23
|
+
numberList: '有序列表',
|
|
24
|
+
codeBlock: '代码块',
|
|
25
|
+
mathBlock: '公式块',
|
|
26
|
+
table: '表格',
|
|
27
|
+
twoColumns: '2 列',
|
|
28
|
+
threeColumns: '3 列',
|
|
29
|
+
image: '图片',
|
|
30
|
+
iframe: 'iframe',
|
|
31
|
+
youtube: 'YouTube',
|
|
32
|
+
blockquote: '引用',
|
|
33
|
+
title1Info: '大标题',
|
|
34
|
+
title2Info: '较小标题',
|
|
35
|
+
title3Info: '中等标题',
|
|
36
|
+
unorderedListInfo: '无序列表',
|
|
37
|
+
numberListInfo: '1, 2, 3, 4',
|
|
38
|
+
codeBlockInfo: '带语法高亮的代码块',
|
|
39
|
+
mathBlockInfo: '公式块',
|
|
40
|
+
tableInfo: '插入表格',
|
|
41
|
+
twoColumnsInfo: '两列布局',
|
|
42
|
+
threeColumnsInfo: '三列布局',
|
|
43
|
+
imageInfo: '图片',
|
|
44
|
+
iframeInfo: '嵌入其他网站',
|
|
45
|
+
youtubeInfo: '嵌入 YouTube 视频',
|
|
46
|
+
blockquoteInfo: '引用块',
|
|
47
|
+
newLineInfo: '按 / 输入命令,或',
|
|
48
|
+
placeholder: '在此输入内容...',
|
|
49
|
+
insertCode: '插入代码'
|
|
50
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
target: string[];
|
|
3
|
+
lang: string;
|
|
4
|
+
country: string;
|
|
5
|
+
text: string;
|
|
6
|
+
block: string;
|
|
7
|
+
loading: string;
|
|
8
|
+
delete: string;
|
|
9
|
+
close: string;
|
|
10
|
+
cancel: string;
|
|
11
|
+
insert: string;
|
|
12
|
+
noResult: string;
|
|
13
|
+
default: string;
|
|
14
|
+
auto: string;
|
|
15
|
+
title: string;
|
|
16
|
+
paragraph: string;
|
|
17
|
+
link: string;
|
|
18
|
+
alignLeft: string;
|
|
19
|
+
alignCenter: string;
|
|
20
|
+
alignRight: string;
|
|
21
|
+
alignJustify: string;
|
|
22
|
+
unorderedList: string;
|
|
23
|
+
numberList: string;
|
|
24
|
+
codeBlock: string;
|
|
25
|
+
mathBlock: string;
|
|
26
|
+
table: string;
|
|
27
|
+
twoColumns: string;
|
|
28
|
+
threeColumns: string;
|
|
29
|
+
image: string;
|
|
30
|
+
iframe: string;
|
|
31
|
+
youtube: string;
|
|
32
|
+
blockquote: string;
|
|
33
|
+
title1Info: string;
|
|
34
|
+
title2Info: string;
|
|
35
|
+
title3Info: string;
|
|
36
|
+
unorderedListInfo: string;
|
|
37
|
+
numberListInfo: string;
|
|
38
|
+
codeBlockInfo: string;
|
|
39
|
+
mathBlockInfo: string;
|
|
40
|
+
tableInfo: string;
|
|
41
|
+
twoColumnsInfo: string;
|
|
42
|
+
threeColumnsInfo: string;
|
|
43
|
+
imageInfo: string;
|
|
44
|
+
iframeInfo: string;
|
|
45
|
+
youtubeInfo: string;
|
|
46
|
+
blockquoteInfo: string;
|
|
47
|
+
newLineInfo: string;
|
|
48
|
+
placeholder: string;
|
|
49
|
+
insertCode: string;
|
|
50
|
+
};
|
|
51
|
+
export default _default;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
target: ['zh-TW', 'zh-HK', 'zh-MO', 'zh-Hant'],
|
|
3
|
+
lang: 'zh',
|
|
4
|
+
country: 'TW',
|
|
5
|
+
text: '文字',
|
|
6
|
+
block: '區塊',
|
|
7
|
+
loading: '載入中...',
|
|
8
|
+
delete: '刪除',
|
|
9
|
+
close: '關閉',
|
|
10
|
+
cancel: '取消',
|
|
11
|
+
insert: '插入',
|
|
12
|
+
noResult: '無結果',
|
|
13
|
+
default: '預設',
|
|
14
|
+
auto: '自動',
|
|
15
|
+
title: '標題',
|
|
16
|
+
paragraph: '段落',
|
|
17
|
+
link: '連結',
|
|
18
|
+
alignLeft: '靠左對齊',
|
|
19
|
+
alignCenter: '置中對齊',
|
|
20
|
+
alignRight: '靠右對齊',
|
|
21
|
+
alignJustify: '左右對齊',
|
|
22
|
+
unorderedList: '項目清單',
|
|
23
|
+
numberList: '編號清單',
|
|
24
|
+
codeBlock: '程式碼區塊',
|
|
25
|
+
mathBlock: '數學區塊',
|
|
26
|
+
table: '表格',
|
|
27
|
+
twoColumns: '2 欄',
|
|
28
|
+
threeColumns: '3 欄',
|
|
29
|
+
image: '圖片',
|
|
30
|
+
iframe: 'iframe',
|
|
31
|
+
youtube: 'YouTube',
|
|
32
|
+
blockquote: '引用',
|
|
33
|
+
title1Info: '大標題',
|
|
34
|
+
title2Info: '較小標題',
|
|
35
|
+
title3Info: '中等標題',
|
|
36
|
+
unorderedListInfo: '無序清單',
|
|
37
|
+
numberListInfo: '1, 2, 3, 4',
|
|
38
|
+
codeBlockInfo: '含語法高亮的程式碼區塊',
|
|
39
|
+
mathBlockInfo: '數學區塊',
|
|
40
|
+
tableInfo: '插入表格',
|
|
41
|
+
twoColumnsInfo: '雙欄版面',
|
|
42
|
+
threeColumnsInfo: '三欄版面',
|
|
43
|
+
imageInfo: '圖片',
|
|
44
|
+
iframeInfo: '嵌入其他網站',
|
|
45
|
+
youtubeInfo: '嵌入 YouTube 影片',
|
|
46
|
+
blockquoteInfo: '引用區塊',
|
|
47
|
+
newLineInfo: '按 / 輸入指令,或',
|
|
48
|
+
placeholder: '在此輸入內容...',
|
|
49
|
+
insertCode: '插入程式碼'
|
|
50
|
+
};
|
|
@@ -19,6 +19,15 @@ const minHeight = {
|
|
|
19
19
|
attr: 160
|
|
20
20
|
};
|
|
21
21
|
const maxHeight = 1600;
|
|
22
|
+
const aspectRatioOptions = [
|
|
23
|
+
{ label: 'Auto', value: null },
|
|
24
|
+
{ label: '16:9', value: '16:9' },
|
|
25
|
+
{ label: '3:2', value: '3:2' },
|
|
26
|
+
{ label: '21:9', value: '21:9' },
|
|
27
|
+
{ label: '1:1', value: '1:1' },
|
|
28
|
+
{ label: '4:3', value: '4:3' },
|
|
29
|
+
{ label: '9:16', value: '9:16' }
|
|
30
|
+
];
|
|
22
31
|
function isResizableType(value) {
|
|
23
32
|
return typeSet.has(value);
|
|
24
33
|
}
|
|
@@ -37,6 +46,69 @@ function parseNumericSize(value) {
|
|
|
37
46
|
const parsed = Number.parseFloat(normalized);
|
|
38
47
|
return Number.isFinite(parsed) ? parsed : null;
|
|
39
48
|
}
|
|
49
|
+
function parseAspectRatio(value) {
|
|
50
|
+
if (typeof value === 'number')
|
|
51
|
+
return Number.isFinite(value) && value > 0 ? value : null;
|
|
52
|
+
if (typeof value !== 'string')
|
|
53
|
+
return null;
|
|
54
|
+
const trimmed = value.trim();
|
|
55
|
+
if (!trimmed)
|
|
56
|
+
return null;
|
|
57
|
+
const ratioMatch = /^([0-9]+(?:\.[0-9]+)?)\s*:\s*([0-9]+(?:\.[0-9]+)?)$/.exec(trimmed);
|
|
58
|
+
if (ratioMatch) {
|
|
59
|
+
const widthPart = Number.parseFloat(ratioMatch[1]);
|
|
60
|
+
const heightPart = Number.parseFloat(ratioMatch[2]);
|
|
61
|
+
if (!Number.isFinite(widthPart) || !Number.isFinite(heightPart))
|
|
62
|
+
return null;
|
|
63
|
+
if (widthPart <= 0 || heightPart <= 0)
|
|
64
|
+
return null;
|
|
65
|
+
return widthPart / heightPart;
|
|
66
|
+
}
|
|
67
|
+
const parsed = Number.parseFloat(trimmed);
|
|
68
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
69
|
+
return null;
|
|
70
|
+
return parsed;
|
|
71
|
+
}
|
|
72
|
+
function formatDecimal(value, precision = 6) {
|
|
73
|
+
return String(Number(value.toFixed(precision)));
|
|
74
|
+
}
|
|
75
|
+
function normalizeAspectRatioAttr(value) {
|
|
76
|
+
if (typeof value === 'number') {
|
|
77
|
+
if (!Number.isFinite(value) || value <= 0)
|
|
78
|
+
return null;
|
|
79
|
+
return formatDecimal(value);
|
|
80
|
+
}
|
|
81
|
+
if (typeof value !== 'string')
|
|
82
|
+
return null;
|
|
83
|
+
const trimmed = value.trim();
|
|
84
|
+
if (!trimmed)
|
|
85
|
+
return null;
|
|
86
|
+
const ratioMatch = /^([0-9]+(?:\.[0-9]+)?)\s*:\s*([0-9]+(?:\.[0-9]+)?)$/.exec(trimmed);
|
|
87
|
+
if (ratioMatch) {
|
|
88
|
+
const widthPart = Number.parseFloat(ratioMatch[1]);
|
|
89
|
+
const heightPart = Number.parseFloat(ratioMatch[2]);
|
|
90
|
+
if (!Number.isFinite(widthPart) || !Number.isFinite(heightPart))
|
|
91
|
+
return null;
|
|
92
|
+
if (widthPart <= 0 || heightPart <= 0)
|
|
93
|
+
return null;
|
|
94
|
+
return `${formatDecimal(widthPart, 4)}:${formatDecimal(heightPart, 4)}`;
|
|
95
|
+
}
|
|
96
|
+
const parsed = Number.parseFloat(trimmed);
|
|
97
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
98
|
+
return null;
|
|
99
|
+
return formatDecimal(parsed);
|
|
100
|
+
}
|
|
101
|
+
function sameAspectRatio(left, right) {
|
|
102
|
+
if (!left && !right)
|
|
103
|
+
return true;
|
|
104
|
+
if (!left || !right)
|
|
105
|
+
return false;
|
|
106
|
+
const leftRatio = parseAspectRatio(left);
|
|
107
|
+
const rightRatio = parseAspectRatio(right);
|
|
108
|
+
if (leftRatio === null || rightRatio === null)
|
|
109
|
+
return false;
|
|
110
|
+
return Math.abs(leftRatio - rightRatio) <= 0.001;
|
|
111
|
+
}
|
|
40
112
|
function normalizeStringAttr(value) {
|
|
41
113
|
if (typeof value !== 'string')
|
|
42
114
|
return null;
|
|
@@ -113,6 +185,15 @@ function resolveStartHeight(kind, node, element) {
|
|
|
113
185
|
return fromAttr;
|
|
114
186
|
return defaultHeight[kind];
|
|
115
187
|
}
|
|
188
|
+
function resolveElementWidth(node, element) {
|
|
189
|
+
const rect = element.getBoundingClientRect();
|
|
190
|
+
if (rect.width > 0)
|
|
191
|
+
return rect.width;
|
|
192
|
+
const fromAttr = parseNumericSize(node.attrs.width);
|
|
193
|
+
if (fromAttr !== null)
|
|
194
|
+
return fromAttr;
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
116
197
|
function resolveTargetElement(view, pos, resizeMeta, node) {
|
|
117
198
|
const nodeDom = view.nodeDOM(pos);
|
|
118
199
|
if (!(nodeDom instanceof HTMLElement))
|
|
@@ -153,7 +234,7 @@ function resolveTargetElement(view, pos, resizeMeta, node) {
|
|
|
153
234
|
nodeDom.querySelector('iframe') ||
|
|
154
235
|
nodeDom);
|
|
155
236
|
}
|
|
156
|
-
function buildResizeAttrs(kind, node, height, imageRatio) {
|
|
237
|
+
function buildResizeAttrs(kind, node, height, imageRatio, aspectRatio = normalizeAspectRatioAttr(node.attrs.aspectRatio)) {
|
|
157
238
|
const attrs = { ...node.attrs };
|
|
158
239
|
const roundedHeight = String(Math.round(height));
|
|
159
240
|
if (kind === 'image') {
|
|
@@ -161,12 +242,26 @@ function buildResizeAttrs(kind, node, height, imageRatio) {
|
|
|
161
242
|
// Keep image responsive by storing width only; fixed height causes ratio distortion on narrow layouts.
|
|
162
243
|
return { ...attrs, width: roundedWidth, height: null };
|
|
163
244
|
}
|
|
164
|
-
if (kind === 'iframe'
|
|
165
|
-
|
|
245
|
+
if (kind === 'iframe') {
|
|
246
|
+
if (aspectRatio) {
|
|
247
|
+
// A fixed height prevents CSS aspect-ratio from taking effect.
|
|
248
|
+
return { ...attrs, width: attrs.width || '100%', height: null, aspectRatio };
|
|
249
|
+
}
|
|
250
|
+
return { ...attrs, width: attrs.width || '100%', height: roundedHeight, aspectRatio: null };
|
|
251
|
+
}
|
|
252
|
+
if (kind === 'embed') {
|
|
253
|
+
if (aspectRatio) {
|
|
254
|
+
// Keep PDF/embed responsive with CSS aspect-ratio when a preset is selected.
|
|
255
|
+
return { ...attrs, width: attrs.width || '100%', height: null, aspectRatio };
|
|
256
|
+
}
|
|
257
|
+
return { ...attrs, width: attrs.width || '100%', height: roundedHeight, aspectRatio: null };
|
|
166
258
|
}
|
|
167
|
-
return { ...attrs, height: roundedHeight };
|
|
259
|
+
return { ...attrs, height: roundedHeight, aspectRatio: null };
|
|
168
260
|
}
|
|
169
|
-
function
|
|
261
|
+
function canUseAspectRatioPreset(kind) {
|
|
262
|
+
return kind === 'iframe' || kind === 'embed';
|
|
263
|
+
}
|
|
264
|
+
function createResizeHandleDecoration(nodePos, widgetPos, resizeMeta, node) {
|
|
170
265
|
return Decoration.widget(widgetPos, () => {
|
|
171
266
|
const anchor = document.createElement('div');
|
|
172
267
|
anchor.className = 'tiptap-media-resize-anchor';
|
|
@@ -175,8 +270,30 @@ function createResizeHandleDecoration(nodePos, widgetPos, resizeMeta) {
|
|
|
175
270
|
button.className = 'tiptap-media-resize-handle';
|
|
176
271
|
button.dataset.resizePos = String(nodePos);
|
|
177
272
|
button.dataset.resizeKind = resizeMeta.kind;
|
|
178
|
-
button.setAttribute('aria-label', 'Resize media height');
|
|
273
|
+
button.setAttribute('aria-label', 'Resize media height (click for aspect ratio)');
|
|
179
274
|
anchor.append(button);
|
|
275
|
+
if (canUseAspectRatioPreset(resizeMeta.kind)) {
|
|
276
|
+
const selectedAspectRatio = normalizeAspectRatioAttr(node.attrs.aspectRatio);
|
|
277
|
+
const toolbar = document.createElement('div');
|
|
278
|
+
toolbar.className = 'tiptap-media-aspect-ratio-toolbar';
|
|
279
|
+
toolbar.setAttribute('role', 'toolbar');
|
|
280
|
+
toolbar.setAttribute('aria-label', 'Aspect ratio presets');
|
|
281
|
+
toolbar.dataset.resizePos = String(nodePos);
|
|
282
|
+
for (const option of aspectRatioOptions) {
|
|
283
|
+
const optionButton = document.createElement('button');
|
|
284
|
+
optionButton.type = 'button';
|
|
285
|
+
optionButton.className = 'tiptap-media-aspect-ratio-option';
|
|
286
|
+
optionButton.dataset.resizePos = String(nodePos);
|
|
287
|
+
optionButton.dataset.aspectRatio = option.value ?? 'auto';
|
|
288
|
+
optionButton.textContent = option.label;
|
|
289
|
+
const isActive = option.value
|
|
290
|
+
? sameAspectRatio(option.value, selectedAspectRatio)
|
|
291
|
+
: !selectedAspectRatio;
|
|
292
|
+
optionButton.setAttribute('aria-pressed', isActive ? 'true' : 'false');
|
|
293
|
+
toolbar.append(optionButton);
|
|
294
|
+
}
|
|
295
|
+
anchor.append(toolbar);
|
|
296
|
+
}
|
|
180
297
|
return anchor;
|
|
181
298
|
}, { side: 1, key: `media-resize-${nodePos}-${resizeMeta.typeName}-${resizeMeta.kind}` });
|
|
182
299
|
}
|
|
@@ -239,8 +356,17 @@ export default Extension.create({
|
|
|
239
356
|
},
|
|
240
357
|
height: {
|
|
241
358
|
default: '600',
|
|
242
|
-
parseHTML: (element) =>
|
|
359
|
+
parseHTML: (element) => {
|
|
360
|
+
const aspectRatio = normalizeAspectRatioAttr(element.getAttribute('data-resize-aspect-ratio'));
|
|
361
|
+
if (aspectRatio)
|
|
362
|
+
return null;
|
|
363
|
+
return (normalizeNumericAttr(element.getAttribute('height') || element.style.height) ||
|
|
364
|
+
'600');
|
|
365
|
+
},
|
|
243
366
|
renderHTML: (attributes) => {
|
|
367
|
+
const aspectRatio = normalizeAspectRatioAttr(attributes.aspectRatio);
|
|
368
|
+
if (aspectRatio)
|
|
369
|
+
return {};
|
|
244
370
|
const height = normalizeNumericAttr(attributes.height) || '600';
|
|
245
371
|
return { height };
|
|
246
372
|
}
|
|
@@ -254,9 +380,7 @@ export default Extension.create({
|
|
|
254
380
|
default: false,
|
|
255
381
|
parseHTML: (element) => hasResizeHandler(element.getAttribute('data-resize-handler') ||
|
|
256
382
|
element.getAttribute('resize-handler')),
|
|
257
|
-
renderHTML: (attributes) => hasResizeHandler(attributes.resizeHandler)
|
|
258
|
-
? { 'data-resize-handler': 'true' }
|
|
259
|
-
: {}
|
|
383
|
+
renderHTML: (attributes) => hasResizeHandler(attributes.resizeHandler) ? { 'data-resize-handler': 'true' } : {}
|
|
260
384
|
},
|
|
261
385
|
resizeTarget: {
|
|
262
386
|
default: null,
|
|
@@ -281,6 +405,14 @@ export default Extension.create({
|
|
|
281
405
|
const maxHeight = normalizeNumericAttr(attributes.maxHeight);
|
|
282
406
|
return maxHeight ? { 'data-resize-max-height': maxHeight } : {};
|
|
283
407
|
}
|
|
408
|
+
},
|
|
409
|
+
aspectRatio: {
|
|
410
|
+
default: null,
|
|
411
|
+
parseHTML: (element) => normalizeAspectRatioAttr(element.getAttribute('data-resize-aspect-ratio')),
|
|
412
|
+
renderHTML: (attributes) => {
|
|
413
|
+
const aspectRatio = normalizeAspectRatioAttr(attributes.aspectRatio);
|
|
414
|
+
return aspectRatio ? { 'data-resize-aspect-ratio': aspectRatio } : {};
|
|
415
|
+
}
|
|
284
416
|
}
|
|
285
417
|
}
|
|
286
418
|
}
|
|
@@ -288,6 +420,15 @@ export default Extension.create({
|
|
|
288
420
|
},
|
|
289
421
|
addProseMirrorPlugins() {
|
|
290
422
|
let removeDragListeners = null;
|
|
423
|
+
const closeOpenToolbars = (view, except = null) => {
|
|
424
|
+
view.dom
|
|
425
|
+
.querySelectorAll('.tiptap-media-resize-anchor.is-toolbar-open')
|
|
426
|
+
.forEach((anchor) => {
|
|
427
|
+
if (except && anchor === except)
|
|
428
|
+
return;
|
|
429
|
+
anchor.classList.remove('is-toolbar-open');
|
|
430
|
+
});
|
|
431
|
+
};
|
|
291
432
|
return [
|
|
292
433
|
new Plugin({
|
|
293
434
|
key: pluginKey,
|
|
@@ -328,7 +469,7 @@ export default Extension.create({
|
|
|
328
469
|
const resizeMeta = resolveResizeMeta(node);
|
|
329
470
|
if (!resizeMeta || resizeMeta.kind === 'image' || node.isInline)
|
|
330
471
|
return;
|
|
331
|
-
decorations.push(createResizeHandleDecoration(pos, pos + node.nodeSize, resizeMeta));
|
|
472
|
+
decorations.push(createResizeHandleDecoration(pos, pos + node.nodeSize, resizeMeta, node));
|
|
332
473
|
handled.add(pos);
|
|
333
474
|
});
|
|
334
475
|
}
|
|
@@ -336,7 +477,7 @@ export default Extension.create({
|
|
|
336
477
|
const pos = state.selection.from;
|
|
337
478
|
const resizeMeta = resolveResizeMeta(state.selection.node);
|
|
338
479
|
if (resizeMeta && !handled.has(pos)) {
|
|
339
|
-
decorations.push(createResizeHandleDecoration(pos, pos + state.selection.node.nodeSize, resizeMeta));
|
|
480
|
+
decorations.push(createResizeHandleDecoration(pos, pos + state.selection.node.nodeSize, resizeMeta, state.selection.node));
|
|
340
481
|
}
|
|
341
482
|
}
|
|
342
483
|
if (!decorations.length)
|
|
@@ -349,9 +490,50 @@ export default Extension.create({
|
|
|
349
490
|
return false;
|
|
350
491
|
if (!(event.target instanceof HTMLElement))
|
|
351
492
|
return false;
|
|
493
|
+
const ratioOption = event.target.closest('.tiptap-media-aspect-ratio-option');
|
|
494
|
+
if (ratioOption) {
|
|
495
|
+
event.preventDefault();
|
|
496
|
+
event.stopPropagation();
|
|
497
|
+
const pos = Number.parseInt(ratioOption.dataset.resizePos || '', 10);
|
|
498
|
+
if (!Number.isFinite(pos))
|
|
499
|
+
return true;
|
|
500
|
+
const node = view.state.doc.nodeAt(pos);
|
|
501
|
+
if (!node)
|
|
502
|
+
return true;
|
|
503
|
+
const resizeMeta = resolveResizeMeta(node);
|
|
504
|
+
if (!resizeMeta || !canUseAspectRatioPreset(resizeMeta.kind))
|
|
505
|
+
return true;
|
|
506
|
+
const target = resolveTargetElement(view, pos, resizeMeta, node);
|
|
507
|
+
if (!target)
|
|
508
|
+
return true;
|
|
509
|
+
const selectedRatio = ratioOption.dataset.aspectRatio || 'auto';
|
|
510
|
+
const normalizedAspectRatio = selectedRatio === 'auto' ? null : normalizeAspectRatioAttr(selectedRatio);
|
|
511
|
+
if (selectedRatio !== 'auto' && !normalizedAspectRatio)
|
|
512
|
+
return true;
|
|
513
|
+
let nextHeight = resolveStartHeight(resizeMeta.kind, node, target);
|
|
514
|
+
const ratioValue = parseAspectRatio(normalizedAspectRatio);
|
|
515
|
+
if (ratioValue !== null) {
|
|
516
|
+
const width = resolveElementWidth(node, target);
|
|
517
|
+
if (width > 0) {
|
|
518
|
+
nextHeight = clamp(width / ratioValue, resizeMeta.minHeight, resizeMeta.maxHeight);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
const nextAttrs = buildResizeAttrs(resizeMeta.kind, node, nextHeight, 1, normalizedAspectRatio);
|
|
522
|
+
const nextWidth = 'width' in nextAttrs ? nextAttrs.width : node.attrs.width;
|
|
523
|
+
const nextAspectRatio = 'aspectRatio' in nextAttrs ? nextAttrs.aspectRatio : null;
|
|
524
|
+
if (nextAttrs.height !== node.attrs.height ||
|
|
525
|
+
nextWidth !== node.attrs.width ||
|
|
526
|
+
nextAspectRatio !== node.attrs.aspectRatio) {
|
|
527
|
+
view.dispatch(view.state.tr.setNodeMarkup(pos, node.type, nextAttrs));
|
|
528
|
+
}
|
|
529
|
+
closeOpenToolbars(view);
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
352
532
|
const handle = event.target.closest('.tiptap-media-resize-handle');
|
|
353
|
-
if (!handle)
|
|
533
|
+
if (!handle) {
|
|
534
|
+
closeOpenToolbars(view);
|
|
354
535
|
return false;
|
|
536
|
+
}
|
|
355
537
|
const pos = Number.parseInt(handle.dataset.resizePos || '', 10);
|
|
356
538
|
if (!Number.isFinite(pos))
|
|
357
539
|
return false;
|
|
@@ -369,6 +551,8 @@ export default Extension.create({
|
|
|
369
551
|
return false;
|
|
370
552
|
event.preventDefault();
|
|
371
553
|
event.stopPropagation();
|
|
554
|
+
const anchor = handle.closest('.tiptap-media-resize-anchor');
|
|
555
|
+
const startX = event.clientX;
|
|
372
556
|
const startY = event.clientY;
|
|
373
557
|
const startHeight = resolveStartHeight(resizeMeta.kind, node, target);
|
|
374
558
|
const imageRatio = resizeMeta.kind === 'image' ? resolveImageRatio(node, target) : 1;
|
|
@@ -377,21 +561,10 @@ export default Extension.create({
|
|
|
377
561
|
let restoreTarget = null;
|
|
378
562
|
let frame = 0;
|
|
379
563
|
let pendingHeight = startHeight;
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
resizeProxy.className = 'tiptap-media-resize-proxy';
|
|
385
|
-
resizeProxy.style.height = `${Math.round(startHeight)}px`;
|
|
386
|
-
target.parentElement.insertBefore(resizeProxy, targetElement);
|
|
387
|
-
targetElement.style.display = 'none';
|
|
388
|
-
restoreTarget = () => {
|
|
389
|
-
targetElement.style.display = originalDisplay;
|
|
390
|
-
resizeProxy?.remove();
|
|
391
|
-
resizeProxy = null;
|
|
392
|
-
restoreTarget = null;
|
|
393
|
-
};
|
|
394
|
-
}
|
|
564
|
+
let isDragging = false;
|
|
565
|
+
let appliedDragCursor = false;
|
|
566
|
+
const previousCursor = document.body.style.cursor;
|
|
567
|
+
const previousSelect = document.body.style.userSelect;
|
|
395
568
|
const dispatchHeight = (height) => {
|
|
396
569
|
const current = view.state.doc.nodeAt(pos);
|
|
397
570
|
if (!current || current.type.name !== resizeMeta.typeName)
|
|
@@ -399,19 +572,51 @@ export default Extension.create({
|
|
|
399
572
|
const currentMeta = resolveResizeMeta(current);
|
|
400
573
|
if (!currentMeta)
|
|
401
574
|
return;
|
|
402
|
-
|
|
403
|
-
const
|
|
404
|
-
|
|
575
|
+
// Manual drag should unlock fixed aspect ratio.
|
|
576
|
+
const aspectRatioForDrag = null;
|
|
577
|
+
const nextAttrs = buildResizeAttrs(currentMeta.kind, current, height, imageRatio, aspectRatioForDrag);
|
|
578
|
+
const nextWidth = 'width' in nextAttrs
|
|
579
|
+
? nextAttrs.width
|
|
580
|
+
: current.attrs.width;
|
|
581
|
+
const nextAspectRatio = 'aspectRatio' in nextAttrs ? nextAttrs.aspectRatio : null;
|
|
582
|
+
if (nextAttrs.height === current.attrs.height &&
|
|
583
|
+
nextWidth === current.attrs.width &&
|
|
584
|
+
nextAspectRatio === current.attrs.aspectRatio)
|
|
405
585
|
return;
|
|
406
|
-
|
|
407
|
-
|
|
586
|
+
view.dispatch(view.state.tr.setNodeMarkup(pos, current.type, nextAttrs));
|
|
587
|
+
};
|
|
588
|
+
const beginDrag = () => {
|
|
589
|
+
if (isDragging)
|
|
590
|
+
return;
|
|
591
|
+
isDragging = true;
|
|
592
|
+
closeOpenToolbars(view);
|
|
593
|
+
if (shouldShowProxy && target.parentElement) {
|
|
594
|
+
const targetElement = target;
|
|
595
|
+
const originalDisplay = targetElement.style.display;
|
|
596
|
+
resizeProxy = document.createElement('div');
|
|
597
|
+
resizeProxy.className = 'tiptap-media-resize-proxy';
|
|
598
|
+
resizeProxy.style.height = `${Math.round(startHeight)}px`;
|
|
599
|
+
target.parentElement.insertBefore(resizeProxy, targetElement);
|
|
600
|
+
targetElement.style.display = 'none';
|
|
601
|
+
restoreTarget = () => {
|
|
602
|
+
targetElement.style.display = originalDisplay;
|
|
603
|
+
resizeProxy?.remove();
|
|
604
|
+
resizeProxy = null;
|
|
605
|
+
restoreTarget = null;
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
document.body.style.cursor = 'ns-resize';
|
|
609
|
+
document.body.style.userSelect = 'none';
|
|
610
|
+
appliedDragCursor = true;
|
|
408
611
|
};
|
|
409
|
-
const previousCursor = document.body.style.cursor;
|
|
410
|
-
const previousSelect = document.body.style.userSelect;
|
|
411
|
-
document.body.style.cursor = 'ns-resize';
|
|
412
|
-
document.body.style.userSelect = 'none';
|
|
413
612
|
const onMove = (moveEvent) => {
|
|
414
|
-
const
|
|
613
|
+
const deltaX = moveEvent.clientX - startX;
|
|
614
|
+
const deltaY = moveEvent.clientY - startY;
|
|
615
|
+
if (!isDragging && Math.max(Math.abs(deltaX), Math.abs(deltaY)) < 4)
|
|
616
|
+
return;
|
|
617
|
+
if (!isDragging)
|
|
618
|
+
beginDrag();
|
|
619
|
+
const nextHeight = clamp(startHeight + deltaY, resizeMeta.minHeight, resizeMeta.maxHeight);
|
|
415
620
|
pendingHeight = nextHeight;
|
|
416
621
|
if (resizeProxy)
|
|
417
622
|
resizeProxy.style.height = `${Math.round(nextHeight)}px`;
|
|
@@ -426,12 +631,21 @@ export default Extension.create({
|
|
|
426
631
|
cancelAnimationFrame(frame);
|
|
427
632
|
window.removeEventListener('mousemove', onMove);
|
|
428
633
|
window.removeEventListener('mouseup', onUp);
|
|
429
|
-
|
|
430
|
-
|
|
634
|
+
if (appliedDragCursor) {
|
|
635
|
+
document.body.style.cursor = previousCursor;
|
|
636
|
+
document.body.style.userSelect = previousSelect;
|
|
637
|
+
}
|
|
431
638
|
restoreTarget?.();
|
|
432
639
|
removeDragListeners = null;
|
|
433
640
|
};
|
|
434
641
|
const onUp = () => {
|
|
642
|
+
if (!isDragging) {
|
|
643
|
+
const shouldOpen = Boolean(anchor) && !(anchor?.classList.contains('is-toolbar-open') ?? false);
|
|
644
|
+
closeOpenToolbars(view, shouldOpen && anchor ? anchor : null);
|
|
645
|
+
anchor?.classList.toggle('is-toolbar-open', shouldOpen);
|
|
646
|
+
cleanup();
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
435
649
|
if (shouldShowProxy)
|
|
436
650
|
dispatchHeight(pendingHeight);
|
|
437
651
|
cleanup();
|
|
@@ -81,6 +81,7 @@
|
|
|
81
81
|
'data-resize-target',
|
|
82
82
|
'data-resize-min-height',
|
|
83
83
|
'data-resize-max-height',
|
|
84
|
+
'data-resize-aspect-ratio',
|
|
84
85
|
'data-bubble-menu',
|
|
85
86
|
'data-hide-bubble-menu'
|
|
86
87
|
];
|
|
@@ -343,10 +344,13 @@
|
|
|
343
344
|
.editable :global(.tiptap-media-resize-anchor) {
|
|
344
345
|
width: 100%;
|
|
345
346
|
display: flex;
|
|
346
|
-
|
|
347
|
+
flex-direction: column;
|
|
348
|
+
align-items: center;
|
|
347
349
|
margin: 6px 0 2px;
|
|
348
350
|
line-height: 0;
|
|
349
351
|
pointer-events: none;
|
|
352
|
+
position: relative;
|
|
353
|
+
overflow: visible;
|
|
350
354
|
}
|
|
351
355
|
|
|
352
356
|
.editable :global(.tiptap-media-resize-handle) {
|
|
@@ -379,6 +383,56 @@
|
|
|
379
383
|
transform: translateY(1px);
|
|
380
384
|
}
|
|
381
385
|
|
|
386
|
+
.editable :global(.tiptap-media-aspect-ratio-toolbar) {
|
|
387
|
+
display: none;
|
|
388
|
+
align-items: center;
|
|
389
|
+
gap: 4px;
|
|
390
|
+
padding: 4px;
|
|
391
|
+
border: 1px solid var(--primary-light3, rgba(120, 120, 120, 0.4));
|
|
392
|
+
border-radius: 999px;
|
|
393
|
+
background: var(--surface, #fff);
|
|
394
|
+
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
|
|
395
|
+
pointer-events: auto;
|
|
396
|
+
line-height: 1;
|
|
397
|
+
position: absolute;
|
|
398
|
+
top: calc(100% + 6px);
|
|
399
|
+
left: 50%;
|
|
400
|
+
transform: translateX(-50%);
|
|
401
|
+
z-index: 4;
|
|
402
|
+
white-space: nowrap;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.editable
|
|
406
|
+
:global(.tiptap-media-resize-anchor.is-toolbar-open .tiptap-media-aspect-ratio-toolbar) {
|
|
407
|
+
display: flex;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.editable :global(.tiptap-media-aspect-ratio-option) {
|
|
411
|
+
appearance: none;
|
|
412
|
+
-webkit-appearance: none;
|
|
413
|
+
margin: 0;
|
|
414
|
+
padding: 2px 8px;
|
|
415
|
+
border: 0;
|
|
416
|
+
border-radius: 999px;
|
|
417
|
+
background: transparent;
|
|
418
|
+
color: var(--on-surface, #000);
|
|
419
|
+
font-size: 11px;
|
|
420
|
+
font-weight: 600;
|
|
421
|
+
cursor: pointer;
|
|
422
|
+
transition: background-color 0.15s ease;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.editable :global(.tiptap-media-aspect-ratio-option:hover),
|
|
426
|
+
.editable :global(.tiptap-media-aspect-ratio-option:focus-visible) {
|
|
427
|
+
background: var(--primary-light1, rgba(120, 120, 120, 0.14));
|
|
428
|
+
outline: none;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.editable :global(.tiptap-media-aspect-ratio-option[aria-pressed='true']) {
|
|
432
|
+
background: var(--primary-light4, rgba(120, 120, 120, 0.3));
|
|
433
|
+
color: var(--on-primary, #000);
|
|
434
|
+
}
|
|
435
|
+
|
|
382
436
|
.editable :global(.tiptap-media-resize-proxy) {
|
|
383
437
|
width: 100%;
|
|
384
438
|
border-radius: 12px;
|
|
@@ -471,21 +525,58 @@
|
|
|
471
525
|
border-radius: 12px;
|
|
472
526
|
}
|
|
473
527
|
|
|
474
|
-
& :global(.iframe-wrapper)
|
|
475
|
-
|
|
476
|
-
padding-bottom: 12px;
|
|
528
|
+
& :global(.iframe-wrapper),
|
|
529
|
+
& :global(.embed-wrapper) {
|
|
477
530
|
overflow: hidden;
|
|
478
531
|
width: 100%;
|
|
479
|
-
height: 600px;
|
|
480
532
|
border-radius: 12px;
|
|
481
533
|
}
|
|
482
534
|
|
|
483
|
-
& :global(iframe)
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
left: 0;
|
|
535
|
+
& :global(iframe),
|
|
536
|
+
& :global(embed) {
|
|
537
|
+
display: block;
|
|
487
538
|
width: 100%;
|
|
488
|
-
|
|
539
|
+
max-width: 100%;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
& :global([data-resize-aspect-ratio='16:9']) {
|
|
543
|
+
--tiptap-media-aspect-ratio: 16 / 9;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
& :global([data-resize-aspect-ratio='3:2']) {
|
|
547
|
+
--tiptap-media-aspect-ratio: 3 / 2;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
& :global([data-resize-aspect-ratio='21:9']) {
|
|
551
|
+
--tiptap-media-aspect-ratio: 21 / 9;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
& :global([data-resize-aspect-ratio='1:1']) {
|
|
555
|
+
--tiptap-media-aspect-ratio: 1 / 1;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
& :global([data-resize-aspect-ratio='4:3']) {
|
|
559
|
+
--tiptap-media-aspect-ratio: 4 / 3;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
& :global([data-resize-aspect-ratio='9:16']) {
|
|
563
|
+
--tiptap-media-aspect-ratio: 9 / 16;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
& :global(iframe[data-resize-aspect-ratio='16:9']),
|
|
567
|
+
& :global(iframe[data-resize-aspect-ratio='3:2']),
|
|
568
|
+
& :global(iframe[data-resize-aspect-ratio='21:9']),
|
|
569
|
+
& :global(iframe[data-resize-aspect-ratio='1:1']),
|
|
570
|
+
& :global(iframe[data-resize-aspect-ratio='4:3']),
|
|
571
|
+
& :global(iframe[data-resize-aspect-ratio='9:16']),
|
|
572
|
+
& :global(embed[data-resize-aspect-ratio='16:9']),
|
|
573
|
+
& :global(embed[data-resize-aspect-ratio='3:2']),
|
|
574
|
+
& :global(embed[data-resize-aspect-ratio='21:9']),
|
|
575
|
+
& :global(embed[data-resize-aspect-ratio='1:1']),
|
|
576
|
+
& :global(embed[data-resize-aspect-ratio='4:3']),
|
|
577
|
+
& :global(embed[data-resize-aspect-ratio='9:16']) {
|
|
578
|
+
aspect-ratio: var(--tiptap-media-aspect-ratio, auto);
|
|
579
|
+
height: auto;
|
|
489
580
|
}
|
|
490
581
|
}
|
|
491
582
|
</style>
|