@stll/folio-react 0.0.1-placeholder.0 → 0.1.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/LICENSE +202 -0
- package/NOTICE.md +29 -0
- package/README.md +64 -0
- package/dist/CommentsSidebar-BsYOInaX.js +823 -0
- package/dist/FindReplaceDialog-CG1VKA_g.js +358 -0
- package/dist/FootnotePropertiesDialog-MuW4D3gt.js +265 -0
- package/dist/ImagePositionDialog-BQnOnDf4.js +393 -0
- package/dist/ImagePropertiesDialog-Dn4N7qZv.js +196 -0
- package/dist/PageSetupDialog-CxJUEkoC.js +310 -0
- package/dist/TablePropertiesDialog-mQMnGfHu.js +157 -0
- package/dist/TextContextMenu-DzTbp2sn.js +313 -0
- package/dist/contained-handler-4uiqUVRn.js +45 -0
- package/dist/editor.css +1 -0
- package/dist/folio-ui-o_rftArH.js +483 -0
- package/dist/index.d.ts +1055 -0
- package/dist/index.js +12289 -0
- package/dist/messages.d.ts +13 -0
- package/dist/messages.js +2167 -0
- package/dist/useFindReplace-MZ8wi31A.js +354 -0
- package/package.json +83 -2
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import { r as useFolioUI } from "./folio-ui-o_rftArH.js";
|
|
2
|
+
import "./useFindReplace-MZ8wi31A.js";
|
|
3
|
+
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
+
import { ChevronDownIcon, ChevronUpIcon, SearchIcon, XIcon } from "lucide-react";
|
|
5
|
+
import { useTranslations } from "use-intl";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import "@stll/folio-core/managers/FindReplaceManager";
|
|
8
|
+
//#region src/components/dialogs/findReplaceDialogBehavior.ts
|
|
9
|
+
const getFindDialogOpenBehavior = ({ isOpen, initialSearchText }) => {
|
|
10
|
+
if (!isOpen) return {
|
|
11
|
+
type: "closed",
|
|
12
|
+
shouldClearHighlights: true
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
type: "open",
|
|
16
|
+
searchText: initialSearchText,
|
|
17
|
+
shouldFindInitialText: initialSearchText.length > 0
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
const shouldRefreshFindDialogSearch = ({ isOpen, searchText }) => isOpen && searchText.trim().length > 0;
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/components/dialogs/findReplaceDialogLayout.ts
|
|
23
|
+
const FIND_REPLACE_DIALOG_TOP = "var(--folio-find-replace-top, 7rem)";
|
|
24
|
+
const FIND_REPLACE_DIALOG_LEFT = "var(--folio-find-replace-left, 5.5rem)";
|
|
25
|
+
const FIND_REPLACE_DIALOG_RIGHT = "var(--folio-find-replace-right, 0px)";
|
|
26
|
+
function getFindReplaceOverlayStyle(style) {
|
|
27
|
+
return {
|
|
28
|
+
top: FIND_REPLACE_DIALOG_TOP,
|
|
29
|
+
left: FIND_REPLACE_DIALOG_LEFT,
|
|
30
|
+
right: FIND_REPLACE_DIALOG_RIGHT,
|
|
31
|
+
...style
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/components/dialogs/findReplaceInteraction.ts
|
|
36
|
+
function getFindEnterAction({ searchText, result, shiftKey }) {
|
|
37
|
+
if (!searchText.trim() || !result || result.totalCount === 0) return "search";
|
|
38
|
+
return shiftKey ? "previous" : "next";
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/components/dialogs/FindReplaceDialog.tsx
|
|
42
|
+
/**
|
|
43
|
+
* Find and Replace Dialog Component
|
|
44
|
+
*
|
|
45
|
+
* Modal dialog for searching and replacing text in the document.
|
|
46
|
+
* Supports find, find next/previous, replace, and replace all operations.
|
|
47
|
+
*
|
|
48
|
+
* Logic and utilities are in separate files:
|
|
49
|
+
* - findReplaceUtils.ts — Pure search/replace functions and types
|
|
50
|
+
* - useFindReplace.ts — React hook for dialog state management
|
|
51
|
+
*/
|
|
52
|
+
/**
|
|
53
|
+
* FindReplaceDialog component - Modal for finding and replacing text
|
|
54
|
+
*/
|
|
55
|
+
function FindReplaceDialog({ isOpen, onClose, onFind, onFindNext, onFindPrevious, onHighlightMatches, onClearHighlights, initialSearchText = "", currentResult, className, style }) {
|
|
56
|
+
const id = React.useId();
|
|
57
|
+
const t = useTranslations("folio");
|
|
58
|
+
const { Button, Input, Checkbox } = useFolioUI();
|
|
59
|
+
const [searchText, setSearchText] = useState("");
|
|
60
|
+
const [matchCase, setMatchCase] = useState(false);
|
|
61
|
+
const [matchWholeWord, setMatchWholeWord] = useState(false);
|
|
62
|
+
const [result, setResult] = useState(null);
|
|
63
|
+
const searchInputRef = useRef(null);
|
|
64
|
+
const latestFindCallbacksRef = useRef({
|
|
65
|
+
onFind,
|
|
66
|
+
onHighlightMatches,
|
|
67
|
+
onClearHighlights
|
|
68
|
+
});
|
|
69
|
+
latestFindCallbacksRef.current = {
|
|
70
|
+
onFind,
|
|
71
|
+
onHighlightMatches,
|
|
72
|
+
onClearHighlights
|
|
73
|
+
};
|
|
74
|
+
const latestFindOptionsRef = useRef({
|
|
75
|
+
matchCase,
|
|
76
|
+
matchWholeWord
|
|
77
|
+
});
|
|
78
|
+
latestFindOptionsRef.current = {
|
|
79
|
+
matchCase,
|
|
80
|
+
matchWholeWord
|
|
81
|
+
};
|
|
82
|
+
const latestSearchStateRef = useRef({
|
|
83
|
+
isOpen,
|
|
84
|
+
searchText
|
|
85
|
+
});
|
|
86
|
+
latestSearchStateRef.current = {
|
|
87
|
+
isOpen,
|
|
88
|
+
searchText
|
|
89
|
+
};
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (currentResult !== void 0) setResult(currentResult);
|
|
92
|
+
}, [currentResult]);
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
const behavior = getFindDialogOpenBehavior({
|
|
95
|
+
isOpen,
|
|
96
|
+
initialSearchText
|
|
97
|
+
});
|
|
98
|
+
if (behavior.type === "closed") {
|
|
99
|
+
latestFindCallbacksRef.current.onClearHighlights?.();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
setSearchText(behavior.searchText);
|
|
103
|
+
setResult(null);
|
|
104
|
+
setTimeout(() => {
|
|
105
|
+
searchInputRef.current?.focus();
|
|
106
|
+
searchInputRef.current?.select();
|
|
107
|
+
}, 100);
|
|
108
|
+
if (behavior.shouldFindInitialText) {
|
|
109
|
+
const callbacks = latestFindCallbacksRef.current;
|
|
110
|
+
const options = latestFindOptionsRef.current;
|
|
111
|
+
const searchResult = callbacks.onFind(behavior.searchText, {
|
|
112
|
+
matchCase: options.matchCase,
|
|
113
|
+
matchWholeWord: options.matchWholeWord
|
|
114
|
+
});
|
|
115
|
+
setResult(searchResult);
|
|
116
|
+
if (searchResult?.matches && callbacks.onHighlightMatches) callbacks.onHighlightMatches(searchResult.matches);
|
|
117
|
+
}
|
|
118
|
+
}, [isOpen, initialSearchText]);
|
|
119
|
+
const performSearch = useCallback(() => {
|
|
120
|
+
if (!searchText.trim()) {
|
|
121
|
+
setResult(null);
|
|
122
|
+
if (onClearHighlights) onClearHighlights();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const searchResult = onFind(searchText, {
|
|
126
|
+
matchCase,
|
|
127
|
+
matchWholeWord
|
|
128
|
+
});
|
|
129
|
+
setResult(searchResult);
|
|
130
|
+
if (searchResult?.matches && onHighlightMatches) onHighlightMatches(searchResult.matches);
|
|
131
|
+
else if (onClearHighlights) onClearHighlights();
|
|
132
|
+
}, [
|
|
133
|
+
searchText,
|
|
134
|
+
matchCase,
|
|
135
|
+
matchWholeWord,
|
|
136
|
+
onFind,
|
|
137
|
+
onHighlightMatches,
|
|
138
|
+
onClearHighlights
|
|
139
|
+
]);
|
|
140
|
+
const performSearchRef = useRef(performSearch);
|
|
141
|
+
performSearchRef.current = performSearch;
|
|
142
|
+
useEffect(() => {
|
|
143
|
+
if (shouldRefreshFindDialogSearch(latestSearchStateRef.current)) performSearchRef.current();
|
|
144
|
+
}, [matchCase, matchWholeWord]);
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (!isOpen) return;
|
|
147
|
+
if (!searchText.trim()) {
|
|
148
|
+
setResult(null);
|
|
149
|
+
onClearHighlights?.();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const timeout = setTimeout(performSearch, 120);
|
|
153
|
+
return () => clearTimeout(timeout);
|
|
154
|
+
}, [
|
|
155
|
+
isOpen,
|
|
156
|
+
searchText,
|
|
157
|
+
performSearch,
|
|
158
|
+
onClearHighlights
|
|
159
|
+
]);
|
|
160
|
+
const handleSearchChange = useCallback((e) => {
|
|
161
|
+
setSearchText(e.target.value);
|
|
162
|
+
setResult(null);
|
|
163
|
+
}, []);
|
|
164
|
+
const handleFindNext = useCallback(() => {
|
|
165
|
+
if (!searchText.trim()) {
|
|
166
|
+
performSearch();
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (!result || result.totalCount === 0) {
|
|
170
|
+
performSearch();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (onFindNext()) {
|
|
174
|
+
const newIndex = (result.currentIndex + 1) % result.totalCount;
|
|
175
|
+
setResult({
|
|
176
|
+
...result,
|
|
177
|
+
currentIndex: newIndex
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}, [
|
|
181
|
+
searchText,
|
|
182
|
+
result,
|
|
183
|
+
performSearch,
|
|
184
|
+
onFindNext
|
|
185
|
+
]);
|
|
186
|
+
const handleFindPrevious = useCallback(() => {
|
|
187
|
+
if (!searchText.trim()) {
|
|
188
|
+
performSearch();
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
if (!result || result.totalCount === 0) {
|
|
192
|
+
performSearch();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (onFindPrevious()) {
|
|
196
|
+
const newIndex = result.currentIndex === 0 ? result.totalCount - 1 : result.currentIndex - 1;
|
|
197
|
+
setResult({
|
|
198
|
+
...result,
|
|
199
|
+
currentIndex: newIndex
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}, [
|
|
203
|
+
searchText,
|
|
204
|
+
result,
|
|
205
|
+
performSearch,
|
|
206
|
+
onFindPrevious
|
|
207
|
+
]);
|
|
208
|
+
const handleSearchKeyDown = useCallback((e) => {
|
|
209
|
+
if (e.key === "Enter") {
|
|
210
|
+
e.preventDefault();
|
|
211
|
+
const action = getFindEnterAction({
|
|
212
|
+
searchText,
|
|
213
|
+
result,
|
|
214
|
+
shiftKey: e.shiftKey
|
|
215
|
+
});
|
|
216
|
+
if (action === "search") {
|
|
217
|
+
performSearch();
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (action === "previous") {
|
|
221
|
+
handleFindPrevious();
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
handleFindNext();
|
|
225
|
+
} else if (e.key === "Escape") onClose();
|
|
226
|
+
}, [
|
|
227
|
+
searchText,
|
|
228
|
+
result,
|
|
229
|
+
performSearch,
|
|
230
|
+
handleFindPrevious,
|
|
231
|
+
handleFindNext,
|
|
232
|
+
onClose
|
|
233
|
+
]);
|
|
234
|
+
const handleOverlayClick = useCallback((e) => {
|
|
235
|
+
if (e.target === e.currentTarget) {}
|
|
236
|
+
}, []);
|
|
237
|
+
const handleDialogKeyDown = useCallback((e) => {
|
|
238
|
+
if (e.key === "Escape") onClose();
|
|
239
|
+
}, [onClose]);
|
|
240
|
+
if (!isOpen) return null;
|
|
241
|
+
const hasMatches = result && result.totalCount > 0;
|
|
242
|
+
const noMatches = result && result.totalCount === 0 && searchText.trim();
|
|
243
|
+
const overlayStyle = getFindReplaceOverlayStyle(style);
|
|
244
|
+
const titleId = `${id}-find-replace-dialog-title`;
|
|
245
|
+
const findTextId = `${id}-find-text`;
|
|
246
|
+
return /* @__PURE__ */ jsx("div", {
|
|
247
|
+
role: "presentation",
|
|
248
|
+
className: `docx-find-replace-dialog-overlay pointer-events-none fixed end-0 bottom-0 z-[10002] flex items-start justify-end bg-transparent ${className || ""}`,
|
|
249
|
+
style: overlayStyle,
|
|
250
|
+
"data-slot": "folio-find-replace-overlay",
|
|
251
|
+
onClick: handleOverlayClick,
|
|
252
|
+
onKeyDown: handleDialogKeyDown,
|
|
253
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
254
|
+
className: "docx-find-replace-dialog bg-popover text-popover-foreground pointer-events-auto me-4 mt-3 w-[min(440px,calc(100vw-var(--folio-find-replace-left,5.5rem)-2rem))] rounded-lg border shadow-xl",
|
|
255
|
+
"data-testid": "find-replace-dialog",
|
|
256
|
+
role: "dialog",
|
|
257
|
+
"aria-modal": "false",
|
|
258
|
+
"aria-labelledby": titleId,
|
|
259
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
260
|
+
className: "bg-muted/30 flex items-center justify-between gap-3 border-b px-3 py-2",
|
|
261
|
+
children: [/* @__PURE__ */ jsxs("h2", {
|
|
262
|
+
className: "flex min-w-0 items-center gap-2 text-sm font-medium",
|
|
263
|
+
id: titleId,
|
|
264
|
+
children: [/* @__PURE__ */ jsx(SearchIcon, { className: "text-muted-foreground size-4 shrink-0" }), /* @__PURE__ */ jsx("span", {
|
|
265
|
+
className: "truncate",
|
|
266
|
+
children: t("findReplace.find")
|
|
267
|
+
})]
|
|
268
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
269
|
+
onClick: onClose,
|
|
270
|
+
"aria-label": t("findReplace.close"),
|
|
271
|
+
size: "icon-xs",
|
|
272
|
+
title: t("findReplace.close"),
|
|
273
|
+
variant: "ghost",
|
|
274
|
+
children: /* @__PURE__ */ jsx(XIcon, {})
|
|
275
|
+
})]
|
|
276
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
277
|
+
className: "space-y-2 p-3",
|
|
278
|
+
children: [
|
|
279
|
+
/* @__PURE__ */ jsxs("div", {
|
|
280
|
+
className: "grid grid-cols-[4.5rem_minmax(0,1fr)_auto] items-center gap-2",
|
|
281
|
+
children: [
|
|
282
|
+
/* @__PURE__ */ jsx("label", {
|
|
283
|
+
className: "text-muted-foreground text-xs font-medium",
|
|
284
|
+
htmlFor: findTextId,
|
|
285
|
+
children: t("findReplace.find")
|
|
286
|
+
}),
|
|
287
|
+
/* @__PURE__ */ jsx(Input, {
|
|
288
|
+
ref: searchInputRef,
|
|
289
|
+
id: findTextId,
|
|
290
|
+
nativeInput: true,
|
|
291
|
+
type: "text",
|
|
292
|
+
className: "h-8",
|
|
293
|
+
size: "sm",
|
|
294
|
+
value: searchText,
|
|
295
|
+
onChange: handleSearchChange,
|
|
296
|
+
onKeyDown: handleSearchKeyDown,
|
|
297
|
+
onBlur: () => {
|
|
298
|
+
if (searchText.trim() && !result) performSearch();
|
|
299
|
+
},
|
|
300
|
+
placeholder: t("findReplace.findPlaceholder"),
|
|
301
|
+
"aria-label": t("findReplace.findText")
|
|
302
|
+
}),
|
|
303
|
+
/* @__PURE__ */ jsxs("div", {
|
|
304
|
+
className: "flex items-center gap-0.5",
|
|
305
|
+
children: [/* @__PURE__ */ jsx(Button, {
|
|
306
|
+
onClick: handleFindPrevious,
|
|
307
|
+
disabled: !hasMatches,
|
|
308
|
+
"aria-label": t("findReplace.previous"),
|
|
309
|
+
title: t("findReplace.previousShortcut"),
|
|
310
|
+
size: "icon-xs",
|
|
311
|
+
variant: "ghost",
|
|
312
|
+
children: /* @__PURE__ */ jsx(ChevronUpIcon, {})
|
|
313
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
314
|
+
onClick: handleFindNext,
|
|
315
|
+
disabled: !hasMatches,
|
|
316
|
+
"aria-label": t("findReplace.next"),
|
|
317
|
+
title: t("findReplace.nextShortcut"),
|
|
318
|
+
size: "icon-xs",
|
|
319
|
+
variant: "ghost",
|
|
320
|
+
children: /* @__PURE__ */ jsx(ChevronDownIcon, {})
|
|
321
|
+
})]
|
|
322
|
+
})
|
|
323
|
+
]
|
|
324
|
+
}),
|
|
325
|
+
hasMatches && /* @__PURE__ */ jsx("div", {
|
|
326
|
+
className: "text-muted-foreground ms-20 text-xs tabular-nums",
|
|
327
|
+
children: t("findReplace.matchCounter", {
|
|
328
|
+
current: String(result.currentIndex + 1),
|
|
329
|
+
total: String(result.totalCount)
|
|
330
|
+
})
|
|
331
|
+
}),
|
|
332
|
+
noMatches && /* @__PURE__ */ jsx("div", {
|
|
333
|
+
className: "text-destructive ms-20 text-xs",
|
|
334
|
+
children: t("findReplace.noResults")
|
|
335
|
+
}),
|
|
336
|
+
/* @__PURE__ */ jsxs("div", {
|
|
337
|
+
className: "ms-20 flex flex-wrap items-center gap-x-4 gap-y-2",
|
|
338
|
+
children: [/* @__PURE__ */ jsxs("label", {
|
|
339
|
+
className: "text-muted-foreground flex items-center gap-2 text-xs",
|
|
340
|
+
children: [/* @__PURE__ */ jsx(Checkbox, {
|
|
341
|
+
checked: matchCase,
|
|
342
|
+
onCheckedChange: setMatchCase
|
|
343
|
+
}), t("findReplace.matchCase")]
|
|
344
|
+
}), /* @__PURE__ */ jsxs("label", {
|
|
345
|
+
className: "text-muted-foreground flex items-center gap-2 text-xs",
|
|
346
|
+
children: [/* @__PURE__ */ jsx(Checkbox, {
|
|
347
|
+
checked: matchWholeWord,
|
|
348
|
+
onCheckedChange: setMatchWholeWord
|
|
349
|
+
}), t("findReplace.wholeWords")]
|
|
350
|
+
})]
|
|
351
|
+
})
|
|
352
|
+
]
|
|
353
|
+
})]
|
|
354
|
+
})
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
//#endregion
|
|
358
|
+
export { FindReplaceDialog };
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { r as useFolioUI } from "./folio-ui-o_rftArH.js";
|
|
2
|
+
import { useId, useState } from "react";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
//#region src/components/dialogs/FootnotePropertiesDialog.tsx
|
|
5
|
+
/**
|
|
6
|
+
* Footnote & Endnote Properties Dialog
|
|
7
|
+
*
|
|
8
|
+
* Edits position, numbering format, start number, and restart rules.
|
|
9
|
+
*/
|
|
10
|
+
const numberFormatOptions = [
|
|
11
|
+
{
|
|
12
|
+
value: "decimal",
|
|
13
|
+
label: "1, 2, 3, ..."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
value: "lowerRoman",
|
|
17
|
+
label: "i, ii, iii, ..."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
value: "upperRoman",
|
|
21
|
+
label: "I, II, III, ..."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
value: "lowerLetter",
|
|
25
|
+
label: "a, b, c, ..."
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
value: "upperLetter",
|
|
29
|
+
label: "A, B, C, ..."
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
value: "chicago",
|
|
33
|
+
label: "*, †, ‡, ..."
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
function FootnotePropertiesDialog({ isOpen, onClose, onApply, footnotePr, endnotePr }) {
|
|
37
|
+
const { Root: Dialog, Portal: DialogPortal, Backdrop: DialogBackdrop, Popup: DialogPopup, Title: DialogTitle, Close: DialogClose } = useFolioUI().Dialog;
|
|
38
|
+
const id = useId();
|
|
39
|
+
const [fnPosition, setFnPosition] = useState(footnotePr?.position ?? "pageBottom");
|
|
40
|
+
const [fnNumFmt, setFnNumFmt] = useState(footnotePr?.numFmt ?? "decimal");
|
|
41
|
+
const [fnNumStart, setFnNumStart] = useState(footnotePr?.numStart ?? 1);
|
|
42
|
+
const [fnRestart, setFnRestart] = useState(footnotePr?.numRestart ?? "continuous");
|
|
43
|
+
const [enPosition, setEnPosition] = useState(endnotePr?.position ?? "docEnd");
|
|
44
|
+
const [enNumFmt, setEnNumFmt] = useState(endnotePr?.numFmt ?? "lowerRoman");
|
|
45
|
+
const [enNumStart, setEnNumStart] = useState(endnotePr?.numStart ?? 1);
|
|
46
|
+
const [enRestart, setEnRestart] = useState(endnotePr?.numRestart ?? "continuous");
|
|
47
|
+
const handleApply = () => {
|
|
48
|
+
onApply({
|
|
49
|
+
position: fnPosition,
|
|
50
|
+
numFmt: fnNumFmt,
|
|
51
|
+
numStart: fnNumStart,
|
|
52
|
+
numRestart: fnRestart
|
|
53
|
+
}, {
|
|
54
|
+
position: enPosition,
|
|
55
|
+
numFmt: enNumFmt,
|
|
56
|
+
numStart: enNumStart,
|
|
57
|
+
numRestart: enRestart
|
|
58
|
+
});
|
|
59
|
+
onClose();
|
|
60
|
+
};
|
|
61
|
+
const labelCls = "block text-muted-foreground mb-1 text-xs";
|
|
62
|
+
const selectCls = "border-input bg-background text-foreground mb-2 w-full rounded border px-2 py-1 text-[13px] outline-none";
|
|
63
|
+
const inputCls = "border-input bg-background text-foreground w-[60px] rounded border px-2 py-1 text-[13px] outline-none";
|
|
64
|
+
const sectionCls = "mb-4 rounded border p-3";
|
|
65
|
+
const fieldIds = {
|
|
66
|
+
fnPosition: `${id}-fn-position`,
|
|
67
|
+
fnNumFmt: `${id}-fn-num-fmt`,
|
|
68
|
+
fnStartAt: `${id}-fn-start-at`,
|
|
69
|
+
fnNumbering: `${id}-fn-numbering`,
|
|
70
|
+
enPosition: `${id}-en-position`,
|
|
71
|
+
enNumFmt: `${id}-en-num-fmt`,
|
|
72
|
+
enStartAt: `${id}-en-start-at`,
|
|
73
|
+
enNumbering: `${id}-en-numbering`
|
|
74
|
+
};
|
|
75
|
+
return /* @__PURE__ */ jsx(Dialog, {
|
|
76
|
+
open: isOpen,
|
|
77
|
+
onOpenChange: (open) => {
|
|
78
|
+
if (!open) onClose();
|
|
79
|
+
},
|
|
80
|
+
children: /* @__PURE__ */ jsxs(DialogPortal, { children: [/* @__PURE__ */ jsx(DialogBackdrop, { className: "fixed inset-0 z-[10000] bg-black/50" }), /* @__PURE__ */ jsxs(DialogPopup, {
|
|
81
|
+
className: "bg-popover fixed start-1/2 top-1/2 z-[10001] w-full max-w-[500px] min-w-[400px] -translate-x-1/2 -translate-y-1/2 rounded-lg border shadow-xl",
|
|
82
|
+
children: [
|
|
83
|
+
/* @__PURE__ */ jsx(DialogTitle, {
|
|
84
|
+
className: "border-b px-5 py-3 text-base font-semibold",
|
|
85
|
+
children: "Footnote & Endnote Properties"
|
|
86
|
+
}),
|
|
87
|
+
/* @__PURE__ */ jsxs("div", {
|
|
88
|
+
className: "flex flex-col gap-3 px-5 py-4",
|
|
89
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
90
|
+
className: sectionCls,
|
|
91
|
+
children: [
|
|
92
|
+
/* @__PURE__ */ jsx("h4", {
|
|
93
|
+
className: "mb-2 text-sm font-semibold",
|
|
94
|
+
children: "Footnotes"
|
|
95
|
+
}),
|
|
96
|
+
/* @__PURE__ */ jsx("label", {
|
|
97
|
+
htmlFor: fieldIds.fnPosition,
|
|
98
|
+
className: labelCls,
|
|
99
|
+
children: "Position"
|
|
100
|
+
}),
|
|
101
|
+
/* @__PURE__ */ jsxs("select", {
|
|
102
|
+
id: fieldIds.fnPosition,
|
|
103
|
+
className: selectCls,
|
|
104
|
+
value: fnPosition,
|
|
105
|
+
onChange: (e) => setFnPosition(e.target.value),
|
|
106
|
+
children: [/* @__PURE__ */ jsx("option", {
|
|
107
|
+
value: "pageBottom",
|
|
108
|
+
children: "Bottom of page"
|
|
109
|
+
}), /* @__PURE__ */ jsx("option", {
|
|
110
|
+
value: "beneathText",
|
|
111
|
+
children: "Below text"
|
|
112
|
+
})]
|
|
113
|
+
}),
|
|
114
|
+
/* @__PURE__ */ jsx("label", {
|
|
115
|
+
htmlFor: fieldIds.fnNumFmt,
|
|
116
|
+
className: labelCls,
|
|
117
|
+
children: "Number format"
|
|
118
|
+
}),
|
|
119
|
+
/* @__PURE__ */ jsx("select", {
|
|
120
|
+
id: fieldIds.fnNumFmt,
|
|
121
|
+
className: selectCls,
|
|
122
|
+
value: fnNumFmt,
|
|
123
|
+
onChange: (e) => setFnNumFmt(e.target.value),
|
|
124
|
+
children: numberFormatOptions.map((o) => /* @__PURE__ */ jsx("option", {
|
|
125
|
+
value: o.value,
|
|
126
|
+
children: o.label
|
|
127
|
+
}, o.value))
|
|
128
|
+
}),
|
|
129
|
+
/* @__PURE__ */ jsxs("div", {
|
|
130
|
+
className: "flex items-center gap-3",
|
|
131
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
132
|
+
htmlFor: fieldIds.fnStartAt,
|
|
133
|
+
className: labelCls,
|
|
134
|
+
children: "Start at"
|
|
135
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
136
|
+
id: fieldIds.fnStartAt,
|
|
137
|
+
type: "number",
|
|
138
|
+
min: 1,
|
|
139
|
+
className: inputCls,
|
|
140
|
+
value: fnNumStart,
|
|
141
|
+
onChange: (e) => setFnNumStart(Number.parseInt(e.target.value, 10) || 1)
|
|
142
|
+
})] }), /* @__PURE__ */ jsxs("div", {
|
|
143
|
+
className: "flex-1",
|
|
144
|
+
children: [/* @__PURE__ */ jsx("label", {
|
|
145
|
+
htmlFor: fieldIds.fnNumbering,
|
|
146
|
+
className: labelCls,
|
|
147
|
+
children: "Numbering"
|
|
148
|
+
}), /* @__PURE__ */ jsxs("select", {
|
|
149
|
+
id: fieldIds.fnNumbering,
|
|
150
|
+
className: selectCls,
|
|
151
|
+
value: fnRestart,
|
|
152
|
+
onChange: (e) => setFnRestart(e.target.value),
|
|
153
|
+
children: [
|
|
154
|
+
/* @__PURE__ */ jsx("option", {
|
|
155
|
+
value: "continuous",
|
|
156
|
+
children: "Continuous"
|
|
157
|
+
}),
|
|
158
|
+
/* @__PURE__ */ jsx("option", {
|
|
159
|
+
value: "eachSect",
|
|
160
|
+
children: "Restart each section"
|
|
161
|
+
}),
|
|
162
|
+
/* @__PURE__ */ jsx("option", {
|
|
163
|
+
value: "eachPage",
|
|
164
|
+
children: "Restart each page"
|
|
165
|
+
})
|
|
166
|
+
]
|
|
167
|
+
})]
|
|
168
|
+
})]
|
|
169
|
+
})
|
|
170
|
+
]
|
|
171
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
172
|
+
className: sectionCls,
|
|
173
|
+
children: [
|
|
174
|
+
/* @__PURE__ */ jsx("h4", {
|
|
175
|
+
className: "mb-2 text-sm font-semibold",
|
|
176
|
+
children: "Endnotes"
|
|
177
|
+
}),
|
|
178
|
+
/* @__PURE__ */ jsx("label", {
|
|
179
|
+
htmlFor: fieldIds.enPosition,
|
|
180
|
+
className: labelCls,
|
|
181
|
+
children: "Position"
|
|
182
|
+
}),
|
|
183
|
+
/* @__PURE__ */ jsxs("select", {
|
|
184
|
+
id: fieldIds.enPosition,
|
|
185
|
+
className: selectCls,
|
|
186
|
+
value: enPosition,
|
|
187
|
+
onChange: (e) => setEnPosition(e.target.value),
|
|
188
|
+
children: [/* @__PURE__ */ jsx("option", {
|
|
189
|
+
value: "docEnd",
|
|
190
|
+
children: "End of document"
|
|
191
|
+
}), /* @__PURE__ */ jsx("option", {
|
|
192
|
+
value: "sectEnd",
|
|
193
|
+
children: "End of section"
|
|
194
|
+
})]
|
|
195
|
+
}),
|
|
196
|
+
/* @__PURE__ */ jsx("label", {
|
|
197
|
+
htmlFor: fieldIds.enNumFmt,
|
|
198
|
+
className: labelCls,
|
|
199
|
+
children: "Number format"
|
|
200
|
+
}),
|
|
201
|
+
/* @__PURE__ */ jsx("select", {
|
|
202
|
+
id: fieldIds.enNumFmt,
|
|
203
|
+
className: selectCls,
|
|
204
|
+
value: enNumFmt,
|
|
205
|
+
onChange: (e) => setEnNumFmt(e.target.value),
|
|
206
|
+
children: numberFormatOptions.map((o) => /* @__PURE__ */ jsx("option", {
|
|
207
|
+
value: o.value,
|
|
208
|
+
children: o.label
|
|
209
|
+
}, o.value))
|
|
210
|
+
}),
|
|
211
|
+
/* @__PURE__ */ jsxs("div", {
|
|
212
|
+
className: "flex items-center gap-3",
|
|
213
|
+
children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
|
|
214
|
+
htmlFor: fieldIds.enStartAt,
|
|
215
|
+
className: labelCls,
|
|
216
|
+
children: "Start at"
|
|
217
|
+
}), /* @__PURE__ */ jsx("input", {
|
|
218
|
+
id: fieldIds.enStartAt,
|
|
219
|
+
type: "number",
|
|
220
|
+
min: 1,
|
|
221
|
+
className: inputCls,
|
|
222
|
+
value: enNumStart,
|
|
223
|
+
onChange: (e) => setEnNumStart(Number.parseInt(e.target.value, 10) || 1)
|
|
224
|
+
})] }), /* @__PURE__ */ jsxs("div", {
|
|
225
|
+
className: "flex-1",
|
|
226
|
+
children: [/* @__PURE__ */ jsx("label", {
|
|
227
|
+
htmlFor: fieldIds.enNumbering,
|
|
228
|
+
className: labelCls,
|
|
229
|
+
children: "Numbering"
|
|
230
|
+
}), /* @__PURE__ */ jsxs("select", {
|
|
231
|
+
id: fieldIds.enNumbering,
|
|
232
|
+
className: selectCls,
|
|
233
|
+
value: enRestart,
|
|
234
|
+
onChange: (e) => setEnRestart(e.target.value),
|
|
235
|
+
children: [/* @__PURE__ */ jsx("option", {
|
|
236
|
+
value: "continuous",
|
|
237
|
+
children: "Continuous"
|
|
238
|
+
}), /* @__PURE__ */ jsx("option", {
|
|
239
|
+
value: "eachSect",
|
|
240
|
+
children: "Restart each section"
|
|
241
|
+
})]
|
|
242
|
+
})]
|
|
243
|
+
})]
|
|
244
|
+
})
|
|
245
|
+
]
|
|
246
|
+
})]
|
|
247
|
+
}),
|
|
248
|
+
/* @__PURE__ */ jsxs("div", {
|
|
249
|
+
className: "flex justify-end gap-2 border-t px-5 py-3",
|
|
250
|
+
children: [/* @__PURE__ */ jsx(DialogClose, {
|
|
251
|
+
className: "border-input rounded border px-4 py-1.5 text-[13px]",
|
|
252
|
+
children: "Cancel"
|
|
253
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
254
|
+
className: "bg-primary text-primary-foreground rounded px-4 py-1.5 text-[13px] font-medium",
|
|
255
|
+
onClick: handleApply,
|
|
256
|
+
type: "button",
|
|
257
|
+
children: "Apply"
|
|
258
|
+
})]
|
|
259
|
+
})
|
|
260
|
+
]
|
|
261
|
+
})] })
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
//#endregion
|
|
265
|
+
export { FootnotePropertiesDialog };
|