@team-monolith/cds 1.51.1 → 1.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/icons/Custom.js +2 -2
- package/dist/icons/custom/worksheet-color.svg +5 -5
- package/dist/icons/custom/worksheet.svg +4 -4
- package/dist/patterns/LexicalEditor/LexicalEditor.d.ts +2 -0
- package/dist/patterns/LexicalEditor/LexicalEditor.js +6 -2
- package/dist/patterns/LexicalEditor/Plugins.d.ts +1 -0
- package/dist/patterns/LexicalEditor/Plugins.js +4 -2
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/ProblemSelectNode.d.ts +1 -4
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SelectBox/SelectBoxComponent.d.ts +1 -1
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SelectBox/SelectBoxEdit.d.ts +1 -1
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SelectBox/SelectBoxView.d.ts +1 -1
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/FormSelection.js +1 -1
- package/dist/patterns/LexicalEditor/nodes/SheetInputNode/InputComponent.d.ts +7 -0
- package/dist/patterns/LexicalEditor/nodes/SheetInputNode/InputComponent.js +79 -0
- package/dist/patterns/LexicalEditor/nodes/SheetInputNode/SettingForm.d.ts +11 -0
- package/dist/patterns/LexicalEditor/nodes/SheetInputNode/SettingForm.js +93 -0
- package/dist/patterns/LexicalEditor/nodes/SheetInputNode/SheetInputNode.d.ts +33 -0
- package/dist/patterns/LexicalEditor/nodes/SheetInputNode/SheetInputNode.js +77 -0
- package/dist/patterns/LexicalEditor/nodes/SheetInputNode/index.d.ts +2 -0
- package/dist/patterns/LexicalEditor/nodes/SheetInputNode/index.js +2 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SelectBox/SelectBoxComponent.d.ts +18 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SelectBox/SelectBoxComponent.js +80 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SelectBox/SelectBoxEdit.d.ts +8 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SelectBox/SelectBoxEdit.js +7 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SelectBox/SelectBoxView.d.ts +9 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SelectBox/SelectBoxView.js +22 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SelectBox/index.d.ts +2 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SelectBox/index.js +2 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SelectComponent.d.ts +9 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SelectComponent.js +81 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SettingForm/FormAllowMultipleAnswers.d.ts +6 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SettingForm/FormAllowMultipleAnswers.js +10 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SettingForm/FormSelection.d.ts +11 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SettingForm/FormSelection.js +86 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SettingForm/SettingForm.d.ts +13 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SettingForm/SettingForm.js +148 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SettingForm/index.d.ts +1 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SettingForm/index.js +1 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SheetSelectNode.d.ts +43 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SheetSelectNode.js +82 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/index.d.ts +2 -0
- package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/index.js +2 -0
- package/dist/patterns/LexicalEditor/nodes/{ProblemSelectNode/SettingForm → insertImageDialog}/InsertImageDialog.d.ts +4 -1
- package/dist/patterns/LexicalEditor/nodes/{ProblemSelectNode/SettingForm → insertImageDialog}/InsertImageDialog.js +4 -4
- package/dist/patterns/LexicalEditor/nodes/{ProblemSelectNode/SettingForm → insertImageDialog}/InsertImageUploadedDialogBody.d.ts +1 -1
- package/dist/patterns/LexicalEditor/nodes/{ProblemSelectNode/SettingForm → insertImageDialog}/InsertImageUploadedDialogBody.js +5 -5
- package/dist/patterns/LexicalEditor/nodes/{ProblemSelectNode/SettingForm → insertImageDialog}/InsertImageUriDialogBody.d.ts +1 -1
- package/dist/patterns/LexicalEditor/nodes/{ProblemSelectNode/SettingForm → insertImageDialog}/InsertImageUriDialogBody.js +4 -4
- package/dist/patterns/LexicalEditor/nodes/insertImageDialog/index.d.ts +1 -0
- package/dist/patterns/LexicalEditor/nodes/insertImageDialog/index.js +1 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/ComponentAdderPlugin.d.ts +1 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/ComponentAdderPlugin.js +6 -2
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/useContextMenuOptions.d.ts +1 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/useContextMenuOptions.js +8 -2
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.d.ts +2 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.js +56 -4
- package/dist/patterns/LexicalEditor/plugins/SheetInputPlugin/index.d.ts +5 -0
- package/dist/patterns/LexicalEditor/plugins/SheetInputPlugin/index.js +20 -0
- package/dist/patterns/LexicalEditor/plugins/SheetSelectPlugin/index.d.ts +5 -0
- package/dist/patterns/LexicalEditor/plugins/SheetSelectPlugin/index.js +20 -0
- package/dist/patterns/LexicalEditor/theme.d.ts +2 -0
- package/dist/patterns/LexicalEditor/theme.js +14 -0
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** @jsxImportSource @emotion/react */
|
|
2
|
+
import { NodeKey } from "lexical";
|
|
3
|
+
import { Selection } from "./SheetSelectNode";
|
|
4
|
+
export declare function SelectComponent(props: {
|
|
5
|
+
selections: Selection[];
|
|
6
|
+
selected: string[];
|
|
7
|
+
allowMultipleAnswers: boolean;
|
|
8
|
+
nodeKey: NodeKey;
|
|
9
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,81 @@
|
|
|
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 { $isSheetSelectNode } from "./SheetSelectNode";
|
|
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 styled from "@emotion/styled";
|
|
10
|
+
import { css } from "@emotion/react";
|
|
11
|
+
import { AlarmWarningFillIcon, Settings3FillIcon } from "../../../../icons";
|
|
12
|
+
import { SelectBoxEdit, SelectBoxView } from "./SelectBox";
|
|
13
|
+
import SquareButton from "../../../../components/SquareButton";
|
|
14
|
+
import { SettingForm } from "./SettingForm";
|
|
15
|
+
export function SelectComponent(props) {
|
|
16
|
+
const { selections, selected, allowMultipleAnswers, nodeKey } = props;
|
|
17
|
+
const [editor] = useLexicalComposerContext();
|
|
18
|
+
const [settingOpen, setSettingOpen] = useState(false);
|
|
19
|
+
const isEditable = useLexicalEditable();
|
|
20
|
+
const { freezeProblemNode } = useContext(LexicalCustomConfigContext);
|
|
21
|
+
// view
|
|
22
|
+
if (!isEditable) {
|
|
23
|
+
return (_jsxs(_Fragment, { children: [allowMultipleAnswers && (_jsxs(Alert, { children: [_jsx(AlarmWarningFillIcon, { css: css `
|
|
24
|
+
width: 14px;
|
|
25
|
+
height: 14px;
|
|
26
|
+
` }), "\uC9C8\uBB38\uC5D0 \uD574\uB2F9\uD558\uB294 \uB2F5\uC744 \uBAA8\uB450 \uACE0\uB974\uB294 \uBB38\uD56D\uC785\uB2C8\uB2E4."] })), selections.map((selection, index) => (_jsx(SelectBoxView, { index: index + 1, isSelected: selected.includes(selection.value), image: selection.show.image, text: selection.show.text, onClick: freezeProblemNode
|
|
27
|
+
? undefined
|
|
28
|
+
: () => {
|
|
29
|
+
const isSelected = selected.includes(selection.value);
|
|
30
|
+
if (isSelected) {
|
|
31
|
+
editor.update(() => {
|
|
32
|
+
const node = $getNodeByKey(nodeKey);
|
|
33
|
+
if (!$isSheetSelectNode(node)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
node.setSelected(selected.filter((v) => v !== selection.value));
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
editor.update(() => {
|
|
41
|
+
const node = $getNodeByKey(nodeKey);
|
|
42
|
+
if (!$isSheetSelectNode(node)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (allowMultipleAnswers) {
|
|
46
|
+
node.setSelected([...selected, selection.value]);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
node.setSelected([selection.value]);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
} }, index)))] }));
|
|
54
|
+
}
|
|
55
|
+
// edit
|
|
56
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", Object.assign({ css: css `
|
|
57
|
+
display: flex;
|
|
58
|
+
gap: 4px;
|
|
59
|
+
` }, { children: [_jsx("div", Object.assign({ css: css `
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
gap: 4px;
|
|
63
|
+
` }, { children: selections.map((selection, index) => (_jsx(SelectBoxEdit, { index: index + 1, image: selection.show.image, text: selection.show.text || `${index + 1}번 선택지`, onClick: () => setSettingOpen((open) => !open) }, index))) })), _jsx(SquareButton, { size: "small", color: "icon", icon: _jsx(Settings3FillIcon, {}), onClick: () => {
|
|
64
|
+
setSettingOpen((open) => !open);
|
|
65
|
+
} })] })), settingOpen && (_jsx(SettingForm, { data: {
|
|
66
|
+
selections,
|
|
67
|
+
allowMultipleAnswers,
|
|
68
|
+
}, nodeKey: nodeKey, onClose: () => setSettingOpen(false) }))] }));
|
|
69
|
+
}
|
|
70
|
+
const Alert = styled.div(({ theme }) => css `
|
|
71
|
+
display: flex;
|
|
72
|
+
gap: 4px;
|
|
73
|
+
margin-bottom: 12px;
|
|
74
|
+
color: ${theme.color.foreground.neutralBaseDisabled};
|
|
75
|
+
/* Default/Label/12px-Md */
|
|
76
|
+
font-family: ${theme.fontFamily.ui};
|
|
77
|
+
font-size: 12px;
|
|
78
|
+
font-style: normal;
|
|
79
|
+
font-weight: 500;
|
|
80
|
+
line-height: 16px; /* 133.333% */
|
|
81
|
+
`);
|
package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SettingForm/FormAllowMultipleAnswers.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Control } from "react-hook-form";
|
|
2
|
+
import { SettingFormData } from "./SettingForm";
|
|
3
|
+
/** SheetSelectNode SettingForm의 다중 선택 허용 여부 폼입니다. */
|
|
4
|
+
export declare function FormAllowMultipleAnswers(props: {
|
|
5
|
+
control: Control<SettingFormData, any>;
|
|
6
|
+
}): import("react/jsx-runtime").JSX.Element;
|
package/dist/patterns/LexicalEditor/nodes/SheetSelectNode/SettingForm/FormAllowMultipleAnswers.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Controller } from "react-hook-form";
|
|
3
|
+
import { SegmentedControlButton, SegmentedControlGroup, } from "../../../../SegmentedControl";
|
|
4
|
+
/** SheetSelectNode SettingForm의 다중 선택 허용 여부 폼입니다. */
|
|
5
|
+
export function FormAllowMultipleAnswers(props) {
|
|
6
|
+
const { control } = props;
|
|
7
|
+
return (_jsx(Controller, { name: "allowMultipleAnswers", control: control, render: ({ field: { value, onChange } }) => {
|
|
8
|
+
return (_jsxs(SegmentedControlGroup, Object.assign({ size: "xsmall", value: value, onChange: onChange, fullWidth: true }, { children: [_jsx(SegmentedControlButton, { value: false, label: "\uD558\uB098\uB9CC \uC120\uD0DD" }), _jsx(SegmentedControlButton, { value: true, label: "\uC5EC\uB7EC \uAC1C \uC120\uD0DD" })] })));
|
|
9
|
+
} }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** @jsxImportSource @emotion/react */
|
|
2
|
+
import { Control, UseFormWatch } from "react-hook-form";
|
|
3
|
+
import { SettingFormData } from "./SettingForm";
|
|
4
|
+
/** SheetSelectNode SettingForm의 단일 선택지 폼입니다. */
|
|
5
|
+
export default function FormSelection(props: {
|
|
6
|
+
index: number;
|
|
7
|
+
control: Control<SettingFormData, any>;
|
|
8
|
+
watch: UseFormWatch<SettingFormData>;
|
|
9
|
+
rules?: any;
|
|
10
|
+
onDelete?: () => void;
|
|
11
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
|
+
/** @jsxImportSource @emotion/react */
|
|
3
|
+
import { Controller } from "react-hook-form";
|
|
4
|
+
import styled from "@emotion/styled";
|
|
5
|
+
import { css } from "@emotion/react";
|
|
6
|
+
import { useState } from "react";
|
|
7
|
+
import { InsertImageDialog } from "../../insertImageDialog";
|
|
8
|
+
import { DeleteBinLineIcon, ErrorWarningFillIcon, ImageAddFillIcon, ImageEditFillIcon, Input, SquareButton, } from "../../../../..";
|
|
9
|
+
/** SheetSelectNode SettingForm의 단일 선택지 폼입니다. */
|
|
10
|
+
export default function FormSelection(props) {
|
|
11
|
+
const { index, control, watch, rules, onDelete } = props;
|
|
12
|
+
const [imageOpen, setImageOpen] = useState(false);
|
|
13
|
+
const [inputFocused, setInputFocused] = useState(false);
|
|
14
|
+
return (_jsxs(Container, { children: [_jsx(Index, { children: index + 1 }), _jsxs("div", Object.assign({ css: css `
|
|
15
|
+
display: flex;
|
|
16
|
+
flex: 1;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
gap: 4px;
|
|
19
|
+
` }, { children: [_jsx(Controller, { name: `selections.${index}.show.image`, control: control, render: ({ field: { value, onChange } }) => (_jsxs(_Fragment, { children: [_jsx(InsertImageDialog, { title: value ? "이미지 바꾸기" : "이미지 삽입하기", open: imageOpen, onClose: () => setImageOpen(false), updateImg: (props) => onChange(props), deleteButton: Boolean(value) }), value && value.src && (_jsx("img", { src: value.src, alt: value.altText, css: css `
|
|
20
|
+
height: auto;
|
|
21
|
+
// 이미지로 인해 좌우로 스크롤이 생기는 것을 방지
|
|
22
|
+
max-width: min(400px, 100%);
|
|
23
|
+
width: fit-content;
|
|
24
|
+
border-radius: 6px;
|
|
25
|
+
`, draggable: "false" }))] })) }), _jsx(Controller, { name: `selections.${index}.show.text`, control: control, rules: rules, render: ({ field: { value, onChange }, fieldState: { invalid, error }, }) => (_jsx(Input, { size: "small", color: invalid
|
|
26
|
+
? "activeDanger"
|
|
27
|
+
: inputFocused
|
|
28
|
+
? "activePrimary"
|
|
29
|
+
: "default", value: value, onChange: onChange, inputProps: {
|
|
30
|
+
onFocus: (_e) => {
|
|
31
|
+
setInputFocused(true);
|
|
32
|
+
},
|
|
33
|
+
onBlur: (_e) => {
|
|
34
|
+
setInputFocused(false);
|
|
35
|
+
},
|
|
36
|
+
}, placeholder: `${index + 1}번 선택지`, hintIcon: invalid ? _jsx(ErrorWarningFillIcon, {}) : undefined, hintText: error === null || error === void 0 ? void 0 : error.message, multiline: true, fullWidth: true, css: css `
|
|
37
|
+
flex: 1;
|
|
38
|
+
` })) })] })), _jsxs("div", Object.assign({ css: css `
|
|
39
|
+
display: flex;
|
|
40
|
+
// 이미지가 들어가서 container height가 커져도 높이가 유지되도록 설정
|
|
41
|
+
height: 36px;
|
|
42
|
+
gap: 8px;
|
|
43
|
+
align-items: center;
|
|
44
|
+
` }, { children: [_jsx(SquareButton, { color: "icon", size: "xsmall", icon: watch(`selections.${index}.show.image`) ? (_jsx(ImageEditFillIcon, {})) : (_jsx(ImageAddFillIcon, {})), onClick: () => {
|
|
45
|
+
setImageOpen(true);
|
|
46
|
+
} }), onDelete && (_jsx(SquareButton, { color: "white", size: "xsmall", icon: _jsx(DeleteBinLineIcon, {}), onClick: onDelete }))] }))] }));
|
|
47
|
+
}
|
|
48
|
+
const Container = styled.div(({ theme }) => css `
|
|
49
|
+
display: flex;
|
|
50
|
+
padding: 4px 12px;
|
|
51
|
+
gap: 8px;
|
|
52
|
+
border-radius: 8px;
|
|
53
|
+
background: ${theme.color.background.neutralAlt};
|
|
54
|
+
`);
|
|
55
|
+
const Index = styled.div(({ theme }) => css `
|
|
56
|
+
display: flex;
|
|
57
|
+
box-sizing: border-box;
|
|
58
|
+
width: 20px;
|
|
59
|
+
height: 20px;
|
|
60
|
+
padding: 4px;
|
|
61
|
+
margin-top: 8px;
|
|
62
|
+
justify-content: center;
|
|
63
|
+
align-items: center;
|
|
64
|
+
border-radius: 4px;
|
|
65
|
+
border: 1px solid ${theme.color.background.neutralAltActive};
|
|
66
|
+
background: ${theme.color.background.neutralBase};
|
|
67
|
+
color: ${theme.color.foreground.neutralBaseDisabled};
|
|
68
|
+
font-family: ${theme.fontFamily.ui};
|
|
69
|
+
font-size: 14px;
|
|
70
|
+
font-weight: 800;
|
|
71
|
+
line-height: 16px;
|
|
72
|
+
`);
|
|
73
|
+
const Answer = styled.div(({ theme }) => css `
|
|
74
|
+
display: flex;
|
|
75
|
+
align-items: center;
|
|
76
|
+
padding-right: 4px;
|
|
77
|
+
gap: 8px;
|
|
78
|
+
color: ${theme.color.foreground.neutralBase};
|
|
79
|
+
cursor: pointer;
|
|
80
|
+
/* Default/Label/14px-Md */
|
|
81
|
+
font-family: ${theme.fontFamily.ui};
|
|
82
|
+
font-size: 14px;
|
|
83
|
+
font-style: normal;
|
|
84
|
+
font-weight: 500;
|
|
85
|
+
line-height: 16px; /* 114.286% */
|
|
86
|
+
`);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** @jsxImportSource @emotion/react */
|
|
2
|
+
import { NodeKey } from "lexical";
|
|
3
|
+
import { Selection } from "../SheetSelectNode";
|
|
4
|
+
export interface SettingFormData {
|
|
5
|
+
selections: Selection[];
|
|
6
|
+
allowMultipleAnswers: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** 활동지 활동의 선택형 입력칸 설정 컴포넌트입니다. */
|
|
9
|
+
export declare function SettingForm(props: {
|
|
10
|
+
data: SettingFormData;
|
|
11
|
+
nodeKey: NodeKey;
|
|
12
|
+
onClose: () => void;
|
|
13
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
|
+
/** @jsxImportSource @emotion/react */
|
|
3
|
+
import { $getNodeByKey } from "lexical";
|
|
4
|
+
import { $isSheetSelectNode } from "../SheetSelectNode";
|
|
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, AlarmWarningFillIcon, Button, ListRadioIcon, shadows, } from "../../../../..";
|
|
10
|
+
import { uid } from "uid";
|
|
11
|
+
import FormSelection from "./FormSelection";
|
|
12
|
+
import { FormAllowMultipleAnswers } from "./FormAllowMultipleAnswers";
|
|
13
|
+
/** 활동지 활동의 선택형 입력칸 설정 컴포넌트입니다. */
|
|
14
|
+
export function SettingForm(props) {
|
|
15
|
+
const { data, nodeKey, onClose } = props;
|
|
16
|
+
const { selections, allowMultipleAnswers } = data;
|
|
17
|
+
const [editor] = useLexicalComposerContext();
|
|
18
|
+
const { control, handleSubmit, watch } = useForm({
|
|
19
|
+
mode: "all",
|
|
20
|
+
defaultValues: {
|
|
21
|
+
selections,
|
|
22
|
+
allowMultipleAnswers,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
const { fields, append, remove } = useFieldArray({
|
|
26
|
+
control,
|
|
27
|
+
name: "selections",
|
|
28
|
+
keyName: "uid",
|
|
29
|
+
});
|
|
30
|
+
const onSettingSubmit = (data) => {
|
|
31
|
+
editor.update(() => {
|
|
32
|
+
const node = $getNodeByKey(nodeKey);
|
|
33
|
+
if (!$isSheetSelectNode(node)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
node.setSelections(data.selections);
|
|
37
|
+
node.setAllowMultipleAnswers(data.allowMultipleAnswers);
|
|
38
|
+
});
|
|
39
|
+
onClose();
|
|
40
|
+
};
|
|
41
|
+
function validateDuplicatedSelection(index) {
|
|
42
|
+
const selections = watch("selections");
|
|
43
|
+
if (index === 0)
|
|
44
|
+
return true;
|
|
45
|
+
const duplicatedIndex = selections
|
|
46
|
+
.slice(0, index)
|
|
47
|
+
.findIndex((selection) => selection.show.text === selections[index].show.text);
|
|
48
|
+
if (duplicatedIndex < 0)
|
|
49
|
+
return true;
|
|
50
|
+
return `${duplicatedIndex + 1}번 선택지와 같은 내용입니다.`;
|
|
51
|
+
}
|
|
52
|
+
return (_jsxs(Form, Object.assign({ onSubmit: handleSubmit(onSettingSubmit) }, { children: [_jsxs(Title, { children: [_jsx(ListRadioIcon, { css: css `
|
|
53
|
+
width: 12px;
|
|
54
|
+
height: 12px;
|
|
55
|
+
` }), "\uC120\uD0DD\uD615 \uC785\uB825 \uCE78"] }), _jsxs(Content, { children: [_jsxs(Left, { children: [_jsxs(FormArea, { children: [_jsx(Label, { children: "\uC120\uD0DD\uC9C0" }), fields.map((field, index) => (_jsx(FormSelection, { index: index, control: control, watch: watch, rules: {
|
|
56
|
+
required: "필수 입력 항목입니다.",
|
|
57
|
+
validate: () => validateDuplicatedSelection(index),
|
|
58
|
+
}, onDelete: index !== 0
|
|
59
|
+
? () => {
|
|
60
|
+
remove(index);
|
|
61
|
+
}
|
|
62
|
+
: undefined }, field.uid)))] }), _jsx(Button, { color: "grey", size: "small", startIcon: _jsx(AddFillIcon, {}), label: "\uC120\uD0DD\uC9C0 \uCD94\uAC00", onClick: () => {
|
|
63
|
+
append({
|
|
64
|
+
show: {
|
|
65
|
+
image: null,
|
|
66
|
+
text: "",
|
|
67
|
+
},
|
|
68
|
+
value: uid(),
|
|
69
|
+
});
|
|
70
|
+
} })] }), _jsxs(Right, { children: [_jsxs(FormArea, { children: [_jsx(Label, { children: "\uB2E4\uC911 \uC120\uD0DD \uD5C8\uC6A9 \uC5EC\uBD80" }), _jsx(FormAllowMultipleAnswers, { control: control })] }), watch("allowMultipleAnswers") && (_jsxs(Alert, { children: [_jsx(AlarmWarningFillIcon, { css: css `
|
|
71
|
+
min-width: 14px;
|
|
72
|
+
width: 14px;
|
|
73
|
+
height: 14px;
|
|
74
|
+
` }), "\uB2E4\uC911 \uC120\uD0DD\uC744 \uD5C8\uC6A9\uD55C \uBB38\uD56D\uC5D0\uB294 \uC5EC\uB7EC \uC120\uD0DD\uC9C0\uB97C \uACE0\uB97C \uC218 \uC788\uB2E4\uB294 \uC548\uB0B4\uAC00 \uC81C\uACF5\uB429\uB2C8\uB2E4."] }))] })] }), _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" })] })] })));
|
|
75
|
+
}
|
|
76
|
+
const Form = styled.form(({ theme }) => css `
|
|
77
|
+
display: flex;
|
|
78
|
+
width: 620px;
|
|
79
|
+
flex-direction: column;
|
|
80
|
+
border-radius: 6px;
|
|
81
|
+
border: 1px solid ${theme.color.background.neutralAltActive};
|
|
82
|
+
background: ${theme.color.background.neutralBase};
|
|
83
|
+
box-shadow: ${shadows.shadow08};
|
|
84
|
+
`);
|
|
85
|
+
const Title = styled.div(({ theme }) => css `
|
|
86
|
+
display: flex;
|
|
87
|
+
padding: 8px 12px;
|
|
88
|
+
gap: 4px;
|
|
89
|
+
align-items: center;
|
|
90
|
+
color: ${theme.color.foreground.neutralBase};
|
|
91
|
+
/* Default/Label/12px-Md */
|
|
92
|
+
font-family: ${theme.fontFamily.ui};
|
|
93
|
+
font-size: 12px;
|
|
94
|
+
font-style: normal;
|
|
95
|
+
font-weight: 500;
|
|
96
|
+
line-height: 16px; /* 133.333% */
|
|
97
|
+
`);
|
|
98
|
+
const Content = styled.div(({ theme }) => css `
|
|
99
|
+
display: flex;
|
|
100
|
+
border-top: 1px solid ${theme.color.background.neutralAltActive};
|
|
101
|
+
border-bottom: 1px solid ${theme.color.background.neutralAltActive};
|
|
102
|
+
`);
|
|
103
|
+
const Left = styled.div `
|
|
104
|
+
flex: 1;
|
|
105
|
+
display: flex;
|
|
106
|
+
flex-direction: column;
|
|
107
|
+
gap: 12px;
|
|
108
|
+
padding: 12px;
|
|
109
|
+
`;
|
|
110
|
+
const Right = styled.div `
|
|
111
|
+
width: 240px;
|
|
112
|
+
display: flex;
|
|
113
|
+
flex-direction: column;
|
|
114
|
+
gap: 12px;
|
|
115
|
+
padding: 12px;
|
|
116
|
+
`;
|
|
117
|
+
const FormArea = styled.div `
|
|
118
|
+
display: flex;
|
|
119
|
+
flex-direction: column;
|
|
120
|
+
gap: 8px;
|
|
121
|
+
`;
|
|
122
|
+
const Label = styled.div(({ theme }) => css `
|
|
123
|
+
color: ${theme.color.foreground.neutralBaseDisabled};
|
|
124
|
+
/* Default/Label/12px-Md */
|
|
125
|
+
font-family: ${theme.fontFamily.ui};
|
|
126
|
+
font-size: 12px;
|
|
127
|
+
font-style: normal;
|
|
128
|
+
font-weight: 500;
|
|
129
|
+
line-height: 16px; /* 133.333% */
|
|
130
|
+
`);
|
|
131
|
+
const Buttons = styled.div `
|
|
132
|
+
display: flex;
|
|
133
|
+
padding: 12px;
|
|
134
|
+
justify-content: flex-end;
|
|
135
|
+
align-items: center;
|
|
136
|
+
gap: 8px;
|
|
137
|
+
`;
|
|
138
|
+
const Alert = styled.div(({ theme }) => css `
|
|
139
|
+
display: flex;
|
|
140
|
+
gap: 4px;
|
|
141
|
+
color: ${theme.color.foreground.neutralBaseDisabled};
|
|
142
|
+
/* Default/Label/12px-Md */
|
|
143
|
+
font-family: ${theme.fontFamily.ui};
|
|
144
|
+
font-size: 12px;
|
|
145
|
+
font-style: normal;
|
|
146
|
+
font-weight: 500;
|
|
147
|
+
line-height: 16px; /* 133.333% */
|
|
148
|
+
`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./SettingForm";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./SettingForm";
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { DecoratorNode, EditorConfig, LexicalNode, NodeKey, SerializedLexicalNode, Spread } from "lexical";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
import { ImageProps } from "../insertImageDialog";
|
|
4
|
+
export interface Selection {
|
|
5
|
+
show: {
|
|
6
|
+
image: ImageProps | null;
|
|
7
|
+
text: string;
|
|
8
|
+
};
|
|
9
|
+
value: string;
|
|
10
|
+
}
|
|
11
|
+
export interface SheetSelectPayload {
|
|
12
|
+
selected: string[];
|
|
13
|
+
selections: Selection[];
|
|
14
|
+
allowMultipleAnswers: boolean;
|
|
15
|
+
key?: NodeKey;
|
|
16
|
+
}
|
|
17
|
+
export type SerializedSheetSelectNode = Spread<SheetSelectPayload, SerializedLexicalNode>;
|
|
18
|
+
/**
|
|
19
|
+
* selections는 Selection타입의 배열로서 객관식 정보를 담고 있습니다.
|
|
20
|
+
* selected는 선택한 답의 value를 담고 있습니다.(view mode에서만 노출)
|
|
21
|
+
*/
|
|
22
|
+
export declare class SheetSelectNode extends DecoratorNode<ReactNode> {
|
|
23
|
+
__selections: Selection[];
|
|
24
|
+
__selected: string[];
|
|
25
|
+
__allowMultipleAnswers: boolean;
|
|
26
|
+
isInline(): boolean;
|
|
27
|
+
static getType(): string;
|
|
28
|
+
getSelections(): Selection[];
|
|
29
|
+
getSelected(): string[];
|
|
30
|
+
getAllowMultipleAnswers(): boolean;
|
|
31
|
+
setSelections(selections: Selection[]): void;
|
|
32
|
+
setSelected(selected: string[]): void;
|
|
33
|
+
setAllowMultipleAnswers(allowMultipleAnswers: boolean): void;
|
|
34
|
+
static clone(node: SheetSelectNode): SheetSelectNode;
|
|
35
|
+
constructor(selections: Selection[], selected: string[], allowMultipleAnswers: boolean, key?: NodeKey);
|
|
36
|
+
createDOM(config: EditorConfig): HTMLElement;
|
|
37
|
+
updateDOM(): boolean;
|
|
38
|
+
static importJSON(serializedNode: SerializedSheetSelectNode): SheetSelectNode;
|
|
39
|
+
exportJSON(): SerializedSheetSelectNode;
|
|
40
|
+
decorate(): ReactNode;
|
|
41
|
+
}
|
|
42
|
+
export declare function $createSheetSelectNode(payload: SheetSelectPayload): SheetSelectNode;
|
|
43
|
+
export declare function $isSheetSelectNode(node: LexicalNode | null | undefined): node is SheetSelectNode;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { $applyNodeReplacement, DecoratorNode, } from "lexical";
|
|
3
|
+
import { addClassNamesToElement } from "@lexical/utils";
|
|
4
|
+
import { SelectComponent } from "./SelectComponent";
|
|
5
|
+
/**
|
|
6
|
+
* selections는 Selection타입의 배열로서 객관식 정보를 담고 있습니다.
|
|
7
|
+
* selected는 선택한 답의 value를 담고 있습니다.(view mode에서만 노출)
|
|
8
|
+
*/
|
|
9
|
+
export class SheetSelectNode extends DecoratorNode {
|
|
10
|
+
isInline() {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
static getType() {
|
|
14
|
+
return "sheet-select";
|
|
15
|
+
}
|
|
16
|
+
getSelections() {
|
|
17
|
+
return this.__selections;
|
|
18
|
+
}
|
|
19
|
+
getSelected() {
|
|
20
|
+
return this.__selected;
|
|
21
|
+
}
|
|
22
|
+
getAllowMultipleAnswers() {
|
|
23
|
+
return this.__allowMultipleAnswers;
|
|
24
|
+
}
|
|
25
|
+
setSelections(selections) {
|
|
26
|
+
const self = this.getWritable();
|
|
27
|
+
self.__selections = selections;
|
|
28
|
+
}
|
|
29
|
+
setSelected(selected) {
|
|
30
|
+
const self = this.getWritable();
|
|
31
|
+
self.__selected = selected;
|
|
32
|
+
}
|
|
33
|
+
setAllowMultipleAnswers(allowMultipleAnswers) {
|
|
34
|
+
const self = this.getWritable();
|
|
35
|
+
self.__allowMultipleAnswers = allowMultipleAnswers;
|
|
36
|
+
}
|
|
37
|
+
static clone(node) {
|
|
38
|
+
return new SheetSelectNode(node.__selections, node.__selected, node.__allowMultipleAnswers, node.__key);
|
|
39
|
+
}
|
|
40
|
+
constructor(selections, selected, allowMultipleAnswers, key) {
|
|
41
|
+
super(key);
|
|
42
|
+
this.__selections = selections;
|
|
43
|
+
this.__selected = selected;
|
|
44
|
+
this.__allowMultipleAnswers = allowMultipleAnswers;
|
|
45
|
+
}
|
|
46
|
+
createDOM(config) {
|
|
47
|
+
// Define the DOM element here
|
|
48
|
+
const root = document.createElement("div");
|
|
49
|
+
addClassNamesToElement(root, config.theme.sheetSelect);
|
|
50
|
+
return root;
|
|
51
|
+
}
|
|
52
|
+
updateDOM() {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
static importJSON(serializedNode) {
|
|
56
|
+
return $createSheetSelectNode({
|
|
57
|
+
key: serializedNode.key,
|
|
58
|
+
selections: serializedNode.selections,
|
|
59
|
+
selected: serializedNode.selected,
|
|
60
|
+
allowMultipleAnswers: serializedNode.allowMultipleAnswers,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
exportJSON() {
|
|
64
|
+
return {
|
|
65
|
+
type: "sheet-select",
|
|
66
|
+
version: 1,
|
|
67
|
+
selections: this.__selections,
|
|
68
|
+
selected: this.__selected,
|
|
69
|
+
allowMultipleAnswers: this.__allowMultipleAnswers,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
decorate() {
|
|
73
|
+
return (_jsx(SelectComponent, { selections: this.__selections, selected: this.__selected, allowMultipleAnswers: this.__allowMultipleAnswers, nodeKey: this.__key }));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export function $createSheetSelectNode(payload) {
|
|
77
|
+
const { selections, selected, key, allowMultipleAnswers } = payload;
|
|
78
|
+
return $applyNodeReplacement(new SheetSelectNode(selections, selected, allowMultipleAnswers, key));
|
|
79
|
+
}
|
|
80
|
+
export function $isSheetSelectNode(node) {
|
|
81
|
+
return node instanceof SheetSelectNode;
|
|
82
|
+
}
|
|
@@ -12,10 +12,10 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@emotion/reac
|
|
|
12
12
|
import { useContext, useRef, useState } from "react";
|
|
13
13
|
import styled from "@emotion/styled";
|
|
14
14
|
import { css, useTheme } from "@emotion/react";
|
|
15
|
-
import { ImageFillIcon, LinkIcon, UploadLineIcon } from "
|
|
16
|
-
import { AlertDialog, AlertDialogContent, AlertDialogTitle, } from "
|
|
17
|
-
import Button from "
|
|
18
|
-
import { CodleDesignSystemContext } from "
|
|
15
|
+
import { ImageFillIcon, LinkIcon, UploadLineIcon } from "../../../../icons";
|
|
16
|
+
import { AlertDialog, AlertDialogContent, AlertDialogTitle, } from "../../../../components/AlertDialog";
|
|
17
|
+
import Button from "../../../../components/Button";
|
|
18
|
+
import { CodleDesignSystemContext } from "../../../../CodleDesignSystemProvider";
|
|
19
19
|
import { InsertImageUriDialogBody } from "./InsertImageUriDialogBody";
|
|
20
20
|
import { InsertImageUploadedDialogBody } from "./InsertImageUploadedDialogBody";
|
|
21
21
|
import { useForm } from "react-hook-form";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ImageProps } from "../ProblemSelectNode";
|
|
2
1
|
import { Control, UseFormSetValue, UseFormWatch } from "react-hook-form";
|
|
2
|
+
import { ImageProps } from "./InsertImageDialog";
|
|
3
3
|
export interface InsertImageUploadedDialogBodyProps {
|
|
4
4
|
control: Control<ImageProps, any>;
|
|
5
5
|
watch: UseFormWatch<ImageProps>;
|
|
@@ -10,11 +10,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
11
11
|
import { useContext, useRef } from "react";
|
|
12
12
|
import styled from "@emotion/styled";
|
|
13
|
-
import { CodleDesignSystemContext } from "
|
|
14
|
-
import { AlertDialogActions, AlertDialogContent, } from "
|
|
15
|
-
import Button from "
|
|
16
|
-
import { RefreshLineIcon } from "
|
|
17
|
-
import Input from "
|
|
13
|
+
import { CodleDesignSystemContext } from "../../../../CodleDesignSystemProvider";
|
|
14
|
+
import { AlertDialogActions, AlertDialogContent, } from "../../../../components/AlertDialog";
|
|
15
|
+
import Button from "../../../../components/Button";
|
|
16
|
+
import { RefreshLineIcon } from "../../../../icons";
|
|
17
|
+
import Input from "../../../../components/Input";
|
|
18
18
|
import { Controller, } from "react-hook-form";
|
|
19
19
|
export function InsertImageUploadedDialogBody(props) {
|
|
20
20
|
const { control, watch, setValue } = props;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ImageProps } from "../ProblemSelectNode";
|
|
2
1
|
import { Control, UseFormWatch } from "react-hook-form";
|
|
2
|
+
import { ImageProps } from "./InsertImageDialog";
|
|
3
3
|
export interface InsertImageUriDialogBodyProps {
|
|
4
4
|
control: Control<ImageProps, any>;
|
|
5
5
|
watch: UseFormWatch<ImageProps>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import styled from "@emotion/styled";
|
|
3
|
-
import { AlertDialogActions, AlertDialogContent, } from "
|
|
4
|
-
import Input from "
|
|
5
|
-
import { LinkIcon } from "
|
|
6
|
-
import Button from "
|
|
3
|
+
import { AlertDialogActions, AlertDialogContent, } from "../../../../components/AlertDialog";
|
|
4
|
+
import Input from "../../../../components/Input";
|
|
5
|
+
import { LinkIcon } from "../../../../icons";
|
|
6
|
+
import Button from "../../../../components/Button";
|
|
7
7
|
import { Controller } from "react-hook-form";
|
|
8
8
|
export function InsertImageUriDialogBody(props) {
|
|
9
9
|
const { control, watch } = props;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./InsertImageDialog";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./InsertImageDialog";
|
|
@@ -54,7 +54,7 @@ function getTextUpToAnchor(selection) {
|
|
|
54
54
|
return anchorNode.getTextContent().slice(0, anchorOffset);
|
|
55
55
|
}
|
|
56
56
|
export function ComponentAdderPlugin(props) {
|
|
57
|
-
const { anchorElem, isQuizEnabled } = props;
|
|
57
|
+
const { anchorElem, isSheetEnabled, isQuizEnabled } = props;
|
|
58
58
|
const theme = useTheme();
|
|
59
59
|
const [editor] = useLexicalComposerContext();
|
|
60
60
|
const [nodeKey, setNodeKey] = useState(null);
|
|
@@ -137,7 +137,10 @@ export function ComponentAdderPlugin(props) {
|
|
|
137
137
|
return newResolves.filter((newResolve) => resolves.indexOf(newResolve) === -1);
|
|
138
138
|
});
|
|
139
139
|
});
|
|
140
|
-
const getContextMenuOptions = useContextMenuOptions({
|
|
140
|
+
const getContextMenuOptions = useContextMenuOptions({
|
|
141
|
+
isSheetEnabled,
|
|
142
|
+
isQuizEnabled,
|
|
143
|
+
});
|
|
141
144
|
const filteredOptions = options.filter((option) => {
|
|
142
145
|
if (!query) {
|
|
143
146
|
return true;
|
|
@@ -197,6 +200,7 @@ export function ComponentAdderPlugin(props) {
|
|
|
197
200
|
editor,
|
|
198
201
|
theme,
|
|
199
202
|
setImageOpen,
|
|
203
|
+
isSheetEnabled,
|
|
200
204
|
isQuizEnabled,
|
|
201
205
|
});
|
|
202
206
|
setOptions(baseOptions);
|
|
@@ -4,5 +4,6 @@
|
|
|
4
4
|
import { LexicalEditor, LexicalNode } from "lexical";
|
|
5
5
|
import { ComponentDrawerOption, ComponentPickerOption } from "../ComponentPickerMenuPlugin";
|
|
6
6
|
export declare function useContextMenuOptions(props: {
|
|
7
|
+
isSheetEnabled: boolean;
|
|
7
8
|
isQuizEnabled: boolean;
|
|
8
9
|
}): (editor: LexicalEditor, node: LexicalNode, setImageOpen: (open: boolean) => void) => (ComponentPickerOption | ComponentDrawerOption)[];
|