@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.
Files changed (17) hide show
  1. package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/InputComponent.js +13 -12
  2. package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/ProblemInputNode.d.ts +0 -1
  3. package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/SettingForm/FormCharacterCount.d.ts +2 -3
  4. package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/SettingForm/FormCharacterCount.js +2 -2
  5. package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/SettingForm/FormSolution.d.ts +1 -1
  6. package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/SettingForm/FormSolution.js +13 -12
  7. package/dist/patterns/LexicalEditor/nodes/ProblemInputNode/SettingForm/SettingForm.js +15 -19
  8. package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/ComponentAdderPlugin.js +10 -4
  9. package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/useContextMenuOptions.d.ts +2 -2
  10. package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuList.d.ts +2 -2
  11. package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuList.js +2 -1
  12. package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.d.ts +7 -4
  13. package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.js +20 -22
  14. package/dist/patterns/LexicalEditor/plugins/ProblemInputPlugin/index.d.ts +1 -1
  15. package/dist/patterns/LexicalEditor/plugins/ProblemInputPlugin/index.js +5 -6
  16. package/dist/patterns/LexicalEditor/theme.js +2 -1
  17. 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, } from "./ProblemInputNode";
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: answer, onChange: (e) => {
35
- editor.update(() => {
36
- const node = $getNodeByKey(nodeKey);
37
- if (!$isProblemInputNode(node)) {
38
- return;
39
- }
40
- node.setAnswer(e.target.value);
41
- });
42
- }, color: "default", css: css `
43
- width: 300px;
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 `
@@ -3,7 +3,6 @@ import { ReactNode } from "react";
3
3
  export interface Solution {
4
4
  textType: "normal" | "code";
5
5
  value: string;
6
- destroyed?: boolean;
7
6
  }
8
7
  export interface ProblemInputPayload {
9
8
  showCharacterCount: boolean;
@@ -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
- setMultiAnswerDisabled: React.Dispatch<React.SetStateAction<boolean>>;
5
+ trigger: UseFormTrigger<ProblemInputPayload>;
7
6
  }
8
7
  export declare function FormCharacterCount(props: FormCharacterCountProps): import("react/jsx-runtime").JSX.Element;
@@ -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, setMultiAnswerDisabled } = props;
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
- setMultiAnswerDisabled(value === "true");
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, onDelete, disabled } = props;
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
- required: "정답을 입력해주세요.",
33
- }, render: ({ field: { value, onChange }, fieldState: { invalid, error }, }) => (_jsx(Input, { size: "small", color: invalid ? "activeDanger" : "default", onChange: onChange, disabled: disabled, value: value, hintIcon: invalid ? _jsx(ErrorWarningFillIcon, {}) : undefined, hintText: error === null || error === void 0 ? void 0 : error.message, placeholder: "\uC548\uB155\uD558\uC138\uC694", fullWidth: true, css: css `
34
- > div {
35
- padding: 4px 12px;
36
- }
37
- `, startIcon: TextTypeDropdown, 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 }) })))] })) })) }));
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
- //fixme: multiAnswerDisabled 삭제
21
- const [multiAnswerDisabled, setMultiAnswerDisabled] = useState(showCharacterCount);
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, update } = useFieldArray({
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
- .map((field, index) => ({
52
- field,
53
- index,
54
- }))
55
- .filter(({ field }) => !field.destroyed)
56
- .map(({ field, index }) => (_jsx(FormSolution, { index: index, control: control, disabled: index !== 0 && multiAnswerDisabled, onDelete: index !== 0
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
- if (field.value) {
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, setMultiAnswerDisabled: setMultiAnswerDisabled })] }), _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 `
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.component ||
135
- regex.test(option.title) ||
136
- option.keywords.some((keyword) => regex.test(keyword)));
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)[];
@@ -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;
@@ -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 ("component" in option) {
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: () => {
@@ -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;
@@ -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 { INSERT_SHORT_ANSWER_COMMAND } from "../ProblemInputPlugin";
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: ["short answer", "short answer input", "주관식", "주관식 입력"],
73
- onSelect: () => editor.dispatchCommand(INSERT_SHORT_ANSWER_COMMAND, {
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 ComponentPickerOption("메뉴구분선", {
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) => regex.test(option.title) ||
215
- option.keywords.some((keyword) => regex.test(keyword))),
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 INSERT_SHORT_ANSWER_COMMAND: LexicalCommand<ProblemInputPayload>;
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 { $getSelection, COMMAND_PRIORITY_EDITOR, createCommand, } from "lexical";
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 INSERT_SHORT_ANSWER_COMMAND = createCommand("INSERT_SHORT_ANSWER_COMMAND");
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(INSERT_SHORT_ANSWER_COMMAND, (payload) => {
13
- var _a;
13
+ editor.registerCommand(INSERT_PROBLEM_INPUT_COMMAND, (payload) => {
14
14
  const problemInputNode = $createProblemInputNode(payload);
15
- const currentNode = (_a = $getSelection()) === null || _a === void 0 ? void 0 : _a.getNodes()[0];
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]);
@@ -249,8 +249,9 @@ export function getTheme(theme) {
249
249
  problemInput: css `
250
250
  display: flex;
251
251
  flex-direction: column;
252
- flex: 1;
252
+ flex: 1;
253
253
  gap: 4px;
254
+ margin-bottom: 8px;
254
255
  `,
255
256
  };
256
257
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-monolith/cds",
3
- "version": "1.8.6",
3
+ "version": "1.8.7",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "sideEffects": false,