@team-monolith/cds 1.54.2 → 1.55.1

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 (35) hide show
  1. package/dist/patterns/LexicalEditor/LexicalEditor.js +2 -0
  2. package/dist/patterns/LexicalEditor/Plugins.js +2 -1
  3. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/Evaluation.d.ts +18 -0
  4. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/Evaluation.js +108 -0
  5. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormDescription.d.ts +10 -0
  6. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormDescription.js +55 -0
  7. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormIconAndLabel/FormIconAndLabel.d.ts +7 -0
  8. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormIconAndLabel/FormIconAndLabel.js +55 -0
  9. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormIconAndLabel/FormLabel.d.ts +8 -0
  10. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormIconAndLabel/FormLabel.js +33 -0
  11. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormIconAndLabel/index.d.ts +1 -0
  12. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormIconAndLabel/index.js +1 -0
  13. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/SettingForm.d.ts +20 -0
  14. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/SettingForm.js +109 -0
  15. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/index.d.ts +1 -0
  16. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/index.js +1 -0
  17. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/index.d.ts +2 -0
  18. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/index.js +2 -0
  19. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/EvaluationComponent.d.ts +11 -0
  20. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/EvaluationComponent.js +49 -0
  21. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/index.d.ts +1 -0
  22. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/index.js +1 -0
  23. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/SelfEvaluationNode.d.ts +43 -0
  24. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/SelfEvaluationNode.js +91 -0
  25. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/iconData.d.ts +14 -0
  26. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/iconData.js +61 -0
  27. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/index.d.ts +1 -0
  28. package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/index.js +1 -0
  29. package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/useContextMenuOptions.js +15 -0
  30. package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.js +12 -1
  31. package/dist/patterns/LexicalEditor/plugins/SelfEvaluationPlugin/index.d.ts +5 -0
  32. package/dist/patterns/LexicalEditor/plugins/SelfEvaluationPlugin/index.js +20 -0
  33. package/dist/patterns/LexicalEditor/theme.d.ts +1 -0
  34. package/dist/patterns/LexicalEditor/theme.js +7 -0
  35. package/package.json +1 -1
@@ -18,6 +18,7 @@ import { LayoutItemNode } from "./nodes/LayoutItemNode";
18
18
  import _ from "lodash";
19
19
  import { SheetSelectNode } from "./nodes/SheetSelectNode";
20
20
  import { SheetInputNode } from "./nodes/SheetInputNode";
21
+ import { SelfEvaluationNode } from "./nodes/SelfEvaluationNode";
21
22
  function validateValue(value) {
22
23
  var _a, _b;
23
24
  if (value && typeof value !== "object") {
@@ -80,6 +81,7 @@ export function LexicalEditor(props) {
80
81
  LayoutItemNode,
81
82
  SheetSelectNode,
82
83
  SheetInputNode,
84
+ SelfEvaluationNode,
83
85
  {
84
86
  replace: QuoteNode,
85
87
  with: (_node) => {
@@ -32,6 +32,7 @@ import ProblemSelectPlugin from "./plugins/ProblemSelectPlugin";
32
32
  import { LayoutPlugin } from "./plugins/LayoutPlugin";
33
33
  import SheetSelectPlugin from "./plugins/SheetSelectPlugin";
34
34
  import SheetInputPlugin from "./plugins/SheetInputPlugin";
35
+ import SelfEvaluationPlugin from "./plugins/SelfEvaluationPlugin";
35
36
  export default function Plugins(props) {
36
37
  const { className, contentEditableClassName, onChange, isSheetEnabled, isQuizEnabled, } = props;
37
38
  const isEditable = useLexicalEditable();
@@ -46,7 +47,7 @@ export default function Plugins(props) {
46
47
  onChange === null || onChange === void 0 ? void 0 : onChange(editorState.toJSON());
47
48
  },
48
49
  // ignore 하지 않으면 Form에서 수정하지 않았는데 Dirty로 처리됨.
49
- ignoreSelectionChange: true }), _jsx(AutoFocusPlugin, {}), isEditable && (_jsxs(_Fragment, { children: [_jsx(TabIndentationPlugin, {}), _jsx(ComponentPickerMenuPlugin, { isSheetEnabled: isSheetEnabled, isQuizEnabled: isQuizEnabled }), _jsx(MarkdownShortcutPlugin, { transformers: CODLE_TRANSFORMERS }), _jsx(HistoryPlugin, {})] })), floatingAnchorElem && isEditable && (_jsxs(_Fragment, { children: [_jsx(ComponentAdderPlugin, { anchorElem: floatingAnchorElem, isSheetEnabled: isSheetEnabled, isQuizEnabled: isQuizEnabled }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem }), _jsx(FloatingLinkEditorPlugin, { anchorElem: floatingAnchorElem, isLinkEditMode: isLinkEditMode, setIsLinkEditMode: setIsLinkEditMode })] })), !isEditable && _jsx(LexicalClickableLinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(HorizontalRulePlugin, {}), _jsx(ImagesPlugin, {}), _jsx(TablePlugin, {}), _jsx(LinkPlugin, {}), _jsx(ListMaxIndentLevelPlugin, { maxDepth: 5 }), _jsx(ProblemInputPlugin, {}), _jsx(ProblemSelectPlugin, {}), _jsx(LayoutPlugin, {}), _jsx(SheetSelectPlugin, {}), _jsx(SheetInputPlugin, {})] }));
50
+ ignoreSelectionChange: true }), _jsx(AutoFocusPlugin, {}), isEditable && (_jsxs(_Fragment, { children: [_jsx(TabIndentationPlugin, {}), _jsx(ComponentPickerMenuPlugin, { isSheetEnabled: isSheetEnabled, isQuizEnabled: isQuizEnabled }), _jsx(MarkdownShortcutPlugin, { transformers: CODLE_TRANSFORMERS }), _jsx(HistoryPlugin, {})] })), floatingAnchorElem && isEditable && (_jsxs(_Fragment, { children: [_jsx(ComponentAdderPlugin, { anchorElem: floatingAnchorElem, isSheetEnabled: isSheetEnabled, isQuizEnabled: isQuizEnabled }), _jsx(FloatingTextFormatToolbarPlugin, { anchorElem: floatingAnchorElem }), _jsx(FloatingLinkEditorPlugin, { anchorElem: floatingAnchorElem, isLinkEditMode: isLinkEditMode, setIsLinkEditMode: setIsLinkEditMode })] })), !isEditable && _jsx(LexicalClickableLinkPlugin, {}), _jsx(ListPlugin, {}), _jsx(HorizontalRulePlugin, {}), _jsx(ImagesPlugin, {}), _jsx(TablePlugin, {}), _jsx(LinkPlugin, {}), _jsx(ListMaxIndentLevelPlugin, { maxDepth: 5 }), _jsx(ProblemInputPlugin, {}), _jsx(ProblemSelectPlugin, {}), _jsx(LayoutPlugin, {}), _jsx(SheetSelectPlugin, {}), _jsx(SheetInputPlugin, {}), _jsx(SelfEvaluationPlugin, {})] }));
50
51
  }
