@yurikilian/lex4 0.1.2 → 0.2.0
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 +236 -20
- package/dist/ast/block-mapper.d.ts +24 -0
- package/dist/ast/block-mapper.d.ts.map +1 -0
- package/dist/ast/content-mapper.d.ts +16 -0
- package/dist/ast/content-mapper.d.ts.map +1 -0
- package/dist/ast/document-serializer.d.ts +11 -0
- package/dist/ast/document-serializer.d.ts.map +1 -0
- package/dist/ast/index.d.ts +9 -0
- package/dist/ast/index.d.ts.map +1 -0
- package/dist/ast/inline-mapper.d.ts +41 -0
- package/dist/ast/inline-mapper.d.ts.map +1 -0
- package/dist/ast/payload-builder.d.ts +18 -0
- package/dist/ast/payload-builder.d.ts.map +1 -0
- package/dist/ast/types.d.ts +112 -0
- package/dist/ast/types.d.ts.map +1 -0
- package/dist/components/EditorSidebar.d.ts +1 -0
- package/dist/components/EditorSidebar.d.ts.map +1 -1
- package/dist/components/HistorySidebar.d.ts.map +1 -1
- package/dist/components/Lex4Editor.d.ts +9 -1
- package/dist/components/Lex4Editor.d.ts.map +1 -1
- package/dist/components/PageBody.d.ts +1 -2
- package/dist/components/PageBody.d.ts.map +1 -1
- package/dist/components/PageFooter.d.ts.map +1 -1
- package/dist/components/PageHeader.d.ts.map +1 -1
- package/dist/components/Toolbar.d.ts.map +1 -1
- package/dist/components/VariablePanel.d.ts +15 -0
- package/dist/components/VariablePanel.d.ts.map +1 -0
- package/dist/components/VariablePicker.d.ts +14 -0
- package/dist/components/VariablePicker.d.ts.map +1 -0
- package/dist/extensions/ast-extension.d.ts +16 -0
- package/dist/extensions/ast-extension.d.ts.map +1 -0
- package/dist/extensions/extension-context.d.ts +29 -0
- package/dist/extensions/extension-context.d.ts.map +1 -0
- package/dist/extensions/index.d.ts +6 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/types.d.ts +74 -0
- package/dist/extensions/types.d.ts.map +1 -0
- package/dist/extensions/variables-extension.d.ts +31 -0
- package/dist/extensions/variables-extension.d.ts.map +1 -0
- package/dist/i18n/context.d.ts +28 -0
- package/dist/i18n/context.d.ts.map +1 -0
- package/dist/i18n/defaults.d.ts +3 -0
- package/dist/i18n/defaults.d.ts.map +1 -0
- package/dist/i18n/index.d.ts +4 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/types.d.ts +82 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lex4-editor.cjs +1113 -101
- package/dist/lex4-editor.cjs.map +1 -1
- package/dist/lex4-editor.js +1116 -104
- package/dist/lex4-editor.js.map +1 -1
- package/dist/lexical/editor-setup.d.ts +5 -1
- package/dist/lexical/editor-setup.d.ts.map +1 -1
- package/dist/lexical/plugins/font-plugin.d.ts +1 -1
- package/dist/lexical/plugins/font-plugin.d.ts.map +1 -1
- package/dist/lexical/plugins/font-size-plugin.d.ts +19 -0
- package/dist/lexical/plugins/font-size-plugin.d.ts.map +1 -0
- package/dist/lexical/plugins/index.d.ts +2 -0
- package/dist/lexical/plugins/index.d.ts.map +1 -1
- package/dist/style.css +532 -160
- package/dist/types/editor-handle.d.ts +14 -0
- package/dist/types/editor-handle.d.ts.map +1 -0
- package/dist/types/editor-props.d.ts +23 -0
- package/dist/types/editor-props.d.ts.map +1 -1
- package/dist/variables/index.d.ts +5 -0
- package/dist/variables/index.d.ts.map +1 -0
- package/dist/variables/types.d.ts +26 -0
- package/dist/variables/types.d.ts.map +1 -0
- package/dist/variables/variable-commands.d.ts +11 -0
- package/dist/variables/variable-commands.d.ts.map +1 -0
- package/dist/variables/variable-context.d.ts +21 -0
- package/dist/variables/variable-context.d.ts.map +1 -0
- package/dist/variables/variable-node.d.ts +34 -0
- package/dist/variables/variable-node.d.ts.map +1 -0
- package/dist/variables/variable-plugin.d.ts +8 -0
- package/dist/variables/variable-plugin.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/lex4-editor.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1
4
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React, { createContext, useContext, useReducer, useState, useRef, useMemo, useCallback, useEffect, forwardRef, createElement } from "react";
|
|
3
|
-
import { $getRoot, $createRangeSelectionFromDom, $getSelection, $isRangeSelection, $isTextNode, FORMAT_ELEMENT_COMMAND, FORMAT_TEXT_COMMAND, OUTDENT_CONTENT_COMMAND, INDENT_CONTENT_COMMAND, $selectAll, KEY_TAB_COMMAND, COMMAND_PRIORITY_LOW, $isElementNode, $isParagraphNode, $setSelection, FOCUS_COMMAND, CONTROLLED_TEXT_INSERTION_COMMAND, KEY_DOWN_COMMAND, PASTE_COMMAND, KEY_ENTER_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_CRITICAL } from "lexical";
|
|
5
|
+
import React, { createContext, useContext, useReducer, useState, useRef, useMemo, useCallback, useEffect, forwardRef, createElement, useImperativeHandle } from "react";
|
|
6
|
+
import { $getRoot, $createRangeSelectionFromDom, $getSelection, $isRangeSelection, $isTextNode, FORMAT_ELEMENT_COMMAND, FORMAT_TEXT_COMMAND, OUTDENT_CONTENT_COMMAND, INDENT_CONTENT_COMMAND, $selectAll, KEY_TAB_COMMAND, COMMAND_PRIORITY_LOW, $isElementNode, $isParagraphNode, $setSelection, FOCUS_COMMAND, CONTROLLED_TEXT_INSERTION_COMMAND, KEY_DOWN_COMMAND, PASTE_COMMAND, KEY_ENTER_COMMAND, KEY_BACKSPACE_COMMAND, KEY_DELETE_COMMAND, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_CRITICAL, $applyNodeReplacement, DecoratorNode, createCommand, $insertNodes, COMMAND_PRIORITY_EDITOR } from "lexical";
|
|
4
7
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
5
8
|
import { INSERT_ORDERED_LIST_COMMAND, INSERT_UNORDERED_LIST_COMMAND, ListNode, ListItemNode } from "@lexical/list";
|
|
6
9
|
import { LexicalComposer } from "@lexical/react/LexicalComposer";
|
|
@@ -1131,33 +1134,53 @@ const createLucideIcon = (iconName, iconNode) => {
|
|
|
1131
1134
|
* This source code is licensed under the ISC license.
|
|
1132
1135
|
* See the LICENSE file in the root directory of this source tree.
|
|
1133
1136
|
*/
|
|
1134
|
-
const __iconNode$
|
|
1137
|
+
const __iconNode$m = [
|
|
1135
1138
|
[
|
|
1136
1139
|
"path",
|
|
1137
1140
|
{ d: "M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8", key: "mg9rjx" }
|
|
1138
1141
|
]
|
|
1139
1142
|
];
|
|
1140
|
-
const Bold = createLucideIcon("bold", __iconNode$
|
|
1143
|
+
const Bold = createLucideIcon("bold", __iconNode$m);
|
|
1141
1144
|
/**
|
|
1142
1145
|
* @license lucide-react v1.8.0 - ISC
|
|
1143
1146
|
*
|
|
1144
1147
|
* This source code is licensed under the ISC license.
|
|
1145
1148
|
* See the LICENSE file in the root directory of this source tree.
|
|
1146
1149
|
*/
|
|
1147
|
-
const __iconNode$
|
|
1150
|
+
const __iconNode$l = [
|
|
1151
|
+
[
|
|
1152
|
+
"path",
|
|
1153
|
+
{ d: "M8 3H7a2 2 0 0 0-2 2v5a2 2 0 0 1-2 2 2 2 0 0 1 2 2v5c0 1.1.9 2 2 2h1", key: "ezmyqa" }
|
|
1154
|
+
],
|
|
1155
|
+
[
|
|
1156
|
+
"path",
|
|
1157
|
+
{
|
|
1158
|
+
d: "M16 21h1a2 2 0 0 0 2-2v-5c0-1.1.9-2 2-2a2 2 0 0 1-2-2V5a2 2 0 0 0-2-2h-1",
|
|
1159
|
+
key: "e1hn23"
|
|
1160
|
+
}
|
|
1161
|
+
]
|
|
1162
|
+
];
|
|
1163
|
+
const Braces = createLucideIcon("braces", __iconNode$l);
|
|
1164
|
+
/**
|
|
1165
|
+
* @license lucide-react v1.8.0 - ISC
|
|
1166
|
+
*
|
|
1167
|
+
* This source code is licensed under the ISC license.
|
|
1168
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
1169
|
+
*/
|
|
1170
|
+
const __iconNode$k = [
|
|
1148
1171
|
["line", { x1: "15", x2: "15", y1: "12", y2: "18", key: "1p7wdc" }],
|
|
1149
1172
|
["line", { x1: "12", x2: "18", y1: "15", y2: "15", key: "1nscbv" }],
|
|
1150
1173
|
["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
|
|
1151
1174
|
["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
|
|
1152
1175
|
];
|
|
1153
|
-
const CopyPlus = createLucideIcon("copy-plus", __iconNode$
|
|
1176
|
+
const CopyPlus = createLucideIcon("copy-plus", __iconNode$k);
|
|
1154
1177
|
/**
|
|
1155
1178
|
* @license lucide-react v1.8.0 - ISC
|
|
1156
1179
|
*
|
|
1157
1180
|
* This source code is licensed under the ISC license.
|
|
1158
1181
|
* See the LICENSE file in the root directory of this source tree.
|
|
1159
1182
|
*/
|
|
1160
|
-
const __iconNode$
|
|
1183
|
+
const __iconNode$j = [
|
|
1161
1184
|
[
|
|
1162
1185
|
"path",
|
|
1163
1186
|
{
|
|
@@ -1167,65 +1190,65 @@ const __iconNode$h = [
|
|
|
1167
1190
|
],
|
|
1168
1191
|
["path", { d: "m5.082 11.09 8.828 8.828", key: "1wx5vj" }]
|
|
1169
1192
|
];
|
|
1170
|
-
const Eraser = createLucideIcon("eraser", __iconNode$
|
|
1193
|
+
const Eraser = createLucideIcon("eraser", __iconNode$j);
|
|
1171
1194
|
/**
|
|
1172
1195
|
* @license lucide-react v1.8.0 - ISC
|
|
1173
1196
|
*
|
|
1174
1197
|
* This source code is licensed under the ISC license.
|
|
1175
1198
|
* See the LICENSE file in the root directory of this source tree.
|
|
1176
1199
|
*/
|
|
1177
|
-
const __iconNode$
|
|
1200
|
+
const __iconNode$i = [
|
|
1178
1201
|
["line", { x1: "4", x2: "20", y1: "9", y2: "9", key: "4lhtct" }],
|
|
1179
1202
|
["line", { x1: "4", x2: "20", y1: "15", y2: "15", key: "vyu0kd" }],
|
|
1180
1203
|
["line", { x1: "10", x2: "8", y1: "3", y2: "21", key: "1ggp8o" }],
|
|
1181
1204
|
["line", { x1: "16", x2: "14", y1: "3", y2: "21", key: "weycgp" }]
|
|
1182
1205
|
];
|
|
1183
|
-
const Hash = createLucideIcon("hash", __iconNode$
|
|
1206
|
+
const Hash = createLucideIcon("hash", __iconNode$i);
|
|
1184
1207
|
/**
|
|
1185
1208
|
* @license lucide-react v1.8.0 - ISC
|
|
1186
1209
|
*
|
|
1187
1210
|
* This source code is licensed under the ISC license.
|
|
1188
1211
|
* See the LICENSE file in the root directory of this source tree.
|
|
1189
1212
|
*/
|
|
1190
|
-
const __iconNode$
|
|
1213
|
+
const __iconNode$h = [
|
|
1191
1214
|
["line", { x1: "19", x2: "10", y1: "4", y2: "4", key: "15jd3p" }],
|
|
1192
1215
|
["line", { x1: "14", x2: "5", y1: "20", y2: "20", key: "bu0au3" }],
|
|
1193
1216
|
["line", { x1: "15", x2: "9", y1: "4", y2: "20", key: "uljnxc" }]
|
|
1194
1217
|
];
|
|
1195
|
-
const Italic = createLucideIcon("italic", __iconNode$
|
|
1218
|
+
const Italic = createLucideIcon("italic", __iconNode$h);
|
|
1196
1219
|
/**
|
|
1197
1220
|
* @license lucide-react v1.8.0 - ISC
|
|
1198
1221
|
*
|
|
1199
1222
|
* This source code is licensed under the ISC license.
|
|
1200
1223
|
* See the LICENSE file in the root directory of this source tree.
|
|
1201
1224
|
*/
|
|
1202
|
-
const __iconNode$
|
|
1225
|
+
const __iconNode$g = [
|
|
1203
1226
|
["path", { d: "M21 5H11", key: "us1j55" }],
|
|
1204
1227
|
["path", { d: "M21 12H11", key: "wd7e0v" }],
|
|
1205
1228
|
["path", { d: "M21 19H11", key: "saa85w" }],
|
|
1206
1229
|
["path", { d: "m7 8-4 4 4 4", key: "o5hrat" }]
|
|
1207
1230
|
];
|
|
1208
|
-
const ListIndentDecrease = createLucideIcon("list-indent-decrease", __iconNode$
|
|
1231
|
+
const ListIndentDecrease = createLucideIcon("list-indent-decrease", __iconNode$g);
|
|
1209
1232
|
/**
|
|
1210
1233
|
* @license lucide-react v1.8.0 - ISC
|
|
1211
1234
|
*
|
|
1212
1235
|
* This source code is licensed under the ISC license.
|
|
1213
1236
|
* See the LICENSE file in the root directory of this source tree.
|
|
1214
1237
|
*/
|
|
1215
|
-
const __iconNode$
|
|
1238
|
+
const __iconNode$f = [
|
|
1216
1239
|
["path", { d: "M21 5H11", key: "us1j55" }],
|
|
1217
1240
|
["path", { d: "M21 12H11", key: "wd7e0v" }],
|
|
1218
1241
|
["path", { d: "M21 19H11", key: "saa85w" }],
|
|
1219
1242
|
["path", { d: "m3 8 4 4-4 4", key: "1a3j6y" }]
|
|
1220
1243
|
];
|
|
1221
|
-
const ListIndentIncrease = createLucideIcon("list-indent-increase", __iconNode$
|
|
1244
|
+
const ListIndentIncrease = createLucideIcon("list-indent-increase", __iconNode$f);
|
|
1222
1245
|
/**
|
|
1223
1246
|
* @license lucide-react v1.8.0 - ISC
|
|
1224
1247
|
*
|
|
1225
1248
|
* This source code is licensed under the ISC license.
|
|
1226
1249
|
* See the LICENSE file in the root directory of this source tree.
|
|
1227
1250
|
*/
|
|
1228
|
-
const __iconNode$
|
|
1251
|
+
const __iconNode$e = [
|
|
1229
1252
|
["path", { d: "M11 5h10", key: "1cz7ny" }],
|
|
1230
1253
|
["path", { d: "M11 12h10", key: "1438ji" }],
|
|
1231
1254
|
["path", { d: "M11 19h10", key: "11t30w" }],
|
|
@@ -1233,14 +1256,14 @@ const __iconNode$c = [
|
|
|
1233
1256
|
["path", { d: "M4 9h2", key: "r1h2o0" }],
|
|
1234
1257
|
["path", { d: "M6.5 20H3.4c0-1 2.6-1.925 2.6-3.5a1.5 1.5 0 0 0-2.6-1.02", key: "xtkcd5" }]
|
|
1235
1258
|
];
|
|
1236
|
-
const ListOrdered = createLucideIcon("list-ordered", __iconNode$
|
|
1259
|
+
const ListOrdered = createLucideIcon("list-ordered", __iconNode$e);
|
|
1237
1260
|
/**
|
|
1238
1261
|
* @license lucide-react v1.8.0 - ISC
|
|
1239
1262
|
*
|
|
1240
1263
|
* This source code is licensed under the ISC license.
|
|
1241
1264
|
* See the LICENSE file in the root directory of this source tree.
|
|
1242
1265
|
*/
|
|
1243
|
-
const __iconNode$
|
|
1266
|
+
const __iconNode$d = [
|
|
1244
1267
|
["path", { d: "M3 5h.01", key: "18ugdj" }],
|
|
1245
1268
|
["path", { d: "M3 12h.01", key: "nlz23k" }],
|
|
1246
1269
|
["path", { d: "M3 19h.01", key: "noohij" }],
|
|
@@ -1248,29 +1271,53 @@ const __iconNode$b = [
|
|
|
1248
1271
|
["path", { d: "M8 12h13", key: "1za7za" }],
|
|
1249
1272
|
["path", { d: "M8 19h13", key: "m83p4d" }]
|
|
1250
1273
|
];
|
|
1251
|
-
const List = createLucideIcon("list", __iconNode$
|
|
1274
|
+
const List = createLucideIcon("list", __iconNode$d);
|
|
1252
1275
|
/**
|
|
1253
1276
|
* @license lucide-react v1.8.0 - ISC
|
|
1254
1277
|
*
|
|
1255
1278
|
* This source code is licensed under the ISC license.
|
|
1256
1279
|
* See the LICENSE file in the root directory of this source tree.
|
|
1257
1280
|
*/
|
|
1258
|
-
const __iconNode$
|
|
1281
|
+
const __iconNode$c = [
|
|
1259
1282
|
["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
|
|
1260
1283
|
["path", { d: "M15 3v18", key: "14nvp0" }]
|
|
1261
1284
|
];
|
|
1262
|
-
const PanelRight = createLucideIcon("panel-right", __iconNode$
|
|
1285
|
+
const PanelRight = createLucideIcon("panel-right", __iconNode$c);
|
|
1263
1286
|
/**
|
|
1264
1287
|
* @license lucide-react v1.8.0 - ISC
|
|
1265
1288
|
*
|
|
1266
1289
|
* This source code is licensed under the ISC license.
|
|
1267
1290
|
* See the LICENSE file in the root directory of this source tree.
|
|
1268
1291
|
*/
|
|
1269
|
-
const __iconNode$
|
|
1292
|
+
const __iconNode$b = [
|
|
1270
1293
|
["path", { d: "m15 14 5-5-5-5", key: "12vg1m" }],
|
|
1271
1294
|
["path", { d: "M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13", key: "6uklza" }]
|
|
1272
1295
|
];
|
|
1273
|
-
const Redo2 = createLucideIcon("redo-2", __iconNode$
|
|
1296
|
+
const Redo2 = createLucideIcon("redo-2", __iconNode$b);
|
|
1297
|
+
/**
|
|
1298
|
+
* @license lucide-react v1.8.0 - ISC
|
|
1299
|
+
*
|
|
1300
|
+
* This source code is licensed under the ISC license.
|
|
1301
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
1302
|
+
*/
|
|
1303
|
+
const __iconNode$a = [
|
|
1304
|
+
["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
|
|
1305
|
+
["path", { d: "M21 3v5h-5", key: "1q7to0" }],
|
|
1306
|
+
["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
|
|
1307
|
+
["path", { d: "M8 16H3v5", key: "1cv678" }]
|
|
1308
|
+
];
|
|
1309
|
+
const RefreshCw = createLucideIcon("refresh-cw", __iconNode$a);
|
|
1310
|
+
/**
|
|
1311
|
+
* @license lucide-react v1.8.0 - ISC
|
|
1312
|
+
*
|
|
1313
|
+
* This source code is licensed under the ISC license.
|
|
1314
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
1315
|
+
*/
|
|
1316
|
+
const __iconNode$9 = [
|
|
1317
|
+
["path", { d: "m21 21-4.34-4.34", key: "14j7rj" }],
|
|
1318
|
+
["circle", { cx: "11", cy: "11", r: "8", key: "4ej97u" }]
|
|
1319
|
+
];
|
|
1320
|
+
const Search = createLucideIcon("search", __iconNode$9);
|
|
1274
1321
|
/**
|
|
1275
1322
|
* @license lucide-react v1.8.0 - ISC
|
|
1276
1323
|
*
|
|
@@ -1378,14 +1425,122 @@ const __iconNode = [
|
|
|
1378
1425
|
["path", { d: "m6 6 12 12", key: "d8bk6v" }]
|
|
1379
1426
|
];
|
|
1380
1427
|
const X = createLucideIcon("x", __iconNode);
|
|
1428
|
+
const DEFAULT_TRANSLATIONS = {
|
|
1429
|
+
toolbar: {
|
|
1430
|
+
undo: "Undo",
|
|
1431
|
+
redo: "Redo",
|
|
1432
|
+
bold: "Bold (Ctrl+B)",
|
|
1433
|
+
italic: "Italic (Ctrl+I)",
|
|
1434
|
+
underline: "Underline (Ctrl+U)",
|
|
1435
|
+
strikethrough: "Strikethrough",
|
|
1436
|
+
alignLeft: "Align Left",
|
|
1437
|
+
alignCenter: "Align Center",
|
|
1438
|
+
alignRight: "Align Right",
|
|
1439
|
+
justify: "Justify",
|
|
1440
|
+
numberedList: "Numbered List",
|
|
1441
|
+
bulletList: "Bullet List",
|
|
1442
|
+
indent: "Indent",
|
|
1443
|
+
outdent: "Outdent",
|
|
1444
|
+
openHistory: "Open History",
|
|
1445
|
+
closeHistory: "Close History"
|
|
1446
|
+
},
|
|
1447
|
+
history: {
|
|
1448
|
+
title: "History",
|
|
1449
|
+
subtitle: "Word-style session history (last 100 actions)",
|
|
1450
|
+
empty: "No history yet.",
|
|
1451
|
+
clearHistory: "Clear History",
|
|
1452
|
+
actions: {
|
|
1453
|
+
enabledHeadersFooters: "Enabled headers and footers",
|
|
1454
|
+
disabledHeadersFooters: "Disabled headers and footers",
|
|
1455
|
+
copiedHeaderToAll: "Copied header to all pages",
|
|
1456
|
+
copiedFooterToAll: "Copied footer to all pages",
|
|
1457
|
+
clearedHeader: "Cleared header",
|
|
1458
|
+
clearedFooter: "Cleared footer",
|
|
1459
|
+
clearedAllHeaders: "Cleared all headers",
|
|
1460
|
+
clearedAllFooters: "Cleared all footers",
|
|
1461
|
+
pageCounterSet: "Page counter set to {{value}}",
|
|
1462
|
+
boldApplied: "Bold applied",
|
|
1463
|
+
italicApplied: "Italic applied",
|
|
1464
|
+
underlineApplied: "Underline applied",
|
|
1465
|
+
strikethroughApplied: "Strikethrough applied",
|
|
1466
|
+
alignedLeft: "Aligned left",
|
|
1467
|
+
alignedCenter: "Aligned center",
|
|
1468
|
+
alignedRight: "Aligned right",
|
|
1469
|
+
justifiedText: "Justified text",
|
|
1470
|
+
insertedNumberedList: "Inserted numbered list",
|
|
1471
|
+
insertedBulletList: "Inserted bullet list",
|
|
1472
|
+
indentedContent: "Indented content",
|
|
1473
|
+
outdentedContent: "Outdented content",
|
|
1474
|
+
fontChanged: "Font changed to {{value}}",
|
|
1475
|
+
fontSizeChanged: "Font size changed to {{value}}pt"
|
|
1476
|
+
}
|
|
1477
|
+
},
|
|
1478
|
+
variables: {
|
|
1479
|
+
title: "Variables",
|
|
1480
|
+
available: "{{count}} available",
|
|
1481
|
+
refreshVariables: "Refresh variables",
|
|
1482
|
+
searchPlaceholder: "Search variables...",
|
|
1483
|
+
noVariablesFound: "No variables found",
|
|
1484
|
+
insertVariable: "Insert variable {{key}}",
|
|
1485
|
+
openPanel: "Open Variables",
|
|
1486
|
+
closePanel: "Close Variables"
|
|
1487
|
+
},
|
|
1488
|
+
header: {
|
|
1489
|
+
placeholder: "Header"
|
|
1490
|
+
},
|
|
1491
|
+
footer: {
|
|
1492
|
+
placeholder: "Footer"
|
|
1493
|
+
},
|
|
1494
|
+
sidebar: {
|
|
1495
|
+
close: "Close sidebar"
|
|
1496
|
+
}
|
|
1497
|
+
};
|
|
1498
|
+
function deepMerge(target, source) {
|
|
1499
|
+
if (typeof target !== "object" || target === null) {
|
|
1500
|
+
return source ?? target;
|
|
1501
|
+
}
|
|
1502
|
+
const result = { ...target };
|
|
1503
|
+
for (const key of Object.keys(source)) {
|
|
1504
|
+
const sourceVal = source[key];
|
|
1505
|
+
const targetVal = target[key];
|
|
1506
|
+
if (sourceVal !== void 0 && typeof sourceVal === "object" && sourceVal !== null && !Array.isArray(sourceVal) && typeof targetVal === "object" && targetVal !== null) {
|
|
1507
|
+
result[key] = deepMerge(targetVal, sourceVal);
|
|
1508
|
+
} else if (sourceVal !== void 0) {
|
|
1509
|
+
result[key] = sourceVal;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
return result;
|
|
1513
|
+
}
|
|
1514
|
+
const TranslationsContext = createContext(DEFAULT_TRANSLATIONS);
|
|
1515
|
+
function useTranslations() {
|
|
1516
|
+
return useContext(TranslationsContext);
|
|
1517
|
+
}
|
|
1518
|
+
function interpolate(template, params) {
|
|
1519
|
+
return template.replace(
|
|
1520
|
+
/\{\{(\w+)\}\}/g,
|
|
1521
|
+
(_, key) => String(params[key] ?? `{{${key}}}`)
|
|
1522
|
+
);
|
|
1523
|
+
}
|
|
1524
|
+
const TranslationsProvider = ({
|
|
1525
|
+
translations,
|
|
1526
|
+
children
|
|
1527
|
+
}) => {
|
|
1528
|
+
const merged = useMemo(
|
|
1529
|
+
() => translations ? deepMerge(DEFAULT_TRANSLATIONS, translations) : DEFAULT_TRANSLATIONS,
|
|
1530
|
+
[translations]
|
|
1531
|
+
);
|
|
1532
|
+
return /* @__PURE__ */ jsx(TranslationsContext.Provider, { value: merged, children });
|
|
1533
|
+
};
|
|
1381
1534
|
const EditorSidebar = ({
|
|
1382
1535
|
title,
|
|
1383
1536
|
subtitle,
|
|
1384
1537
|
open,
|
|
1385
1538
|
onClose,
|
|
1386
1539
|
headerActions,
|
|
1540
|
+
testId = "editor-sidebar",
|
|
1387
1541
|
children
|
|
1388
1542
|
}) => {
|
|
1543
|
+
const t = useTranslations();
|
|
1389
1544
|
if (!open) {
|
|
1390
1545
|
return null;
|
|
1391
1546
|
}
|
|
@@ -1393,7 +1548,7 @@ const EditorSidebar = ({
|
|
|
1393
1548
|
"aside",
|
|
1394
1549
|
{
|
|
1395
1550
|
className: "flex h-full w-[320px] shrink-0 flex-col border-l border-gray-200 bg-white",
|
|
1396
|
-
"data-testid":
|
|
1551
|
+
"data-testid": testId,
|
|
1397
1552
|
children: [
|
|
1398
1553
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between border-b border-gray-200 px-3 py-2.5", children: [
|
|
1399
1554
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -1409,7 +1564,7 @@ const EditorSidebar = ({
|
|
|
1409
1564
|
className: "flex h-6 w-6 items-center justify-center rounded text-gray-400\n transition-colors hover:bg-gray-100 hover:text-gray-600",
|
|
1410
1565
|
"data-testid": "close-editor-sidebar",
|
|
1411
1566
|
onClick: onClose,
|
|
1412
|
-
"aria-label":
|
|
1567
|
+
"aria-label": t.sidebar.close,
|
|
1413
1568
|
children: /* @__PURE__ */ jsx(X, { size: 14 })
|
|
1414
1569
|
}
|
|
1415
1570
|
)
|
|
@@ -1437,13 +1592,14 @@ const HistorySidebar = () => {
|
|
|
1437
1592
|
jumpToHistoryEntry: jumpToHistoryEntry2,
|
|
1438
1593
|
setHistorySidebarOpen
|
|
1439
1594
|
} = useDocument();
|
|
1595
|
+
const t = useTranslations();
|
|
1440
1596
|
const visibleEntries = historyEntries.slice().reverse();
|
|
1441
1597
|
const headerActions = /* @__PURE__ */ jsx(
|
|
1442
1598
|
"button",
|
|
1443
1599
|
{
|
|
1444
1600
|
type: "button",
|
|
1445
|
-
title:
|
|
1446
|
-
"aria-label":
|
|
1601
|
+
title: t.history.clearHistory,
|
|
1602
|
+
"aria-label": t.history.clearHistory,
|
|
1447
1603
|
onMouseDown: (e) => e.preventDefault(),
|
|
1448
1604
|
onClick: () => clearHistory("manual"),
|
|
1449
1605
|
className: "flex h-6 w-6 items-center justify-center rounded text-gray-400\n transition-colors hover:bg-gray-100 hover:text-gray-600",
|
|
@@ -1454,17 +1610,18 @@ const HistorySidebar = () => {
|
|
|
1454
1610
|
return /* @__PURE__ */ jsx(
|
|
1455
1611
|
EditorSidebar,
|
|
1456
1612
|
{
|
|
1457
|
-
title:
|
|
1458
|
-
subtitle:
|
|
1613
|
+
title: t.history.title,
|
|
1614
|
+
subtitle: t.history.subtitle,
|
|
1459
1615
|
open: historySidebarOpen,
|
|
1460
1616
|
onClose: () => setHistorySidebarOpen(false),
|
|
1461
1617
|
headerActions,
|
|
1618
|
+
testId: "history-sidebar",
|
|
1462
1619
|
children: visibleEntries.length === 0 ? /* @__PURE__ */ jsx(
|
|
1463
1620
|
"div",
|
|
1464
1621
|
{
|
|
1465
1622
|
className: "px-3 py-4 text-xs text-gray-500",
|
|
1466
1623
|
"data-testid": "history-empty",
|
|
1467
|
-
children:
|
|
1624
|
+
children: t.history.empty
|
|
1468
1625
|
}
|
|
1469
1626
|
) : /* @__PURE__ */ jsx("ol", { className: "divide-y divide-gray-100", "data-testid": "history-entry-list", children: visibleEntries.map((entry, reversedIndex) => {
|
|
1470
1627
|
const actualIndex = historyEntries.length - reversedIndex - 1;
|
|
@@ -1655,7 +1812,81 @@ const ActionIconButton = ({ title, icon, onClick, disabled, testId }) => /* @__P
|
|
|
1655
1812
|
}
|
|
1656
1813
|
);
|
|
1657
1814
|
const ActionDivider = () => /* @__PURE__ */ jsx("div", { className: "mx-0.5 h-4 w-px bg-gray-300/60" });
|
|
1815
|
+
function resolveExtensions(extensions) {
|
|
1816
|
+
const resolved = {
|
|
1817
|
+
nodes: [],
|
|
1818
|
+
bodyPlugins: [],
|
|
1819
|
+
toolbarItems: [],
|
|
1820
|
+
sidePanels: [],
|
|
1821
|
+
providers: [],
|
|
1822
|
+
themeOverrides: {},
|
|
1823
|
+
handleFactories: []
|
|
1824
|
+
};
|
|
1825
|
+
for (const ext of extensions) {
|
|
1826
|
+
if (ext.nodes) resolved.nodes.push(...ext.nodes);
|
|
1827
|
+
if (ext.bodyPlugins) resolved.bodyPlugins.push(...ext.bodyPlugins);
|
|
1828
|
+
if (ext.toolbarItems) resolved.toolbarItems.push(...ext.toolbarItems);
|
|
1829
|
+
if (ext.sidePanel) resolved.sidePanels.push(ext.sidePanel);
|
|
1830
|
+
if (ext.provider) resolved.providers.push(ext.provider);
|
|
1831
|
+
if (ext.themeOverrides) {
|
|
1832
|
+
resolved.themeOverrides = { ...resolved.themeOverrides, ...ext.themeOverrides };
|
|
1833
|
+
}
|
|
1834
|
+
if (ext.handleMethods) resolved.handleFactories.push(ext.handleMethods);
|
|
1835
|
+
}
|
|
1836
|
+
return resolved;
|
|
1837
|
+
}
|
|
1838
|
+
const EMPTY_RESOLVED = {
|
|
1839
|
+
nodes: [],
|
|
1840
|
+
bodyPlugins: [],
|
|
1841
|
+
toolbarItems: [],
|
|
1842
|
+
sidePanels: [],
|
|
1843
|
+
providers: [],
|
|
1844
|
+
themeOverrides: {},
|
|
1845
|
+
handleFactories: []
|
|
1846
|
+
};
|
|
1847
|
+
const ExtensionResolvedContext = createContext(EMPTY_RESOLVED);
|
|
1848
|
+
const ExtensionProvider = ({ extensions, children }) => {
|
|
1849
|
+
const resolved = useMemo(
|
|
1850
|
+
() => extensions && extensions.length > 0 ? resolveExtensions(extensions) : EMPTY_RESOLVED,
|
|
1851
|
+
[extensions]
|
|
1852
|
+
);
|
|
1853
|
+
let wrapped = /* @__PURE__ */ jsx(Fragment, { children });
|
|
1854
|
+
for (let i = resolved.providers.length - 1; i >= 0; i--) {
|
|
1855
|
+
const Provider = resolved.providers[i];
|
|
1856
|
+
wrapped = /* @__PURE__ */ jsx(Provider, { children: wrapped });
|
|
1857
|
+
}
|
|
1858
|
+
return /* @__PURE__ */ jsx(ExtensionResolvedContext.Provider, { value: resolved, children: wrapped });
|
|
1859
|
+
};
|
|
1860
|
+
function useExtensions() {
|
|
1861
|
+
return useContext(ExtensionResolvedContext);
|
|
1862
|
+
}
|
|
1863
|
+
const ExtensionStateContext = createContext(null);
|
|
1864
|
+
const ExtensionStateProvider = ({ children }) => {
|
|
1865
|
+
const stateRef = useRef({});
|
|
1866
|
+
return /* @__PURE__ */ jsx(ExtensionStateContext.Provider, { value: stateRef, children });
|
|
1867
|
+
};
|
|
1868
|
+
function useExtensionState() {
|
|
1869
|
+
const stateRef = useContext(ExtensionStateContext);
|
|
1870
|
+
return {
|
|
1871
|
+
get: (key) => stateRef == null ? void 0 : stateRef.current[key],
|
|
1872
|
+
set: (key, value) => {
|
|
1873
|
+
if (stateRef) stateRef.current[key] = value;
|
|
1874
|
+
}
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
function useExtensionContext(getDocument, getActiveEditor) {
|
|
1878
|
+
const stateRef = useContext(ExtensionStateContext);
|
|
1879
|
+
return useMemo(() => ({
|
|
1880
|
+
getDocument,
|
|
1881
|
+
getActiveEditor,
|
|
1882
|
+
getExtensionState: (key) => stateRef == null ? void 0 : stateRef.current[key],
|
|
1883
|
+
setExtensionState: (key, value) => {
|
|
1884
|
+
if (stateRef) stateRef.current[key] = value;
|
|
1885
|
+
}
|
|
1886
|
+
}), [getDocument, getActiveEditor, stateRef]);
|
|
1887
|
+
}
|
|
1658
1888
|
const SUPPORTED_FONTS = [
|
|
1889
|
+
"Inter",
|
|
1659
1890
|
"Times New Roman",
|
|
1660
1891
|
"Arial",
|
|
1661
1892
|
"Calibri",
|
|
@@ -1674,6 +1905,42 @@ function applyFontFamily(editor, fontFamily) {
|
|
|
1674
1905
|
}
|
|
1675
1906
|
});
|
|
1676
1907
|
}
|
|
1908
|
+
const SUPPORTED_FONT_SIZES = [
|
|
1909
|
+
8,
|
|
1910
|
+
9,
|
|
1911
|
+
10,
|
|
1912
|
+
11,
|
|
1913
|
+
12,
|
|
1914
|
+
14,
|
|
1915
|
+
16,
|
|
1916
|
+
18,
|
|
1917
|
+
20,
|
|
1918
|
+
24,
|
|
1919
|
+
28,
|
|
1920
|
+
32,
|
|
1921
|
+
36,
|
|
1922
|
+
48,
|
|
1923
|
+
72
|
|
1924
|
+
];
|
|
1925
|
+
function applyFontSize(editor, size) {
|
|
1926
|
+
editor.update(() => {
|
|
1927
|
+
const selection = $getSelection();
|
|
1928
|
+
if (!$isRangeSelection(selection)) return;
|
|
1929
|
+
const nodes = selection.getNodes();
|
|
1930
|
+
for (const node of nodes) {
|
|
1931
|
+
if ($isTextNode(node)) {
|
|
1932
|
+
const existing = node.getStyle();
|
|
1933
|
+
const updated = mergeFontSize(existing, size);
|
|
1934
|
+
node.setStyle(updated);
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
});
|
|
1938
|
+
}
|
|
1939
|
+
function mergeFontSize(existingStyle, size) {
|
|
1940
|
+
const stripped = existingStyle.replace(/font-size:\s*[^;]+;?\s*/g, "").trim();
|
|
1941
|
+
const sizeDecl = `font-size: ${size}pt`;
|
|
1942
|
+
return stripped ? `${stripped}; ${sizeDecl}` : sizeDecl;
|
|
1943
|
+
}
|
|
1677
1944
|
function toggleFormat(editor, format) {
|
|
1678
1945
|
editor.dispatchCommand(FORMAT_TEXT_COMMAND, format);
|
|
1679
1946
|
}
|
|
@@ -1721,6 +1988,8 @@ const Toolbar = () => {
|
|
|
1721
1988
|
setHistorySidebarOpen,
|
|
1722
1989
|
undo
|
|
1723
1990
|
} = useDocument();
|
|
1991
|
+
const { toolbarItems } = useExtensions();
|
|
1992
|
+
const t = useTranslations();
|
|
1724
1993
|
const withBodySelection = useCallback(
|
|
1725
1994
|
(editor, action) => {
|
|
1726
1995
|
editor.update(() => {
|
|
@@ -1758,7 +2027,7 @@ const Toolbar = () => {
|
|
|
1758
2027
|
);
|
|
1759
2028
|
const handleToggle = (enabled) => {
|
|
1760
2029
|
runToolbarAction(
|
|
1761
|
-
enabled ?
|
|
2030
|
+
enabled ? t.history.actions.enabledHeadersFooters : t.history.actions.disabledHeadersFooters,
|
|
1762
2031
|
() => {
|
|
1763
2032
|
dispatch({ type: "SET_HEADER_FOOTER_ENABLED", enabled });
|
|
1764
2033
|
}
|
|
@@ -1766,114 +2035,123 @@ const Toolbar = () => {
|
|
|
1766
2035
|
};
|
|
1767
2036
|
const handleCopyHeaderToAll = () => {
|
|
1768
2037
|
if (activePageId) {
|
|
1769
|
-
runToolbarAction(
|
|
2038
|
+
runToolbarAction(t.history.actions.copiedHeaderToAll, () => {
|
|
1770
2039
|
dispatch({ type: "COPY_HEADER_TO_ALL", sourcePageId: activePageId });
|
|
1771
2040
|
});
|
|
1772
2041
|
}
|
|
1773
2042
|
};
|
|
1774
2043
|
const handleCopyFooterToAll = () => {
|
|
1775
2044
|
if (activePageId) {
|
|
1776
|
-
runToolbarAction(
|
|
2045
|
+
runToolbarAction(t.history.actions.copiedFooterToAll, () => {
|
|
1777
2046
|
dispatch({ type: "COPY_FOOTER_TO_ALL", sourcePageId: activePageId });
|
|
1778
2047
|
});
|
|
1779
2048
|
}
|
|
1780
2049
|
};
|
|
1781
2050
|
const handleClearHeader = () => {
|
|
1782
2051
|
if (activePageId) {
|
|
1783
|
-
runToolbarAction(
|
|
2052
|
+
runToolbarAction(t.history.actions.clearedHeader, () => {
|
|
1784
2053
|
dispatch({ type: "CLEAR_HEADER", pageId: activePageId });
|
|
1785
2054
|
});
|
|
1786
2055
|
}
|
|
1787
2056
|
};
|
|
1788
2057
|
const handleClearFooter = () => {
|
|
1789
2058
|
if (activePageId) {
|
|
1790
|
-
runToolbarAction(
|
|
2059
|
+
runToolbarAction(t.history.actions.clearedFooter, () => {
|
|
1791
2060
|
dispatch({ type: "CLEAR_FOOTER", pageId: activePageId });
|
|
1792
2061
|
});
|
|
1793
2062
|
}
|
|
1794
2063
|
};
|
|
1795
|
-
const handleClearAllHeaders = () => runToolbarAction(
|
|
2064
|
+
const handleClearAllHeaders = () => runToolbarAction(t.history.actions.clearedAllHeaders, () => {
|
|
1796
2065
|
dispatch({ type: "CLEAR_ALL_HEADERS" });
|
|
1797
2066
|
});
|
|
1798
|
-
const handleClearAllFooters = () => runToolbarAction(
|
|
2067
|
+
const handleClearAllFooters = () => runToolbarAction(t.history.actions.clearedAllFooters, () => {
|
|
1799
2068
|
dispatch({ type: "CLEAR_ALL_FOOTERS" });
|
|
1800
2069
|
});
|
|
1801
2070
|
const handlePageCounterModeChange = useCallback((mode) => {
|
|
1802
|
-
runToolbarAction(
|
|
2071
|
+
runToolbarAction(interpolate(t.history.actions.pageCounterSet, { value: mode }), () => {
|
|
1803
2072
|
dispatch({ type: "SET_PAGE_COUNTER_MODE", mode });
|
|
1804
2073
|
});
|
|
1805
|
-
}, [dispatch, runToolbarAction]);
|
|
2074
|
+
}, [dispatch, runToolbarAction, t.history.actions.pageCounterSet]);
|
|
1806
2075
|
const handleBold = useCallback(() => {
|
|
1807
2076
|
debug("toolbar", `bold (globalSelection=${globalSelectionActive}, editors=${editorRegistry.all().length}, hasEditor=${!!activeEditor})`);
|
|
1808
|
-
runToolbarAction(
|
|
2077
|
+
runToolbarAction(t.history.actions.boldApplied, () => {
|
|
1809
2078
|
applyToBodyEditors(toggleBold);
|
|
1810
2079
|
});
|
|
1811
|
-
}, [activeEditor, applyToBodyEditors, editorRegistry, globalSelectionActive, runToolbarAction]);
|
|
2080
|
+
}, [activeEditor, applyToBodyEditors, editorRegistry, globalSelectionActive, runToolbarAction, t.history.actions.boldApplied]);
|
|
1812
2081
|
const handleItalic = useCallback(() => {
|
|
1813
2082
|
debug("toolbar", `italic (globalSelection=${globalSelectionActive}, hasEditor=${!!activeEditor})`);
|
|
1814
|
-
runToolbarAction(
|
|
2083
|
+
runToolbarAction(t.history.actions.italicApplied, () => {
|
|
1815
2084
|
applyToBodyEditors(toggleItalic);
|
|
1816
2085
|
});
|
|
1817
|
-
}, [activeEditor, applyToBodyEditors, globalSelectionActive, runToolbarAction]);
|
|
2086
|
+
}, [activeEditor, applyToBodyEditors, globalSelectionActive, runToolbarAction, t.history.actions.italicApplied]);
|
|
1818
2087
|
const handleUnderline = useCallback(() => {
|
|
1819
2088
|
debug("toolbar", `underline (globalSelection=${globalSelectionActive}, hasEditor=${!!activeEditor})`);
|
|
1820
|
-
runToolbarAction(
|
|
2089
|
+
runToolbarAction(t.history.actions.underlineApplied, () => {
|
|
1821
2090
|
applyToBodyEditors(toggleUnderline);
|
|
1822
2091
|
});
|
|
1823
|
-
}, [activeEditor, applyToBodyEditors, globalSelectionActive, runToolbarAction]);
|
|
2092
|
+
}, [activeEditor, applyToBodyEditors, globalSelectionActive, runToolbarAction, t.history.actions.underlineApplied]);
|
|
1824
2093
|
const handleStrikethrough = useCallback(() => {
|
|
1825
2094
|
debug("toolbar", `strikethrough (globalSelection=${globalSelectionActive}, hasEditor=${!!activeEditor})`);
|
|
1826
|
-
runToolbarAction(
|
|
2095
|
+
runToolbarAction(t.history.actions.strikethroughApplied, () => {
|
|
1827
2096
|
applyToBodyEditors(toggleStrikethrough);
|
|
1828
2097
|
});
|
|
1829
|
-
}, [activeEditor, applyToBodyEditors, globalSelectionActive, runToolbarAction]);
|
|
2098
|
+
}, [activeEditor, applyToBodyEditors, globalSelectionActive, runToolbarAction, t.history.actions.strikethroughApplied]);
|
|
1830
2099
|
const handleAlignLeft = useCallback(() => {
|
|
1831
|
-
runToolbarAction(
|
|
2100
|
+
runToolbarAction(t.history.actions.alignedLeft, () => {
|
|
1832
2101
|
applyToBodyEditors((editor) => setAlignment(editor, "left"));
|
|
1833
2102
|
});
|
|
1834
|
-
}, [applyToBodyEditors, runToolbarAction]);
|
|
2103
|
+
}, [applyToBodyEditors, runToolbarAction, t.history.actions.alignedLeft]);
|
|
1835
2104
|
const handleAlignCenter = useCallback(() => {
|
|
1836
|
-
runToolbarAction(
|
|
2105
|
+
runToolbarAction(t.history.actions.alignedCenter, () => {
|
|
1837
2106
|
applyToBodyEditors((editor) => setAlignment(editor, "center"));
|
|
1838
2107
|
});
|
|
1839
|
-
}, [applyToBodyEditors, runToolbarAction]);
|
|
2108
|
+
}, [applyToBodyEditors, runToolbarAction, t.history.actions.alignedCenter]);
|
|
1840
2109
|
const handleAlignRight = useCallback(() => {
|
|
1841
|
-
runToolbarAction(
|
|
2110
|
+
runToolbarAction(t.history.actions.alignedRight, () => {
|
|
1842
2111
|
applyToBodyEditors((editor) => setAlignment(editor, "right"));
|
|
1843
2112
|
});
|
|
1844
|
-
}, [applyToBodyEditors, runToolbarAction]);
|
|
2113
|
+
}, [applyToBodyEditors, runToolbarAction, t.history.actions.alignedRight]);
|
|
1845
2114
|
const handleAlignJustify = useCallback(() => {
|
|
1846
|
-
runToolbarAction(
|
|
2115
|
+
runToolbarAction(t.history.actions.justifiedText, () => {
|
|
1847
2116
|
applyToBodyEditors((editor) => setAlignment(editor, "justify"));
|
|
1848
2117
|
});
|
|
1849
|
-
}, [applyToBodyEditors, runToolbarAction]);
|
|
2118
|
+
}, [applyToBodyEditors, runToolbarAction, t.history.actions.justifiedText]);
|
|
1850
2119
|
const handleListNumber = useCallback(() => {
|
|
1851
|
-
runToolbarAction(
|
|
2120
|
+
runToolbarAction(t.history.actions.insertedNumberedList, () => {
|
|
1852
2121
|
applyToBodyEditors((editor) => insertList(editor, "number"));
|
|
1853
2122
|
});
|
|
1854
|
-
}, [applyToBodyEditors, runToolbarAction]);
|
|
2123
|
+
}, [applyToBodyEditors, runToolbarAction, t.history.actions.insertedNumberedList]);
|
|
1855
2124
|
const handleListBullet = useCallback(() => {
|
|
1856
|
-
runToolbarAction(
|
|
2125
|
+
runToolbarAction(t.history.actions.insertedBulletList, () => {
|
|
1857
2126
|
applyToBodyEditors((editor) => insertList(editor, "bullet"));
|
|
1858
2127
|
});
|
|
1859
|
-
}, [applyToBodyEditors, runToolbarAction]);
|
|
2128
|
+
}, [applyToBodyEditors, runToolbarAction, t.history.actions.insertedBulletList]);
|
|
1860
2129
|
const handleIndent = useCallback(() => {
|
|
1861
|
-
runToolbarAction(
|
|
2130
|
+
runToolbarAction(t.history.actions.indentedContent, () => {
|
|
1862
2131
|
applyToBodyEditors(indentContent);
|
|
1863
2132
|
});
|
|
1864
|
-
}, [applyToBodyEditors, runToolbarAction]);
|
|
2133
|
+
}, [applyToBodyEditors, runToolbarAction, t.history.actions.indentedContent]);
|
|
1865
2134
|
const handleOutdent = useCallback(() => {
|
|
1866
|
-
runToolbarAction(
|
|
2135
|
+
runToolbarAction(t.history.actions.outdentedContent, () => {
|
|
1867
2136
|
applyToBodyEditors(outdentContent);
|
|
1868
2137
|
});
|
|
1869
|
-
}, [applyToBodyEditors, runToolbarAction]);
|
|
2138
|
+
}, [applyToBodyEditors, runToolbarAction, t.history.actions.outdentedContent]);
|
|
1870
2139
|
const handleFontChange = useCallback(
|
|
1871
2140
|
(e) => {
|
|
1872
|
-
runToolbarAction(
|
|
2141
|
+
runToolbarAction(interpolate(t.history.actions.fontChanged, { value: e.target.value }), () => {
|
|
1873
2142
|
applyToBodyEditors((editor) => applyFontFamily(editor, e.target.value));
|
|
1874
2143
|
});
|
|
1875
2144
|
},
|
|
1876
|
-
[applyToBodyEditors, runToolbarAction]
|
|
2145
|
+
[applyToBodyEditors, runToolbarAction, t.history.actions.fontChanged]
|
|
2146
|
+
);
|
|
2147
|
+
const handleFontSizeChange = useCallback(
|
|
2148
|
+
(e) => {
|
|
2149
|
+
const size = parseInt(e.target.value, 10);
|
|
2150
|
+
runToolbarAction(interpolate(t.history.actions.fontSizeChanged, { value: String(size) }), () => {
|
|
2151
|
+
applyToBodyEditors((editor) => applyFontSize(editor, size));
|
|
2152
|
+
});
|
|
2153
|
+
},
|
|
2154
|
+
[applyToBodyEditors, runToolbarAction, t.history.actions.fontSizeChanged]
|
|
1877
2155
|
);
|
|
1878
2156
|
return /* @__PURE__ */ jsx(
|
|
1879
2157
|
"div",
|
|
@@ -1885,7 +2163,7 @@ const Toolbar = () => {
|
|
|
1885
2163
|
/* @__PURE__ */ jsx(
|
|
1886
2164
|
ToolbarIconButton,
|
|
1887
2165
|
{
|
|
1888
|
-
title:
|
|
2166
|
+
title: t.toolbar.undo,
|
|
1889
2167
|
testId: "btn-undo",
|
|
1890
2168
|
disabled: !canUndo,
|
|
1891
2169
|
onClick: undo,
|
|
@@ -1895,7 +2173,7 @@ const Toolbar = () => {
|
|
|
1895
2173
|
/* @__PURE__ */ jsx(
|
|
1896
2174
|
ToolbarIconButton,
|
|
1897
2175
|
{
|
|
1898
|
-
title:
|
|
2176
|
+
title: t.toolbar.redo,
|
|
1899
2177
|
testId: "btn-redo",
|
|
1900
2178
|
disabled: !canRedo,
|
|
1901
2179
|
onClick: redo,
|
|
@@ -1909,31 +2187,41 @@ const Toolbar = () => {
|
|
|
1909
2187
|
{
|
|
1910
2188
|
className: "h-7 rounded border border-gray-200 bg-white px-2 text-xs text-gray-700\n focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400",
|
|
1911
2189
|
"data-testid": "font-selector",
|
|
1912
|
-
defaultValue: "
|
|
2190
|
+
defaultValue: "Inter",
|
|
1913
2191
|
onChange: handleFontChange,
|
|
1914
2192
|
children: SUPPORTED_FONTS.map((font) => /* @__PURE__ */ jsx("option", { value: font, style: { fontFamily: font }, children: font }, font))
|
|
1915
2193
|
}
|
|
1916
2194
|
),
|
|
2195
|
+
/* @__PURE__ */ jsx(
|
|
2196
|
+
"select",
|
|
2197
|
+
{
|
|
2198
|
+
className: "h-7 w-16 rounded border border-gray-200 bg-white px-1 text-xs text-gray-700\n focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400",
|
|
2199
|
+
"data-testid": "font-size-selector",
|
|
2200
|
+
defaultValue: "12",
|
|
2201
|
+
onChange: handleFontSizeChange,
|
|
2202
|
+
children: SUPPORTED_FONT_SIZES.map((size) => /* @__PURE__ */ jsx("option", { value: size, children: size }, size))
|
|
2203
|
+
}
|
|
2204
|
+
),
|
|
1917
2205
|
/* @__PURE__ */ jsx(Divider, {}),
|
|
1918
2206
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "format-group", children: [
|
|
1919
|
-
/* @__PURE__ */ jsx(ToolbarIconButton, { title:
|
|
1920
|
-
/* @__PURE__ */ jsx(ToolbarIconButton, { title:
|
|
1921
|
-
/* @__PURE__ */ jsx(ToolbarIconButton, { title:
|
|
1922
|
-
/* @__PURE__ */ jsx(ToolbarIconButton, { title:
|
|
2207
|
+
/* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.bold, testId: "btn-bold", onClick: handleBold, children: /* @__PURE__ */ jsx(Bold, { size: 15 }) }),
|
|
2208
|
+
/* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.italic, testId: "btn-italic", onClick: handleItalic, children: /* @__PURE__ */ jsx(Italic, { size: 15 }) }),
|
|
2209
|
+
/* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.underline, testId: "btn-underline", onClick: handleUnderline, children: /* @__PURE__ */ jsx(Underline, { size: 15 }) }),
|
|
2210
|
+
/* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.strikethrough, testId: "btn-strike", onClick: handleStrikethrough, children: /* @__PURE__ */ jsx(Strikethrough, { size: 15 }) })
|
|
1923
2211
|
] }),
|
|
1924
2212
|
/* @__PURE__ */ jsx(Divider, {}),
|
|
1925
2213
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "align-group", children: [
|
|
1926
|
-
/* @__PURE__ */ jsx(ToolbarIconButton, { title:
|
|
1927
|
-
/* @__PURE__ */ jsx(ToolbarIconButton, { title:
|
|
1928
|
-
/* @__PURE__ */ jsx(ToolbarIconButton, { title:
|
|
1929
|
-
/* @__PURE__ */ jsx(ToolbarIconButton, { title:
|
|
2214
|
+
/* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.alignLeft, testId: "btn-align-left", onClick: handleAlignLeft, children: /* @__PURE__ */ jsx(TextAlignStart, { size: 15 }) }),
|
|
2215
|
+
/* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.alignCenter, testId: "btn-align-center", onClick: handleAlignCenter, children: /* @__PURE__ */ jsx(TextAlignCenter, { size: 15 }) }),
|
|
2216
|
+
/* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.alignRight, testId: "btn-align-right", onClick: handleAlignRight, children: /* @__PURE__ */ jsx(TextAlignEnd, { size: 15 }) }),
|
|
2217
|
+
/* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.justify, testId: "btn-align-justify", onClick: handleAlignJustify, children: /* @__PURE__ */ jsx(TextAlignJustify, { size: 15 }) })
|
|
1930
2218
|
] }),
|
|
1931
2219
|
/* @__PURE__ */ jsx(Divider, {}),
|
|
1932
2220
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": "list-group", children: [
|
|
1933
|
-
/* @__PURE__ */ jsx(ToolbarIconButton, { title:
|
|
1934
|
-
/* @__PURE__ */ jsx(ToolbarIconButton, { title:
|
|
1935
|
-
/* @__PURE__ */ jsx(ToolbarIconButton, { title:
|
|
1936
|
-
/* @__PURE__ */ jsx(ToolbarIconButton, { title:
|
|
2221
|
+
/* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.numberedList, testId: "btn-list-number", onClick: handleListNumber, children: /* @__PURE__ */ jsx(ListOrdered, { size: 15 }) }),
|
|
2222
|
+
/* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.bulletList, testId: "btn-list-bullet", onClick: handleListBullet, children: /* @__PURE__ */ jsx(List, { size: 15 }) }),
|
|
2223
|
+
/* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.indent, testId: "btn-indent", onClick: handleIndent, children: /* @__PURE__ */ jsx(ListIndentIncrease, { size: 15 }) }),
|
|
2224
|
+
/* @__PURE__ */ jsx(ToolbarIconButton, { title: t.toolbar.outdent, testId: "btn-outdent", onClick: handleOutdent, children: /* @__PURE__ */ jsx(ListIndentDecrease, { size: 15 }) })
|
|
1937
2225
|
] }),
|
|
1938
2226
|
/* @__PURE__ */ jsx(Divider, {}),
|
|
1939
2227
|
/* @__PURE__ */ jsx(
|
|
@@ -1960,10 +2248,14 @@ const Toolbar = () => {
|
|
|
1960
2248
|
}
|
|
1961
2249
|
)
|
|
1962
2250
|
] }),
|
|
2251
|
+
toolbarItems.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2252
|
+
/* @__PURE__ */ jsx(Divider, {}),
|
|
2253
|
+
toolbarItems.map((ToolbarItem, idx) => /* @__PURE__ */ jsx(ToolbarItem, {}, idx))
|
|
2254
|
+
] }),
|
|
1963
2255
|
/* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center", children: /* @__PURE__ */ jsx(
|
|
1964
2256
|
ToolbarIconButton,
|
|
1965
2257
|
{
|
|
1966
|
-
title: historySidebarOpen ?
|
|
2258
|
+
title: historySidebarOpen ? t.toolbar.closeHistory : t.toolbar.openHistory,
|
|
1967
2259
|
testId: "toggle-history-sidebar",
|
|
1968
2260
|
active: historySidebarOpen,
|
|
1969
2261
|
onClick: () => setHistorySidebarOpen(!historySidebarOpen),
|
|
@@ -2266,12 +2558,13 @@ const lexicalTheme = {
|
|
|
2266
2558
|
},
|
|
2267
2559
|
quote: "border-l-4 border-gray-300 pl-4 italic text-gray-600"
|
|
2268
2560
|
};
|
|
2269
|
-
|
|
2561
|
+
const DEFAULT_NODES = [HeadingNode, QuoteNode, ListNode, ListItemNode];
|
|
2562
|
+
function createEditorConfig(mode, pageId, extraNodes = [], themeOverrides = {}) {
|
|
2270
2563
|
const namespace = pageId ? `lex4-${mode}-${pageId}` : `lex4-${mode}`;
|
|
2271
2564
|
return {
|
|
2272
2565
|
namespace,
|
|
2273
|
-
theme: lexicalTheme,
|
|
2274
|
-
nodes: [
|
|
2566
|
+
theme: { ...lexicalTheme, ...themeOverrides },
|
|
2567
|
+
nodes: [...DEFAULT_NODES, ...extraNodes],
|
|
2275
2568
|
onError: (error) => {
|
|
2276
2569
|
console.error(`[Lex4 ${mode} editor error]`, error);
|
|
2277
2570
|
}
|
|
@@ -2849,11 +3142,12 @@ const PageBody = ({
|
|
|
2849
3142
|
onMoveToNextPage,
|
|
2850
3143
|
readOnly = false
|
|
2851
3144
|
}) => {
|
|
3145
|
+
const { nodes, bodyPlugins, themeOverrides } = useExtensions();
|
|
2852
3146
|
const config = useMemo(
|
|
2853
3147
|
() => {
|
|
2854
3148
|
var _a, _b;
|
|
2855
3149
|
const baseConfig = {
|
|
2856
|
-
...createEditorConfig("body", pageId),
|
|
3150
|
+
...createEditorConfig("body", pageId, nodes, themeOverrides),
|
|
2857
3151
|
editable: !readOnly
|
|
2858
3152
|
};
|
|
2859
3153
|
if (initialBodyState) {
|
|
@@ -2867,7 +3161,7 @@ const PageBody = ({
|
|
|
2867
3161
|
return baseConfig;
|
|
2868
3162
|
},
|
|
2869
3163
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally only use initialBodyState at mount time
|
|
2870
|
-
[pageId, readOnly]
|
|
3164
|
+
[pageId, readOnly, nodes, themeOverrides]
|
|
2871
3165
|
);
|
|
2872
3166
|
const handleChange = useCallback(
|
|
2873
3167
|
(editorState) => {
|
|
@@ -2923,6 +3217,7 @@ const PageBody = ({
|
|
|
2923
3217
|
}
|
|
2924
3218
|
),
|
|
2925
3219
|
/* @__PURE__ */ jsx(OverflowPlugin, { onOverflow: handleOverflow }),
|
|
3220
|
+
bodyPlugins.map((Plugin, idx) => /* @__PURE__ */ jsx(Plugin, {}, idx)),
|
|
2926
3221
|
/* @__PURE__ */ jsx(OnChangePlugin, { onChange: handleChange, ignoreSelectionChange: true })
|
|
2927
3222
|
] })
|
|
2928
3223
|
}
|
|
@@ -2992,16 +3287,18 @@ const PageHeader = ({
|
|
|
2992
3287
|
onHeaderChange
|
|
2993
3288
|
}) => {
|
|
2994
3289
|
const hasPageCounter = !!pageCounterLabel;
|
|
3290
|
+
const { nodes, themeOverrides } = useExtensions();
|
|
3291
|
+
const t = useTranslations();
|
|
2995
3292
|
const config = useMemo(
|
|
2996
3293
|
() => {
|
|
2997
|
-
const baseConfig = createEditorConfig("header", pageId);
|
|
3294
|
+
const baseConfig = createEditorConfig("header", pageId, nodes, themeOverrides);
|
|
2998
3295
|
if (initialHeaderState) {
|
|
2999
3296
|
return { ...baseConfig, editorState: JSON.stringify(initialHeaderState) };
|
|
3000
3297
|
}
|
|
3001
3298
|
return baseConfig;
|
|
3002
3299
|
},
|
|
3003
3300
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- syncVersion forces remount via key, only used at init
|
|
3004
|
-
[pageId]
|
|
3301
|
+
[pageId, nodes, themeOverrides]
|
|
3005
3302
|
);
|
|
3006
3303
|
const contentRef = useRef(null);
|
|
3007
3304
|
const handleChange = useCallback(
|
|
@@ -3027,7 +3324,7 @@ const PageHeader = ({
|
|
|
3027
3324
|
return /* @__PURE__ */ jsxs(
|
|
3028
3325
|
"div",
|
|
3029
3326
|
{
|
|
3030
|
-
className: "lex4-page-header border-b border-dashed border-
|
|
3327
|
+
className: "lex4-page-header bg-blue-50/60 border-t-2 border-t-blue-200 border-b border-dashed border-blue-100 relative flex-shrink-0",
|
|
3031
3328
|
style: { maxHeight: MAX_HEADER_HEIGHT_PX, overflow: "clip" },
|
|
3032
3329
|
"data-testid": `page-header-${pageId}`,
|
|
3033
3330
|
children: [
|
|
@@ -3042,7 +3339,7 @@ const PageHeader = ({
|
|
|
3042
3339
|
className: `outline-none p-2 text-sm text-gray-600 min-h-[24px] ${hasPageCounter ? "pr-24" : ""}`
|
|
3043
3340
|
}
|
|
3044
3341
|
),
|
|
3045
|
-
placeholder: /* @__PURE__ */ jsx("div", { className: `absolute top-0 left-0 text-gray-400 pointer-events-none select-none p-2 text-sm ${hasPageCounter ? "pr-24" : ""}`, children:
|
|
3342
|
+
placeholder: /* @__PURE__ */ jsx("div", { className: `absolute top-0 left-0 text-gray-400 pointer-events-none select-none p-2 text-sm ${hasPageCounter ? "pr-24" : ""}`, children: t.header.placeholder }),
|
|
3046
3343
|
ErrorBoundary: LexicalErrorBoundary
|
|
3047
3344
|
}
|
|
3048
3345
|
),
|
|
@@ -3070,16 +3367,18 @@ const PageFooter = ({
|
|
|
3070
3367
|
onFooterChange
|
|
3071
3368
|
}) => {
|
|
3072
3369
|
const hasPageCounter = !!pageCounterLabel;
|
|
3370
|
+
const { nodes, themeOverrides } = useExtensions();
|
|
3371
|
+
const t = useTranslations();
|
|
3073
3372
|
const config = useMemo(
|
|
3074
3373
|
() => {
|
|
3075
|
-
const baseConfig = createEditorConfig("footer", pageId);
|
|
3374
|
+
const baseConfig = createEditorConfig("footer", pageId, nodes, themeOverrides);
|
|
3076
3375
|
if (initialFooterState) {
|
|
3077
3376
|
return { ...baseConfig, editorState: JSON.stringify(initialFooterState) };
|
|
3078
3377
|
}
|
|
3079
3378
|
return baseConfig;
|
|
3080
3379
|
},
|
|
3081
3380
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- syncVersion forces remount via key, only used at init
|
|
3082
|
-
[pageId]
|
|
3381
|
+
[pageId, nodes, themeOverrides]
|
|
3083
3382
|
);
|
|
3084
3383
|
const contentRef = useRef(null);
|
|
3085
3384
|
const handleChange = useCallback(
|
|
@@ -3105,7 +3404,7 @@ const PageFooter = ({
|
|
|
3105
3404
|
return /* @__PURE__ */ jsxs(
|
|
3106
3405
|
"div",
|
|
3107
3406
|
{
|
|
3108
|
-
className: "lex4-page-footer border-t border-dashed border-
|
|
3407
|
+
className: "lex4-page-footer bg-blue-50/60 border-b-2 border-b-blue-200 border-t border-dashed border-blue-100 relative flex-shrink-0",
|
|
3109
3408
|
style: { maxHeight: MAX_FOOTER_HEIGHT_PX, overflow: "clip" },
|
|
3110
3409
|
"data-testid": `page-footer-${pageId}`,
|
|
3111
3410
|
children: [
|
|
@@ -3120,7 +3419,7 @@ const PageFooter = ({
|
|
|
3120
3419
|
className: `outline-none p-2 text-sm text-gray-600 min-h-[24px] ${hasPageCounter ? "pr-24" : ""}`
|
|
3121
3420
|
}
|
|
3122
3421
|
),
|
|
3123
|
-
placeholder: /* @__PURE__ */ jsx("div", { className: `absolute top-0 left-0 text-gray-400 pointer-events-none select-none p-2 text-sm ${hasPageCounter ? "pr-24" : ""}`, children:
|
|
3422
|
+
placeholder: /* @__PURE__ */ jsx("div", { className: `absolute top-0 left-0 text-gray-400 pointer-events-none select-none p-2 text-sm ${hasPageCounter ? "pr-24" : ""}`, children: t.footer.placeholder }),
|
|
3124
3423
|
ErrorBoundary: LexicalErrorBoundary
|
|
3125
3424
|
}
|
|
3126
3425
|
),
|
|
@@ -3186,7 +3485,7 @@ const PageView = React.memo(({
|
|
|
3186
3485
|
return /* @__PURE__ */ jsxs(
|
|
3187
3486
|
"div",
|
|
3188
3487
|
{
|
|
3189
|
-
className: "lex4-page bg-white shadow-
|
|
3488
|
+
className: "lex4-page bg-white shadow-xl flex flex-col",
|
|
3190
3489
|
style: {
|
|
3191
3490
|
width: A4_WIDTH_PX,
|
|
3192
3491
|
height: A4_HEIGHT_PX,
|
|
@@ -3522,6 +3821,7 @@ const EditorChrome = ({
|
|
|
3522
3821
|
undo,
|
|
3523
3822
|
redo
|
|
3524
3823
|
} = useDocument();
|
|
3824
|
+
const { sidePanels } = useExtensions();
|
|
3525
3825
|
const rootRef = useRef(null);
|
|
3526
3826
|
const selectionBufferRef = useRef(null);
|
|
3527
3827
|
const clearGlobalSelection = useCallback(() => {
|
|
@@ -3663,35 +3963,63 @@ const EditorChrome = ({
|
|
|
3663
3963
|
}
|
|
3664
3964
|
),
|
|
3665
3965
|
/* @__PURE__ */ jsx(Toolbar, {}),
|
|
3666
|
-
/* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden bg-gray-
|
|
3966
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 overflow-hidden bg-gray-700", children: [
|
|
3667
3967
|
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 overflow-auto", children: /* @__PURE__ */ jsx(DocumentView, {}) }),
|
|
3968
|
+
sidePanels.map((Panel, idx) => /* @__PURE__ */ jsx(Panel, {}, idx)),
|
|
3668
3969
|
/* @__PURE__ */ jsx(HistorySidebar, {})
|
|
3669
3970
|
] })
|
|
3670
3971
|
]
|
|
3671
3972
|
}
|
|
3672
3973
|
);
|
|
3673
3974
|
};
|
|
3674
|
-
const
|
|
3975
|
+
const EditorWithHandle = forwardRef(({ captureHistoryShortcutsOnWindow, className }, ref) => {
|
|
3976
|
+
const { document: doc, activeEditor } = useDocument();
|
|
3977
|
+
const { handleFactories } = useExtensions();
|
|
3978
|
+
const getDocument = useCallback(() => doc, [doc]);
|
|
3979
|
+
const getActiveEditor = useCallback(() => activeEditor, [activeEditor]);
|
|
3980
|
+
const extensionCtx = useExtensionContext(getDocument, getActiveEditor);
|
|
3981
|
+
useImperativeHandle(ref, () => {
|
|
3982
|
+
const handle = {};
|
|
3983
|
+
for (const factory of handleFactories) {
|
|
3984
|
+
const methods = factory(extensionCtx);
|
|
3985
|
+
Object.assign(handle, methods);
|
|
3986
|
+
}
|
|
3987
|
+
return handle;
|
|
3988
|
+
}, [extensionCtx, handleFactories]);
|
|
3989
|
+
return /* @__PURE__ */ jsx(
|
|
3990
|
+
EditorChrome,
|
|
3991
|
+
{
|
|
3992
|
+
captureHistoryShortcutsOnWindow,
|
|
3993
|
+
className
|
|
3994
|
+
}
|
|
3995
|
+
);
|
|
3996
|
+
});
|
|
3997
|
+
EditorWithHandle.displayName = "EditorWithHandle";
|
|
3998
|
+
const Lex4Editor = forwardRef(({
|
|
3675
3999
|
captureHistoryShortcutsOnWindow = true,
|
|
3676
4000
|
initialDocument,
|
|
3677
4001
|
onDocumentChange,
|
|
4002
|
+
extensions,
|
|
4003
|
+
translations,
|
|
3678
4004
|
className
|
|
3679
|
-
}) => {
|
|
3680
|
-
return /* @__PURE__ */ jsx(
|
|
4005
|
+
}, ref) => {
|
|
4006
|
+
return /* @__PURE__ */ jsx(TranslationsProvider, { translations, children: /* @__PURE__ */ jsx(ExtensionStateProvider, { children: /* @__PURE__ */ jsx(ExtensionProvider, { extensions, children: /* @__PURE__ */ jsx(
|
|
3681
4007
|
DocumentProvider,
|
|
3682
4008
|
{
|
|
3683
4009
|
initialDocument,
|
|
3684
4010
|
onDocumentChange,
|
|
3685
4011
|
children: /* @__PURE__ */ jsx(
|
|
3686
|
-
|
|
4012
|
+
EditorWithHandle,
|
|
3687
4013
|
{
|
|
4014
|
+
ref,
|
|
3688
4015
|
captureHistoryShortcutsOnWindow,
|
|
3689
4016
|
className
|
|
3690
4017
|
}
|
|
3691
4018
|
)
|
|
3692
4019
|
}
|
|
3693
|
-
);
|
|
3694
|
-
};
|
|
4020
|
+
) }) }) });
|
|
4021
|
+
});
|
|
4022
|
+
Lex4Editor.displayName = "Lex4Editor";
|
|
3695
4023
|
function useOverflowDetection(bodyHeight, onOverflow, debounceMs = 100) {
|
|
3696
4024
|
const timerRef = useRef(null);
|
|
3697
4025
|
const observerRef = useRef(null);
|
|
@@ -3757,18 +4085,702 @@ function useHeaderFooter(maxHeight, onHeightChange) {
|
|
|
3757
4085
|
}, []);
|
|
3758
4086
|
return { attachRef };
|
|
3759
4087
|
}
|
|
4088
|
+
const AST_VERSION = "1.0.0";
|
|
4089
|
+
const IS_BOLD = 1;
|
|
4090
|
+
const IS_ITALIC = 2;
|
|
4091
|
+
const IS_STRIKETHROUGH = 4;
|
|
4092
|
+
const IS_UNDERLINE = 8;
|
|
4093
|
+
function decodeFormatBitmask(format) {
|
|
4094
|
+
const marks = {};
|
|
4095
|
+
if (format & IS_BOLD) marks.bold = true;
|
|
4096
|
+
if (format & IS_ITALIC) marks.italic = true;
|
|
4097
|
+
if (format & IS_UNDERLINE) marks.underline = true;
|
|
4098
|
+
if (format & IS_STRIKETHROUGH) marks.strikethrough = true;
|
|
4099
|
+
return marks;
|
|
4100
|
+
}
|
|
4101
|
+
function extractFontFamily(style) {
|
|
4102
|
+
const match = style.match(/font-family:\s*([^;]+)/);
|
|
4103
|
+
return match ? match[1].trim().replace(/['"]/g, "") : void 0;
|
|
4104
|
+
}
|
|
4105
|
+
function extractFontSizePt(style) {
|
|
4106
|
+
const match = style.match(/font-size:\s*(\d+(?:\.\d+)?)\s*pt/);
|
|
4107
|
+
return match ? parseFloat(match[1]) : void 0;
|
|
4108
|
+
}
|
|
4109
|
+
function buildTextMarks(format, style) {
|
|
4110
|
+
const formatMarks = decodeFormatBitmask(format);
|
|
4111
|
+
const fontFamily = style ? extractFontFamily(style) : void 0;
|
|
4112
|
+
const fontSize = style ? extractFontSizePt(style) : void 0;
|
|
4113
|
+
const marks = {
|
|
4114
|
+
...formatMarks,
|
|
4115
|
+
...fontFamily ? { fontFamily } : {},
|
|
4116
|
+
...fontSize ? { fontSize } : {}
|
|
4117
|
+
};
|
|
4118
|
+
return Object.keys(marks).length > 0 ? marks : void 0;
|
|
4119
|
+
}
|
|
4120
|
+
function mapInlineNode(node) {
|
|
4121
|
+
switch (node.type) {
|
|
4122
|
+
case "text":
|
|
4123
|
+
return mapTextNode(node);
|
|
4124
|
+
case "variable-node":
|
|
4125
|
+
return mapVariableNode(node);
|
|
4126
|
+
case "linebreak":
|
|
4127
|
+
return mapLineBreak();
|
|
4128
|
+
default:
|
|
4129
|
+
return { type: "text", text: "" };
|
|
4130
|
+
}
|
|
4131
|
+
}
|
|
4132
|
+
function mapTextNode(node) {
|
|
4133
|
+
const marks = buildTextMarks(node.format, node.style);
|
|
4134
|
+
return {
|
|
4135
|
+
type: "text",
|
|
4136
|
+
text: node.text,
|
|
4137
|
+
...marks ? { marks } : {}
|
|
4138
|
+
};
|
|
4139
|
+
}
|
|
4140
|
+
function mapVariableNode(node) {
|
|
4141
|
+
return {
|
|
4142
|
+
type: "variable",
|
|
4143
|
+
key: node.variableKey
|
|
4144
|
+
};
|
|
4145
|
+
}
|
|
4146
|
+
function mapLineBreak() {
|
|
4147
|
+
return { type: "linebreak" };
|
|
4148
|
+
}
|
|
4149
|
+
function mapInlineNodes(nodes) {
|
|
4150
|
+
return nodes.map(mapInlineNode);
|
|
4151
|
+
}
|
|
4152
|
+
const ALIGN_LEFT = 1;
|
|
4153
|
+
const ALIGN_CENTER = 2;
|
|
4154
|
+
const ALIGN_RIGHT = 3;
|
|
4155
|
+
const ALIGN_JUSTIFY = 4;
|
|
4156
|
+
function decodeAlignment(format) {
|
|
4157
|
+
if (typeof format === "string") {
|
|
4158
|
+
if (["left", "center", "right", "justify"].includes(format)) {
|
|
4159
|
+
return format;
|
|
4160
|
+
}
|
|
4161
|
+
return void 0;
|
|
4162
|
+
}
|
|
4163
|
+
if (typeof format !== "number" || format === 0) return void 0;
|
|
4164
|
+
switch (format) {
|
|
4165
|
+
case ALIGN_LEFT:
|
|
4166
|
+
return "left";
|
|
4167
|
+
case ALIGN_CENTER:
|
|
4168
|
+
return "center";
|
|
4169
|
+
case ALIGN_RIGHT:
|
|
4170
|
+
return "right";
|
|
4171
|
+
case ALIGN_JUSTIFY:
|
|
4172
|
+
return "justify";
|
|
4173
|
+
default:
|
|
4174
|
+
return void 0;
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
function mapBlockNode(node) {
|
|
4178
|
+
switch (node.type) {
|
|
4179
|
+
case "paragraph":
|
|
4180
|
+
return mapParagraph(node);
|
|
4181
|
+
case "heading":
|
|
4182
|
+
return mapHeading(node);
|
|
4183
|
+
case "list":
|
|
4184
|
+
return mapList(node);
|
|
4185
|
+
case "quote":
|
|
4186
|
+
return mapBlockQuote(node);
|
|
4187
|
+
default:
|
|
4188
|
+
return {
|
|
4189
|
+
type: "paragraph",
|
|
4190
|
+
children: mapInlineChildren(node)
|
|
4191
|
+
};
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
4194
|
+
function mapParagraph(node) {
|
|
4195
|
+
const alignment = decodeAlignment(node.format);
|
|
4196
|
+
const indent = node.indent && node.indent > 0 ? node.indent : void 0;
|
|
4197
|
+
return {
|
|
4198
|
+
type: "paragraph",
|
|
4199
|
+
...alignment ? { alignment } : {},
|
|
4200
|
+
...indent ? { indent } : {},
|
|
4201
|
+
children: mapInlineChildren(node)
|
|
4202
|
+
};
|
|
4203
|
+
}
|
|
4204
|
+
function mapHeading(node) {
|
|
4205
|
+
var _a;
|
|
4206
|
+
const alignment = decodeAlignment(node.format);
|
|
4207
|
+
const tagMatch = (_a = node.tag) == null ? void 0 : _a.match(/^h(\d)$/);
|
|
4208
|
+
const level = tagMatch ? parseInt(tagMatch[1], 10) : 1;
|
|
4209
|
+
return {
|
|
4210
|
+
type: "heading",
|
|
4211
|
+
level,
|
|
4212
|
+
...alignment ? { alignment } : {},
|
|
4213
|
+
children: mapInlineChildren(node)
|
|
4214
|
+
};
|
|
4215
|
+
}
|
|
4216
|
+
function mapList(node) {
|
|
4217
|
+
const listType = node.listType === "number" ? "ordered" : "unordered";
|
|
4218
|
+
const items = (node.children ?? []).filter((c) => c.type === "listitem").map(mapListItem);
|
|
4219
|
+
return {
|
|
4220
|
+
type: "list",
|
|
4221
|
+
listType,
|
|
4222
|
+
items
|
|
4223
|
+
};
|
|
4224
|
+
}
|
|
4225
|
+
function mapListItem(node) {
|
|
4226
|
+
const inlineChildren = [];
|
|
4227
|
+
let nestedList;
|
|
4228
|
+
for (const child of node.children ?? []) {
|
|
4229
|
+
if (child.type === "list") {
|
|
4230
|
+
nestedList = mapList(child);
|
|
4231
|
+
} else {
|
|
4232
|
+
const mapped = mapInlineNodes([child]);
|
|
4233
|
+
inlineChildren.push(...mapped);
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
return {
|
|
4237
|
+
type: "list-item",
|
|
4238
|
+
children: inlineChildren,
|
|
4239
|
+
...nestedList ? { nestedList } : {}
|
|
4240
|
+
};
|
|
4241
|
+
}
|
|
4242
|
+
function mapBlockQuote(node) {
|
|
4243
|
+
return {
|
|
4244
|
+
type: "blockquote",
|
|
4245
|
+
children: mapInlineChildren(node)
|
|
4246
|
+
};
|
|
4247
|
+
}
|
|
4248
|
+
function mapInlineChildren(node) {
|
|
4249
|
+
if (!node.children || node.children.length === 0) return [];
|
|
4250
|
+
return mapInlineNodes(node.children);
|
|
4251
|
+
}
|
|
4252
|
+
function mapBlockNodes(nodes) {
|
|
4253
|
+
return nodes.map(mapBlockNode);
|
|
4254
|
+
}
|
|
4255
|
+
function mapEditorStateToContent(state) {
|
|
4256
|
+
if (!state || !state.root || !state.root.children) {
|
|
4257
|
+
return null;
|
|
4258
|
+
}
|
|
4259
|
+
const blocks = mapBlockNodes(state.root.children);
|
|
4260
|
+
return { blocks };
|
|
4261
|
+
}
|
|
4262
|
+
function mapEditorStateToBlocks(state) {
|
|
4263
|
+
if (!state || !state.root || !state.root.children) {
|
|
4264
|
+
return [];
|
|
4265
|
+
}
|
|
4266
|
+
return mapBlockNodes(state.root.children);
|
|
4267
|
+
}
|
|
4268
|
+
const MARGIN_MM = Math.round(PAGE_MARGIN_PX / PX_PER_MM * 10) / 10;
|
|
4269
|
+
function serializeDocument(document2, variableDefinitions = []) {
|
|
4270
|
+
const pages = document2.pages.map(
|
|
4271
|
+
(page, index) => serializePage(page, index)
|
|
4272
|
+
);
|
|
4273
|
+
const metadata = buildMetadata(variableDefinitions);
|
|
4274
|
+
return {
|
|
4275
|
+
version: AST_VERSION,
|
|
4276
|
+
page: {
|
|
4277
|
+
format: "A4",
|
|
4278
|
+
widthMm: 210,
|
|
4279
|
+
heightMm: 297,
|
|
4280
|
+
margins: {
|
|
4281
|
+
topMm: MARGIN_MM,
|
|
4282
|
+
rightMm: MARGIN_MM,
|
|
4283
|
+
bottomMm: MARGIN_MM,
|
|
4284
|
+
leftMm: MARGIN_MM
|
|
4285
|
+
}
|
|
4286
|
+
},
|
|
4287
|
+
headerFooter: {
|
|
4288
|
+
enabled: document2.headerFooterEnabled,
|
|
4289
|
+
pageCounterMode: document2.pageCounterMode,
|
|
4290
|
+
defaultHeader: document2.pages.length > 0 ? mapEditorStateToContent(document2.pages[0].headerState) : null,
|
|
4291
|
+
defaultFooter: document2.pages.length > 0 ? mapEditorStateToContent(document2.pages[0].footerState) : null
|
|
4292
|
+
},
|
|
4293
|
+
pages,
|
|
4294
|
+
metadata
|
|
4295
|
+
};
|
|
4296
|
+
}
|
|
4297
|
+
function serializePage(page, pageIndex) {
|
|
4298
|
+
return {
|
|
4299
|
+
pageIndex,
|
|
4300
|
+
body: mapEditorStateToBlocks(page.bodyState),
|
|
4301
|
+
header: mapEditorStateToContent(page.headerState),
|
|
4302
|
+
footer: mapEditorStateToContent(page.footerState)
|
|
4303
|
+
};
|
|
4304
|
+
}
|
|
4305
|
+
function buildMetadata(variableDefinitions) {
|
|
4306
|
+
const variables = {};
|
|
4307
|
+
for (const def of variableDefinitions) {
|
|
4308
|
+
variables[def.key] = {
|
|
4309
|
+
key: def.key,
|
|
4310
|
+
label: def.label,
|
|
4311
|
+
...def.description ? { description: def.description } : {},
|
|
4312
|
+
...def.valueType ? { valueType: def.valueType } : {},
|
|
4313
|
+
...def.group ? { group: def.group } : {}
|
|
4314
|
+
};
|
|
4315
|
+
}
|
|
4316
|
+
return { variables };
|
|
4317
|
+
}
|
|
4318
|
+
function buildSavePayload(ast, options) {
|
|
4319
|
+
return {
|
|
4320
|
+
document: ast,
|
|
4321
|
+
...(options == null ? void 0 : options.exportTarget) ? { exportTarget: options.exportTarget } : {},
|
|
4322
|
+
...(options == null ? void 0 : options.documentId) ? { documentId: options.documentId } : {},
|
|
4323
|
+
...(options == null ? void 0 : options.metadata) ? { metadata: options.metadata } : {}
|
|
4324
|
+
};
|
|
4325
|
+
}
|
|
4326
|
+
function serializeDocumentJson(ast) {
|
|
4327
|
+
return JSON.stringify(ast, null, 2);
|
|
4328
|
+
}
|
|
4329
|
+
function astExtension() {
|
|
4330
|
+
return {
|
|
4331
|
+
name: "ast",
|
|
4332
|
+
handleMethods: (ctx) => {
|
|
4333
|
+
const getDefinitions = () => {
|
|
4334
|
+
return ctx.getExtensionState("variableDefinitions") ?? [];
|
|
4335
|
+
};
|
|
4336
|
+
return {
|
|
4337
|
+
getDocumentAst: () => {
|
|
4338
|
+
const doc = ctx.getDocument();
|
|
4339
|
+
return serializeDocument(doc, getDefinitions());
|
|
4340
|
+
},
|
|
4341
|
+
getDocumentJson: () => {
|
|
4342
|
+
const doc = ctx.getDocument();
|
|
4343
|
+
const ast = serializeDocument(doc, getDefinitions());
|
|
4344
|
+
return serializeDocumentJson(ast);
|
|
4345
|
+
},
|
|
4346
|
+
buildSavePayload: (options) => {
|
|
4347
|
+
const doc = ctx.getDocument();
|
|
4348
|
+
const ast = serializeDocument(doc, getDefinitions());
|
|
4349
|
+
return buildSavePayload(ast, options);
|
|
4350
|
+
}
|
|
4351
|
+
};
|
|
4352
|
+
}
|
|
4353
|
+
};
|
|
4354
|
+
}
|
|
4355
|
+
class VariableNode extends DecoratorNode {
|
|
4356
|
+
constructor(variableKey, key) {
|
|
4357
|
+
super(key);
|
|
4358
|
+
__publicField(this, "__variableKey");
|
|
4359
|
+
this.__variableKey = variableKey;
|
|
4360
|
+
}
|
|
4361
|
+
static getType() {
|
|
4362
|
+
return "variable-node";
|
|
4363
|
+
}
|
|
4364
|
+
static clone(node) {
|
|
4365
|
+
return new VariableNode(node.__variableKey, node.__key);
|
|
4366
|
+
}
|
|
4367
|
+
getVariableKey() {
|
|
4368
|
+
return this.__variableKey;
|
|
4369
|
+
}
|
|
4370
|
+
// -- Serialization --
|
|
4371
|
+
static importJSON(serializedNode) {
|
|
4372
|
+
return $createVariableNode(serializedNode.variableKey);
|
|
4373
|
+
}
|
|
4374
|
+
exportJSON() {
|
|
4375
|
+
return {
|
|
4376
|
+
type: "variable-node",
|
|
4377
|
+
version: 1,
|
|
4378
|
+
variableKey: this.__variableKey
|
|
4379
|
+
};
|
|
4380
|
+
}
|
|
4381
|
+
// -- DOM --
|
|
4382
|
+
createDOM() {
|
|
4383
|
+
const span = document.createElement("span");
|
|
4384
|
+
span.className = "lex4-variable";
|
|
4385
|
+
span.setAttribute("data-variable-key", this.__variableKey);
|
|
4386
|
+
span.setAttribute("data-testid", `variable-${this.__variableKey}`);
|
|
4387
|
+
span.contentEditable = "false";
|
|
4388
|
+
return span;
|
|
4389
|
+
}
|
|
4390
|
+
updateDOM() {
|
|
4391
|
+
return false;
|
|
4392
|
+
}
|
|
4393
|
+
exportDOM() {
|
|
4394
|
+
const span = document.createElement("span");
|
|
4395
|
+
span.setAttribute("data-variable-key", this.__variableKey);
|
|
4396
|
+
span.textContent = `{{${this.__variableKey}}}`;
|
|
4397
|
+
return { element: span };
|
|
4398
|
+
}
|
|
4399
|
+
static importDOM() {
|
|
4400
|
+
return null;
|
|
4401
|
+
}
|
|
4402
|
+
// -- Behavior --
|
|
4403
|
+
isInline() {
|
|
4404
|
+
return true;
|
|
4405
|
+
}
|
|
4406
|
+
isKeyboardSelectable() {
|
|
4407
|
+
return true;
|
|
4408
|
+
}
|
|
4409
|
+
getTextContent() {
|
|
4410
|
+
return `{{${this.__variableKey}}}`;
|
|
4411
|
+
}
|
|
4412
|
+
// -- Rendering --
|
|
4413
|
+
decorate() {
|
|
4414
|
+
return /* @__PURE__ */ jsx(
|
|
4415
|
+
"span",
|
|
4416
|
+
{
|
|
4417
|
+
className: "lex4-variable-chip inline-flex items-center rounded bg-white px-1.5 py-0.5\n text-xs font-medium text-blue-700 border border-blue-300 select-none\n cursor-default whitespace-nowrap mx-0.5",
|
|
4418
|
+
"data-testid": `variable-chip-${this.__variableKey}`,
|
|
4419
|
+
title: this.__variableKey,
|
|
4420
|
+
children: `{{${this.__variableKey}}}`
|
|
4421
|
+
}
|
|
4422
|
+
);
|
|
4423
|
+
}
|
|
4424
|
+
}
|
|
4425
|
+
function $createVariableNode(variableKey) {
|
|
4426
|
+
return $applyNodeReplacement(new VariableNode(variableKey));
|
|
4427
|
+
}
|
|
4428
|
+
function $isVariableNode(node) {
|
|
4429
|
+
return node instanceof VariableNode;
|
|
4430
|
+
}
|
|
4431
|
+
const INSERT_VARIABLE_COMMAND = createCommand("INSERT_VARIABLE");
|
|
4432
|
+
const VariablePlugin = () => {
|
|
4433
|
+
const [editor] = useLexicalComposerContext();
|
|
4434
|
+
useEffect(() => {
|
|
4435
|
+
return editor.registerCommand(
|
|
4436
|
+
INSERT_VARIABLE_COMMAND,
|
|
4437
|
+
(variableKey) => {
|
|
4438
|
+
editor.update(() => {
|
|
4439
|
+
const selection = $getSelection();
|
|
4440
|
+
if (!$isRangeSelection(selection)) return;
|
|
4441
|
+
const variableNode = $createVariableNode(variableKey);
|
|
4442
|
+
$insertNodes([variableNode]);
|
|
4443
|
+
});
|
|
4444
|
+
return true;
|
|
4445
|
+
},
|
|
4446
|
+
COMMAND_PRIORITY_EDITOR
|
|
4447
|
+
);
|
|
4448
|
+
}, [editor]);
|
|
4449
|
+
return null;
|
|
4450
|
+
};
|
|
4451
|
+
const EMPTY_CONTEXT = {
|
|
4452
|
+
definitions: [],
|
|
4453
|
+
refreshDefinitions: () => {
|
|
4454
|
+
},
|
|
4455
|
+
getDefinition: () => void 0
|
|
4456
|
+
};
|
|
4457
|
+
const VariableContext = createContext(EMPTY_CONTEXT);
|
|
4458
|
+
const VariableProvider = ({
|
|
4459
|
+
initialDefinitions = [],
|
|
4460
|
+
children
|
|
4461
|
+
}) => {
|
|
4462
|
+
const [definitions, setDefinitions] = useState(initialDefinitions);
|
|
4463
|
+
const refresh = useCallback((newDefinitions) => {
|
|
4464
|
+
setDefinitions(newDefinitions);
|
|
4465
|
+
}, []);
|
|
4466
|
+
const getDefinition = useCallback(
|
|
4467
|
+
(key) => {
|
|
4468
|
+
return definitions.find((d) => d.key === key);
|
|
4469
|
+
},
|
|
4470
|
+
[definitions]
|
|
4471
|
+
);
|
|
4472
|
+
const value = useMemo(
|
|
4473
|
+
() => ({ definitions, refreshDefinitions: refresh, getDefinition }),
|
|
4474
|
+
[definitions, refresh, getDefinition]
|
|
4475
|
+
);
|
|
4476
|
+
return /* @__PURE__ */ jsx(VariableContext.Provider, { value, children });
|
|
4477
|
+
};
|
|
4478
|
+
function useVariables() {
|
|
4479
|
+
return useContext(VariableContext);
|
|
4480
|
+
}
|
|
4481
|
+
const VariablePicker = ({ onInsert, disabled = false }) => {
|
|
4482
|
+
const { definitions } = useVariables();
|
|
4483
|
+
const [open, setOpen] = useState(false);
|
|
4484
|
+
const [filter, setFilter] = useState("");
|
|
4485
|
+
const containerRef = useRef(null);
|
|
4486
|
+
const filtered = useMemo(() => {
|
|
4487
|
+
if (!filter) return definitions;
|
|
4488
|
+
const lower = filter.toLowerCase();
|
|
4489
|
+
return definitions.filter(
|
|
4490
|
+
(d) => d.key.toLowerCase().includes(lower) || d.label.toLowerCase().includes(lower)
|
|
4491
|
+
);
|
|
4492
|
+
}, [definitions, filter]);
|
|
4493
|
+
const grouped = useMemo(() => {
|
|
4494
|
+
const groups = {};
|
|
4495
|
+
for (const def of filtered) {
|
|
4496
|
+
const g = def.group ?? "Other";
|
|
4497
|
+
if (!groups[g]) groups[g] = [];
|
|
4498
|
+
groups[g].push(def);
|
|
4499
|
+
}
|
|
4500
|
+
return groups;
|
|
4501
|
+
}, [filtered]);
|
|
4502
|
+
const handleInsert = useCallback(
|
|
4503
|
+
(key) => {
|
|
4504
|
+
onInsert(key);
|
|
4505
|
+
setOpen(false);
|
|
4506
|
+
setFilter("");
|
|
4507
|
+
},
|
|
4508
|
+
[onInsert]
|
|
4509
|
+
);
|
|
4510
|
+
useEffect(() => {
|
|
4511
|
+
if (!open) return;
|
|
4512
|
+
const handler = (e) => {
|
|
4513
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
4514
|
+
setOpen(false);
|
|
4515
|
+
setFilter("");
|
|
4516
|
+
}
|
|
4517
|
+
};
|
|
4518
|
+
document.addEventListener("mousedown", handler);
|
|
4519
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
4520
|
+
}, [open]);
|
|
4521
|
+
return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative inline-block", children: [
|
|
4522
|
+
/* @__PURE__ */ jsx(
|
|
4523
|
+
"button",
|
|
4524
|
+
{
|
|
4525
|
+
type: "button",
|
|
4526
|
+
className: "h-7 rounded border border-gray-200 bg-white px-2 text-xs font-medium text-gray-700\n hover:bg-gray-50 focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400\n disabled:opacity-50 disabled:cursor-not-allowed",
|
|
4527
|
+
"data-testid": "variable-picker-button",
|
|
4528
|
+
disabled: disabled || definitions.length === 0,
|
|
4529
|
+
onClick: () => setOpen(!open),
|
|
4530
|
+
title: "Insert variable",
|
|
4531
|
+
children: "{x}"
|
|
4532
|
+
}
|
|
4533
|
+
),
|
|
4534
|
+
open && /* @__PURE__ */ jsxs(
|
|
4535
|
+
"div",
|
|
4536
|
+
{
|
|
4537
|
+
className: "absolute left-0 top-full z-50 mt-1 w-64 rounded-md border border-gray-200\n bg-white shadow-lg",
|
|
4538
|
+
"data-testid": "variable-picker-dropdown",
|
|
4539
|
+
children: [
|
|
4540
|
+
/* @__PURE__ */ jsx("div", { className: "border-b border-gray-100 p-2", children: /* @__PURE__ */ jsx(
|
|
4541
|
+
"input",
|
|
4542
|
+
{
|
|
4543
|
+
type: "text",
|
|
4544
|
+
className: "w-full rounded border border-gray-200 px-2 py-1 text-xs\n focus:border-blue-400 focus:outline-none",
|
|
4545
|
+
placeholder: "Search variables...",
|
|
4546
|
+
"data-testid": "variable-picker-search",
|
|
4547
|
+
value: filter,
|
|
4548
|
+
onChange: (e) => setFilter(e.target.value),
|
|
4549
|
+
autoFocus: true
|
|
4550
|
+
}
|
|
4551
|
+
) }),
|
|
4552
|
+
/* @__PURE__ */ jsxs("div", { className: "max-h-48 overflow-y-auto p-1", children: [
|
|
4553
|
+
Object.keys(grouped).length === 0 && /* @__PURE__ */ jsx("div", { className: "px-2 py-1 text-xs text-gray-400", children: "No variables found" }),
|
|
4554
|
+
Object.entries(grouped).map(([group, defs]) => /* @__PURE__ */ jsxs("div", { children: [
|
|
4555
|
+
/* @__PURE__ */ jsx("div", { className: "px-2 py-1 text-[10px] font-semibold uppercase text-gray-400", children: group }),
|
|
4556
|
+
defs.map((def) => /* @__PURE__ */ jsxs(
|
|
4557
|
+
"button",
|
|
4558
|
+
{
|
|
4559
|
+
type: "button",
|
|
4560
|
+
className: "flex w-full items-center gap-2 rounded px-2 py-1 text-left text-xs\n hover:bg-blue-50",
|
|
4561
|
+
"data-testid": `variable-option-${def.key}`,
|
|
4562
|
+
onClick: () => handleInsert(def.key),
|
|
4563
|
+
children: [
|
|
4564
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium text-blue-700", children: `{{${def.key}}}` }),
|
|
4565
|
+
/* @__PURE__ */ jsx("span", { className: "text-gray-500", children: def.label })
|
|
4566
|
+
]
|
|
4567
|
+
},
|
|
4568
|
+
def.key
|
|
4569
|
+
))
|
|
4570
|
+
] }, group))
|
|
4571
|
+
] })
|
|
4572
|
+
]
|
|
4573
|
+
}
|
|
4574
|
+
)
|
|
4575
|
+
] });
|
|
4576
|
+
};
|
|
4577
|
+
const VariablePanel = ({ open, onClose }) => {
|
|
4578
|
+
const { definitions } = useVariables();
|
|
4579
|
+
const { activeEditor, runHistoryAction } = useDocument();
|
|
4580
|
+
const t = useTranslations();
|
|
4581
|
+
const [filter, setFilter] = useState("");
|
|
4582
|
+
const filtered = useMemo(() => {
|
|
4583
|
+
if (!filter) return definitions;
|
|
4584
|
+
const lower = filter.toLowerCase();
|
|
4585
|
+
return definitions.filter(
|
|
4586
|
+
(d) => d.key.toLowerCase().includes(lower) || d.label.toLowerCase().includes(lower)
|
|
4587
|
+
);
|
|
4588
|
+
}, [definitions, filter]);
|
|
4589
|
+
const grouped = useMemo(() => {
|
|
4590
|
+
const groups = {};
|
|
4591
|
+
for (const def of filtered) {
|
|
4592
|
+
const g = def.group ?? "Other";
|
|
4593
|
+
if (!groups[g]) groups[g] = [];
|
|
4594
|
+
groups[g].push(def);
|
|
4595
|
+
}
|
|
4596
|
+
return groups;
|
|
4597
|
+
}, [filtered]);
|
|
4598
|
+
const handleInsert = useCallback(
|
|
4599
|
+
(key) => {
|
|
4600
|
+
if (!activeEditor) return;
|
|
4601
|
+
runHistoryAction(
|
|
4602
|
+
{ label: interpolate(t.variables.insertVariable, { key }), source: "toolbar", region: "document" },
|
|
4603
|
+
() => {
|
|
4604
|
+
activeEditor.dispatchCommand(INSERT_VARIABLE_COMMAND, key);
|
|
4605
|
+
}
|
|
4606
|
+
);
|
|
4607
|
+
},
|
|
4608
|
+
[activeEditor, runHistoryAction]
|
|
4609
|
+
);
|
|
4610
|
+
if (definitions.length === 0) return null;
|
|
4611
|
+
return /* @__PURE__ */ jsxs(
|
|
4612
|
+
EditorSidebar,
|
|
4613
|
+
{
|
|
4614
|
+
title: t.variables.title,
|
|
4615
|
+
subtitle: interpolate(t.variables.available, { count: String(definitions.length) }),
|
|
4616
|
+
open,
|
|
4617
|
+
onClose,
|
|
4618
|
+
testId: "variable-panel",
|
|
4619
|
+
headerActions: /* @__PURE__ */ jsx(
|
|
4620
|
+
"button",
|
|
4621
|
+
{
|
|
4622
|
+
type: "button",
|
|
4623
|
+
className: "flex h-6 w-6 items-center justify-center rounded text-gray-400\n transition-colors hover:bg-gray-100 hover:text-gray-600",
|
|
4624
|
+
title: t.variables.refreshVariables,
|
|
4625
|
+
"data-testid": "btn-refresh-variables",
|
|
4626
|
+
children: /* @__PURE__ */ jsx(RefreshCw, { size: 12 })
|
|
4627
|
+
}
|
|
4628
|
+
),
|
|
4629
|
+
children: [
|
|
4630
|
+
/* @__PURE__ */ jsx("div", { className: "p-3", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
4631
|
+
/* @__PURE__ */ jsx(Search, { size: 14, className: "absolute left-2.5 top-1/2 -translate-y-1/2 text-gray-400" }),
|
|
4632
|
+
/* @__PURE__ */ jsx(
|
|
4633
|
+
"input",
|
|
4634
|
+
{
|
|
4635
|
+
type: "text",
|
|
4636
|
+
className: "w-full rounded-lg border border-gray-200 bg-gray-50 py-1.5 pl-8 pr-3 text-xs\n placeholder-gray-400 focus:border-blue-400 focus:bg-white focus:outline-none\n focus:ring-1 focus:ring-blue-400",
|
|
4637
|
+
placeholder: t.variables.searchPlaceholder,
|
|
4638
|
+
"data-testid": "variable-panel-search",
|
|
4639
|
+
value: filter,
|
|
4640
|
+
onChange: (e) => setFilter(e.target.value)
|
|
4641
|
+
}
|
|
4642
|
+
)
|
|
4643
|
+
] }) }),
|
|
4644
|
+
/* @__PURE__ */ jsxs("div", { className: "px-3 pb-3", children: [
|
|
4645
|
+
Object.keys(grouped).length === 0 && /* @__PURE__ */ jsx("div", { className: "py-4 text-center text-xs text-gray-400", children: t.variables.noVariablesFound }),
|
|
4646
|
+
Object.entries(grouped).map(([group, defs]) => /* @__PURE__ */ jsx("div", { className: "mb-3", children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: defs.map((def) => /* @__PURE__ */ jsxs(
|
|
4647
|
+
"button",
|
|
4648
|
+
{
|
|
4649
|
+
type: "button",
|
|
4650
|
+
className: "flex items-center justify-between gap-2 rounded-lg px-2.5 py-1.5 text-left text-xs\n transition-colors hover:bg-blue-50 group",
|
|
4651
|
+
"data-testid": `variable-panel-${def.key}`,
|
|
4652
|
+
onClick: () => handleInsert(def.key),
|
|
4653
|
+
disabled: !activeEditor,
|
|
4654
|
+
children: [
|
|
4655
|
+
/* @__PURE__ */ jsx(
|
|
4656
|
+
"span",
|
|
4657
|
+
{
|
|
4658
|
+
className: "inline-flex items-center rounded-full border border-blue-300 bg-white\n px-2 py-0.5 text-[11px] font-medium text-blue-700\n group-hover:border-blue-400 group-hover:bg-blue-50",
|
|
4659
|
+
children: def.label
|
|
4660
|
+
}
|
|
4661
|
+
),
|
|
4662
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-gray-400", children: group })
|
|
4663
|
+
]
|
|
4664
|
+
},
|
|
4665
|
+
def.key
|
|
4666
|
+
)) }) }, group))
|
|
4667
|
+
] })
|
|
4668
|
+
]
|
|
4669
|
+
}
|
|
4670
|
+
);
|
|
4671
|
+
};
|
|
4672
|
+
const VariablePanelContext = createContext({
|
|
4673
|
+
panelOpen: false,
|
|
4674
|
+
setPanelOpen: () => {
|
|
4675
|
+
}
|
|
4676
|
+
});
|
|
4677
|
+
function useVariablePanelState() {
|
|
4678
|
+
return useContext(VariablePanelContext);
|
|
4679
|
+
}
|
|
4680
|
+
const VariablePanelStateProvider = ({ children }) => {
|
|
4681
|
+
const [panelOpen, setPanelOpen] = useState(false);
|
|
4682
|
+
return /* @__PURE__ */ jsx(VariablePanelContext.Provider, { value: { panelOpen, setPanelOpen }, children });
|
|
4683
|
+
};
|
|
4684
|
+
const VariableToolbarItem = () => {
|
|
4685
|
+
const { activeEditor, runHistoryAction } = useDocument();
|
|
4686
|
+
const t = useTranslations();
|
|
4687
|
+
const handleInsert = useCallback((variableKey) => {
|
|
4688
|
+
runHistoryAction(
|
|
4689
|
+
{ label: interpolate(t.variables.insertVariable, { key: variableKey }), source: "toolbar", region: "document" },
|
|
4690
|
+
() => {
|
|
4691
|
+
if (activeEditor) {
|
|
4692
|
+
activeEditor.dispatchCommand(INSERT_VARIABLE_COMMAND, variableKey);
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
);
|
|
4696
|
+
}, [activeEditor, runHistoryAction]);
|
|
4697
|
+
return /* @__PURE__ */ jsx(
|
|
4698
|
+
VariablePicker,
|
|
4699
|
+
{
|
|
4700
|
+
onInsert: handleInsert,
|
|
4701
|
+
disabled: !activeEditor
|
|
4702
|
+
}
|
|
4703
|
+
);
|
|
4704
|
+
};
|
|
4705
|
+
const VariablePanelToggle = () => {
|
|
4706
|
+
const { panelOpen, setPanelOpen } = useVariablePanelState();
|
|
4707
|
+
const t = useTranslations();
|
|
4708
|
+
return /* @__PURE__ */ jsx(
|
|
4709
|
+
"button",
|
|
4710
|
+
{
|
|
4711
|
+
type: "button",
|
|
4712
|
+
title: panelOpen ? t.variables.closePanel : t.variables.openPanel,
|
|
4713
|
+
"aria-label": panelOpen ? t.variables.closePanel : t.variables.openPanel,
|
|
4714
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
4715
|
+
onClick: () => setPanelOpen(!panelOpen),
|
|
4716
|
+
className: `
|
|
4717
|
+
flex h-7 w-7 items-center justify-center rounded transition-colors
|
|
4718
|
+
${panelOpen ? "bg-blue-50 text-blue-600" : "text-gray-600 hover:bg-gray-100 hover:text-gray-900"}
|
|
4719
|
+
`,
|
|
4720
|
+
"data-testid": "toggle-variable-panel",
|
|
4721
|
+
children: /* @__PURE__ */ jsx(Braces, { size: 15 })
|
|
4722
|
+
}
|
|
4723
|
+
);
|
|
4724
|
+
};
|
|
4725
|
+
const VariablePanelWithState = () => {
|
|
4726
|
+
const { panelOpen, setPanelOpen } = useVariablePanelState();
|
|
4727
|
+
return /* @__PURE__ */ jsx(VariablePanel, { open: panelOpen, onClose: () => setPanelOpen(false) });
|
|
4728
|
+
};
|
|
4729
|
+
const VariableDefinitionsSync = ({ children }) => {
|
|
4730
|
+
const { definitions } = useVariables();
|
|
4731
|
+
const extState = useExtensionState();
|
|
4732
|
+
useEffect(() => {
|
|
4733
|
+
extState.set("variableDefinitions", definitions);
|
|
4734
|
+
}, [definitions, extState]);
|
|
4735
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
4736
|
+
};
|
|
4737
|
+
function variablesExtension(definitions = []) {
|
|
4738
|
+
const ProviderWrapper = ({ children }) => {
|
|
4739
|
+
return /* @__PURE__ */ jsx(VariablePanelStateProvider, { children: /* @__PURE__ */ jsx(VariableProvider, { initialDefinitions: definitions, children: /* @__PURE__ */ jsx(VariableDefinitionsSync, { children }) }) });
|
|
4740
|
+
};
|
|
4741
|
+
return {
|
|
4742
|
+
name: "variables",
|
|
4743
|
+
nodes: [VariableNode],
|
|
4744
|
+
bodyPlugins: [VariablePlugin],
|
|
4745
|
+
toolbarItems: [VariableToolbarItem, VariablePanelToggle],
|
|
4746
|
+
sidePanel: VariablePanelWithState,
|
|
4747
|
+
provider: ProviderWrapper,
|
|
4748
|
+
handleMethods: (ctx) => ({
|
|
4749
|
+
insertVariable: (key) => {
|
|
4750
|
+
const editor = ctx.getActiveEditor();
|
|
4751
|
+
if (editor) {
|
|
4752
|
+
editor.dispatchCommand(INSERT_VARIABLE_COMMAND, key);
|
|
4753
|
+
}
|
|
4754
|
+
},
|
|
4755
|
+
refreshVariables: (newDefs) => {
|
|
4756
|
+
ctx.setExtensionState("variableDefinitions", newDefs);
|
|
4757
|
+
}
|
|
4758
|
+
})
|
|
4759
|
+
};
|
|
4760
|
+
}
|
|
3760
4761
|
export {
|
|
4762
|
+
$createVariableNode,
|
|
4763
|
+
$isVariableNode,
|
|
3761
4764
|
A4_HEIGHT_MM,
|
|
3762
4765
|
A4_HEIGHT_PX,
|
|
3763
4766
|
A4_WIDTH_MM,
|
|
3764
4767
|
A4_WIDTH_PX,
|
|
4768
|
+
AST_VERSION,
|
|
4769
|
+
DEFAULT_TRANSLATIONS,
|
|
4770
|
+
INSERT_VARIABLE_COMMAND,
|
|
3765
4771
|
Lex4Editor,
|
|
3766
4772
|
MAX_FOOTER_HEIGHT_PX,
|
|
3767
4773
|
MAX_HEADER_HEIGHT_PX,
|
|
4774
|
+
VariableNode,
|
|
4775
|
+
astExtension,
|
|
4776
|
+
buildSavePayload,
|
|
3768
4777
|
createEmptyDocument,
|
|
3769
4778
|
createEmptyPage,
|
|
4779
|
+
serializeDocument,
|
|
4780
|
+
serializeDocumentJson,
|
|
3770
4781
|
useHeaderFooter,
|
|
3771
4782
|
useOverflowDetection,
|
|
3772
|
-
usePagination
|
|
4783
|
+
usePagination,
|
|
4784
|
+
variablesExtension
|
|
3773
4785
|
};
|
|
3774
4786
|
//# sourceMappingURL=lex4-editor.js.map
|