@churchapps/apphelper 0.3.15 → 0.3.17

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.
Files changed (42) hide show
  1. package/dist/components/markdownEditor/Editor.d.ts +3 -1
  2. package/dist/components/markdownEditor/Editor.d.ts.map +1 -1
  3. package/dist/components/markdownEditor/Editor.js +25 -4
  4. package/dist/components/markdownEditor/Editor.js.map +1 -1
  5. package/dist/components/markdownEditor/MarkdownPreview.d.ts +3 -1
  6. package/dist/components/markdownEditor/MarkdownPreview.d.ts.map +1 -1
  7. package/dist/components/markdownEditor/MarkdownPreview.js +14 -2
  8. package/dist/components/markdownEditor/MarkdownPreview.js.map +1 -1
  9. package/dist/components/markdownEditor/MarkdownPreviewLight.d.ts.map +1 -1
  10. package/dist/components/markdownEditor/MarkdownPreviewLight.js +5 -1
  11. package/dist/components/markdownEditor/MarkdownPreviewLight.js.map +1 -1
  12. package/dist/components/markdownEditor/plugins/FloatingTextMenu/FloatingTextFormatToolbarPlugin.d.ts +13 -0
  13. package/dist/components/markdownEditor/plugins/FloatingTextMenu/FloatingTextFormatToolbarPlugin.d.ts.map +1 -0
  14. package/dist/components/markdownEditor/plugins/FloatingTextMenu/FloatingTextFormatToolbarPlugin.js +311 -0
  15. package/dist/components/markdownEditor/plugins/FloatingTextMenu/FloatingTextFormatToolbarPlugin.js.map +1 -0
  16. package/dist/components/markdownEditor/plugins/FloatingTextMenu/getDOMRangeRect.d.ts +2 -0
  17. package/dist/components/markdownEditor/plugins/FloatingTextMenu/getDOMRangeRect.d.ts.map +1 -0
  18. package/dist/components/markdownEditor/plugins/FloatingTextMenu/getDOMRangeRect.js +20 -0
  19. package/dist/components/markdownEditor/plugins/FloatingTextMenu/getDOMRangeRect.js.map +1 -0
  20. package/dist/components/markdownEditor/plugins/FloatingTextMenu/getSelectNode.d.ts +2 -0
  21. package/dist/components/markdownEditor/plugins/FloatingTextMenu/getSelectNode.d.ts.map +1 -0
  22. package/dist/components/markdownEditor/plugins/FloatingTextMenu/getSelectNode.js +22 -0
  23. package/dist/components/markdownEditor/plugins/FloatingTextMenu/getSelectNode.js.map +1 -0
  24. package/dist/components/markdownEditor/plugins/FloatingTextMenu/setFloatingElemPosition.d.ts +2 -0
  25. package/dist/components/markdownEditor/plugins/FloatingTextMenu/setFloatingElemPosition.d.ts.map +1 -0
  26. package/dist/components/markdownEditor/plugins/FloatingTextMenu/setFloatingElemPosition.js +30 -0
  27. package/dist/components/markdownEditor/plugins/FloatingTextMenu/setFloatingElemPosition.js.map +1 -0
  28. package/dist/components/markdownEditor/plugins/MarkdownTransformers.js +2 -2
  29. package/dist/components/markdownEditor/plugins/MarkdownTransformers.js.map +1 -1
  30. package/dist/pageComponents/components/SelectChurchSearch.d.ts.map +1 -1
  31. package/dist/pageComponents/components/SelectChurchSearch.js +1 -1
  32. package/dist/pageComponents/components/SelectChurchSearch.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/components/markdownEditor/Editor.tsx +33 -16
  35. package/src/components/markdownEditor/MarkdownPreview.tsx +5 -3
  36. package/src/components/markdownEditor/MarkdownPreviewLight.tsx +5 -1
  37. package/src/components/markdownEditor/plugins/FloatingTextMenu/FloatingTextFormatToolbarPlugin.tsx +445 -0
  38. package/src/components/markdownEditor/plugins/FloatingTextMenu/getDOMRangeRect.tsx +17 -0
  39. package/src/components/markdownEditor/plugins/FloatingTextMenu/getSelectNode.tsx +17 -0
  40. package/src/components/markdownEditor/plugins/FloatingTextMenu/setFloatingElemPosition.tsx +33 -0
  41. package/src/components/markdownEditor/plugins/MarkdownTransformers.ts +2 -2
  42. package/src/pageComponents/components/SelectChurchSearch.tsx +48 -48