51
52
  const ScrollArea = styled.div `
52
53
  min-height: 150px;
@@ -0,0 +1,18 @@
1
+ /// <reference types="react" />
2
+ /** 데이터를 받아 자기평가 항목과 ToggleButtonGroup을 그리는 컴포넌트입니다.
3
+ * 비지니스 로직이 포함되어 있지 않습니다.
4
+ */
5
+ export declare function Evaluation(props: {
6
+ className?: string;
7
+ icons: {
8
+ onIcon: React.ReactNode;
9
+ offIcon: React.ReactNode;
10
+ }[];
11
+ labels: string[];
12
+ descriptions: string[];
13
+ selectedLabelIndexes: (number | null)[];
14
+ /** 블럭을 클릭했을 때 실행될 콜백. 전달하지 않으면 cursor: pointer가 적용되지 않습니다. */
15
+ onClick?: () => void;
16
+ /** 전달하지 않으면 ToggleButtonGroup 호버, 클릭 동작이 없습니다. (pointer-events: none) */
17
+ onChange?: (selectedLabelIndexes: (number | null)[]) => void;
18
+ }): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -0,0 +1,108 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
+ /** @jsxImportSource @emotion/react */
3
+ import styled from "@emotion/styled";
4
+ import { css } from "@emotion/react";
5
+ import { ToggleButtonGroup } from "../../../../../ToggleButtonGroup";
6
+ const TOGGLE_BUTTON_GROUP_WIDTH = 320;
7
+ /** 데이터를 받아 자기평가 항목과 ToggleButtonGroup을 그리는 컴포넌트입니다.
8
+ * 비지니스 로직이 포함되어 있지 않습니다.
9
+ */
10
+ export function Evaluation(props) {
11
+ const { className, icons, labels, descriptions, selectedLabelIndexes, onClick, onChange, } = props;
12
+ return (_jsxs(Container, Object.assign({ className: className, onClick: onClick }, { children: [_jsx(Header, { children: _jsx(LabelArea, { children: labels.map((label, index) => (_jsx(Label, { children: label }, index))) }) }), descriptions.map((description, index) => {
13
+ var _a;
14
+ return (_jsxs(Item, { children: [_jsx(Index, { children: index + 1 }), _jsx(Description, { children: description }), _jsx(StyledToggleButtonGroup, { css: !onChange &&
15
+ css `
16
+ pointer-events: none;
17
+ `, data: labels.map((label, index) => (Object.assign({ label }, icons[index]))), activeIndex: (_a = selectedLabelIndexes[index]) !== null && _a !== void 0 ? _a : null, setActiveIndex: (activeIndex) => {
18
+ if (onChange) {
19
+ const newSelectedLabelIndexes = [...selectedLabelIndexes];
20
+ newSelectedLabelIndexes[index] = activeIndex;
21
+ onChange(newSelectedLabelIndexes);
22
+ }
23
+ } })] }, index));
24
+ })] })));
25
+ }
26
+ const Container = styled.div(({ onClick }) => css `
27
+ ${onClick && "cursor: pointer;"}
28
+ display: flex;
29
+ flex-direction: column;
30
+ align-items: flex-start;
31
+ align-self: stretch;
32
+ `);
33
+ const Header = styled.div(({ theme }) => css `
34
+ border-bottom: 1px solid ${theme.color.background.neutralAltActive};
35
+ display: flex;
36
+ padding: 8px 0px;
37
+ justify-content: flex-end;
38
+ align-items: center;
39
+ gap: 8px;
40
+ align-self: stretch;
41
+ `);
42
+ const LabelArea = styled.div `
43
+ display: flex;
44
+ width: ${TOGGLE_BUTTON_GROUP_WIDTH}px;
45
+ justify-content: space-between;
46
+ align-items: flex-start;
47
+ `;
48
+ const Label = styled.span(({ theme }) => css `
49
+ color: ${theme.color.foreground.neutralBaseDisabled};
50
+ text-align: center;
51
+ /* Default/Label/12px-Md */
52
+ font-family: Pretendard;
53
+ font-size: 12px;
54
+ font-style: normal;
55
+ font-weight: 500;
56
+ line-height: 16px; /* 133.333% */
57
+ `);
58
+ const Item = styled.div(({ theme }) => css `
59
+ display: flex;
60
+ padding: 4px 0px;
61
+ align-items: center;
62
+ gap: 8px;
63
+ align-self: stretch;
64
+ border-bottom: 1px solid ${theme.color.background.neutralAltActive};
65
+ `);
66
+ const Index = styled.div(({ theme }) => css `
67
+ display: flex;
68
+ width: 20px;
69
+ height: 20px;
70
+ padding: 4px;
71
+
72
+ user-select: none; /* Standard syntax */
73
+
74
+ border-radius: 4px;
75
+ background: ${theme.color.background.neutralBase};
76
+ border: 1px solid ${theme.color.background.neutralAltActive};
77
+
78
+ justify-content: center;
79
+ align-items: center;
80
+
81
+ color: ${theme.color.foreground.neutralBaseDisabled};
82
+ font-family: ${theme.fontFamily.ui};
83
+ font-size: 14px;
84
+ font-weight: 800;
85
+ line-height: 16px;
86
+ `);
87
+ const Description = styled.div(({ theme }) => css `
88
+ display: flex;
89
+ padding: 4px 8px;
90
+ flex-direction: column;
91
+ justify-content: center;
92
+ align-items: flex-start;
93
+ gap: 8px;
94
+ flex: 1 0 0;
95
+ align-self: stretch;
96
+
97
+ color: ${theme.color.foreground.neutralBase};
98
+ /* Default/Paragraph/16px-Rg */
99
+ font-family: ${theme.fontFamily.ui};
100
+ font-size: 16px;
101
+ font-style: normal;
102
+ font-weight: 400;
103
+ line-height: 24px; /* 150% */
104
+ `);
105
+ const StyledToggleButtonGroup = styled(ToggleButtonGroup) `
106
+ flex: none;
107
+ width: ${TOGGLE_BUTTON_GROUP_WIDTH}px;
108
+ `;
@@ -0,0 +1,10 @@
1
+ /** @jsxImportSource @emotion/react */
2
+ import { Control } from "react-hook-form";
3
+ import { SettingFormData } from "./SettingForm";
4
+ /** 평가 항목을 설정하는 Form입니다. */
5
+ export declare function FormDescription(props: {
6
+ index: number;
7
+ control: Control<SettingFormData, any>;
8
+ rules: any;
9
+ onDelete?: () => void;
10
+ }): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
+ /** @jsxImportSource @emotion/react */
3
+ import { useController } from "react-hook-form";
4
+ import styled from "@emotion/styled";
5
+ import { css } from "@emotion/react";
6
+ import { useState } from "react";
7
+ import { DeleteBinLineIcon, ErrorWarningFillIcon, Input, SquareButton, } from "../../../../../../..";
8
+ /** 평가 항목을 설정하는 Form입니다. */
9
+ export function FormDescription(props) {
10
+ const { control, index, rules, onDelete } = props;
11
+ const [inputFocused, setInputFocused] = useState(false);
12
+ const { field: { value, onChange }, fieldState: { invalid, error }, } = useController({
13
+ control,
14
+ name: `descriptions.${index}.text`,
15
+ rules,
16
+ });
17
+ return (_jsxs(Item, { children: [_jsx(Index, { children: index + 1 }), _jsx(Input, { size: "small", color: invalid ? "activeDanger" : inputFocused ? "activePrimary" : "default", value: value, onChange: (e) => onChange(e.target.value), inputProps: {
18
+ onFocus: () => {
19
+ setInputFocused(true);
20
+ },
21
+ onBlur: () => {
22
+ setInputFocused(false);
23
+ },
24
+ }, placeholder: `${index + 1}번 평가 항목`, hintIcon: invalid ? _jsx(ErrorWarningFillIcon, {}) : undefined, hintText: error === null || error === void 0 ? void 0 : error.message, multiline: true, fullWidth: true }), onDelete && (_jsx(SquareButton, { color: "white", size: "xsmall", icon: _jsx(DeleteBinLineIcon, {}), onClick: onDelete }))] }));
25
+ }
26
+ const Item = styled.div(({ theme }) => css `
27
+ display: flex;
28
+ padding: 4px 12px;
29
+ align-items: center;
30
+ gap: 4px;
31
+ align-self: stretch;
32
+ border-radius: 8px;
33
+ background: ${theme.color.background.neutralAlt};
34
+ `);
35
+ const Index = styled.div(({ theme }) => css `
36
+ display: flex;
37
+ width: 20px;
38
+ height: 20px;
39
+ padding: 4px;
40
+
41
+ user-select: none; /* Standard syntax */
42
+
43
+ border-radius: 4px;
44
+ background: ${theme.color.background.neutralBase};
45
+ border: 1px solid ${theme.color.background.neutralAltActive};
46
+
47
+ justify-content: center;
48
+ align-items: center;
49
+
50
+ color: ${theme.color.foreground.neutralBaseDisabled};
51
+ font-family: ${theme.fontFamily.ui};
52
+ font-size: 14px;
53
+ font-weight: 800;
54
+ line-height: 16px;
55
+ `);
@@ -0,0 +1,7 @@
1
+ import { Control } from "react-hook-form";
2
+ import { SettingFormData } from "../SettingForm";
3
+ /** 아이콘, 레이블을 설정하는 Form입니다. */
4
+ export declare function FormIconAndLabel(props: {
5
+ control: Control<SettingFormData, any>;
6
+ labelsLength: number;
7
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Controller } from "react-hook-form";
3
+ import { css, useTheme } from "@emotion/react";
4
+ import { ICON_DATA, ICON_TYPES } from "../../../../iconData";
5
+ import styled from "@emotion/styled";
6
+ import { Dropdown, DropdownItem } from "../../../../../../../Dropdown";
7
+ import { FormLabel } from "./FormLabel";
8
+ import { ArrowDownSFillIcon } from "../../../../../../../..";
9
+ /** 아이콘, 레이블을 설정하는 Form입니다. */
10
+ export function FormIconAndLabel(props) {
11
+ const { control, labelsLength } = props;
12
+ const theme = useTheme();
13
+ const iconDataMap = ICON_DATA(theme);
14
+ return (_jsx(Controller, { control: control, name: "iconType", render: ({ field: { value, onChange } }) => {
15
+ const { startIcon, title, icons } = iconDataMap[value];
16
+ return (_jsxs(_Fragment, { children: [_jsx(Dropdown, Object.assign({ label: "아이콘: " + title, size: "small", color: "grey", closeOnItemClick: true, bold: true, startIcon: startIcon, endIcon: _jsx(ArrowDownSFillIcon, {}), menuProps: {
17
+ menuCss: css `
18
+ width: 216px;
19
+ `,
20
+ anchorOrigin: {
21
+ vertical: "bottom",
22
+ horizontal: "center",
23
+ },
24
+ transformOrigin: {
25
+ vertical: "top",
26
+ horizontal: "center",
27
+ },
28
+ } }, { children: ICON_TYPES.map((type, index) => {
29
+ const { title, startIcon } = iconDataMap[type];
30
+ return (_jsx(DropdownItem, { index: index, label: title, preserveIconColor: true, startIcon: startIcon, onClick: () => {
31
+ onChange(type);
32
+ } }, index));
33
+ }) })), Array.from({ length: labelsLength }).map((_, index) => (_jsxs(LabelLine, { children: [_jsx(Icon, { children: icons[index].onIcon }), _jsx(FormLabel, { control: control, index: index, rules: {
34
+ required: "필수 입력 항목입니다.",
35
+ maxLength: 12,
36
+ } })] }, index)))] }));
37
+ } }));
38
+ }
39
+ const LabelLine = styled.div `
40
+ display: flex;
41
+ align-items: flex-start;
42
+ gap: 4px;
43
+ align-self: stretch;
44
+ `;
45
+ const Icon = styled.div(({ theme }) => css `
46
+ display: flex;
47
+ min-width: 36px;
48
+ min-height: 36px;
49
+ justify-content: center;
50
+ align-items: center;
51
+ gap: 10px;
52
+
53
+ border-radius: 8px;
54
+ border: 0.5px solid ${theme.color.background.neutralAltActive};
55
+ `);
@@ -0,0 +1,8 @@
1
+ import { Control, RegisterOptions } from "react-hook-form";
2
+ import { SettingFormData } from "../SettingForm";
3
+ /** 레이블을 설정하는 Form입니다. */
4
+ export declare function FormLabel(props: {
5
+ control: Control<SettingFormData, any>;
6
+ index: number;
7
+ rules?: Omit<RegisterOptions<SettingFormData, `labels.${number}`>, "valueAsNumber" | "valueAsDate" | "setValueAs" | "disabled">;
8
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Controller } from "react-hook-form";
3
+ import { useState } from "react";
4
+ import { ErrorWarningFillIcon, Input } from "../../../../../../../..";
5
+ import styled from "@emotion/styled";
6
+ import { css } from "@emotion/react";
7
+ /** 레이블을 설정하는 Form입니다. */
8
+ export function FormLabel(props) {
9
+ const { control, index, rules } = props;
10
+ const [inputFocused, setInputFocused] = useState(false);
11
+ return (_jsx(Controller, { control: control, name: `labels.${index}`, rules: rules, render: ({ field: { value, onChange }, fieldState: { invalid, error }, }) => (_jsx(Input, { size: "small", color: invalid
12
+ ? "activeDanger"
13
+ : inputFocused
14
+ ? "activePrimary"
15
+ : "default", value: value, endIcon: (rules === null || rules === void 0 ? void 0 : rules.maxLength) && (_jsx(Counter, Object.assign({ error: Boolean(error) }, { children: value.length + "/" + rules.maxLength }))), onChange: (e) => onChange(e.target.value), inputProps: {
16
+ onFocus: () => {
17
+ setInputFocused(true);
18
+ },
19
+ onBlur: () => {
20
+ setInputFocused(false);
21
+ },
22
+ }, hintIcon: invalid ? _jsx(ErrorWarningFillIcon, {}) : undefined, hintText: error === null || error === void 0 ? void 0 : error.message, fullWidth: true })) }));
23
+ }
24
+ const Counter = styled.span(({ theme, error }) => css `
25
+ color: ${error
26
+ ? theme.color.foreground.dangerActive
27
+ : theme.color.foreground.neutralBase};
28
+ font-family: ${theme.fontFamily.ui};
29
+ font-size: 14px;
30
+ font-style: normal;
31
+ font-weight: 700;
32
+ line-height: 20px; /* 142.857% */
33
+ `);
@@ -0,0 +1,20 @@
1
+ /** @jsxImportSource @emotion/react */
2
+ import { NodeKey } from "lexical";
3
+ import { IconType } from "../../../SelfEvaluationNode";
4
+ export interface Description {
5
+ text: string;
6
+ }
7
+ export interface SettingFormData {
8
+ iconType: IconType;
9
+ labels: string[];
10
+ descriptions: Description[];
11
+ }
12
+ /** 자기평가 edit의 설정 컴포넌트입니다. */
13
+ export declare function SettingForm(props: {
14
+ className?: string;
15
+ iconType: IconType;
16
+ labels: string[];
17
+ descriptions: string[];
18
+ nodeKey: NodeKey;
19
+ onClose: () => void;
20
+ }): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -0,0 +1,109 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
+ /** @jsxImportSource @emotion/react */
3
+ import { $getNodeByKey } from "lexical";
4
+ import { $isSelfEvaluationNode } from "../../../SelfEvaluationNode";
5
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
6
+ import { useFieldArray, useForm } from "react-hook-form";
7
+ import styled from "@emotion/styled";
8
+ import { css } from "@emotion/react";
9
+ import { AddFillIcon, Button, EmojiStickerLineIcon, shadows, } from "../../../../../../..";
10
+ import { FormIconAndLabel } from "./FormIconAndLabel";
11
+ import { FormDescription } from "./FormDescription";
12
+ /** 자기평가 edit의 설정 컴포넌트입니다. */
13
+ export function SettingForm(props) {
14
+ const { className, iconType, labels, descriptions, nodeKey, onClose } = props;
15
+ const [editor] = useLexicalComposerContext();
16
+ const { control, handleSubmit } = useForm({
17
+ mode: "all",
18
+ defaultValues: {
19
+ iconType,
20
+ labels,
21
+ descriptions: descriptions.map((text) => ({ text })),
22
+ },
23
+ });
24
+ const { fields, append, remove } = useFieldArray({
25
+ control,
26
+ name: "descriptions",
27
+ keyName: "uid",
28
+ });
29
+ const onSubmit = (data) => {
30
+ editor.update(() => {
31
+ const node = $getNodeByKey(nodeKey);
32
+ if (!$isSelfEvaluationNode(node)) {
33
+ return;
34
+ }
35
+ node.setIconType(data.iconType);
36
+ node.setLabels(data.labels);
37
+ node.setDescriptions(data.descriptions.map((description) => description.text));
38
+ });
39
+ onClose();
40
+ };
41
+ return (_jsxs(Form, Object.assign({ className: className, onSubmit: handleSubmit(onSubmit) }, { children: [_jsxs(Title, { children: [_jsx(EmojiStickerLineIcon, { css: css `
42
+ width: 12px;
43
+ height: 12px;
44
+ ` }), "3\uB2E8\uACC4 \uD3C9\uAC00"] }), _jsxs(Content, { children: [_jsxs(Left, { children: [_jsx(Label, { children: "\uD3C9\uAC00 \uD56D\uBAA9" }), fields.map((field, index) => (_jsx(FormDescription, { index: index, control: control, rules: {
45
+ required: "필수 입력 항목입니다.",
46
+ }, onDelete: index !== 0
47
+ ? () => {
48
+ remove(index);
49
+ }
50
+ : undefined }, field.uid))), _jsx(Button, { color: "grey", size: "small", startIcon: _jsx(AddFillIcon, {}), label: "\uD3C9\uAC00 \uD56D\uBAA9 \uCD94\uAC00", onClick: () => {
51
+ append({ text: "평가 항목을 입력하세요." });
52
+ } })] }), _jsxs(Right, { children: [_jsx(Label, { children: "\uC544\uC774\uCF58, \uB808\uC774\uBE14" }), _jsx(FormIconAndLabel, { control: control, labelsLength: labels.length })] })] }), _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", bold: true, type: "submit" })] })] })));
53
+ }
54
+ const Form = styled.form(({ theme }) => css `
55
+ display: flex;
56
+ flex-direction: column;
57
+ border-radius: 6px;
58
+ border: 1px solid ${theme.color.background.neutralAltActive};
59
+ background: ${theme.color.background.neutralBase};
60
+ box-shadow: ${shadows.shadow08};
61
+ `);
62
+ const Title = styled.div(({ theme }) => css `
63
+ display: flex;
64
+ padding: 8px 12px;
65
+ gap: 4px;
66
+ align-items: center;
67
+ color: ${theme.color.foreground.neutralBase};
68
+ /* Default/Label/12px-Md */
69
+ font-family: ${theme.fontFamily.ui};
70
+ font-size: 12px;
71
+ font-style: normal;
72
+ font-weight: 500;
73
+ line-height: 16px; /* 133.333% */
74
+ `);
75
+ const Content = styled.div(({ theme }) => css `
76
+ display: flex;
77
+ border-top: 1px solid ${theme.color.background.neutralAltActive};
78
+ border-bottom: 1px solid ${theme.color.background.neutralAltActive};
79
+ `);
80
+ const Left = styled.div `
81
+ flex: 1;
82
+ display: flex;
83
+ flex-direction: column;
84
+ gap: 12px;
85
+ padding: 12px;
86
+ `;
87
+ const Right = styled.div `
88
+ width: 240px;
89
+ display: flex;
90
+ flex-direction: column;
91
+ gap: 8px;
92
+ padding: 12px;
93
+ `;
94
+ const Label = styled.div(({ theme }) => css `
95
+ color: ${theme.color.foreground.neutralBaseDisabled};
96
+ /* Default/Label/12px-Md */
97
+ font-family: ${theme.fontFamily.ui};
98
+ font-size: 12px;
99
+ font-style: normal;
100
+ font-weight: 500;
101
+ line-height: 16px; /* 133.333% */
102
+ `);
103
+ const Buttons = styled.div `
104
+ display: flex;
105
+ padding: 12px;
106
+ justify-content: flex-end;
107
+ align-items: center;
108
+ gap: 8px;
109
+ `;
@@ -0,0 +1,2 @@
1
+ export * from "./Evaluation";
2
+ export * from "./SettingForm";
@@ -0,0 +1,2 @@
1
+ export * from "./Evaluation";
2
+ export * from "./SettingForm";
@@ -0,0 +1,11 @@
1
+ /** @jsxImportSource @emotion/react */
2
+ import { NodeKey } from "lexical";
3
+ import { IconType } from "../SelfEvaluationNode";
4
+ /** SelfEvaluationNode를 그리는 컴포넌트입니다. */
5
+ export declare function EvaluationComponent(props: {
6
+ iconType: IconType;
7
+ labels: string[];
8
+ descriptions: string[];
9
+ selectedLabelIndexes: (number | null)[];
10
+ nodeKey: NodeKey;
11
+ }): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -0,0 +1,49 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@emotion/react/jsx-runtime";
2
+ /** @jsxImportSource @emotion/react */
3
+ import { $getNodeByKey } from "lexical";
4
+ import { $isSelfEvaluationNode } from "../SelfEvaluationNode";
5
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
6
+ import { useContext, useState } from "react";
7
+ import useLexicalEditable from "@lexical/react/useLexicalEditable";
8
+ import { LexicalCustomConfigContext } from "../../../LexicalCustomConfigContext";
9
+ import { Evaluation, SettingForm } from "./Evaluation";
10
+ import { ICON_DATA } from "../iconData";
11
+ import { css, useTheme } from "@emotion/react";
12
+ import SquareButton from "../../../../../components/SquareButton";
13
+ import { Settings3FillIcon } from "../../../../../icons";
14
+ const EVALUATION_EDIT_WIDTH = 620;
15
+ /** SelfEvaluationNode를 그리는 컴포넌트입니다. */
16
+ export function EvaluationComponent(props) {
17
+ const { iconType, labels, descriptions, selectedLabelIndexes, nodeKey } = props;
18
+ const theme = useTheme();
19
+ const [editor] = useLexicalComposerContext();
20
+ const isEditable = useLexicalEditable();
21
+ const { freezeProblemNode } = useContext(LexicalCustomConfigContext);
22
+ const [settingOpen, setSettingOpen] = useState(false);
23
+ const { icons } = ICON_DATA(theme)[iconType];
24
+ // view
25
+ if (!isEditable) {
26
+ return (_jsx(Evaluation, { icons: icons, labels: labels, descriptions: descriptions, selectedLabelIndexes: selectedLabelIndexes, onChange: freezeProblemNode
27
+ ? undefined
28
+ : (indexes) => {
29
+ editor.update(() => {
30
+ const node = $getNodeByKey(nodeKey);
31
+ if (!$isSelfEvaluationNode(node)) {
32
+ return;
33
+ }
34
+ node.setSelectedLabelIndexes(indexes);
35
+ });
36
+ } }));
37
+ }
38
+ // edit
39
+ return (_jsxs(_Fragment, { children: [_jsxs("div", Object.assign({ css: css `
40
+ display: flex;
41
+ gap: 4px;
42
+ ` }, { children: [_jsx(Evaluation, { css: css `
43
+ width: ${EVALUATION_EDIT_WIDTH}px;
44
+ `, icons: icons, labels: labels, descriptions: descriptions, selectedLabelIndexes: selectedLabelIndexes, onClick: () => setSettingOpen((open) => !open) }), _jsx(SquareButton, { size: "small", color: "icon", icon: _jsx(Settings3FillIcon, {}), onClick: () => {
45
+ setSettingOpen((open) => !open);
46
+ } })] })), settingOpen && (_jsx(SettingForm, { css: css `
47
+ width: ${EVALUATION_EDIT_WIDTH}px;
48
+ `, iconType: iconType, labels: labels, descriptions: descriptions, nodeKey: nodeKey, onClose: () => setSettingOpen(false) }))] }));
49
+ }
@@ -0,0 +1 @@
1
+ export * from "./EvaluationComponent";
@@ -0,0 +1 @@
1
+ export * from "./EvaluationComponent";
@@ -0,0 +1,43 @@
1
+ import { DecoratorNode, EditorConfig, LexicalNode, NodeKey, SerializedLexicalNode, Spread } from "lexical";
2
+ import { ReactNode } from "react";
3
+ export type IconType = "emoji" | "ascendingNumber" | "descendingNumber";
4
+ export interface SelfEvaluationPayload {
5
+ /** 아이콘 프리셋 타입 */
6
+ iconType: IconType;
7
+ /** 자기평가 레이블 */
8
+ labels: string[];
9
+ /** 자기평가 항목들 */
10
+ descriptions: string[];
11
+ /** 선택된 레이블 인덱스 */
12
+ selectedLabelIndexes: (number | null)[];
13
+ key?: NodeKey;
14
+ }
15
+ export type SerializedSelfEvaluationNode = Spread<SelfEvaluationPayload, SerializedLexicalNode>;
16
+ /** 3단계 평가 입력칸을 나타내는 노드입니다.
17
+ * 도구 이름으로서는 "3단계 평가 입력 칸"으로 나타나지만, n단계로 확장 가능합니다.
18
+ */
19
+ export declare class SelfEvaluationNode extends DecoratorNode<ReactNode> {
20
+ __iconType: IconType;
21
+ __labels: string[];
22
+ __descriptions: string[];
23
+ __selectedLabelIndexes: (number | null)[];
24
+ isInline(): boolean;
25
+ static getType(): string;
26
+ getIconType(): IconType;
27
+ getLabels(): string[];
28
+ getDescriptions(): string[];
29
+ getSelectedLabelIndexes(): (number | null)[];
30
+ setIconType(iconType: IconType): void;
31
+ setLabels(labels: string[]): void;
32
+ setDescriptions(descriptions: string[]): void;
33
+ setSelectedLabelIndexes(selectedLabelIndexes: (number | null)[]): void;
34
+ static clone(node: SelfEvaluationNode): SelfEvaluationNode;
35
+ constructor(iconType: IconType, labels: string[], descriptions: string[], selectedLabelIndexes: (number | null)[], key?: NodeKey);
36
+ createDOM(config: EditorConfig): HTMLElement;
37
+ updateDOM(): boolean;
38
+ static importJSON(serializedNode: SerializedSelfEvaluationNode): SelfEvaluationNode;
39
+ exportJSON(): SerializedSelfEvaluationNode;
40
+ decorate(): ReactNode;
41
+ }
42
+ export declare function $createSelfEvaluationNode(payload: SelfEvaluationPayload): SelfEvaluationNode;
43
+ export declare function $isSelfEvaluationNode(node: LexicalNode | null | undefined): node is SelfEvaluationNode;
@@ -0,0 +1,91 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { $applyNodeReplacement, DecoratorNode, } from "lexical";
3
+ import { addClassNamesToElement } from "@lexical/utils";
4
+ import { EvaluationComponent } from "./EvaluationComponent";
5
+ /** 3단계 평가 입력칸을 나타내는 노드입니다.
6
+ * 도구 이름으로서는 "3단계 평가 입력 칸"으로 나타나지만, n단계로 확장 가능합니다.
7
+ */
8
+ export class SelfEvaluationNode extends DecoratorNode {
9
+ isInline() {
10
+ return false;
11
+ }
12
+ static getType() {
13
+ return "self-evaluation";
14
+ }
15
+ getIconType() {
16
+ return this.__iconType;
17
+ }
18
+ getLabels() {
19
+ return this.__labels;
20
+ }
21
+ getDescriptions() {
22
+ return this.__descriptions;
23
+ }
24
+ getSelectedLabelIndexes() {
25
+ return this.__selectedLabelIndexes;
26
+ }
27
+ setIconType(iconType) {
28
+ const self = this.getWritable();
29
+ self.__iconType = iconType;
30
+ }
31
+ setLabels(labels) {
32
+ const self = this.getWritable();
33
+ self.__labels = labels;
34
+ }
35
+ setDescriptions(descriptions) {
36
+ const self = this.getWritable();
37
+ self.__descriptions = descriptions;
38
+ }
39
+ setSelectedLabelIndexes(selectedLabelIndexes) {
40
+ const self = this.getWritable();
41
+ self.__selectedLabelIndexes = selectedLabelIndexes;
42
+ }
43
+ static clone(node) {
44
+ return new SelfEvaluationNode(node.__iconType, node.__labels, node.__descriptions, node.__selectedLabelIndexes);
45
+ }
46
+ constructor(iconType, labels, descriptions, selectedLabelIndexes, key) {
47
+ super(key);
48
+ this.__iconType = iconType;
49
+ this.__labels = labels;
50
+ this.__descriptions = descriptions;
51
+ this.__selectedLabelIndexes = selectedLabelIndexes;
52
+ }
53
+ createDOM(config) {
54
+ // Define the DOM element here
55
+ const root = document.createElement("div");
56
+ addClassNamesToElement(root, config.theme.selfEvaluation);
57
+ return root;
58
+ }
59
+ updateDOM() {
60
+ return false;
61
+ }
62
+ static importJSON(serializedNode) {
63
+ return $createSelfEvaluationNode({
64
+ key: serializedNode.key,
65
+ iconType: serializedNode.iconType,
66
+ labels: serializedNode.labels,
67
+ descriptions: serializedNode.descriptions,
68
+ selectedLabelIndexes: serializedNode.selectedLabelIndexes,
69
+ });
70
+ }
71
+ exportJSON() {
72
+ return {
73
+ type: "self-evaluation",
74
+ version: 1,
75
+ iconType: this.__iconType,
76
+ labels: this.__labels,
77
+ descriptions: this.__descriptions,
78
+ selectedLabelIndexes: this.__selectedLabelIndexes,
79
+ };
80
+ }
81
+ decorate() {
82
+ return (_jsx(EvaluationComponent, { iconType: this.__iconType, labels: this.__labels, descriptions: this.__descriptions, selectedLabelIndexes: this.__selectedLabelIndexes, nodeKey: this.getKey() }));
83
+ }
84
+ }
85
+ export function $createSelfEvaluationNode(payload) {
86
+ const { iconType, labels, descriptions, selectedLabelIndexes, key } = payload;
87
+ return $applyNodeReplacement(new SelfEvaluationNode(iconType, labels, descriptions, selectedLabelIndexes, key));
88
+ }
89
+ export function $isSelfEvaluationNode(node) {
90
+ return node instanceof SelfEvaluationNode;
91
+ }
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+ import { IconType } from "./SelfEvaluationNode";
3
+ import { Theme } from "@emotion/react";
4
+ interface IconData {
5
+ startIcon: React.ReactNode;
6
+ title: string;
7
+ icons: {
8
+ onIcon: React.ReactNode;
9
+ offIcon: React.ReactNode;
10
+ }[];
11
+ }
12
+ export declare const ICON_TYPES: IconType[];
13
+ export declare const ICON_DATA: (theme: Theme) => Record<IconType, IconData>;
14
+ export {};
@@ -0,0 +1,61 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { CustomEmoGoodColorIcon, CustomEmoGoodIcon, CustomEmoGreatColorIcon, CustomEmoGreatIcon, CustomEmoNeutralColorIcon, CustomEmoNeutralIcon, } from "../../../../icons/Custom";
3
+ import { Number1Icon, Number2Icon, Number3Icon } from "../../../../icons";
4
+ export const ICON_TYPES = [
5
+ "emoji",
6
+ "ascendingNumber",
7
+ "descendingNumber",
8
+ ];
9
+ export const ICON_DATA = (theme) => ({
10
+ emoji: {
11
+ startIcon: _jsx(CustomEmoGreatColorIcon, {}),
12
+ title: "이모지",
13
+ icons: [
14
+ {
15
+ onIcon: _jsx(CustomEmoGreatColorIcon, {}),
16
+ offIcon: _jsx(CustomEmoGreatIcon, {}),
17
+ },
18
+ { onIcon: _jsx(CustomEmoGoodColorIcon, {}), offIcon: _jsx(CustomEmoGoodIcon, {}) },
19
+ {
20
+ onIcon: _jsx(CustomEmoNeutralColorIcon, {}),
21
+ offIcon: _jsx(CustomEmoNeutralIcon, {}),
22
+ },
23
+ ],
24
+ },
25
+ ascendingNumber: {
26
+ startIcon: _jsx(Number1Icon, {}),
27
+ title: "숫자, 1-2-3",
28
+ icons: [
29
+ {
30
+ onIcon: _jsx(Number1Icon, { color: theme.color.background.primary }),
31
+ offIcon: _jsx(Number1Icon, {}),
32
+ },
33
+ {
34
+ onIcon: _jsx(Number2Icon, { color: theme.color.background.primary }),
35
+ offIcon: _jsx(Number2Icon, {}),
36
+ },
37
+ {
38
+ onIcon: _jsx(Number3Icon, { color: theme.color.background.primary }),
39
+ offIcon: _jsx(Number3Icon, {}),
40
+ },
41
+ ],
42
+ },
43
+ descendingNumber: {
44
+ startIcon: _jsx(Number3Icon, {}),
45
+ title: "숫자, 3-2-1",
46
+ icons: [
47
+ {
48
+ onIcon: _jsx(Number3Icon, { color: theme.color.background.primary }),
49
+ offIcon: _jsx(Number3Icon, {}),
50
+ },
51
+ {
52
+ onIcon: _jsx(Number2Icon, { color: theme.color.background.primary }),
53
+ offIcon: _jsx(Number2Icon, {}),
54
+ },
55
+ {
56
+ onIcon: _jsx(Number1Icon, { color: theme.color.background.primary }),
57
+ offIcon: _jsx(Number1Icon, {}),
58
+ },
59
+ ],
60
+ },
61
+ });
@@ -0,0 +1 @@
1
+ export * from "./SelfEvaluationNode";
@@ -0,0 +1 @@
1
+ export * from "./SelfEvaluationNode";
@@ -17,6 +17,7 @@ import { $isProblemInputNode } from "../../nodes";
17
17
  import { $isProblemSelectNode, } from "../../nodes/ProblemSelectNode";
18
18
  import { $isSheetInputNode } from "../../nodes/SheetInputNode";
19
19
  import { $isSheetSelectNode, } from "../../nodes/SheetSelectNode";
20
+ import { $isSelfEvaluationNode, } from "../../nodes/SelfEvaluationNode";
20
21
  function getParagraphContextMenuOptions(editor, node, setOpen) {
21
22
  return [
22
23
  new ComponentPickerOption("본문", {
@@ -221,6 +222,17 @@ function getSheetSelectContextMenuOptions(node) {
221
222
  }),
222
223
  ];
223
224
  }
225
+ function getSelfEvaluationContextMenuOptions(node) {
226
+ return [
227
+ new ComponentPickerOption("블록 삭제", {
228
+ icon: _jsx(CloseFillIcon, {}),
229
+ keywords: [],
230
+ onSelect: () => {
231
+ node.remove();
232
+ },
233
+ }),
234
+ ];
235
+ }
224
236
  function getColoredQuoteContextMenuOptions(editor, theme, node) {
225
237
  return [
226
238
  new ComponentPickerOption("회색", {
@@ -310,6 +322,9 @@ export function useContextMenuOptions(props) {
310
322
  else if ($isSheetSelectNode(node)) {
311
323
  return getSheetSelectContextMenuOptions(node);
312
324
  }
325
+ else if ($isSelfEvaluationNode(node)) {
326
+ return getSelfEvaluationContextMenuOptions(node);
327
+ }
313
328
  else if (node instanceof ParagraphNode) {
314
329
  return getParagraphContextMenuOptions(editor, node, setImageOpen);
315
330
  }
@@ -21,7 +21,7 @@ import * as ReactDOM from "react-dom";
21
21
  import { css as cssToClassName } from "@emotion/css";
22
22
  import { ComponentPickerMenuList } from "./ComponentPickerMenuList";
23
23
  import { InsertImageDialog } from "../ImagesPlugin/InsertImageDialog";
24
- import { TextIcon, H1Icon, H2Icon, H3Icon, ListUnorderedIcon, ListOrderedIcon, DoubleQuotesLIcon, CodeViewIcon, SeparatorIcon, ImageLineIcon, InputMethodLineIcon, ListRadioIcon, LayoutColumnLineIcon, FileList2LineIcon, } from "../../../../icons";
24
+ import { TextIcon, H1Icon, H2Icon, H3Icon, ListUnorderedIcon, ListOrderedIcon, DoubleQuotesLIcon, CodeViewIcon, SeparatorIcon, ImageLineIcon, InputMethodLineIcon, ListRadioIcon, LayoutColumnLineIcon, FileList2LineIcon, EmojiStickerLineIcon, } from "../../../../icons";
25
25
  import { ZINDEX } from "../../../../utils/zIndex";
26
26
  import { css, useTheme } from "@emotion/react";
27
27
  import { INSERT_PROBLEM_INPUT_COMMAND } from "../ProblemInputPlugin";
@@ -29,6 +29,7 @@ import { INSERT_PROBLEM_SELECT_COMMAND } from "../ProblemSelectPlugin";
29
29
  import { INSERT_LAYOUT_COMMAND } from "../LayoutPlugin";
30
30
  import { INSERT_SHEET_SELECT_COMMAND } from "../SheetSelectPlugin";
31
31
  import { INSERT_SHEET_INPUT_COMMAND } from "../SheetInputPlugin";
32
+ import { INSERT_SELF_EVALUATION_COMMAND } from "../SelfEvaluationPlugin";
32
33
  // import useModal from "../../hooks/useModal";
33
34
  // import catTypingGif from "../../images/cat-typing.gif";
34
35
  // import { INSERT_IMAGE_COMMAND, InsertImageDialog } from "../ImagesPlugin";
@@ -88,6 +89,16 @@ function getSheetContextOptions(editor, theme) {
88
89
  allowMultipleAnswers: false,
89
90
  }),
90
91
  }),
92
+ new ComponentPickerOption("3단계 평가 입력 칸", {
93
+ icon: _jsx(EmojiStickerLineIcon, { color: theme.color.foreground.primary }),
94
+ keywords: ["self evaluation", "3단계 평가 입력"],
95
+ onSelect: () => editor.dispatchCommand(INSERT_SELF_EVALUATION_COMMAND, {
96
+ iconType: "emoji",
97
+ labels: ["아주 잘했어요!", "보통이에요.", "잘 모르겠어요."],
98
+ descriptions: ["평가 항목을 입력하세요."],
99
+ selectedLabelIndexes: [null],
100
+ }),
101
+ }),
91
102
  new ComponentPickerOption("단답형 입력 칸", {
92
103
  icon: _jsx(InputMethodLineIcon, { color: theme.color.foreground.primary }),
93
104
  keywords: ["sheet short input", "단답형 입력"],
@@ -0,0 +1,5 @@
1
+ /// <reference types="react" />
2
+ import { LexicalCommand } from "lexical";
3
+ import { SelfEvaluationPayload } from "../../nodes/SelfEvaluationNode";
4
+ export declare const INSERT_SELF_EVALUATION_COMMAND: LexicalCommand<SelfEvaluationPayload>;
5
+ export default function SelfEvaluationPlugin(): JSX.Element | null;
@@ -0,0 +1,20 @@
1
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
2
+ import { $insertNodeToNearestRoot } from "@lexical/utils";
3
+ import { COMMAND_PRIORITY_EDITOR, createCommand, } from "lexical";
4
+ import { useEffect } from "react";
5
+ import { $createSelfEvaluationNode, SelfEvaluationNode, } from "../../nodes/SelfEvaluationNode";
6
+ export const INSERT_SELF_EVALUATION_COMMAND = createCommand("INSERT_SELF_EVALUATION_COMMAND");
7
+ export default function SelfEvaluationPlugin() {
8
+ const [editor] = useLexicalComposerContext();
9
+ useEffect(() => {
10
+ if (!editor.hasNodes([SelfEvaluationNode])) {
11
+ throw new Error("SelfEvaluationNode: SelfEvaluationNode not registered on editor");
12
+ }
13
+ editor.registerCommand(INSERT_SELF_EVALUATION_COMMAND, (payload) => {
14
+ const selfEvaluationNode = $createSelfEvaluationNode(payload);
15
+ $insertNodeToNearestRoot(selfEvaluationNode);
16
+ return true;
17
+ }, COMMAND_PRIORITY_EDITOR);
18
+ }, [editor]);
19
+ return null;
20
+ }
@@ -35,4 +35,5 @@ export declare function getTheme(theme: Theme, editable: boolean): {
35
35
  sheetInput: string;
36
36
  layoutContainer: string;
37
37
  layoutItem: string | false;
38
+ selfEvaluation: string;
38
39
  };
@@ -285,5 +285,12 @@ export function getTheme(theme, editable) {
285
285
  css `
286
286
  border: 1px dashed ${theme.color.background.neutralAltActive};
287
287
  `,
288
+ selfEvaluation: css `
289
+ display: flex;
290
+ flex-direction: column;
291
+ flex: 1;
292
+ gap: 4px;
293
+ margin-bottom: 8px;
294
+ `,
288
295
  };
289
296
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-monolith/cds",
3
- "version": "1.54.2",
3
+ "version": "1.55.1",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "sideEffects": false,