@team-monolith/cds 1.8.6 → 1.8.7
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/patterns/LexicalEditor/nodes/ProblemInputNode/InputComponent.js +13 -12
- package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/ProblemInputNode.d.ts +0 -1
- package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/SettingForm/FormCharacterCount.d.ts +2 -3
- package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/SettingForm/FormCharacterCount.js +2 -2
- package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/SettingForm/FormSolution.d.ts +1 -1
- package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/SettingForm/FormSolution.js +13 -12
- package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/SettingForm/SettingForm.js +15 -19
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/ComponentAdderPlugin.js +10 -4
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/useContextMenuOptions.d.ts +2 -2
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuList.d.ts +2 -2
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuList.js +2 -1
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.d.ts +7 -4
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.js +20 -22
- package/dist/patterns/LexicalEditor/plugins/ProblemInputPlugin/index.d.ts +1 -1
- package/dist/patterns/LexicalEditor/plugins/ProblemInputPlugin/index.js +5 -6
- package/dist/patterns/LexicalEditor/theme.js +2 -1
- package/package.json +1 -1
|
@@ -19,7 +19,7 @@ import { Settings3FillIcon } from "../../../../icons";
|
|
|
19
19
|
import styled from "@emotion/styled";
|
|
20
20
|
import SettingForm from "./SettingForm";
|
|
21
21
|
import useLexicalEditable from "@lexical/react/useLexicalEditable";
|
|
22
|
-
import { $isProblemInputNode
|
|
22
|
+
import { $isProblemInputNode } from "./ProblemInputNode";
|
|
23
23
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
24
24
|
import { $getNodeByKey } from "lexical";
|
|
25
25
|
export function InputComponent(props) {
|
|
@@ -28,20 +28,21 @@ export function InputComponent(props) {
|
|
|
28
28
|
const [editor] = useLexicalComposerContext();
|
|
29
29
|
const [settingOpen, setSettingOpen] = useState(false);
|
|
30
30
|
const isEditable = useLexicalEditable();
|
|
31
|
+
const [answerInput, setAnswerInput] = useState(answer);
|
|
31
32
|
// 학생 view
|
|
32
33
|
// TODO: "글자 수대로" 옵션시에 글자 수대로 입력칸을 표시해야 합니다.
|
|
33
34
|
if (!isEditable) {
|
|
34
|
-
return (_jsx(Input, { size: "small", placeholder: placeholder || "여기에 입력하세요.", value:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
35
|
+
return (_jsx(Input, { size: "small", placeholder: placeholder || "여기에 입력하세요.", value: answerInput, onChange: (e) => setAnswerInput(e.target.value), inputProps: {
|
|
36
|
+
onBlur: (_e) => {
|
|
37
|
+
editor.update(() => {
|
|
38
|
+
const node = $getNodeByKey(nodeKey);
|
|
39
|
+
if (!$isProblemInputNode(node)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
node.setAnswer(answerInput);
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
}, color: "default", fullWidth: true }));
|
|
45
46
|
}
|
|
46
47
|
// 교사 edit view
|
|
47
48
|
return (_jsxs(_Fragment, { children: [_jsxs("div", Object.assign({ css: css `
|
package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/SettingForm/FormCharacterCount.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { Control } from "react-hook-form";
|
|
2
|
-
import React from "react";
|
|
1
|
+
import { Control, UseFormTrigger } from "react-hook-form";
|
|
3
2
|
import { ProblemInputPayload } from "../ProblemInputNode";
|
|
4
3
|
export interface FormCharacterCountProps {
|
|
5
4
|
control: Control<ProblemInputPayload, any>;
|
|
6
|
-
|
|
5
|
+
trigger: UseFormTrigger<ProblemInputPayload>;
|
|
7
6
|
}
|
|
8
7
|
export declare function FormCharacterCount(props: FormCharacterCountProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/SettingForm/FormCharacterCount.js
CHANGED
|
@@ -2,11 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Controller } from "react-hook-form";
|
|
3
3
|
import { SegmentedControlButton, SegmentedControlGroup, } from "../../../../SegmentedControl";
|
|
4
4
|
export function FormCharacterCount(props) {
|
|
5
|
-
const { control,
|
|
5
|
+
const { control, trigger } = props;
|
|
6
6
|
return (_jsx(Controller, { name: "showCharacterCount", control: control, render: ({ field: { value, onChange } }) => {
|
|
7
7
|
return (_jsxs(SegmentedControlGroup, Object.assign({ size: "xsmall", value: value.toString(), onChange: (value) => {
|
|
8
8
|
onChange(value === "true");
|
|
9
|
-
|
|
9
|
+
trigger("solutions");
|
|
10
10
|
}, fullWidth: true }, { children: [_jsx(SegmentedControlButton, { value: "false", label: "\uD55C \uCE78\uC73C\uB85C" }), _jsx(SegmentedControlButton, { value: "true", label: "\uAE00\uC790 \uC218\uB300\uB85C" })] })));
|
|
11
11
|
} }));
|
|
12
12
|
}
|
|
@@ -3,7 +3,7 @@ import { ProblemInputPayload } from "../ProblemInputNode";
|
|
|
3
3
|
export interface FormAnswerProps {
|
|
4
4
|
index: number;
|
|
5
5
|
control: Control<ProblemInputPayload, any>;
|
|
6
|
+
rules?: any;
|
|
6
7
|
onDelete?: () => void;
|
|
7
|
-
disabled?: boolean;
|
|
8
8
|
}
|
|
9
9
|
export declare function FormSolution(props: FormAnswerProps): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -9,9 +9,9 @@ import { AlertFillIcon, DeleteBinLineIcon, ErrorWarningFillIcon, } from "../../.
|
|
|
9
9
|
import SquareButton from "../../../../../components/SquareButton";
|
|
10
10
|
import Tooltip from "../../../../../components/Tooltip";
|
|
11
11
|
export function FormSolution(props) {
|
|
12
|
-
const { index, control,
|
|
12
|
+
const { index, control, rules, onDelete } = props;
|
|
13
13
|
const theme = useTheme();
|
|
14
|
-
const TextTypeDropdown = (_jsx(Controller, { name: `solutions.${index}.textType`, control: control, render: ({ field: { value, onChange } }) => (_jsx(Dropdown, Object.assign({ label: value === "normal" ? "일반 텍스트" : "코드 텍스트", size: "xsmall", color: "textNeutral", closeOnItemClick: true, disabled: disabled, buttonCss: css `
|
|
14
|
+
const TextTypeDropdown = (disabled) => (_jsx(Controller, { name: `solutions.${index}.textType`, control: control, render: ({ field: { value, onChange } }) => (_jsx(Dropdown, Object.assign({ label: value === "normal" ? "일반 텍스트" : "코드 텍스트", size: "xsmall", color: "textNeutral", closeOnItemClick: true, disabled: disabled, buttonCss: css `
|
|
15
15
|
${disabled && `color: ${theme.color.foreground.neutralAlt};`}
|
|
16
16
|
> span {
|
|
17
17
|
font-weight: 700;
|
|
@@ -28,14 +28,15 @@ export function FormSolution(props) {
|
|
|
28
28
|
} }, { children: _jsx(DropdownItem, { index: 0, label: value === "normal" ? "코드 텍스트" : "일반 텍스트", onClick: () => {
|
|
29
29
|
onChange(value === "normal" ? "code" : "normal");
|
|
30
30
|
} }) }))) }));
|
|
31
|
-
return (_jsx(Controller, { name: `solutions.${index}.value`, control: control, rules: {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
return (_jsx(Controller, { name: `solutions.${index}.value`, control: control, rules: rules, render: ({ field: { value, onChange }, fieldState: { invalid, error }, }) => {
|
|
32
|
+
const disabled = (error === null || error === void 0 ? void 0 : error.type) === "multiAnswerDisabled";
|
|
33
|
+
return (_jsx(Input, { size: "small", color: invalid ? "activeDanger" : "default", onChange: onChange, disabled: disabled, value: value, hintIcon: !disabled && invalid ? _jsx(ErrorWarningFillIcon, {}) : undefined, hintText: !disabled ? error === null || error === void 0 ? void 0 : error.message : undefined, placeholder: "\uC548\uB155\uD558\uC138\uC694", fullWidth: true, css: css `
|
|
34
|
+
> div {
|
|
35
|
+
padding: 4px 12px;
|
|
36
|
+
}
|
|
37
|
+
`, startIcon: TextTypeDropdown(disabled), endIcon: _jsxs("div", Object.assign({ css: css `
|
|
38
|
+
display: flex;
|
|
39
|
+
gap: 4px;
|
|
40
|
+
` }, { children: [onDelete && (_jsx(SquareButton, { color: "white", size: "xsmall", icon: _jsx(DeleteBinLineIcon, {}), onClick: onDelete })), disabled && (_jsx(Tooltip, Object.assign({ text: _jsxs("span", { children: ["\uC785\uB825 \uCE78 \uC124\uC815\uC774 '\uAE00\uC790 \uC218\uB300\uB85C'\uC778 \uACBD\uC6B0", _jsx("br", {}), "\uC815\uB2F5\uC744 \uD558\uB098\uB9CC \uB4F1\uB85D\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4."] }) }, { children: _jsx(SquareButton, { color: "danger", size: "xsmall", icon: _jsx(AlertFillIcon, { color: theme.color.foreground.neutralAlt }), disabled: true }) })))] })) }));
|
|
41
|
+
} }));
|
|
41
42
|
}
|
|
@@ -13,13 +13,11 @@ import { FormCharacterCount } from "./FormCharacterCount";
|
|
|
13
13
|
import { FormPlaceholder } from "./FormPlaceholder";
|
|
14
14
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
15
15
|
import { $getNodeByKey } from "lexical";
|
|
16
|
-
import { useState } from "react";
|
|
17
16
|
export default function SettingForm(props) {
|
|
18
17
|
const { solutions, showCharacterCount, placeholder, nodeKey, onClose } = props;
|
|
19
18
|
const [editor] = useLexicalComposerContext();
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const { control, handleSubmit } = useForm({
|
|
19
|
+
const { control, handleSubmit, watch, trigger } = useForm({
|
|
20
|
+
mode: "all",
|
|
23
21
|
defaultValues: {
|
|
24
22
|
solutions,
|
|
25
23
|
showCharacterCount,
|
|
@@ -27,7 +25,7 @@ export default function SettingForm(props) {
|
|
|
27
25
|
},
|
|
28
26
|
});
|
|
29
27
|
const theme = useTheme();
|
|
30
|
-
const { fields, append, remove
|
|
28
|
+
const { fields, append, remove } = useFieldArray({
|
|
31
29
|
control,
|
|
32
30
|
name: "solutions",
|
|
33
31
|
keyName: "uid",
|
|
@@ -44,23 +42,21 @@ export default function SettingForm(props) {
|
|
|
44
42
|
});
|
|
45
43
|
onClose();
|
|
46
44
|
};
|
|
45
|
+
const multiAnswerDisabled = watch("showCharacterCount");
|
|
47
46
|
return (_jsxs(Form, Object.assign({ onSubmit: handleSubmit(onSettingSubmit) }, { children: [_jsxs(Title, { children: [_jsx(InputMethodLineIcon, { css: css `
|
|
48
47
|
width: 12px;
|
|
49
48
|
height: 12px;
|
|
50
|
-
` }), "\uC8FC\uAD00\uC2DD \uC785\uB825 \uCE78"] }), _jsxs(Content, { children: [_jsxs(Left, { children: [_jsxs(FormArea, { children: [_jsx(Label, { children: "\uC815\uB2F5" }), fields
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
` }), "\uC8FC\uAD00\uC2DD \uC785\uB825 \uCE78"] }), _jsxs(Content, { children: [_jsxs(Left, { children: [_jsxs(FormArea, { children: [_jsx(Label, { children: "\uC815\uB2F5" }), fields.map((field, index) => (_jsx(FormSolution, { index: index, control: control, rules: {
|
|
50
|
+
validate: {
|
|
51
|
+
// required 옵션보다 먼저 검증되어야 하는데 priority 옵션이 없어서 validate에서 통합해서 검증합니다.
|
|
52
|
+
multiAnswerDisabled: () => index === 0 ||
|
|
53
|
+
!multiAnswerDisabled ||
|
|
54
|
+
"복수 정답이 불가능합니다.",
|
|
55
|
+
required: (value) => value !== "" || "정답을 입력해주세요.",
|
|
56
|
+
},
|
|
57
|
+
}, onDelete: index !== 0
|
|
57
58
|
? () => {
|
|
58
|
-
|
|
59
|
-
update(index, Object.assign(Object.assign({}, field), { destroyed: true }));
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
remove(index);
|
|
63
|
-
}
|
|
59
|
+
remove(index);
|
|
64
60
|
}
|
|
65
61
|
: undefined }, field.uid)))] }), _jsx(Button, { color: "grey", size: "small", startIcon: _jsx(AddFillIcon, {}), label: "\uBCF5\uC218 \uC815\uB2F5 \uCD94\uAC00", disabled: multiAnswerDisabled, onClick: () => {
|
|
66
62
|
append({
|
|
@@ -76,7 +72,7 @@ export default function SettingForm(props) {
|
|
|
76
72
|
` }), _jsxs(Label, { children: ["\uB744\uC5B4\uC4F0\uAE30, \uC54C\uD30C\uBCB3\uC758 \uB300\uC18C\uBB38\uC790 \uAD6C\uBD84\uAE4C\uC9C0 \uC77C\uCE58\uD574\uC57C \uC815\uB2F5\uC73C\uB85C \uCC98\uB9AC\uB429\uB2C8\uB2E4.", _jsx("br", {}), "\uAC00\uB2A5\uD55C \uC815\uB2F5\uC744 \uBAA8\uB450 \uCD94\uAC00\uD574\uC57C \uC6D0\uD65C\uD558\uAC8C \uC790\uB3D9 \uCC44\uC810\uD560 \uC218 \uC788\uC5B4\uC694."] })] }))] }), _jsxs(Right, { children: [_jsxs(FormArea, { children: [_jsxs(Label, { children: ["\uC785\uB825 \uCE78", _jsx(Tooltip, Object.assign({ text: _jsxs("span", { children: ["\uC608\uB97C \uB4E4\uC5B4 \uC815\uB2F5\uC774 '\uAE00\uC790 \uC218'\uC774\uACE0", _jsx("br", {}), _jsx("b", { children: "\uAE00\uC790 \uC218\uB300\uB85C" }), " \uC635\uC158\uC744 \uC120\uD0DD\uD588\uB2E4\uBA74", _jsx("br", {}), "\uC785\uB825 \uCE78\uC774 '\u2610\u2610 \u2610' \uCC98\uB7FC \uD45C\uC2DC\uB429\uB2C8\uB2E4."] }), placement: "top" }, { children: _jsx(QuestionFillIcon, { css: css `
|
|
77
73
|
width: 12px;
|
|
78
74
|
height: 12px;
|
|
79
|
-
` }) }))] }), _jsx(FormCharacterCount, { control: control,
|
|
75
|
+
` }) }))] }), _jsx(FormCharacterCount, { control: control, trigger: trigger })] }), _jsxs(FormArea, { children: [_jsxs(Label, { children: ["\uC790\uB9AC \uD45C\uC2DC\uC790", _jsx(Tooltip, Object.assign({ text: _jsx("span", { children: "\uC785\uB825 \uCE78\uC5D0 \uAE30\uBCF8\uC73C\uB85C \uB178\uCD9C\uB418\uB294 \uD14D\uC2A4\uD2B8\uC785\uB2C8\uB2E4." }), placement: "top" }, { children: _jsx(QuestionFillIcon, { css: css `
|
|
80
76
|
width: 12px;
|
|
81
77
|
height: 12px;
|
|
82
78
|
` }) }))] }), _jsx(FormPlaceholder, { control: control })] })] })] }), _jsxs(Buttons, { children: [_jsx(Button, { color: "grey", size: "xsmall", label: "\uB2EB\uAE30", onClick: onClose }), _jsx(Button, { color: "primary", size: "xsmall", label: "\uC774\uB300\uB85C \uB123\uAE30", type: "submit" })] })] })));
|
|
@@ -22,7 +22,7 @@ import ReactDOM, { createPortal } from "react-dom";
|
|
|
22
22
|
import { LexicalNodeMenuPlugin } from "@lexical/react/LexicalNodeMenuPlugin";
|
|
23
23
|
import { useDraggableBlockMenu } from "./useDraggableBlockMenu";
|
|
24
24
|
import { css as cssToClassName } from "@emotion/css";
|
|
25
|
-
import { ComponentPickerMenuList, getBaseOptions, } from "../ComponentPickerMenuPlugin";
|
|
25
|
+
import { ComponentPickerMenuList, getBaseOptions, ComponentDrawerOption, } from "../ComponentPickerMenuPlugin";
|
|
26
26
|
import { useFloatingMenu } from "./useFloatingMenu";
|
|
27
27
|
import ComponentAdder from "./ComponentAdder";
|
|
28
28
|
import styled from "@emotion/styled";
|
|
@@ -115,6 +115,9 @@ export function ComponentAdderPlugin(props) {
|
|
|
115
115
|
};
|
|
116
116
|
}, [editor, nodeKey, plusOrMenu]);
|
|
117
117
|
const onSelectOption = useCallback((selectedOption, textNodeContainingQuery, closeMenu, matchingString) => {
|
|
118
|
+
if (selectedOption instanceof ComponentDrawerOption) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
118
121
|
editor.update(() => {
|
|
119
122
|
selectedOption.onSelect(matchingString);
|
|
120
123
|
setNodeKey(null);
|
|
@@ -130,10 +133,13 @@ export function ComponentAdderPlugin(props) {
|
|
|
130
133
|
});
|
|
131
134
|
const getContextMenuOptions = useContextMenuOptions();
|
|
132
135
|
const filteredOptions = options.filter((option) => {
|
|
136
|
+
if (!query) {
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
133
139
|
const regex = new RegExp(query, "i");
|
|
134
|
-
return (option.
|
|
135
|
-
regex.test(option.title) ||
|
|
136
|
-
|
|
140
|
+
return (option.keywords &&
|
|
141
|
+
(regex.test(option.title) ||
|
|
142
|
+
option.keywords.some((keyword) => regex.test(keyword))));
|
|
137
143
|
});
|
|
138
144
|
const { onDragStart, onDragEnd, targetLineRef } = useDraggableBlockMenu(editor, anchorElem, blockElem, setBlockElem);
|
|
139
145
|
return (_jsxs(_Fragment, { children: [_jsx(InsertImageDialog, { open: imageOpen, activeEditor: editor, onClose: () => setImageOpen(false) }), _jsx(LexicalNodeMenuPlugin, { nodeKey: nodeKey, anchorClassName: cssToClassName `
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* Context Menu (:: 버튼)
|
|
3
3
|
*/
|
|
4
4
|
import { LexicalEditor, LexicalNode } from "lexical";
|
|
5
|
-
import { ComponentPickerOption } from "../ComponentPickerMenuPlugin";
|
|
6
|
-
export declare function useContextMenuOptions(): (editor: LexicalEditor, node: LexicalNode, setImageOpen: (open: boolean) => void) => ComponentPickerOption[];
|
|
5
|
+
import { ComponentDrawerOption, ComponentPickerOption } from "../ComponentPickerMenuPlugin";
|
|
6
|
+
export declare function useContextMenuOptions(): (editor: LexicalEditor, node: LexicalNode, setImageOpen: (open: boolean) => void) => (ComponentPickerOption | ComponentDrawerOption)[];
|
package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuList.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import { ComponentPickerOption } from "./ComponentPickerMenuPlugin";
|
|
2
|
+
import { ComponentDrawerOption, ComponentPickerOption } from "./ComponentPickerMenuPlugin";
|
|
3
3
|
export interface ComponentPickerMenuListProps {
|
|
4
|
-
options: ComponentPickerOption[];
|
|
4
|
+
options: (ComponentPickerOption | ComponentDrawerOption)[];
|
|
5
5
|
selectedIndex: number | null;
|
|
6
6
|
selectOptionAndCleanUp: (option: ComponentPickerOption) => void;
|
|
7
7
|
setHighlightedIndex: (index: number) => void;
|
package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuList.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import styled from "@emotion/styled";
|
|
3
|
+
import { ComponentDrawerOption, } from "./ComponentPickerMenuPlugin";
|
|
3
4
|
import { ComponentPickerMenuItem } from "./ComponentPickerMenuItem";
|
|
4
5
|
import React from "react";
|
|
5
6
|
export function ComponentPickerMenuList(props) {
|
|
6
7
|
const { options, selectedIndex, selectOptionAndCleanUp, setHighlightedIndex, } = props;
|
|
7
8
|
return (_jsx(Container, { children: _jsx(ListContainer, { children: options.map((option, i) => {
|
|
8
|
-
if (
|
|
9
|
+
if (option instanceof ComponentDrawerOption) {
|
|
9
10
|
return (_jsx(React.Fragment, { children: option.component }, option.key));
|
|
10
11
|
}
|
|
11
12
|
return (_jsx(ComponentPickerMenuItem, { index: i, isSelected: selectedIndex === i, onClick: () => {
|
package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.d.ts
CHANGED
|
@@ -10,8 +10,13 @@ import { MenuOption } from "@lexical/react/LexicalTypeaheadMenuPlugin";
|
|
|
10
10
|
import { LexicalEditor } from "lexical";
|
|
11
11
|
import { ReactElement } from "react";
|
|
12
12
|
import { Theme } from "@emotion/react";
|
|
13
|
+
export declare class ComponentDrawerOption extends MenuOption {
|
|
14
|
+
title: string;
|
|
15
|
+
component: ReactElement;
|
|
16
|
+
keywords?: Array<string>;
|
|
17
|
+
constructor(title: string, component: ReactElement, keywords?: Array<string>);
|
|
18
|
+
}
|
|
13
19
|
export declare class ComponentPickerOption extends MenuOption {
|
|
14
|
-
component?: ReactElement;
|
|
15
20
|
title: string;
|
|
16
21
|
icon?: ReactElement;
|
|
17
22
|
iconContainerClassName?: string;
|
|
@@ -24,9 +29,7 @@ export declare class ComponentPickerOption extends MenuOption {
|
|
|
24
29
|
keywords?: Array<string>;
|
|
25
30
|
keyboardShortcut?: string;
|
|
26
31
|
onSelect: (queryString: string) => void;
|
|
27
|
-
} | {
|
|
28
|
-
component: ReactElement;
|
|
29
32
|
});
|
|
30
33
|
}
|
|
31
|
-
export declare function getBaseOptions(editor: LexicalEditor, theme: Theme, setImageOpen: (open: boolean) => void): ComponentPickerOption[];
|
|
34
|
+
export declare function getBaseOptions(editor: LexicalEditor, theme: Theme, setImageOpen: (open: boolean) => void): (ComponentPickerOption | ComponentDrawerOption)[];
|
|
32
35
|
export declare function ComponentPickerMenuPlugin(): JSX.Element;
|
package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.js
CHANGED
|
@@ -24,20 +24,21 @@ import { InsertImageDialog } from "../ImagesPlugin/InsertImageDialog";
|
|
|
24
24
|
import { TextIcon, H1Icon, H2Icon, H3Icon, ListUnorderedIcon, ListOrderedIcon, DoubleQuotesLIcon, CodeViewIcon, SeparatorIcon, ImageLineIcon, InputMethodLineIcon, ListRadioIcon, } from "../../../../icons";
|
|
25
25
|
import { ZINDEX } from "../../../../utils/zIndex";
|
|
26
26
|
import { css, useTheme } from "@emotion/react";
|
|
27
|
-
import {
|
|
27
|
+
import { INSERT_PROBLEM_INPUT_COMMAND } from "../ProblemInputPlugin";
|
|
28
28
|
// import useModal from "../../hooks/useModal";
|
|
29
29
|
// import catTypingGif from "../../images/cat-typing.gif";
|
|
30
30
|
// import { INSERT_IMAGE_COMMAND, InsertImageDialog } from "../ImagesPlugin";
|
|
31
|
+
export class ComponentDrawerOption extends MenuOption {
|
|
32
|
+
constructor(title, component, keywords) {
|
|
33
|
+
super(title);
|
|
34
|
+
this.title = title;
|
|
35
|
+
this.component = component;
|
|
36
|
+
this.keywords = keywords;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
31
39
|
export class ComponentPickerOption extends MenuOption {
|
|
32
40
|
constructor(title, options) {
|
|
33
41
|
super(title);
|
|
34
|
-
if ("component" in options) {
|
|
35
|
-
this.title = title;
|
|
36
|
-
this.component = options.component;
|
|
37
|
-
this.keywords = [];
|
|
38
|
-
this.onSelect = () => { };
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
42
|
this.title = title;
|
|
42
43
|
this.keywords = options.keywords || [];
|
|
43
44
|
this.icon = options.icon;
|
|
@@ -69,8 +70,8 @@ function getQuizContextOptions(editor, theme) {
|
|
|
69
70
|
return [
|
|
70
71
|
new ComponentPickerOption("주관식 입력 칸", {
|
|
71
72
|
icon: _jsx(InputMethodLineIcon, { color: theme.color.foreground.primary }),
|
|
72
|
-
keywords: ["
|
|
73
|
-
onSelect: () => editor.dispatchCommand(
|
|
73
|
+
keywords: ["problem input", "주관식 입력"],
|
|
74
|
+
onSelect: () => editor.dispatchCommand(INSERT_PROBLEM_INPUT_COMMAND, {
|
|
74
75
|
solutions: [
|
|
75
76
|
{
|
|
76
77
|
textType: "normal",
|
|
@@ -84,24 +85,17 @@ function getQuizContextOptions(editor, theme) {
|
|
|
84
85
|
}),
|
|
85
86
|
new ComponentPickerOption("객관식 입력 칸", {
|
|
86
87
|
icon: _jsx(ListRadioIcon, { color: theme.color.foreground.primary }),
|
|
87
|
-
keywords: [
|
|
88
|
-
"multiple select",
|
|
89
|
-
"multiple select input",
|
|
90
|
-
"객관식",
|
|
91
|
-
"객관식 입력",
|
|
92
|
-
],
|
|
88
|
+
keywords: ["problem select", "객관식 입력"],
|
|
93
89
|
onSelect: () => {
|
|
94
90
|
// TODO: 객관식 입력 칸 추가
|
|
95
91
|
},
|
|
96
92
|
}),
|
|
97
|
-
new
|
|
98
|
-
component: (_jsx("div", { css: css `
|
|
93
|
+
new ComponentDrawerOption("메뉴구분선", (_jsx("div", { css: css `
|
|
99
94
|
width: 100%;
|
|
100
95
|
height: 1px;
|
|
101
96
|
background: ${theme.color.background.neutralAltActive};
|
|
102
97
|
margin: 4px 0;
|
|
103
|
-
` })),
|
|
104
|
-
}),
|
|
98
|
+
` }))),
|
|
105
99
|
];
|
|
106
100
|
}
|
|
107
101
|
export function getBaseOptions(editor, theme, setImageOpen) {
|
|
@@ -211,11 +205,15 @@ export function ComponentPickerMenuPlugin() {
|
|
|
211
205
|
const regex = new RegExp(queryString, "i");
|
|
212
206
|
return [
|
|
213
207
|
...getDynamicOptions(editor, queryString),
|
|
214
|
-
...baseOptions.filter((option) =>
|
|
215
|
-
|
|
208
|
+
...baseOptions.filter((option) => option.keywords &&
|
|
209
|
+
(regex.test(option.title) ||
|
|
210
|
+
option.keywords.some((keyword) => regex.test(keyword)))),
|
|
216
211
|
];
|
|
217
212
|
}, [editor, queryString, setOpen]);
|
|
218
213
|
const onSelectOption = useCallback((selectedOption, nodeToRemove, closeMenu, matchingString) => {
|
|
214
|
+
if (selectedOption instanceof ComponentDrawerOption) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
219
217
|
editor.update(() => {
|
|
220
218
|
nodeToRemove === null || nodeToRemove === void 0 ? void 0 : nodeToRemove.remove();
|
|
221
219
|
selectedOption.onSelect(matchingString);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
2
|
import { LexicalCommand } from "lexical";
|
|
3
3
|
import { ProblemInputPayload } from "../../nodes/ProblemInputNode";
|
|
4
|
-
export declare const
|
|
4
|
+
export declare const INSERT_PROBLEM_INPUT_COMMAND: LexicalCommand<ProblemInputPayload>;
|
|
5
5
|
export default function ProblemInputPlugin(): JSX.Element | null;
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
2
|
-
import { $
|
|
2
|
+
import { $insertNodeToNearestRoot } from "@lexical/utils";
|
|
3
|
+
import { COMMAND_PRIORITY_EDITOR, createCommand, } from "lexical";
|
|
3
4
|
import { useEffect } from "react";
|
|
4
5
|
import { $createProblemInputNode, ProblemInputNode, } from "../../nodes/ProblemInputNode";
|
|
5
|
-
export const
|
|
6
|
+
export const INSERT_PROBLEM_INPUT_COMMAND = createCommand("INSERT_PROBLEM_INPUT_COMMAND");
|
|
6
7
|
export default function ProblemInputPlugin() {
|
|
7
8
|
const [editor] = useLexicalComposerContext();
|
|
8
9
|
useEffect(() => {
|
|
9
10
|
if (!editor.hasNodes([ProblemInputNode])) {
|
|
10
11
|
throw new Error("ProblemInputNode: ProblemInputNode not registered on editor");
|
|
11
12
|
}
|
|
12
|
-
editor.registerCommand(
|
|
13
|
-
var _a;
|
|
13
|
+
editor.registerCommand(INSERT_PROBLEM_INPUT_COMMAND, (payload) => {
|
|
14
14
|
const problemInputNode = $createProblemInputNode(payload);
|
|
15
|
-
|
|
16
|
-
currentNode === null || currentNode === void 0 ? void 0 : currentNode.replace(problemInputNode);
|
|
15
|
+
$insertNodeToNearestRoot(problemInputNode);
|
|
17
16
|
return true;
|
|
18
17
|
}, COMMAND_PRIORITY_EDITOR);
|
|
19
18
|
}, [editor]);
|