@team-monolith/cds 1.119.4 → 1.120.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/dist/components/Slider.d.ts +21 -0
- package/dist/components/Slider.js +93 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/patterns/LexicalEditor/Plugins.js +1 -2
- package/dist/patterns/LexicalEditor/nodes/index.d.ts +0 -3
- package/dist/patterns/LexicalEditor/nodes/index.js +0 -3
- package/dist/patterns/LexicalEditor/nodes/nodes.d.ts +2 -4
- package/dist/patterns/LexicalEditor/nodes/nodes.js +0 -6
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/useContextMenuOptions.js +2 -66
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.js +6 -27
- package/dist/patterns/LexicalEditor/plugins/ParagraphPlaceholderPlugin/index.js +8 -17
- package/dist/patterns/LexicalEditor/theme.d.ts +0 -6
- package/dist/patterns/LexicalEditor/theme.js +0 -38
- package/package.json +3 -3
- package/dist/patterns/LexicalEditor/nodes/toggleNodes/ToggleContainerNode.d.ts +0 -32
- package/dist/patterns/LexicalEditor/nodes/toggleNodes/ToggleContainerNode.js +0 -131
- package/dist/patterns/LexicalEditor/nodes/toggleNodes/ToggleContentNode.d.ts +0 -23
- package/dist/patterns/LexicalEditor/nodes/toggleNodes/ToggleContentNode.js +0 -88
- package/dist/patterns/LexicalEditor/nodes/toggleNodes/ToggleTitleNode.d.ts +0 -24
- package/dist/patterns/LexicalEditor/nodes/toggleNodes/ToggleTitleNode.js +0 -105
- package/dist/patterns/LexicalEditor/plugins/TogglePlugin/index.d.ts +0 -9
- package/dist/patterns/LexicalEditor/plugins/TogglePlugin/index.js +0 -198
- package/dist/patterns/LexicalEditor/utils/toggleUtils.d.ts +0 -9
- package/dist/patterns/LexicalEditor/utils/toggleUtils.js +0 -18
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SliderProps as MuiSliderProps } from "@mui/material";
|
|
2
|
+
import React from "react";
|
|
3
|
+
export interface SliderProps extends Omit<MuiSliderProps, "onChangeCommitted"> {
|
|
4
|
+
className?: string;
|
|
5
|
+
/** 슬라이더의 최소값 (기본값: 0) */
|
|
6
|
+
min?: number;
|
|
7
|
+
/** 슬라이더의 최대값 (기본값: 100) */
|
|
8
|
+
max?: number;
|
|
9
|
+
/** 슬라이더의 기본값 (없는 경우 최소값을 사용) */
|
|
10
|
+
defaultValue?: number;
|
|
11
|
+
/** 슬라이더 하단의 마크 배열 */
|
|
12
|
+
marks?: {
|
|
13
|
+
value: number;
|
|
14
|
+
label: React.ReactNode;
|
|
15
|
+
}[];
|
|
16
|
+
/** 슬라이더 값 변경이 완료되었을 때 호출되는 콜백 */
|
|
17
|
+
onChangeCommitted?: (value: number) => void;
|
|
18
|
+
/** 값 레이블에 단위를 렌더링하는 함수 */
|
|
19
|
+
renderUnit?: (count: number) => string;
|
|
20
|
+
}
|
|
21
|
+
export declare const Slider: React.ForwardRefExoticComponent<Omit<SliderProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
+
import styled from "@emotion/styled";
|
|
14
|
+
import { css } from "@emotion/react";
|
|
15
|
+
import { Slider as MuiSlider, } from "@mui/material";
|
|
16
|
+
import React from "react";
|
|
17
|
+
import { shadows } from "../foundation/shadows";
|
|
18
|
+
export const Slider = React.forwardRef(function Slider(props, ref) {
|
|
19
|
+
var _a, _b;
|
|
20
|
+
const { onChangeCommitted, renderUnit } = props, others = __rest(props, ["onChangeCommitted", "renderUnit"]);
|
|
21
|
+
return (_jsx(Wrapper, { children: _jsx(StyledSlider, Object.assign({ ref: ref }, others, { valueLabelDisplay: "on", valueLabelFormat: (value) => { var _a; return (_a = renderUnit === null || renderUnit === void 0 ? void 0 : renderUnit(value)) !== null && _a !== void 0 ? _a : value; }, onChangeCommitted: (_e, value) => {
|
|
22
|
+
// AIDEV-NOTE:
|
|
23
|
+
// MUI Slider onChangeCommitted 콜백의 value는 number | number[] 타입이지만
|
|
24
|
+
// 해당 컴포넌트는 항상 단일 슬라이더이므로 number 타입만 처리함
|
|
25
|
+
if (Array.isArray(value))
|
|
26
|
+
return;
|
|
27
|
+
onChangeCommitted === null || onChangeCommitted === void 0 ? void 0 : onChangeCommitted(value);
|
|
28
|
+
}, marksLength: (_b = (_a = props.marks) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0 })) }));
|
|
29
|
+
});
|
|
30
|
+
const Wrapper = styled.div `
|
|
31
|
+
padding: 0 21px; // thumb 크기(42px)의 절반만큼 좌우 여백 추가
|
|
32
|
+
`;
|
|
33
|
+
const StyledSlider = styled(MuiSlider, {
|
|
34
|
+
shouldForwardProp: (prop) => prop !== "marksLength",
|
|
35
|
+
})(({ theme, marksLength }) => css `
|
|
36
|
+
// figma 디자인과 간격 맞추기 위해 조정
|
|
37
|
+
padding: 7px 0;
|
|
38
|
+
margin-top: 24px;
|
|
39
|
+
&.MuiSlider-marked {
|
|
40
|
+
margin-bottom: 20px;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.MuiSlider-rail {
|
|
44
|
+
border-radius: 100px;
|
|
45
|
+
background: ${theme.color.foreground.neutralBaseDisabled};
|
|
46
|
+
}
|
|
47
|
+
.MuiSlider-track {
|
|
48
|
+
border-radius: 100px;
|
|
49
|
+
background: ${theme.color.foreground.primary};
|
|
50
|
+
}
|
|
51
|
+
.MuiSlider-thumb {
|
|
52
|
+
width: 42px;
|
|
53
|
+
height: 18px;
|
|
54
|
+
border-radius: 23px;
|
|
55
|
+
border: 2px solid ${theme.color.foreground.neutralBaseDisabled};
|
|
56
|
+
background: #fff;
|
|
57
|
+
/* Shadow-4 */
|
|
58
|
+
box-shadow: ${shadows.shadow04};
|
|
59
|
+
|
|
60
|
+
.MuiSlider-valueLabel {
|
|
61
|
+
background: transparent;
|
|
62
|
+
padding: 0;
|
|
63
|
+
color: ${theme.color.foreground.neutralBase};
|
|
64
|
+
/* Default/Label/16px-Eb */
|
|
65
|
+
font-family: Pretendard;
|
|
66
|
+
font-size: 16px;
|
|
67
|
+
font-style: normal;
|
|
68
|
+
font-weight: 800;
|
|
69
|
+
line-height: 20px; /* 125% */
|
|
70
|
+
top: -6px; // figma 디자인과 간격 맞추기 위해 조정
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
.MuiSlider-mark {
|
|
74
|
+
display: none;
|
|
75
|
+
}
|
|
76
|
+
.MuiSlider-markLabel {
|
|
77
|
+
color: ${theme.color.foreground.neutralBaseDisabled};
|
|
78
|
+
/* Default/Label/12px-Md */
|
|
79
|
+
font-family: ${theme.fontFamily.ui};
|
|
80
|
+
font-size: 12px;
|
|
81
|
+
font-style: normal;
|
|
82
|
+
font-weight: 500;
|
|
83
|
+
line-height: 16px; /* 133.333% */
|
|
84
|
+
top: 22px; // figma 디자인과 간격 맞추기 위해 조정
|
|
85
|
+
|
|
86
|
+
&[data-index="0"] {
|
|
87
|
+
transform: translateX(0%);
|
|
88
|
+
}
|
|
89
|
+
&[data-index="${marksLength - 1}"] {
|
|
90
|
+
transform: translateX(-100%);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
`);
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export * from "./components/SwitchButton";
|
|
|
20
20
|
export * from "./components/Tag";
|
|
21
21
|
export * from "./components/Tooltip";
|
|
22
22
|
export * from "./components/OverflowTooltip";
|
|
23
|
+
export * from "./components/Slider";
|
|
23
24
|
export * from "./foundation/color";
|
|
24
25
|
export * from "./foundation/shadows";
|
|
25
26
|
export * from "./foundation/breakpoints";
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ export * from "./components/SwitchButton";
|
|
|
20
20
|
export * from "./components/Tag";
|
|
21
21
|
export * from "./components/Tooltip";
|
|
22
22
|
export * from "./components/OverflowTooltip";
|
|
23
|
+
export * from "./components/Slider";
|
|
23
24
|
export * from "./foundation/color";
|
|
24
25
|
export * from "./foundation/shadows";
|
|
25
26
|
export * from "./foundation/breakpoints";
|
|
@@ -38,7 +38,6 @@ import { FilePlugin } from "./plugins/FilePlugin";
|
|
|
38
38
|
import { DragDropPastePlugin } from "./plugins/DragDropPastePlugin";
|
|
39
39
|
import { ParagraphPlaceholderPlugin } from "./plugins/ParagraphPlaceholderPlugin";
|
|
40
40
|
import { AutoLinkPlugin } from "./plugins/AutoLinkPlugin";
|
|
41
|
-
import { TogglePlugin } from "./plugins/TogglePlugin";
|
|
42
41
|
import { useTranslation } from "react-i18next";
|
|
43
42
|
export function Plugins(props) {
|
|
44
43
|
const { className, contentEditableClassName, onChange, isSheetEnabled, isQuizEnabled, showFileUpload, autoFocus, } = props;
|
|
@@ -55,7 +54,7 @@ export function Plugins(props) {
|
|
|
55
54
|
onChange === null || onChange === void 0 ? void 0 : onChange(editorState.toJSON());
|
|
56
55
|
},
|
|
57
56
|
// ignore 하지 않으면 Form에서 수정하지 않았는데 Dirty로 처리됨.
|
|
58
|
-
ignoreSelectionChange: true }), autoFocus && _jsx(AutoFocusPlugin, {}), isEditable && (_jsxs(_Fragment, { children: [_jsx(TabIndentationPlugin, {}), _jsx(ComponentPickerMenuPlugin, { isSheetEnabled: isSheetEnabled, isQuizEnabled: isQuizEnabled, showFileUpload: showFileUpload }), _jsx(MarkdownShortcutPlugin, { transformers: CUSTOM_TRANSFORMERS }), _jsx(HistoryPlugin, {}), _jsx(ParagraphPlaceholderPlugin, { placeholder: t("명령어 사용 시에는 '/'를 누르세요. 블록에 마우스를 올려 나타나는 '+'를 클릭하면 아래에 블록을 추가할 수 있습니다.") })] })), floatingAnchorElem && isEditable && (_jsxs(_Fragment, { children: [_jsx(ComponentAdderPlugin, { anchorElem: floatingAnchorElem, isSheetEnabled: isSheetEnabled, isQuizEnabled: isQuizEnabled, showFileUpload: showFileUpload }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem }), _jsx(FloatingLinkEditorPlugin, { anchorElem: floatingAnchorElem, isLinkEditMode: isLinkEditMode, setIsLinkEditMode: setIsLinkEditMode })] })), !isEditable && _jsx(ClickableLinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(HorizontalRulePlugin, {}), _jsx(ImagesPlugin, {}), _jsx(VideosPlugin, {}), _jsx(FilePlugin, {}), _jsx(TablePlugin, {}), _jsx(LinkPlugin, {}), _jsx(ListMaxIndentLevelPlugin, { maxDepth: 5 }), _jsx(ProblemInputPlugin, {}), _jsx(ProblemSelectPlugin, {}), _jsx(LayoutPlugin, {}), _jsx(SheetSelectPlugin, {}), _jsx(SheetInputPlugin, {}), _jsx(SelfEvaluationPlugin, {}), _jsx(AutoLinkPlugin, {})
|
|
57
|
+
ignoreSelectionChange: true }), autoFocus && _jsx(AutoFocusPlugin, {}), isEditable && (_jsxs(_Fragment, { children: [_jsx(TabIndentationPlugin, {}), _jsx(ComponentPickerMenuPlugin, { isSheetEnabled: isSheetEnabled, isQuizEnabled: isQuizEnabled, showFileUpload: showFileUpload }), _jsx(MarkdownShortcutPlugin, { transformers: CUSTOM_TRANSFORMERS }), _jsx(HistoryPlugin, {}), _jsx(ParagraphPlaceholderPlugin, { placeholder: t("명령어 사용 시에는 '/'를 누르세요. 블록에 마우스를 올려 나타나는 '+'를 클릭하면 아래에 블록을 추가할 수 있습니다.") })] })), floatingAnchorElem && isEditable && (_jsxs(_Fragment, { children: [_jsx(ComponentAdderPlugin, { anchorElem: floatingAnchorElem, isSheetEnabled: isSheetEnabled, isQuizEnabled: isQuizEnabled, showFileUpload: showFileUpload }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem }), _jsx(FloatingLinkEditorPlugin, { anchorElem: floatingAnchorElem, isLinkEditMode: isLinkEditMode, setIsLinkEditMode: setIsLinkEditMode })] })), !isEditable && _jsx(ClickableLinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(HorizontalRulePlugin, {}), _jsx(ImagesPlugin, {}), _jsx(VideosPlugin, {}), _jsx(FilePlugin, {}), _jsx(TablePlugin, {}), _jsx(LinkPlugin, {}), _jsx(ListMaxIndentLevelPlugin, { maxDepth: 5 }), _jsx(ProblemInputPlugin, {}), _jsx(ProblemSelectPlugin, {}), _jsx(LayoutPlugin, {}), _jsx(SheetSelectPlugin, {}), _jsx(SheetInputPlugin, {}), _jsx(SelfEvaluationPlugin, {}), _jsx(AutoLinkPlugin, {})] }));
|
|
59
58
|
}
|
|
60
59
|
const ScrollArea = styled.div `
|
|
61
60
|
min-height: 150px;
|
|
@@ -10,6 +10,3 @@ export * from "./SheetSelectNode";
|
|
|
10
10
|
export * from "./LayoutContainerNode";
|
|
11
11
|
export * from "./LayoutItemNode";
|
|
12
12
|
export * from "./nodes";
|
|
13
|
-
export * from "./toggleNodes/ToggleContainerNode";
|
|
14
|
-
export * from "./toggleNodes/ToggleTitleNode";
|
|
15
|
-
export * from "./toggleNodes/ToggleContentNode";
|
|
@@ -10,6 +10,3 @@ export * from "./SheetSelectNode";
|
|
|
10
10
|
export * from "./LayoutContainerNode";
|
|
11
11
|
export * from "./LayoutItemNode";
|
|
12
12
|
export * from "./nodes";
|
|
13
|
-
export * from "./toggleNodes/ToggleContainerNode";
|
|
14
|
-
export * from "./toggleNodes/ToggleTitleNode";
|
|
15
|
-
export * from "./toggleNodes/ToggleContentNode";
|
|
@@ -3,21 +3,19 @@ import { ColoredQuoteNode } from "./ColoredQuoteNode";
|
|
|
3
3
|
import { ImageNode } from "./ImageNode";
|
|
4
4
|
import { VideoNode } from "./VideoNode";
|
|
5
5
|
import { LayoutContainerNode } from "./LayoutContainerNode";
|
|
6
|
+
import { LayoutItemNode } from "./LayoutItemNode";
|
|
6
7
|
import { ProblemInputNode } from "./ProblemInputNode";
|
|
7
8
|
import { ProblemSelectNode } from "./ProblemSelectNode";
|
|
8
9
|
import { SelfEvaluationNode } from "./SelfEvaluationNode";
|
|
9
10
|
import { SheetInputNode } from "./SheetInputNode";
|
|
10
11
|
import { SheetSelectNode } from "./SheetSelectNode";
|
|
11
|
-
import { ToggleContainerNode } from "./toggleNodes/ToggleContainerNode";
|
|
12
|
-
import { ToggleTitleNode } from "./toggleNodes/ToggleTitleNode";
|
|
13
|
-
import { ToggleContentNode } from "./toggleNodes/ToggleContentNode";
|
|
14
12
|
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
|
|
15
13
|
import { TableCellNode, TableNode, TableRowNode } from "@lexical/table";
|
|
16
14
|
import { ListItemNode, ListNode } from "@lexical/list";
|
|
17
15
|
import { CodeHighlightNode, CodeNode } from "@lexical/code";
|
|
18
16
|
import { LinkNode } from "@lexical/link";
|
|
19
17
|
import { FileNode } from "./FileNode";
|
|
20
|
-
export declare const nodes: (typeof ColoredQuoteNode | typeof QuoteNode | typeof ImageNode | typeof VideoNode | typeof FileNode | typeof ProblemInputNode | typeof ProblemSelectNode | typeof SelfEvaluationNode | typeof SheetInputNode | typeof SheetSelectNode | typeof LayoutContainerNode | typeof
|
|
18
|
+
export declare const nodes: (typeof ColoredQuoteNode | typeof QuoteNode | typeof ImageNode | typeof VideoNode | typeof FileNode | typeof ProblemInputNode | typeof ProblemSelectNode | typeof SelfEvaluationNode | typeof SheetInputNode | typeof SheetSelectNode | typeof LayoutContainerNode | typeof LayoutItemNode | typeof HeadingNode | typeof ListNode | typeof ListItemNode | typeof CodeNode | typeof CodeHighlightNode | typeof TableNode | typeof TableCellNode | typeof TableRowNode | typeof LinkNode | typeof HorizontalRuleNode | {
|
|
21
19
|
replace: typeof QuoteNode;
|
|
22
20
|
with: () => ColoredQuoteNode;
|
|
23
21
|
})[];
|
|
@@ -9,9 +9,6 @@ import { ProblemSelectNode } from "./ProblemSelectNode";
|
|
|
9
9
|
import { SelfEvaluationNode } from "./SelfEvaluationNode";
|
|
10
10
|
import { SheetInputNode } from "./SheetInputNode";
|
|
11
11
|
import { SheetSelectNode } from "./SheetSelectNode";
|
|
12
|
-
import { ToggleContainerNode } from "./toggleNodes/ToggleContainerNode";
|
|
13
|
-
import { ToggleTitleNode } from "./toggleNodes/ToggleTitleNode";
|
|
14
|
-
import { ToggleContentNode } from "./toggleNodes/ToggleContentNode";
|
|
15
12
|
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
|
|
16
13
|
import { TableCellNode, TableNode, TableRowNode } from "@lexical/table";
|
|
17
14
|
import { ListItemNode, ListNode } from "@lexical/list";
|
|
@@ -42,9 +39,6 @@ export const nodes = [
|
|
|
42
39
|
SheetSelectNode,
|
|
43
40
|
SheetInputNode,
|
|
44
41
|
SelfEvaluationNode,
|
|
45
|
-
ToggleContainerNode,
|
|
46
|
-
ToggleTitleNode,
|
|
47
|
-
ToggleContentNode,
|
|
48
42
|
{
|
|
49
43
|
replace: QuoteNode,
|
|
50
44
|
with: () => new ColoredQuoteNode("grey"),
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
/**
|
|
3
3
|
* Context Menu (:: 버튼)
|
|
4
4
|
*/
|
|
5
|
-
import { $createParagraphNode,
|
|
5
|
+
import { $createParagraphNode, ParagraphNode, } from "lexical";
|
|
6
6
|
import { ComponentPickerOption, getBaseOptions, } from "../ComponentPickerMenuPlugin";
|
|
7
7
|
import { $createHeadingNode, $createQuoteNode, HeadingNode, } from "@lexical/rich-text";
|
|
8
8
|
import { $setBlocksType } from "@lexical/selection";
|
|
@@ -12,8 +12,7 @@ import { H1Icon, H2Icon, H3Icon, CloseFillIcon, ListUnorderedIcon, ListOrderedIc
|
|
|
12
12
|
import { $isColoredQuoteNode, } from "../../nodes/ColoredQuoteNode";
|
|
13
13
|
import { useTheme } from "@emotion/react";
|
|
14
14
|
import { css } from "@emotion/css";
|
|
15
|
-
import { $
|
|
16
|
-
import { $isFileNode, $isImageNode, $isVideoNode, $isProblemInputNode, $isToggleContainerNode, $isToggleTitleNode, $isToggleContentNode, } from "../../nodes";
|
|
15
|
+
import { $isFileNode, $isImageNode, $isVideoNode, $isProblemInputNode, } from "../../nodes";
|
|
17
16
|
import { $isProblemSelectNode, } from "../../nodes/ProblemSelectNode";
|
|
18
17
|
import { $isSheetInputNode } from "../../nodes/SheetInputNode";
|
|
19
18
|
import { $isSheetSelectNode, } from "../../nodes/SheetSelectNode";
|
|
@@ -164,66 +163,6 @@ function getListContextMenuOptions(editor, theme, node, t) {
|
|
|
164
163
|
}),
|
|
165
164
|
];
|
|
166
165
|
}
|
|
167
|
-
/**
|
|
168
|
-
* 토글 노드를 헤딩 노드로 변환하는 함수
|
|
169
|
-
* - 토글 컨테이너의 제목을 지정된 Heading 노드로 변환하고 (제목이 빈 경우 빈 텍스트 노드를 추가)
|
|
170
|
-
* - 본문과 컨테이너를 언랩하여 계층을 평탄화한다.
|
|
171
|
-
*/
|
|
172
|
-
function convertToggleTitleToHeading(container, tag) {
|
|
173
|
-
const children = container.getChildren();
|
|
174
|
-
const titleNode = children.find($isToggleTitleNode);
|
|
175
|
-
if (!titleNode) {
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
const contentNode = children.find($isToggleContentNode);
|
|
179
|
-
if (!contentNode) {
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
const headingNode = $createHeadingNode(tag);
|
|
183
|
-
const nodesForHeading = titleNode
|
|
184
|
-
.getChildren()
|
|
185
|
-
.flatMap((child) => $isElementNode(child) ? child.getChildren() : [child]);
|
|
186
|
-
if (nodesForHeading.length > 0) {
|
|
187
|
-
headingNode.append(...nodesForHeading);
|
|
188
|
-
}
|
|
189
|
-
if (headingNode.getChildrenSize() === 0) {
|
|
190
|
-
headingNode.append($createTextNode(""));
|
|
191
|
-
}
|
|
192
|
-
titleNode.replace(headingNode);
|
|
193
|
-
$unwrapNode(contentNode);
|
|
194
|
-
$unwrapNode(container);
|
|
195
|
-
headingNode.selectStart();
|
|
196
|
-
}
|
|
197
|
-
function getToggleContextMenuOptions(editor, container, t) {
|
|
198
|
-
return [
|
|
199
|
-
...[1, 2, 3].map((n) => {
|
|
200
|
-
const titleMap = {
|
|
201
|
-
1: t("큰 제목", { context: "렉시컬 드롭다운 메뉴" }),
|
|
202
|
-
2: t("중간 제목", { context: "렉시컬 드롭다운 메뉴" }),
|
|
203
|
-
3: t("작은 제목", { context: "렉시컬 드롭다운 메뉴" }),
|
|
204
|
-
};
|
|
205
|
-
const iconMap = {
|
|
206
|
-
1: _jsx(H1Icon, {}),
|
|
207
|
-
2: _jsx(H2Icon, {}),
|
|
208
|
-
3: _jsx(H3Icon, {}),
|
|
209
|
-
};
|
|
210
|
-
return new ComponentPickerOption(titleMap[n], {
|
|
211
|
-
icon: iconMap[n],
|
|
212
|
-
keywords: ["heading", "header", `h${n}`, titleMap[n]],
|
|
213
|
-
onSelect: () => editor.update(() => {
|
|
214
|
-
convertToggleTitleToHeading(container, `h${n}`);
|
|
215
|
-
}),
|
|
216
|
-
});
|
|
217
|
-
}),
|
|
218
|
-
new ComponentPickerOption(t("블록 삭제"), {
|
|
219
|
-
icon: _jsx(CloseFillIcon, {}),
|
|
220
|
-
keywords: [],
|
|
221
|
-
onSelect: () => {
|
|
222
|
-
container.remove();
|
|
223
|
-
},
|
|
224
|
-
}),
|
|
225
|
-
];
|
|
226
|
-
}
|
|
227
166
|
function getProblemInputContextMenuOptions(node, t) {
|
|
228
167
|
return [
|
|
229
168
|
new ComponentPickerOption(t("블록 삭제"), {
|
|
@@ -422,9 +361,6 @@ export function useContextMenuOptions(props) {
|
|
|
422
361
|
else if (node instanceof ListNode || node instanceof ListItemNode) {
|
|
423
362
|
return getListContextMenuOptions(editor, theme, node, t);
|
|
424
363
|
}
|
|
425
|
-
else if ($isToggleContainerNode(node)) {
|
|
426
|
-
return getToggleContextMenuOptions(editor, node, t);
|
|
427
|
-
}
|
|
428
364
|
else if ($isColoredQuoteNode(node)) {
|
|
429
365
|
return getColoredQuoteContextMenuOptions(editor, theme, node, t);
|
|
430
366
|
}
|
package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.js
CHANGED
|
@@ -15,12 +15,12 @@ import { LexicalTypeaheadMenuPlugin, MenuOption, useBasicTypeaheadTriggerMatch,
|
|
|
15
15
|
import { $createHeadingNode, $createQuoteNode } from "@lexical/rich-text";
|
|
16
16
|
import { $setBlocksType } from "@lexical/selection";
|
|
17
17
|
import { INSERT_TABLE_COMMAND } from "@lexical/table";
|
|
18
|
-
import { $createParagraphNode, $
|
|
18
|
+
import { $createParagraphNode, $getSelection, $isRangeSelection, } from "lexical";
|
|
19
19
|
import { useCallback, useMemo, useState } from "react";
|
|
20
20
|
import * as ReactDOM from "react-dom";
|
|
21
21
|
import { css as cssToClassName } from "@emotion/css";
|
|
22
22
|
import { ComponentPickerMenuList } from "./ComponentPickerMenuList";
|
|
23
|
-
import { TextIcon, H1Icon, H2Icon, H3Icon, ListUnorderedIcon, ListOrderedIcon, DoubleQuotesLIcon, CodeViewIcon, SeparatorIcon, ImageLineIcon, VideoLineIcon, InputMethodLineIcon, ListRadioIcon, LayoutColumnLineIcon, FileList2LineIcon, EmojiStickerLineIcon, File3LineIcon,
|
|
23
|
+
import { TextIcon, H1Icon, H2Icon, H3Icon, ListUnorderedIcon, ListOrderedIcon, DoubleQuotesLIcon, CodeViewIcon, SeparatorIcon, ImageLineIcon, VideoLineIcon, InputMethodLineIcon, ListRadioIcon, LayoutColumnLineIcon, FileList2LineIcon, EmojiStickerLineIcon, File3LineIcon, } from "../../../../icons";
|
|
24
24
|
import { ZINDEX } from "../../../../utils/zIndex";
|
|
25
25
|
import { css, useTheme } from "@emotion/react";
|
|
26
26
|
import { INSERT_PROBLEM_INPUT_COMMAND } from "../ProblemInputPlugin";
|
|
@@ -37,8 +37,6 @@ import { UploadFileDialog } from "../../components/UploadFileDialog";
|
|
|
37
37
|
import { UPLOAD_FILE_COMMAND } from "../FilePlugin";
|
|
38
38
|
import { useTranslation } from "react-i18next";
|
|
39
39
|
import { getTexts } from "../../../../texts";
|
|
40
|
-
import { INSERT_TOGGLE_COMMAND } from "../TogglePlugin";
|
|
41
|
-
import { $isToggleTitleNode } from "../../nodes";
|
|
42
40
|
// import useModal from "../../hooks/useModal";
|
|
43
41
|
// import catTypingGif from "../../images/cat-typing.gif";
|
|
44
42
|
// import { INSERT_IMAGE_COMMAND, InsertImageDialog } from "../ImagesPlugin";
|
|
@@ -60,16 +58,6 @@ export class ComponentPickerOption extends MenuOption {
|
|
|
60
58
|
this.onSelect = options.onSelect.bind(this);
|
|
61
59
|
}
|
|
62
60
|
}
|
|
63
|
-
function isSelectionInsideToggleTitle(editor) {
|
|
64
|
-
return editor.getEditorState().read(() => {
|
|
65
|
-
const selection = $getSelection();
|
|
66
|
-
if (!$isRangeSelection(selection)) {
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
const titleNode = $findMatchingParent(selection.anchor.getNode(), (node) => $isToggleTitleNode(node));
|
|
70
|
-
return Boolean(titleNode);
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
61
|
function getDynamicOptions(editor, queryString) {
|
|
74
62
|
const options = [];
|
|
75
63
|
// Lexical 원본 코드를 가져왔기 때문에 코드 변경을 최소화하기위함
|
|
@@ -250,11 +238,6 @@ export function getBaseOptions(props) {
|
|
|
250
238
|
keywords: ["numbered list", "ordered list", "ol"],
|
|
251
239
|
onSelect: () => editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined),
|
|
252
240
|
}),
|
|
253
|
-
new ComponentPickerOption(t("토글 목록", { context: "렉시컬 드롭다운 메뉴" }), {
|
|
254
|
-
icon: _jsx(PlayList2FillIcon, {}),
|
|
255
|
-
keywords: ["toggle", "collapsible"],
|
|
256
|
-
onSelect: () => editor.dispatchCommand(INSERT_TOGGLE_COMMAND, undefined),
|
|
257
|
-
}),
|
|
258
241
|
new ComponentPickerOption(t("인용 블록", { context: "렉시컬 드롭다운 메뉴" }), {
|
|
259
242
|
icon: _jsx(DoubleQuotesLIcon, {}),
|
|
260
243
|
keywords: ["block quote"],
|
|
@@ -338,13 +321,9 @@ export function ComponentPickerMenuPlugin(props) {
|
|
|
338
321
|
const [imageOpen, setImageOpen] = useState(false);
|
|
339
322
|
const [videoOpen, setVideoOpen] = useState(false);
|
|
340
323
|
const [fileOpen, setFileOpen] = useState(false);
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
return null; // 메뉴를 열지 않음 → "/" 명령 무시
|
|
345
|
-
}
|
|
346
|
-
return baseTrigger(text, editor);
|
|
347
|
-
}, [baseTrigger]);
|
|
324
|
+
const checkForTriggerMatch = useBasicTypeaheadTriggerMatch("/", {
|
|
325
|
+
minLength: 0,
|
|
326
|
+
});
|
|
348
327
|
const options = useMemo(() => {
|
|
349
328
|
const baseOptions = getBaseOptions({
|
|
350
329
|
editor,
|
|
@@ -389,7 +368,7 @@ export function ComponentPickerMenuPlugin(props) {
|
|
|
389
368
|
editor.dispatchCommand(INSERT_VIDEO_COMMAND, props);
|
|
390
369
|
}, onClose: () => setVideoOpen(false), shouldReset: true }), _jsx(UploadFileDialog, { open: fileOpen, onClose: () => setFileOpen(false), onChange: (props) => {
|
|
391
370
|
editor.dispatchCommand(UPLOAD_FILE_COMMAND, props);
|
|
392
|
-
} }), _jsx(LexicalTypeaheadMenuPlugin, { onQueryChange: setQueryString, onSelectOption: onSelectOption, triggerFn:
|
|
371
|
+
} }), _jsx(LexicalTypeaheadMenuPlugin, { onQueryChange: setQueryString, onSelectOption: onSelectOption, triggerFn: checkForTriggerMatch, options: options, anchorClassName: cssToClassName `
|
|
393
372
|
z-index: ${ZINDEX.DIALOG + 1};
|
|
394
373
|
`, menuRenderFn: (anchorElementRef, { selectedIndex, selectOptionAndCleanUp, setHighlightedIndex }) => anchorElementRef.current && options.length
|
|
395
374
|
? ReactDOM.createPortal(_jsx(ComponentPickerMenuList, { options: options, selectedIndex: selectedIndex, selectOptionAndCleanUp: selectOptionAndCleanUp, setHighlightedIndex: setHighlightedIndex }), anchorElementRef.current)
|
|
@@ -3,15 +3,13 @@ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext
|
|
|
3
3
|
import { $getSelection, $isParagraphNode, $isRangeSelection } from "lexical";
|
|
4
4
|
import { css as cssToClassName } from "@emotion/css";
|
|
5
5
|
import { useTheme } from "@emotion/react";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
pointer-events: none;
|
|
14
|
-
}
|
|
6
|
+
const getPlaceholderClassName = (theme) => cssToClassName `
|
|
7
|
+
&::before {
|
|
8
|
+
position: absolute;
|
|
9
|
+
content: attr(data-placeholder);
|
|
10
|
+
color: ${theme.color.foreground.neutralBaseDisabled};
|
|
11
|
+
pointer-events: none;
|
|
12
|
+
}
|
|
15
13
|
`;
|
|
16
14
|
export const ParagraphPlaceholderPlugin = ({ placeholder, }) => {
|
|
17
15
|
const [editor] = useLexicalComposerContext();
|
|
@@ -19,7 +17,6 @@ export const ParagraphPlaceholderPlugin = ({ placeholder, }) => {
|
|
|
19
17
|
const theme = useTheme();
|
|
20
18
|
const placeholderClassName = useMemo(() => getPlaceholderClassName(theme), [theme]);
|
|
21
19
|
useEffect(() => {
|
|
22
|
-
const { t } = useTranslation();
|
|
23
20
|
const removeUpdateListener = editor.registerUpdateListener(({ editorState }) => {
|
|
24
21
|
editorState.read(() => {
|
|
25
22
|
// 기존에 있던 placeholder를 제거합니다.
|
|
@@ -39,16 +36,10 @@ export const ParagraphPlaceholderPlugin = ({ placeholder, }) => {
|
|
|
39
36
|
const focusedNode = selection.focus.getNode();
|
|
40
37
|
if (!$isParagraphNode(focusedNode) || !focusedNode.isEmpty())
|
|
41
38
|
return;
|
|
42
|
-
const parentNode = focusedNode.getParent();
|
|
43
|
-
// 토글 제목 노드인 경우, / 명령어를 사용하지 않으므로
|
|
44
|
-
// 기존 placeholder 값을 "토글 제목"으로 고정합니다.
|
|
45
|
-
const placeholderText = $isToggleTitleNode(parentNode)
|
|
46
|
-
? t("토글 제목")
|
|
47
|
-
: placeholder;
|
|
48
39
|
const el = editor.getElementByKey(focusedNode.getKey());
|
|
49
40
|
if (el instanceof HTMLParagraphElement) {
|
|
50
41
|
paragraphRef.current = el;
|
|
51
|
-
paragraphRef.current.setAttribute("data-placeholder",
|
|
42
|
+
paragraphRef.current.setAttribute("data-placeholder", placeholder);
|
|
52
43
|
paragraphRef.current.classList.add(placeholderClassName);
|
|
53
44
|
}
|
|
54
45
|
});
|
|
@@ -36,10 +36,4 @@ export declare function getTheme(theme: Theme, editable: boolean): {
|
|
|
36
36
|
layoutContainer: string;
|
|
37
37
|
layoutItem: string | false;
|
|
38
38
|
selfEvaluation: string;
|
|
39
|
-
toggle: {
|
|
40
|
-
container: string;
|
|
41
|
-
title: string;
|
|
42
|
-
iconBtn: string;
|
|
43
|
-
content: string;
|
|
44
|
-
};
|
|
45
39
|
};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { css } from "@emotion/css";
|
|
2
|
-
import { arrowDownSFillSvg, arrowRightSFillSvg } from "../../icons";
|
|
3
2
|
export function getTheme(theme, editable) {
|
|
4
3
|
return {
|
|
5
4
|
paragraph: css `
|
|
@@ -294,42 +293,5 @@ export function getTheme(theme, editable) {
|
|
|
294
293
|
gap: 4px;
|
|
295
294
|
margin-bottom: 8px;
|
|
296
295
|
`,
|
|
297
|
-
toggle: {
|
|
298
|
-
container: css `
|
|
299
|
-
margin: 16px 0;
|
|
300
|
-
&[open] > summary {
|
|
301
|
-
margin-bottom: 4px;
|
|
302
|
-
}
|
|
303
|
-
`,
|
|
304
|
-
title: css `
|
|
305
|
-
display: flex;
|
|
306
|
-
align-items: center;
|
|
307
|
-
gap: 4px;
|
|
308
|
-
|
|
309
|
-
& > :last-child {
|
|
310
|
-
flex: 1;
|
|
311
|
-
min-width: 0;
|
|
312
|
-
margin: 0;
|
|
313
|
-
}
|
|
314
|
-
`,
|
|
315
|
-
iconBtn: css `
|
|
316
|
-
width: 24px;
|
|
317
|
-
height: 24px;
|
|
318
|
-
border: 0;
|
|
319
|
-
padding: 0;
|
|
320
|
-
background-color: transparent;
|
|
321
|
-
cursor: pointer;
|
|
322
|
-
|
|
323
|
-
&[aria-expanded="false"] {
|
|
324
|
-
background-image: url(${arrowRightSFillSvg});
|
|
325
|
-
}
|
|
326
|
-
&[aria-expanded="true"] {
|
|
327
|
-
background-image: url(${arrowDownSFillSvg});
|
|
328
|
-
}
|
|
329
|
-
`,
|
|
330
|
-
content: css `
|
|
331
|
-
padding-left: 28px;
|
|
332
|
-
`,
|
|
333
|
-
},
|
|
334
296
|
};
|
|
335
297
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-monolith/cds",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.120.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"@emotion/css": "^11.11.2",
|
|
9
9
|
"@emotion/react": "^11.8.2",
|
|
10
10
|
"@emotion/styled": "^11.8.1",
|
|
11
|
-
"@lexical/react": "^0.
|
|
11
|
+
"@lexical/react": "^0.32.1",
|
|
12
12
|
"@mui/material": "^5.13.6",
|
|
13
13
|
"@types/node": "^16.11.26",
|
|
14
14
|
"@types/react": "^18.2.28",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"i18next": "^25.5.2",
|
|
19
19
|
"i18next-browser-languagedetector": "^8.2.0",
|
|
20
20
|
"i18next-locize-backend": "^7.0.4",
|
|
21
|
-
"lexical": "^0.
|
|
21
|
+
"lexical": "^0.32.1",
|
|
22
22
|
"lodash": "^4.17.21",
|
|
23
23
|
"moment": "^2.30.1",
|
|
24
24
|
"react": "^18.2.0",
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
import { DOMConversionMap, DOMConversionOutput, DOMExportOutput, EditorConfig, ElementNode, LexicalEditor, LexicalNode, NodeKey, RangeSelection, SerializedElementNode, Spread } from "lexical";
|
|
9
|
-
type SerializedToggleContainerNode = Spread<{
|
|
10
|
-
open: boolean;
|
|
11
|
-
}, SerializedElementNode>;
|
|
12
|
-
export declare function $convertToggleContainerElement(domNode: HTMLDetailsElement): DOMConversionOutput | null;
|
|
13
|
-
export declare class ToggleContainerNode extends ElementNode {
|
|
14
|
-
__open: boolean;
|
|
15
|
-
constructor(open: boolean, key?: NodeKey);
|
|
16
|
-
static getType(): string;
|
|
17
|
-
static clone(node: ToggleContainerNode): ToggleContainerNode;
|
|
18
|
-
isShadowRoot(): boolean;
|
|
19
|
-
collapseAtStart(_selection: RangeSelection): boolean;
|
|
20
|
-
createDOM(config: EditorConfig): HTMLElement;
|
|
21
|
-
updateDOM(prevNode: this, dom: HTMLDivElement): boolean;
|
|
22
|
-
static importDOM(): DOMConversionMap<HTMLDetailsElement> | null;
|
|
23
|
-
static importJSON(serializedNode: SerializedToggleContainerNode): ToggleContainerNode;
|
|
24
|
-
exportDOM(editor: LexicalEditor): DOMExportOutput;
|
|
25
|
-
exportJSON(): SerializedToggleContainerNode;
|
|
26
|
-
setOpen(open: boolean): void;
|
|
27
|
-
getOpen(): boolean;
|
|
28
|
-
toggleOpen(): void;
|
|
29
|
-
}
|
|
30
|
-
export declare function $createToggleContainerNode(isOpen: boolean): ToggleContainerNode;
|
|
31
|
-
export declare function $isToggleContainerNode(node: LexicalNode | null | undefined): node is ToggleContainerNode;
|
|
32
|
-
export {};
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
// https://github.com/facebook/lexical/blob/main/packages/lexical-playground/src/plugins/CollapsiblePlugin/CollapsibleContainerNode.ts
|
|
9
|
-
// details, summary 구조의 타이틀(summary) 클릭 시 토글 기능의 방지를 위해
|
|
10
|
-
// CHROME 브라우저를 포함한 모든 브라우저에서 div 요소로 그려지도록 수정했습니다.
|
|
11
|
-
import { addClassNamesToElement } from "@lexical/utils";
|
|
12
|
-
import { $getSiblingCaret, $isElementNode, $rewindSiblingCaret, ElementNode, isHTMLElement, } from "lexical";
|
|
13
|
-
import { setDomHiddenUntilFound } from "../../utils/toggleUtils";
|
|
14
|
-
import { DATA_LEXICAL_TOGGLE_ICON_BUTTON } from "./ToggleTitleNode";
|
|
15
|
-
export function $convertToggleContainerElement(domNode) {
|
|
16
|
-
// open 속성이 없으면 기본값 false
|
|
17
|
-
const isOpen = "open" in domNode ? domNode.open : false;
|
|
18
|
-
const node = $createToggleContainerNode(isOpen);
|
|
19
|
-
return {
|
|
20
|
-
node,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
export class ToggleContainerNode extends ElementNode {
|
|
24
|
-
constructor(open, key) {
|
|
25
|
-
super(key);
|
|
26
|
-
this.__open = open;
|
|
27
|
-
}
|
|
28
|
-
static getType() {
|
|
29
|
-
return "toggle-container";
|
|
30
|
-
}
|
|
31
|
-
static clone(node) {
|
|
32
|
-
return new ToggleContainerNode(node.__open, node.__key);
|
|
33
|
-
}
|
|
34
|
-
isShadowRoot() {
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
collapseAtStart(_selection) {
|
|
38
|
-
// Unwrap the ToggleContainerNode by replacing it with the children
|
|
39
|
-
// of its children (ToggleTitleNode, ToggleContentNode)
|
|
40
|
-
const nodesToInsert = [];
|
|
41
|
-
for (const child of this.getChildren()) {
|
|
42
|
-
if ($isElementNode(child)) {
|
|
43
|
-
nodesToInsert.push(...child.getChildren());
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
const caret = $rewindSiblingCaret($getSiblingCaret(this, "previous"));
|
|
47
|
-
caret.splice(1, nodesToInsert);
|
|
48
|
-
// Merge the first child of the ToggleTitleNode with the
|
|
49
|
-
// previous sibling of the ToggleContainerNode
|
|
50
|
-
if (nodesToInsert.length === 0)
|
|
51
|
-
return true;
|
|
52
|
-
const firstChild = nodesToInsert[0];
|
|
53
|
-
firstChild.selectStart().deleteCharacter(true);
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
// IS_CHROME 분기 제거
|
|
57
|
-
createDOM(config) {
|
|
58
|
-
const dom = document.createElement("div");
|
|
59
|
-
if (this.__open) {
|
|
60
|
-
dom.setAttribute("open", "");
|
|
61
|
-
}
|
|
62
|
-
addClassNamesToElement(dom, config.theme.toggle.container);
|
|
63
|
-
return dom;
|
|
64
|
-
}
|
|
65
|
-
// IS_CHROME 분기 제거
|
|
66
|
-
updateDOM(prevNode, dom) {
|
|
67
|
-
const currentOpen = this.__open;
|
|
68
|
-
if (prevNode.__open !== currentOpen) {
|
|
69
|
-
const contentDom = dom.children[1];
|
|
70
|
-
if (!isHTMLElement(contentDom)) {
|
|
71
|
-
throw new Error("Expected contentDom to be an HTMLElement");
|
|
72
|
-
}
|
|
73
|
-
if (currentOpen) {
|
|
74
|
-
dom.setAttribute("open", "");
|
|
75
|
-
contentDom.hidden = false;
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
dom.removeAttribute("open");
|
|
79
|
-
setDomHiddenUntilFound(contentDom);
|
|
80
|
-
}
|
|
81
|
-
// 토글 버튼 클릭 또는 타이틀 영역의 엔터 키 입력으로 토글되는 경우
|
|
82
|
-
// ToggleTitleNode의 아이콘 버튼의 aria-expanded 속성 업데이트
|
|
83
|
-
const titleDom = dom.children[0];
|
|
84
|
-
if (!isHTMLElement(titleDom)) {
|
|
85
|
-
throw new Error("Expected titleDom to be an HTMLElement");
|
|
86
|
-
}
|
|
87
|
-
const iconButton = titleDom.querySelector(`[${DATA_LEXICAL_TOGGLE_ICON_BUTTON}]`);
|
|
88
|
-
if (iconButton) {
|
|
89
|
-
iconButton.setAttribute("aria-expanded", currentOpen.toString());
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
static importDOM() {
|
|
95
|
-
return {
|
|
96
|
-
details: (_domNode) => ({
|
|
97
|
-
conversion: $convertToggleContainerElement,
|
|
98
|
-
priority: 1,
|
|
99
|
-
}),
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
static importJSON(serializedNode) {
|
|
103
|
-
return $createToggleContainerNode(serializedNode.open).updateFromJSON(serializedNode);
|
|
104
|
-
}
|
|
105
|
-
// className 적용을 위한 editor 파라미터 추가
|
|
106
|
-
exportDOM(editor) {
|
|
107
|
-
const element = document.createElement("details");
|
|
108
|
-
addClassNamesToElement(element, editor._config.theme.toggle.container);
|
|
109
|
-
element.setAttribute("open", this.__open.toString());
|
|
110
|
-
return { element };
|
|
111
|
-
}
|
|
112
|
-
exportJSON() {
|
|
113
|
-
return Object.assign(Object.assign({}, super.exportJSON()), { open: this.__open });
|
|
114
|
-
}
|
|
115
|
-
setOpen(open) {
|
|
116
|
-
const writable = this.getWritable();
|
|
117
|
-
writable.__open = open;
|
|
118
|
-
}
|
|
119
|
-
getOpen() {
|
|
120
|
-
return this.getLatest().__open;
|
|
121
|
-
}
|
|
122
|
-
toggleOpen() {
|
|
123
|
-
this.setOpen(!this.getOpen());
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
export function $createToggleContainerNode(isOpen) {
|
|
127
|
-
return new ToggleContainerNode(isOpen);
|
|
128
|
-
}
|
|
129
|
-
export function $isToggleContainerNode(node) {
|
|
130
|
-
return node instanceof ToggleContainerNode;
|
|
131
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
import { DOMConversionMap, DOMConversionOutput, DOMExportOutput, EditorConfig, ElementNode, LexicalEditor, LexicalNode, SerializedElementNode } from "lexical";
|
|
9
|
-
type SerializedToggleContentNode = SerializedElementNode;
|
|
10
|
-
export declare function $convertToggleContentElement(_domNode: HTMLElement): DOMConversionOutput | null;
|
|
11
|
-
export declare class ToggleContentNode extends ElementNode {
|
|
12
|
-
static getType(): string;
|
|
13
|
-
static clone(node: ToggleContentNode): ToggleContentNode;
|
|
14
|
-
createDOM(config: EditorConfig, editor: LexicalEditor): HTMLElement;
|
|
15
|
-
updateDOM(_prevNode: this, _dom: HTMLElement): boolean;
|
|
16
|
-
static importDOM(): DOMConversionMap | null;
|
|
17
|
-
exportDOM(editor: LexicalEditor): DOMExportOutput;
|
|
18
|
-
static importJSON(serializedNode: SerializedToggleContentNode): ToggleContentNode;
|
|
19
|
-
isShadowRoot(): boolean;
|
|
20
|
-
}
|
|
21
|
-
export declare function $createToggleContentNode(): ToggleContentNode;
|
|
22
|
-
export declare function $isToggleContentNode(node: LexicalNode | null | undefined): node is ToggleContentNode;
|
|
23
|
-
export {};
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
// https://github.com/facebook/lexical/blob/main/packages/lexical-playground/src/plugins/CollapsiblePlugin/CollapsibleContentNode.ts
|
|
9
|
-
import { addClassNamesToElement } from "@lexical/utils";
|
|
10
|
-
import { ElementNode, } from "lexical";
|
|
11
|
-
import { $isToggleContainerNode } from "./ToggleContainerNode";
|
|
12
|
-
import { domOnBeforeMatch, setDomHiddenUntilFound, } from "../../utils/toggleUtils";
|
|
13
|
-
const DATA_LEXICAL_TOGGLE_CONTENT = "data-lexical-toggle-content";
|
|
14
|
-
export function $convertToggleContentElement(_domNode) {
|
|
15
|
-
const node = $createToggleContentNode();
|
|
16
|
-
return {
|
|
17
|
-
node,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
export class ToggleContentNode extends ElementNode {
|
|
21
|
-
static getType() {
|
|
22
|
-
return "toggle-content";
|
|
23
|
-
}
|
|
24
|
-
static clone(node) {
|
|
25
|
-
return new ToggleContentNode(node.__key);
|
|
26
|
-
}
|
|
27
|
-
// IS_CHROME 분기 제거
|
|
28
|
-
createDOM(config, editor) {
|
|
29
|
-
const dom = document.createElement("div");
|
|
30
|
-
addClassNamesToElement(dom, config.theme.toggle.content);
|
|
31
|
-
editor.getEditorState().read(() => {
|
|
32
|
-
const containerNode = this.getParentOrThrow();
|
|
33
|
-
if (!$isToggleContainerNode(containerNode)) {
|
|
34
|
-
throw new Error("Expected parent node to be a ToggleContainerNode");
|
|
35
|
-
}
|
|
36
|
-
if (!containerNode.__open) {
|
|
37
|
-
setDomHiddenUntilFound(dom);
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
domOnBeforeMatch(dom, () => {
|
|
41
|
-
editor.update(() => {
|
|
42
|
-
const containerNode = this.getParentOrThrow().getLatest();
|
|
43
|
-
if (!$isToggleContainerNode(containerNode)) {
|
|
44
|
-
throw new Error("Expected parent node to be a ToggleContainerNode");
|
|
45
|
-
}
|
|
46
|
-
if (!containerNode.__open) {
|
|
47
|
-
containerNode.toggleOpen();
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
return dom;
|
|
52
|
-
}
|
|
53
|
-
updateDOM(_prevNode, _dom) {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
static importDOM() {
|
|
57
|
-
return {
|
|
58
|
-
div: (domNode) => {
|
|
59
|
-
if (!domNode.hasAttribute(DATA_LEXICAL_TOGGLE_CONTENT)) {
|
|
60
|
-
return null;
|
|
61
|
-
}
|
|
62
|
-
return {
|
|
63
|
-
conversion: $convertToggleContentElement,
|
|
64
|
-
priority: 2,
|
|
65
|
-
};
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
// className 적용을 위한 editor 파라미터 추가
|
|
70
|
-
exportDOM(editor) {
|
|
71
|
-
const element = document.createElement("div");
|
|
72
|
-
addClassNamesToElement(element, editor._config.theme.toggle.content);
|
|
73
|
-
element.setAttribute(DATA_LEXICAL_TOGGLE_CONTENT, "true");
|
|
74
|
-
return { element };
|
|
75
|
-
}
|
|
76
|
-
static importJSON(serializedNode) {
|
|
77
|
-
return $createToggleContentNode().updateFromJSON(serializedNode);
|
|
78
|
-
}
|
|
79
|
-
isShadowRoot() {
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
export function $createToggleContentNode() {
|
|
84
|
-
return new ToggleContentNode();
|
|
85
|
-
}
|
|
86
|
-
export function $isToggleContentNode(node) {
|
|
87
|
-
return node instanceof ToggleContentNode;
|
|
88
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
import { DOMConversionOutput, EditorConfig, ElementNode, LexicalEditor, LexicalNode, RangeSelection } from "lexical";
|
|
9
|
-
export declare const DATA_LEXICAL_TOGGLE_ICON_BUTTON = "data-lexical-toggle-icon-button";
|
|
10
|
-
export declare function $convertToggleTitleElement(_domNode: HTMLElement): DOMConversionOutput | null;
|
|
11
|
-
/** @noInheritDoc */
|
|
12
|
-
export declare class ToggleTitleNode extends ElementNode {
|
|
13
|
-
/** @internal */
|
|
14
|
-
$config(): import("lexical").StaticNodeConfigRecord<"toggle-title", {
|
|
15
|
-
$transform(node: ToggleTitleNode): void;
|
|
16
|
-
extends: typeof ElementNode;
|
|
17
|
-
importDOM: import("lexical").DOMConversionMap<HTMLElement>;
|
|
18
|
-
}>;
|
|
19
|
-
createDOM(config: EditorConfig, editor: LexicalEditor): HTMLElement;
|
|
20
|
-
updateDOM(_prevNode: this, _dom: HTMLElement): boolean;
|
|
21
|
-
insertNewAfter(_: RangeSelection, restoreSelection?: boolean): ElementNode;
|
|
22
|
-
}
|
|
23
|
-
export declare function $createToggleTitleNode(): ToggleTitleNode;
|
|
24
|
-
export declare function $isToggleTitleNode(node: LexicalNode | null | undefined): node is ToggleTitleNode;
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
// https://github.com/facebook/lexical/blob/main/packages/lexical-playground/src/plugins/CollapsiblePlugin/CollapsibleTitleNode.ts
|
|
9
|
-
import { addClassNamesToElement } from "@lexical/utils";
|
|
10
|
-
import { $createParagraphNode, $isElementNode, buildImportMap, ElementNode, } from "lexical";
|
|
11
|
-
import { $isToggleContainerNode } from "./ToggleContainerNode";
|
|
12
|
-
import { $isToggleContentNode } from "./ToggleContentNode";
|
|
13
|
-
export const DATA_LEXICAL_TOGGLE_ICON_BUTTON = "data-lexical-toggle-icon-button";
|
|
14
|
-
export function $convertToggleTitleElement(_domNode) {
|
|
15
|
-
const node = $createToggleTitleNode();
|
|
16
|
-
return {
|
|
17
|
-
node,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
/** @noInheritDoc */
|
|
21
|
-
export class ToggleTitleNode extends ElementNode {
|
|
22
|
-
/** @internal */
|
|
23
|
-
$config() {
|
|
24
|
-
return this.config("toggle-title", {
|
|
25
|
-
$transform(node) {
|
|
26
|
-
if (node.isEmpty()) {
|
|
27
|
-
node.remove();
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
extends: ElementNode,
|
|
31
|
-
importDOM: buildImportMap({
|
|
32
|
-
summary: () => ({
|
|
33
|
-
conversion: $convertToggleTitleElement,
|
|
34
|
-
priority: 1,
|
|
35
|
-
}),
|
|
36
|
-
}),
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
// IS_CHROME 분기를 제거하여 모든 브라우저에서 클릭 시 토글 이벤트를 수동으로 처리
|
|
40
|
-
createDOM(config, editor) {
|
|
41
|
-
const dom = document.createElement("summary");
|
|
42
|
-
addClassNamesToElement(dom, config.theme.toggle.title);
|
|
43
|
-
// 토글 동작을 아이콘 버튼에만 적용하도록 iconBtn 요소를 추가
|
|
44
|
-
const iconBtn = document.createElement("div");
|
|
45
|
-
addClassNamesToElement(iconBtn, config.theme.toggle.iconBtn);
|
|
46
|
-
iconBtn.setAttribute("aria-label", "Toggle content");
|
|
47
|
-
iconBtn.setAttribute("contenteditable", "false");
|
|
48
|
-
iconBtn.setAttribute(DATA_LEXICAL_TOGGLE_ICON_BUTTON, "true");
|
|
49
|
-
editor.getEditorState().read(() => {
|
|
50
|
-
const containerNode = this.getParent();
|
|
51
|
-
if ($isToggleContainerNode(containerNode)) {
|
|
52
|
-
iconBtn.setAttribute("aria-expanded", containerNode.getOpen().toString());
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
iconBtn.setAttribute("aria-expanded", "false");
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
iconBtn.addEventListener("click", () => {
|
|
59
|
-
editor.update(() => {
|
|
60
|
-
const toggleContainer = this.getLatest().getParentOrThrow();
|
|
61
|
-
if (!$isToggleContainerNode(toggleContainer)) {
|
|
62
|
-
throw new Error("Expected parent node to be a ToggleContainerNode");
|
|
63
|
-
}
|
|
64
|
-
toggleContainer.toggleOpen();
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
dom.appendChild(iconBtn);
|
|
68
|
-
return dom;
|
|
69
|
-
}
|
|
70
|
-
updateDOM(_prevNode, _dom) {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
insertNewAfter(_, restoreSelection = true) {
|
|
74
|
-
const containerNode = this.getParentOrThrow();
|
|
75
|
-
if (!$isToggleContainerNode(containerNode)) {
|
|
76
|
-
throw new Error("ToggleTitleNode expects to be child of ToggleContainerNode");
|
|
77
|
-
}
|
|
78
|
-
if (containerNode.getOpen()) {
|
|
79
|
-
const contentNode = this.getNextSibling();
|
|
80
|
-
if (!$isToggleContentNode(contentNode)) {
|
|
81
|
-
throw new Error("ToggleTitleNode expects to have ToggleContentNode sibling");
|
|
82
|
-
}
|
|
83
|
-
const firstChild = contentNode.getFirstChild();
|
|
84
|
-
if ($isElementNode(firstChild)) {
|
|
85
|
-
return firstChild;
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
const paragraph = $createParagraphNode();
|
|
89
|
-
contentNode.append(paragraph);
|
|
90
|
-
return paragraph;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
const paragraph = $createParagraphNode();
|
|
95
|
-
containerNode.insertAfter(paragraph, restoreSelection);
|
|
96
|
-
return paragraph;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
export function $createToggleTitleNode() {
|
|
101
|
-
return new ToggleTitleNode();
|
|
102
|
-
}
|
|
103
|
-
export function $isToggleTitleNode(node) {
|
|
104
|
-
return node instanceof ToggleTitleNode;
|
|
105
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
export declare const INSERT_TOGGLE_COMMAND: import("lexical").LexicalCommand<void>;
|
|
9
|
-
export declare function TogglePlugin(): null;
|
|
@@ -1,198 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
// https://github.com/facebook/lexical/blob/main/packages/lexical-playground/src/plugins/CollapsiblePlugin/index.ts
|
|
9
|
-
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
10
|
-
import { $setBlocksType } from "@lexical/selection";
|
|
11
|
-
import { $findMatchingParent, $insertNodeToNearestRoot, mergeRegister, } from "@lexical/utils";
|
|
12
|
-
import { $createParagraphNode, $getSelection, $isElementNode, $isParagraphNode, $isRangeSelection, $isTextNode, COMMAND_PRIORITY_LOW, createCommand, INSERT_PARAGRAPH_COMMAND, KEY_ARROW_DOWN_COMMAND, KEY_ARROW_LEFT_COMMAND, KEY_ARROW_RIGHT_COMMAND, KEY_ARROW_UP_COMMAND, } from "lexical";
|
|
13
|
-
import { useEffect } from "react";
|
|
14
|
-
import { $createToggleContainerNode, $createToggleTitleNode, $createToggleContentNode, $isToggleContainerNode, $isToggleTitleNode, $isToggleContentNode, ToggleContainerNode, ToggleTitleNode, ToggleContentNode, } from "../../nodes";
|
|
15
|
-
export const INSERT_TOGGLE_COMMAND = createCommand("INSERT_TOGGLE_COMMAND");
|
|
16
|
-
export function TogglePlugin() {
|
|
17
|
-
const [editor] = useLexicalComposerContext();
|
|
18
|
-
useEffect(() => {
|
|
19
|
-
if (!editor.hasNodes([
|
|
20
|
-
ToggleContainerNode,
|
|
21
|
-
ToggleTitleNode,
|
|
22
|
-
ToggleContentNode,
|
|
23
|
-
])) {
|
|
24
|
-
throw new Error("TogglePlugin: ToggleContainerNode, ToggleTitleNode, or ToggleContentNode not registered on editor");
|
|
25
|
-
}
|
|
26
|
-
const $onEscapeUp = () => {
|
|
27
|
-
var _a;
|
|
28
|
-
const selection = $getSelection();
|
|
29
|
-
if ($isRangeSelection(selection) &&
|
|
30
|
-
selection.isCollapsed() &&
|
|
31
|
-
selection.anchor.offset === 0) {
|
|
32
|
-
const container = $findMatchingParent(selection.anchor.getNode(), $isToggleContainerNode);
|
|
33
|
-
if ($isToggleContainerNode(container)) {
|
|
34
|
-
const parent = container.getParent();
|
|
35
|
-
if (parent !== null &&
|
|
36
|
-
parent.getFirstChild() === container &&
|
|
37
|
-
selection.anchor.key === ((_a = container.getFirstDescendant()) === null || _a === void 0 ? void 0 : _a.getKey())) {
|
|
38
|
-
container.insertBefore($createParagraphNode());
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return false;
|
|
43
|
-
};
|
|
44
|
-
const $onEscapeDown = () => {
|
|
45
|
-
const selection = $getSelection();
|
|
46
|
-
if ($isRangeSelection(selection) && selection.isCollapsed()) {
|
|
47
|
-
const container = $findMatchingParent(selection.anchor.getNode(), $isToggleContainerNode);
|
|
48
|
-
if ($isToggleContainerNode(container)) {
|
|
49
|
-
const parent = container.getParent();
|
|
50
|
-
if (parent !== null && parent.getLastChild() === container) {
|
|
51
|
-
const titleParagraph = container.getFirstDescendant();
|
|
52
|
-
const contentParagraph = container.getLastDescendant();
|
|
53
|
-
if ((contentParagraph !== null &&
|
|
54
|
-
selection.anchor.key === contentParagraph.getKey() &&
|
|
55
|
-
selection.anchor.offset ===
|
|
56
|
-
contentParagraph.getTextContentSize()) ||
|
|
57
|
-
(titleParagraph !== null &&
|
|
58
|
-
selection.anchor.key === titleParagraph.getKey() &&
|
|
59
|
-
selection.anchor.offset === titleParagraph.getTextContentSize())) {
|
|
60
|
-
container.insertAfter($createParagraphNode());
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return false;
|
|
66
|
-
};
|
|
67
|
-
return mergeRegister(
|
|
68
|
-
// Structure enforcing transformers for each node type. In case nesting structure is not
|
|
69
|
-
// "Container > Title + Content" it'll unwrap nodes and convert it back
|
|
70
|
-
// to regular content.
|
|
71
|
-
editor.registerNodeTransform(ToggleContentNode, (node) => {
|
|
72
|
-
const parent = node.getParent();
|
|
73
|
-
if (!$isToggleContainerNode(parent)) {
|
|
74
|
-
const children = node.getChildren();
|
|
75
|
-
for (const child of children) {
|
|
76
|
-
node.insertBefore(child);
|
|
77
|
-
}
|
|
78
|
-
node.remove();
|
|
79
|
-
}
|
|
80
|
-
}),
|
|
81
|
-
/**
|
|
82
|
-
* ToggleTitleNode 변경 시 내부 요소는 ParagraphNode만 허용하도록
|
|
83
|
-
* 각 자식 요소를 순회하면서 내부 텍스트를 ParagraphNode로 호이스트합니다.
|
|
84
|
-
*/
|
|
85
|
-
editor.registerNodeTransform(ToggleTitleNode, (node) => {
|
|
86
|
-
const parent = node.getParent();
|
|
87
|
-
if (!$isToggleContainerNode(parent)) {
|
|
88
|
-
node.replace($createParagraphNode().append(...node.getChildren()));
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
let paragraph = node.getFirstChild();
|
|
92
|
-
if (!$isParagraphNode(paragraph)) {
|
|
93
|
-
const p = $createParagraphNode();
|
|
94
|
-
if (paragraph) {
|
|
95
|
-
paragraph.insertBefore(p);
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
node.append(p);
|
|
99
|
-
}
|
|
100
|
-
paragraph = p;
|
|
101
|
-
}
|
|
102
|
-
// AIDEV-NOTE:
|
|
103
|
-
// paragraph의 타입이 LexicalNode인 경우 append 메서드가 없기 때문에
|
|
104
|
-
// $isParagraphNode 체크를 통해 타입 가드를 수행합니다.
|
|
105
|
-
if (!$isParagraphNode(paragraph)) {
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
const hoistTexts = (n) => {
|
|
109
|
-
if ($isTextNode(n)) {
|
|
110
|
-
paragraph.append(n);
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
if ($isElementNode(n)) {
|
|
114
|
-
const children = n.getChildren();
|
|
115
|
-
for (const c of children) {
|
|
116
|
-
hoistTexts(c);
|
|
117
|
-
}
|
|
118
|
-
if (n !== paragraph && n.getChildrenSize() === 0) {
|
|
119
|
-
n.remove();
|
|
120
|
-
}
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
let child = node.getFirstChild();
|
|
125
|
-
while (child) {
|
|
126
|
-
const next = child.getNextSibling();
|
|
127
|
-
if (child !== paragraph) {
|
|
128
|
-
hoistTexts(child);
|
|
129
|
-
if ($isElementNode(child) && child.getChildrenSize() === 0) {
|
|
130
|
-
child.remove();
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
child = next;
|
|
134
|
-
}
|
|
135
|
-
if (paragraph.getParent() !== node) {
|
|
136
|
-
node.append(paragraph);
|
|
137
|
-
}
|
|
138
|
-
}), editor.registerNodeTransform(ToggleContainerNode, (node) => {
|
|
139
|
-
const children = node.getChildren();
|
|
140
|
-
if (children.length !== 2 ||
|
|
141
|
-
!$isToggleTitleNode(children[0]) ||
|
|
142
|
-
!$isToggleContentNode(children[1])) {
|
|
143
|
-
for (const child of children) {
|
|
144
|
-
node.insertBefore(child);
|
|
145
|
-
}
|
|
146
|
-
node.remove();
|
|
147
|
-
}
|
|
148
|
-
}),
|
|
149
|
-
// When toggle is the last child pressing down/right arrow will insert paragraph
|
|
150
|
-
// below it to allow adding more content. It's similar what $insertBlockNode
|
|
151
|
-
// (mainly for decorators), except it'll always be possible to continue adding
|
|
152
|
-
// new content even if trailing paragraph is accidentally deleted
|
|
153
|
-
editor.registerCommand(KEY_ARROW_DOWN_COMMAND, $onEscapeDown, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ARROW_RIGHT_COMMAND, $onEscapeDown, COMMAND_PRIORITY_LOW),
|
|
154
|
-
// When toggle is the first child pressing up/left arrow will insert paragraph
|
|
155
|
-
// above it to allow adding more content. It's similar what $insertBlockNode
|
|
156
|
-
// (mainly for decorators), except it'll always be possible to continue adding
|
|
157
|
-
// new content even if leading paragraph is accidentally deleted
|
|
158
|
-
editor.registerCommand(KEY_ARROW_UP_COMMAND, $onEscapeUp, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ARROW_LEFT_COMMAND, $onEscapeUp, COMMAND_PRIORITY_LOW),
|
|
159
|
-
// Enter goes from Title to Content rather than a new line inside Title
|
|
160
|
-
editor.registerCommand(INSERT_PARAGRAPH_COMMAND, () => {
|
|
161
|
-
var _a;
|
|
162
|
-
const selection = $getSelection();
|
|
163
|
-
if ($isRangeSelection(selection)) {
|
|
164
|
-
const titleNode = $findMatchingParent(selection.anchor.getNode(), (node) => $isToggleTitleNode(node));
|
|
165
|
-
if ($isToggleTitleNode(titleNode)) {
|
|
166
|
-
const container = titleNode.getParent();
|
|
167
|
-
if (container && $isToggleContainerNode(container)) {
|
|
168
|
-
if (!container.getOpen()) {
|
|
169
|
-
container.toggleOpen();
|
|
170
|
-
}
|
|
171
|
-
(_a = titleNode.getNextSibling()) === null || _a === void 0 ? void 0 : _a.selectEnd();
|
|
172
|
-
return true;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return false;
|
|
177
|
-
}, COMMAND_PRIORITY_LOW), editor.registerCommand(INSERT_TOGGLE_COMMAND, () => {
|
|
178
|
-
editor.update(() => {
|
|
179
|
-
const title = $createToggleTitleNode();
|
|
180
|
-
const paragraph = $createParagraphNode();
|
|
181
|
-
const selection = $getSelection();
|
|
182
|
-
if ($isRangeSelection(selection)) {
|
|
183
|
-
// 선택된 블록이 있는 경우 기존 블록을 토글 블록으로 변환하여 불필요한 줄바꿈 생성 방지
|
|
184
|
-
$setBlocksType(selection, () =>
|
|
185
|
-
// 생성된 토글 블록의 open 속성은 기본값 false (노션 동작을 따름)
|
|
186
|
-
$createToggleContainerNode(false).append(title.append(paragraph), $createToggleContentNode().append($createParagraphNode())));
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
// 선택된 블록이 없는 경우 레퍼런스 코드의 동작을 따름
|
|
190
|
-
$insertNodeToNearestRoot($createToggleContainerNode(false).append(title.append(paragraph), $createToggleContentNode().append($createParagraphNode())));
|
|
191
|
-
}
|
|
192
|
-
paragraph.select();
|
|
193
|
-
});
|
|
194
|
-
return true;
|
|
195
|
-
}, COMMAND_PRIORITY_LOW));
|
|
196
|
-
}, [editor]);
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
export declare function setDomHiddenUntilFound(dom: HTMLElement): void;
|
|
9
|
-
export declare function domOnBeforeMatch(dom: HTMLElement, callback: () => void): void;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the MIT license found in the
|
|
5
|
-
* LICENSE file in the root directory of this source tree.
|
|
6
|
-
*
|
|
7
|
-
*/
|
|
8
|
-
// https://github.com/facebook/lexical/blob/main/packages/lexical-playground/src/plugins/CollapsiblePlugin/CollapsibleUtils.ts
|
|
9
|
-
export function setDomHiddenUntilFound(dom) {
|
|
10
|
-
// @ts-expect-error 'hidden' 속성은 boolean으로만 정의되어 있지만,
|
|
11
|
-
// Chromium 계열 브라우저에서는 실험적으로 'until-found' 문자열 값도 지원한다.
|
|
12
|
-
dom.hidden = "until-found";
|
|
13
|
-
}
|
|
14
|
-
export function domOnBeforeMatch(dom, callback) {
|
|
15
|
-
if ("onbeforematch" in dom) {
|
|
16
|
-
dom.onbeforematch = callback;
|
|
17
|
-
}
|
|
18
|
-
}
|