@@ -0,0 +1,445 @@
1
+ import { $isCodeHighlightNode } from "@lexical/code";
2
+ import { $isLinkNode, TOGGLE_LINK_COMMAND } from "@lexical/link";
3
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
4
+ import { mergeRegister } from "@lexical/utils";
5
+ import { $convertToMarkdownString } from "@lexical/markdown";
6
+ import { $createParagraphNode, $getSelection, $isRangeSelection, $isTextNode, COMMAND_PRIORITY_LOW, FORMAT_TEXT_COMMAND, SELECTION_CHANGE_COMMAND } from "lexical";
7
+ import { $createHeadingNode, $isHeadingNode } from "@lexical/rich-text";
8
+ import { $wrapNodes } from "@lexical/selection";
9
+ import React, { useCallback, useEffect, useRef, useState } from "react";
10
+ import { createPortal } from "react-dom";
11
+ import { Box, styled, IconButton, Icon, Select, MenuItem } from "@mui/material";
12
+
13
+ import { getDOMRangeRect } from "./getDOMRangeRect";
14
+ import { getSelectedNode } from "./getSelectNode";
15
+ import { setFloatingElemPosition } from "./setFloatingElemPosition";
16
+ import { PLAYGROUND_TRANSFORMERS } from "../MarkdownTransformers";
17
+ import { ApiHelper } from "../../../../helpers";
18
+
19
+ export const FloatingDivContainer = styled(Box)({
20
+ display: "flex",
21
+ background: "#fff",
22
+ padding: 4,
23
+ verticalAlign: "middle",
24
+ position: "absolute",
25
+ top: 0,
26
+ left: 0,
27
+ zIndex: 1400,
28
+ opacity: 0,
29
+ backgroundColor: "#fff",
30
+ boxShadow: "0px 5px 10px rgba(0, 0, 0, 0.3)",
31
+ borderRadius: 8,
32
+ transition: "opacity 0.5s",
33
+ height: 35,
34
+ willChange: "transform",
35
+ });
36
+
37
+ function TextFormatFloatingToolbar({ editor, anchorElem, isLink, isBold, isItalic, isUnderline, isCode, isStrikethrough, isSubscript, isSuperscript, blockType, setBlockType }: any) {
38
+ const popupCharStylesEditorRef = useRef(null);
39
+
40
+ const applyFormatting = (command: string) => {
41
+ editor.dispatchCommand(FORMAT_TEXT_COMMAND, command);
42
+ saveChanges(editor);
43
+ }
44
+
45
+ const insertLink = useCallback(() => {
46
+ if (!isLink) {
47
+ editor.dispatchCommand(TOGGLE_LINK_COMMAND, "https://");
48
+ } else {
49
+ editor.dispatchCommand(TOGGLE_LINK_COMMAND, null);
50
+ }
51
+ }, [editor, isLink]);
52
+
53
+ function mouseMoveListener(e: any) {
54
+ if (
55
+ popupCharStylesEditorRef?.current &&
56
+ (e.buttons === 1 || e.buttons === 3)
57
+ ) {
58
+ popupCharStylesEditorRef.current.style.pointerEvents = "none";
59
+ }
60
+ }
61
+ function mouseUpListener(e: any) {
62
+ if (popupCharStylesEditorRef?.current) {
63
+ popupCharStylesEditorRef.current.style.pointerEvents = "auto";
64
+ }
65
+ }
66
+
67
+ useEffect(() => {
68
+ if (popupCharStylesEditorRef?.current) {
69
+ document.addEventListener("mousemove", mouseMoveListener);
70
+ document.addEventListener("mouseup", mouseUpListener);
71
+
72
+ return () => {
73
+ document.removeEventListener("mousemove", mouseMoveListener);
74
+ document.removeEventListener("mouseup", mouseUpListener);
75
+ };
76
+ }
77
+ }, [popupCharStylesEditorRef]);
78
+
79
+ const updateTextFormatFloatingToolbar = useCallback(() => {
80
+ const selection = $getSelection();
81
+
82
+ const popupCharStylesEditorElem = popupCharStylesEditorRef.current;
83
+ const nativeSelection = window.getSelection();
84
+
85
+ if (popupCharStylesEditorElem === null) {
86
+ return;
87
+ }
88
+
89
+ const rootElement = editor.getRootElement();
90
+ if (
91
+ selection !== null &&
92
+ nativeSelection !== null &&
93
+ !nativeSelection.isCollapsed &&
94
+ rootElement !== null &&
95
+ rootElement.contains(nativeSelection.anchorNode)
96
+ ) {
97
+ const rangeRect = getDOMRangeRect(nativeSelection, rootElement);
98
+
99
+ setFloatingElemPosition(rangeRect, popupCharStylesEditorElem, anchorElem);
100
+ }
101
+ }, [editor, anchorElem]);
102
+
103
+ useEffect(() => {
104
+ const scrollerElem = anchorElem.parentElement;
105
+
106
+ const update = () => {
107
+ editor.getEditorState().read(() => {
108
+ updateTextFormatFloatingToolbar();
109
+ });
110
+ };
111
+
112
+ window.addEventListener("resize", update);
113
+ if (scrollerElem) {
114
+ scrollerElem.addEventListener("scroll", update);
115
+ }
116
+
117
+ return () => {
118
+ window.removeEventListener("resize", update);
119
+ if (scrollerElem) {
120
+ scrollerElem.removeEventListener("scroll", update);
121
+ }
122
+ };
123
+ }, [editor, updateTextFormatFloatingToolbar, anchorElem]);
124
+
125
+ useEffect(() => {
126
+ editor.getEditorState().read(() => {
127
+ updateTextFormatFloatingToolbar();
128
+ });
129
+ return mergeRegister(
130
+ editor.registerUpdateListener(({ editorState }: any) => {
131
+ editorState.read(() => {
132
+ updateTextFormatFloatingToolbar();
133
+ });
134
+ }),
135
+
136
+ editor.registerCommand(
137
+ SELECTION_CHANGE_COMMAND,
138
+ () => {
139
+ updateTextFormatFloatingToolbar();
140
+ return false;
141
+ },
142
+ COMMAND_PRIORITY_LOW
143
+ )
144
+ );
145
+ }, [editor, updateTextFormatFloatingToolbar]);
146
+
147
+ const formatBlock = (type: string) => {
148
+ editor.update(() => {
149
+ const selection = $getSelection();
150
+ if ($isRangeSelection(selection)) {
151
+ $wrapNodes(selection, () =>
152
+ //@ts-ignore
153
+ type === "paragraph" ? $createParagraphNode() : $createHeadingNode(type)
154
+ );
155
+ }
156
+ });
157
+ setBlockType(type);
158
+ saveChanges(editor)
159
+ };
160
+
161
+ return (
162
+ <FloatingDivContainer ref={popupCharStylesEditorRef}>
163
+ <>
164
+ <Select
165
+ value={blockType}
166
+ onChange={(e) => formatBlock(e.target.value)}
167
+ sx={{
168
+ minWidth: 120,
169
+ backgroundColor: "#fff",
170
+ borderRadius: 2,
171
+ marginRight: 0.3,
172
+ }}
173
+ >
174
+ <MenuItem value="paragraph">Normal</MenuItem>
175
+ <MenuItem value="h1">Heading 1</MenuItem>
176
+ <MenuItem value="h2">Heading 2</MenuItem>
177
+ <MenuItem value="h3">Heading 3</MenuItem>
178
+ <MenuItem value="h4">Heading 4</MenuItem>
179
+ </Select>
180
+ <IconButton
181
+ onClick={() => {
182
+ applyFormatting("bold");
183
+ }}
184
+ sx={{ backgroundColor: isBold ? "#e0e0e0" : undefined, borderRadius: 2, marginRight: 0.3 }}
185
+ >
186
+ <Icon>format_bold_outline</Icon>
187
+ </IconButton>
188
+
189
+ <IconButton
190
+ onClick={() => {
191
+ applyFormatting("italic");
192
+ }}
193
+ sx={{ backgroundColor: isItalic ? "#e0e0e0" : undefined, borderRadius: 2, marginRight: 0.3 }}
194
+ >
195
+ <Icon>format_italic_outline</Icon>
196
+ </IconButton>
197
+
198
+ <IconButton
199
+ onClick={() => {
200
+ applyFormatting("underline");
201
+ }}
202
+ sx={{ backgroundColor: isUnderline ? "#e0e0e0" : undefined, borderRadius: 2, marginRight: 0.3 }}
203
+ >
204
+ <Icon>format_underlined_outline</Icon>
205
+ </IconButton>
206
+
207
+ {/* <IconButton
208
+ onClick={() => {
209
+ applyFormatting("strikethrough");
210
+ }}
211
+ sx={{ backgroundColor: isStrikethrough ? "#e0e0e0" : undefined, borderRadius: 2, marginRight: 0.3 }}
212
+ >
213
+ <Icon>strikethrough_s_outline</Icon>
214
+ </IconButton> */}
215
+
216
+ <IconButton
217
+ onClick={() => {
218
+ applyFormatting("code");
219
+ }}
220
+ sx={{ backgroundColor: isCode ? "#e0e0e0" : undefined, borderRadius: 2, marginRight: 0.3 }}
221
+ >
222
+ <Icon>code</Icon>
223
+ </IconButton>
224
+
225
+ {/* <IconButton
226
+ onClick={insertLink}
227
+ sx={{ backgroundColor: isLink ? "#e0e0e0" : undefined, borderRadius: 2 }}
228
+ >
229
+ <Icon>insert_link_outline</Icon>
230
+ </IconButton> */}
231
+ </>
232
+ </FloatingDivContainer>
233
+ );
234
+ }
235
+
236
+ let lastSavedText = ""; // Track last saved text
237
+ let lastFormattingState = {}; // Track last formatting state
238
+
239
+ //@ts-ignore
240
+ const getFormattingState = (selection) => {
241
+ const node = getSelectedNode(selection);
242
+ let blockType = "paragraph";
243
+ if ($isHeadingNode(node)) {
244
+ blockType = node.getTag(); // "h1", "h2", etc.
245
+ }
246
+
247
+ return {
248
+ isBold: selection.hasFormat("bold"),
249
+ isItalic: selection.hasFormat("italic"),
250
+ isUnderline: selection.hasFormat("underline"),
251
+ // isStrikethrough: selection.hasFormat("strikethrough"),
252
+ isCode: selection.hasFormat("code"),
253
+ blockType
254
+ }
255
+ }
256
+
257
+ const saveChanges = (editor: any) => {
258
+ editor.update(() => {
259
+ const selection = $getSelection();
260
+ if ($isRangeSelection(selection)) {
261
+ const text = selection.getTextContent().trim();
262
+ const newFormattingState = getFormattingState(selection); // Get current formatting
263
+
264
+ // Get the parent block node (ensuring it's not just a text node)
265
+ const node = getSelectedNode(selection);
266
+ const parentNode = node.getParent(); // Get the parent block-level node
267
+ let blockType = "paragraph";
268
+
269
+ if ($isHeadingNode(parentNode)) {
270
+ blockType = parentNode.getTag(); // Get heading type
271
+ } else if ($isHeadingNode(node)) {
272
+ blockType = node.getTag();
273
+ }
274
+
275
+ //@ts-ignore
276
+ if (JSON.stringify(newFormattingState) !== JSON.stringify(lastFormattingState) || blockType !== lastFormattingState.blockType) {
277
+ lastSavedText = text;
278
+ lastFormattingState = { ...newFormattingState, blockType };
279
+
280
+ const editorNode = editor.getRootElement();
281
+ const elementJSON = editorNode?.dataset?.element;
282
+ if (elementJSON) {
283
+ const markdown = $convertToMarkdownString(PLAYGROUND_TRANSFORMERS)
284
+ const element: any = JSON.parse(elementJSON);
285
+
286
+ element.answers.text = markdown;
287
+ element.answersJSON = JSON.stringify(element.answers);
288
+
289
+ ApiHelper.post("/elements", [element], "ContentApi");
290
+ }
291
+ }
292
+ }
293
+ })
294
+ }
295
+
296
+ const updateFormattingState = (editor: any) => {
297
+ editor.update(() => {
298
+ const selection = $getSelection();
299
+ if ($isRangeSelection(selection)) {
300
+ lastFormattingState = getFormattingState(selection);
301
+ }
302
+ })
303
+ }
304
+
305
+ function useFloatingTextFormatToolbar(editor: any, anchorElem: any) {
306
+ const [isText, setIsText] = useState(false);
307
+ const [isLink, setIsLink] = useState(false);
308
+ const [isBold, setIsBold] = useState(false);
309
+ const [isItalic, setIsItalic] = useState(false);
310
+ const [isUnderline, setIsUnderline] = useState(false);
311
+ const [isStrikethrough, setIsStrikethrough] = useState(false);
312
+ const [isSubscript, setIsSubscript] = useState(false);
313
+ const [isSuperscript, setIsSuperscript] = useState(false);
314
+ const [isCode, setIsCode] = useState(false);
315
+ const [blockType, setBlockType] = useState("paragraph");
316
+
317
+ const updatePopup = useCallback(() => {
318
+ editor.getEditorState().read(() => {
319
+ // Should not to pop up the floating toolbar when using IME input
320
+ if (editor.isComposing()) {
321
+ return;
322
+ }
323
+ const selection = $getSelection();
324
+ const nativeSelection = window.getSelection();
325
+ const rootElement = editor.getRootElement();
326
+
327
+ if (
328
+ nativeSelection !== null &&
329
+ (!$isRangeSelection(selection) ||
330
+ rootElement === null ||
331
+ !rootElement.contains(nativeSelection.anchorNode))
332
+ ) {
333
+ setIsText(false);
334
+ return;
335
+ }
336
+
337
+ if (!$isRangeSelection(selection)) {
338
+ return;
339
+ }
340
+
341
+ const node = getSelectedNode(selection);
342
+
343
+ // Update text format
344
+ setIsBold(selection.hasFormat("bold"));
345
+ setIsItalic(selection.hasFormat("italic"));
346
+ setIsUnderline(selection.hasFormat("underline"));
347
+ setIsStrikethrough(selection.hasFormat("strikethrough"));
348
+ setIsSubscript(selection.hasFormat("subscript"));
349
+ setIsSuperscript(selection.hasFormat("superscript"));
350
+ setIsCode(selection.hasFormat("code"));
351
+
352
+ // Update links
353
+ const parent = node.getParent();
354
+ if ($isLinkNode(parent) || $isLinkNode(node)) {
355
+ setIsLink(true);
356
+ } else {
357
+ setIsLink(false);
358
+ }
359
+
360
+ if (
361
+ !$isCodeHighlightNode(selection.anchor.getNode()) &&
362
+ selection.getTextContent() !== ""
363
+ ) {
364
+ setIsText($isTextNode(node));
365
+ } else {
366
+ setIsText(false);
367
+ }
368
+
369
+ const rawTextContent = selection.getTextContent().replace(/\n/g, "");
370
+ if (!selection.isCollapsed() && rawTextContent === "") {
371
+ setIsText(false);
372
+ return;
373
+ }
374
+
375
+ if ($isRangeSelection(selection)) {
376
+ const text = selection.getTextContent().trim();
377
+ if (text && JSON.stringify(lastFormattingState === "{}")) {
378
+ updateFormattingState(editor);
379
+ }
380
+ }
381
+
382
+ let type = "paragraph";
383
+ if ($isHeadingNode(parent)) {
384
+ type = parent.getTag();
385
+ } else if ($isHeadingNode(node)) {
386
+ type = node.getTag();
387
+ }
388
+ setBlockType(type);
389
+ });
390
+
391
+ }, [editor]);
392
+
393
+ useEffect(() => {
394
+ document.addEventListener("selectionchange", updatePopup);
395
+ return () => {
396
+ document.removeEventListener("selectionchange", updatePopup);
397
+ };
398
+ }, [updatePopup]);
399
+
400
+ useEffect(() => {
401
+ return mergeRegister(
402
+ editor.registerUpdateListener(() => {
403
+ updatePopup();
404
+ }),
405
+ editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
406
+ updatePopup();
407
+ return false;
408
+ },COMMAND_PRIORITY_LOW),
409
+ editor.registerRootListener(() => {
410
+ if (editor.getRootElement() === null) {
411
+ setIsText(false);
412
+ }
413
+ })
414
+ );
415
+ }, [editor, updatePopup]);
416
+
417
+ if (!isText || isLink) {
418
+ return null;
419
+ }
420
+
421
+ return createPortal(
422
+ <TextFormatFloatingToolbar
423
+ editor={editor}
424
+ anchorElem={anchorElem}
425
+ isLink={isLink}
426
+ isBold={isBold}
427
+ isItalic={isItalic}
428
+ isStrikethrough={isStrikethrough}
429
+ isSubscript={isSubscript}
430
+ isSuperscript={isSuperscript}
431
+ isUnderline={isUnderline}
432
+ isCode={isCode}
433
+ blockType={blockType}
434
+ setBlockType={setBlockType}
435
+ />,
436
+ anchorElem
437
+ );
438
+ }
439
+
440
+ export default function FloatingTextFormatToolbarPlugin({
441
+ anchorElem = document.body,
442
+ }) {
443
+ const [editor] = useLexicalComposerContext();
444
+ return useFloatingTextFormatToolbar(editor, anchorElem);
445
+ }
@@ -0,0 +1,17 @@
1
+ export function getDOMRangeRect(nativeSelection: any, rootElement: any) {
2
+ const domRange = nativeSelection.getRangeAt(0);
3
+
4
+ let rect;
5
+
6
+ if (nativeSelection.anchorNode === rootElement) {
7
+ let inner = rootElement;
8
+ while (inner.firstElementChild != null) {
9
+ inner = inner.firstElementChild;
10
+ }
11
+ rect = inner.getBoundingClientRect();
12
+ } else {
13
+ rect = domRange.getBoundingClientRect();
14
+ }
15
+
16
+ return rect;
17
+ }
@@ -0,0 +1,17 @@
1
+ import { $isAtNodeEnd } from "@lexical/selection";
2
+
3
+ export function getSelectedNode(selection: any) {
4
+ const anchor = selection.anchor;
5
+ const focus = selection.focus;
6
+ const anchorNode = selection.anchor.getNode();
7
+ const focusNode = selection.focus.getNode();
8
+ if (anchorNode === focusNode) {
9
+ return anchorNode;
10
+ }
11
+ const isBackward = selection.isBackward();
12
+ if (isBackward) {
13
+ return $isAtNodeEnd(focus) ? anchorNode : focusNode;
14
+ } else {
15
+ return $isAtNodeEnd(anchor) ? anchorNode : focusNode;
16
+ }
17
+ }
@@ -0,0 +1,33 @@
1
+ const VERTICAL_GAP = 10;
2
+ const HORIZONTAL_OFFSET = 5;
3
+
4
+ export function setFloatingElemPosition( targetRect: any, floatingElem: any, anchorElem: any, verticalGap = VERTICAL_GAP, horizontalOffset = HORIZONTAL_OFFSET ) {
5
+ const scrollerElem = anchorElem.parentElement;
6
+
7
+ if (targetRect === null || !scrollerElem) {
8
+ floatingElem.style.opacity = "0";
9
+ floatingElem.style.transform = "translate(-10000px, -10000px)";
10
+ return;
11
+ }
12
+
13
+ const floatingElemRect = floatingElem.getBoundingClientRect();
14
+ const anchorElementRect = anchorElem.getBoundingClientRect();
15
+ const editorScrollerRect = scrollerElem.getBoundingClientRect();
16
+
17
+ let top = targetRect.top - floatingElemRect.height - verticalGap;
18
+ let left = targetRect.left - horizontalOffset;
19
+
20
+ if (top < editorScrollerRect.top) {
21
+ top += floatingElemRect.height + targetRect.height + verticalGap * 2;
22
+ }
23
+
24
+ if (left + floatingElemRect.width > editorScrollerRect.right) {
25
+ left = editorScrollerRect.right - floatingElemRect.width - horizontalOffset;
26
+ }
27
+
28
+ top -= anchorElementRect.top;
29
+ left -= anchorElementRect.left;
30
+
31
+ floatingElem.style.opacity = "1";
32
+ floatingElem.style.transform = `translate(${left}px, ${top}px)`;
33
+ }
@@ -61,7 +61,7 @@ export type TextMatchTransformer = Readonly<{
61
61
  export const UNDERLINE: TextFormatTransformer = {
62
62
  format: ["underline"],
63
63
  intraword: false,
64
- tag: "_",
64
+ tag: "__",
65
65
  type: "text-format"
66
66
  };
67
67
  /*
@@ -96,11 +96,11 @@ export const UNDERLINE: TextMatchTransformer = {
96
96
  const modifiedTextTransformers = [EMOJI_NODE_MARKDOWN_TRANSFORM];
97
97
 
98
98
  export const PLAYGROUND_TRANSFORMERS: Array<Transformer> = [
99
- UNDERLINE,
100
99
  ...modifiedTextTransformers,
101
100
  HR,
102
101
  ...ELEMENT_TRANSFORMERS,
103
102
 
104
103
  CUSTOM_LINK_NODE_TRANSFORMER,
105
104
  ...TRANSFORMERS.splice(0, 13),
105
+ UNDERLINE,
106
106
  ];
@@ -8,64 +8,64 @@ import { SelectableChurch } from "./SelectableChurch";
8
8
  import { SelectChurchRegister } from "./SelectChurchRegister";
9
9
 
10
10
  interface Props {
11
- selectChurch: (churchId: string) => void,
12
- registeredChurchCallback?: (church: ChurchInterface) => void,
13
- appName: string
11
+ selectChurch: (churchId: string) => void,
12
+ registeredChurchCallback?: (church: ChurchInterface) => void,
13
+ appName: string
14
14
  }
15
15
 
16
16
  export const SelectChurchSearch: React.FC<Props> = (props) => {
17
- const [searchText, setSearchText] = React.useState("");
18
- const [churches, setChurches] = React.useState<ChurchInterface[]>(null);
19
- const [showRegister, setShowRegister] = React.useState(false);
17
+ const [searchText, setSearchText] = React.useState("");
18
+ const [churches, setChurches] = React.useState<ChurchInterface[]>(null);
19
+ const [showRegister, setShowRegister] = React.useState(false);
20
20
 
21
- const handleSubmit = (e: React.MouseEvent) => {
22
- if (e !== null) e.preventDefault();
23
- let term = escape(searchText.trim());
24
- ApiHelper.post("/churches/search", { name: term }, "MembershipApi").then(data => setChurches(data));
25
- }
21
+ const handleSubmit = (e: React.MouseEvent) => {
22
+ if (e !== null) e.preventDefault();
23
+ let term = searchText.trim();
24
+ ApiHelper.post("/churches/search", { name: term }, "MembershipApi").then(data => setChurches(data));
25
+ }
26
26
 
27
- const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => setSearchText(e.currentTarget.value);
27
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => setSearchText(e.currentTarget.value);
28
28
 
29
- const handleKeyDown = (e: React.KeyboardEvent<any>) => { if (e.key === "Enter") { e.preventDefault(); handleSubmit(null); } }
29
+ const handleKeyDown = (e: React.KeyboardEvent<any>) => { if (e.key === "Enter") { e.preventDefault(); handleSubmit(null); } }
30
30
 
31
- const handleRegisterClick = (e: React.MouseEvent) => {
32
- e.preventDefault();
33
- if (window.confirm(Locale.label("selectChurch.confirmRegister"))) {
34
- setShowRegister(true);
35
- }
36
- }
31
+ const handleRegisterClick = (e: React.MouseEvent) => {
32
+ e.preventDefault();
33
+ if (window.confirm(Locale.label("selectChurch.confirmRegister"))) {
34
+ setShowRegister(true);
35
+ }
36
+ }
37
37
 
38
- const getRegisterLink = () => (
39
- <div>
40
- <a style={{ display: "block", textAlign: "center" }} href="about:blank" onClick={handleRegisterClick}>
41
- {Locale.label("selectChurch.register")}
42
- </a>
43
- </div>
44
- )
38
+ const getRegisterLink = () => (
39
+ <div>
40
+ <a style={{ display: "block", textAlign: "center" }} href="about:blank" onClick={handleRegisterClick}>
41
+ {Locale.label("selectChurch.register")}
42
+ </a>
43
+ </div>
44
+ )
45
45
 
46
- const getChurches = () => {
47
- const result: JSX.Element[] = [];
48
- churches.forEach(church => {
49
- result.push(<SelectableChurch church={church} selectChurch={props.selectChurch} />);
50
- });
51
- result.push(getRegisterLink());
52
- return result;
53
- }
46
+ const getChurches = () => {
47
+ const result: JSX.Element[] = [];
48
+ churches.forEach(church => {
49
+ result.push(<SelectableChurch church={church} selectChurch={props.selectChurch} />);
50
+ });
51
+ result.push(getRegisterLink());
52
+ return result;
53
+ }
54
54
 
55
- const getResults = () => {
56
- if (churches === null) return;
57
- else if (churches.length === 0) return <><p>{Locale.label("selectChurch.noMatches")}</p>{getRegisterLink()}</>
58
- else return getChurches();
59
- }
55
+ const getResults = () => {
56
+ if (churches === null) return;
57
+ else if (churches.length === 0) return <><p>{Locale.label("selectChurch.noMatches")}</p>{getRegisterLink()}</>
58
+ else return getChurches();
59
+ }
60
60
 
61
- if (showRegister) return (<SelectChurchRegister selectChurch={props.selectChurch} registeredChurchCallback={props.registeredChurchCallback} appName={props.appName} initialChurchName={searchText} />)
62
- else return (
63
- <>
64
- <TextField fullWidth name="searchText" label="Name" value={searchText} onChange={handleChange} onKeyDown={handleKeyDown}
65
- InputProps={{ endAdornment: <Button variant="contained" id="searchButton" data-cy="search-button" onClick={handleSubmit}>{Locale.label("common.search")}</Button> }}
66
- />
67
- {getResults()}
68
- </>
61
+ if (showRegister) return (<SelectChurchRegister selectChurch={props.selectChurch} registeredChurchCallback={props.registeredChurchCallback} appName={props.appName} initialChurchName={searchText} />)
62
+ else return (
63
+ <>
64
+ <TextField fullWidth name="searchText" label="Name" value={searchText} onChange={handleChange} onKeyDown={handleKeyDown}
65
+ InputProps={{ endAdornment: <Button variant="contained" id="searchButton" data-cy="search-button" onClick={handleSubmit}>{Locale.label("common.search")}</Button> }}
66
+ />
67
+ {getResults()}
68
+ </>
69
69
 
70
- );
70
+ );
71
71
  };