@team-monolith/cds 1.55.0 → 1.55.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/patterns/LexicalEditor/nodes/Form/FormInput.d.ts +11 -0
- package/dist/patterns/LexicalEditor/nodes/Form/FormInput.js +51 -0
- package/dist/patterns/LexicalEditor/nodes/Form/index.d.ts +1 -0
- package/dist/patterns/LexicalEditor/nodes/Form/index.js +1 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/Evaluation.d.ts +8 -10
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/Evaluation.js +11 -17
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormIconAndLabel/FormLabel.js +9 -16
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/{FormDescription.d.ts → FormQuestion.d.ts} +1 -1
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormQuestion.js +40 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/SettingForm.d.ts +4 -6
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/SettingForm.js +12 -11
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/EvaluationComponent.d.ts +2 -4
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/EvaluationComponent.js +18 -8
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/SelfEvaluationNode.d.ts +12 -10
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/SelfEvaluationNode.js +12 -22
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.js +8 -2
- package/dist/patterns/ToggleButtonGroup/ToggleButtonGroup.d.ts +3 -3
- package/dist/patterns/ToggleButtonGroup/ToggleButtonGroup.js +8 -3
- package/package.json +1 -1
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormDescription.js +0 -55
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { DistributiveOmit } from "@emotion/react";
|
|
2
|
+
import { InputProps } from "../../../..";
|
|
3
|
+
export type FormInputProps = DistributiveOmit<InputProps, "color" | "onChange" | "value" | "hintIcon" | "hintText"> & {
|
|
4
|
+
name: string;
|
|
5
|
+
control: any;
|
|
6
|
+
baseColor?: "default" | "base";
|
|
7
|
+
rules?: any;
|
|
8
|
+
/** html event에서 얻은 value를 form에 주입하기 전에 변환합니다. */
|
|
9
|
+
transform?: (value: string) => any;
|
|
10
|
+
};
|
|
11
|
+
export declare function FormInput(props: FormInputProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
13
|
+
import { ErrorWarningFillIcon, Input } from "../../../..";
|
|
14
|
+
import { Controller } from "react-hook-form";
|
|
15
|
+
import { useState } from "react";
|
|
16
|
+
export function FormInput(props) {
|
|
17
|
+
const { name, control, rules, baseColor = "default", transform = (value) => value } = props, inputComponentProps = __rest(props, ["name", "control", "rules", "baseColor", "transform"]);
|
|
18
|
+
const [inputFocused, setInputFocused] = useState(false);
|
|
19
|
+
return (_jsx(Controller, { name: name, control: control, rules: rules, render: ({ field: { onChange, value }, fieldState: { invalid, error }, }) => {
|
|
20
|
+
const onChangeHandler = (e) => {
|
|
21
|
+
onChange(transform(e.target.value));
|
|
22
|
+
};
|
|
23
|
+
// multiline 여부에 따라 inputProps의 type이 변경되므로 (union type)
|
|
24
|
+
// type-safety를 보장하기 위해 if 문을 분기하여 inputProps에 `name` 을 추가합니다.
|
|
25
|
+
// 코드 반복 없이 가능할까요?
|
|
26
|
+
if (inputComponentProps.multiline) {
|
|
27
|
+
const { inputProps } = inputComponentProps, other = __rest(inputComponentProps, ["inputProps"]);
|
|
28
|
+
return (_jsx(Input, Object.assign({ color: invalid
|
|
29
|
+
? "activeDanger"
|
|
30
|
+
: inputFocused
|
|
31
|
+
? "activePrimary"
|
|
32
|
+
: baseColor, onChange: onChangeHandler, value: value !== null && value !== void 0 ? value : "", hintIcon: invalid ? _jsx(ErrorWarningFillIcon, {}) : undefined, hintText: error === null || error === void 0 ? void 0 : error.message, inputProps: Object.assign(Object.assign({ name }, inputProps), { onFocus: () => {
|
|
33
|
+
setInputFocused(true);
|
|
34
|
+
}, onBlur: () => {
|
|
35
|
+
setInputFocused(false);
|
|
36
|
+
} }) }, other)));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
const { inputProps } = inputComponentProps, other = __rest(inputComponentProps, ["inputProps"]);
|
|
40
|
+
return (_jsx(Input, Object.assign({ color: invalid
|
|
41
|
+
? "activeDanger"
|
|
42
|
+
: inputFocused
|
|
43
|
+
? "activePrimary"
|
|
44
|
+
: baseColor, onChange: onChangeHandler, value: value !== null && value !== void 0 ? value : "", hintIcon: invalid ? _jsx(ErrorWarningFillIcon, {}) : undefined, hintText: error === null || error === void 0 ? void 0 : error.message, inputProps: Object.assign(Object.assign({ name }, inputProps), { onFocus: () => {
|
|
45
|
+
setInputFocused(true);
|
|
46
|
+
}, onBlur: () => {
|
|
47
|
+
setInputFocused(false);
|
|
48
|
+
} }) }, other)));
|
|
49
|
+
}
|
|
50
|
+
} }));
|
|
51
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./FormInput";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./FormInput";
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { ToggleButtonDatum } from "../../../../../ToggleButtonGroup";
|
|
2
|
+
import { EvaluationItem } from "../../SelfEvaluationNode";
|
|
3
|
+
/** 데이터를 받아 자기평가 항목들과 ToggleButtonGroup을 그리는 컴포넌트입니다.
|
|
3
4
|
* 비지니스 로직이 포함되어 있지 않습니다.
|
|
4
5
|
*/
|
|
5
6
|
export declare function Evaluation(props: {
|
|
6
7
|
className?: string;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
labels: string[];
|
|
12
|
-
descriptions: string[];
|
|
13
|
-
selectedLabelIndexes: (number | null)[];
|
|
8
|
+
/** 자기 평가 항목들 */
|
|
9
|
+
evaluations: EvaluationItem[];
|
|
10
|
+
/** ToggleButton의 데이터 배열 */
|
|
11
|
+
data: ToggleButtonDatum[];
|
|
14
12
|
/** 블럭을 클릭했을 때 실행될 콜백. 전달하지 않으면 cursor: pointer가 적용되지 않습니다. */
|
|
15
13
|
onClick?: () => void;
|
|
16
14
|
/** 전달하지 않으면 ToggleButtonGroup 호버, 클릭 동작이 없습니다. (pointer-events: none) */
|
|
17
|
-
onChange?: (
|
|
15
|
+
onChange?: (evaluations: EvaluationItem[]) => void;
|
|
18
16
|
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -2,26 +2,20 @@ import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
|
2
2
|
/** @jsxImportSource @emotion/react */
|
|
3
3
|
import styled from "@emotion/styled";
|
|
4
4
|
import { css } from "@emotion/react";
|
|
5
|
-
import { ToggleButtonGroup } from "../../../../../ToggleButtonGroup";
|
|
5
|
+
import { ToggleButtonGroup, } from "../../../../../ToggleButtonGroup";
|
|
6
6
|
const TOGGLE_BUTTON_GROUP_WIDTH = 320;
|
|
7
|
-
/** 데이터를 받아 자기평가
|
|
7
|
+
/** 데이터를 받아 자기평가 항목들과 ToggleButtonGroup을 그리는 컴포넌트입니다.
|
|
8
8
|
* 비지니스 로직이 포함되어 있지 않습니다.
|
|
9
9
|
*/
|
|
10
10
|
export function Evaluation(props) {
|
|
11
|
-
const { className,
|
|
12
|
-
return (_jsxs(Container, Object.assign({ className: className, onClick: onClick }, { children: [_jsx(Header, { children: _jsx(LabelArea, { children:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const newSelectedLabelIndexes = [...selectedLabelIndexes];
|
|
20
|
-
newSelectedLabelIndexes[index] = activeIndex;
|
|
21
|
-
onChange(newSelectedLabelIndexes);
|
|
22
|
-
}
|
|
23
|
-
} })] }, index));
|
|
24
|
-
})] })));
|
|
11
|
+
const { className, evaluations, data, onClick, onChange } = props;
|
|
12
|
+
return (_jsxs(Container, Object.assign({ className: className, onClick: onClick }, { children: [_jsx(Header, { children: _jsx(LabelArea, { children: data.map(({ label }, index) => (_jsx(Label, { children: label }, index))) }) }), evaluations.map(({ question, selectedLabelIndex }, index) => (_jsxs(Item, { children: [_jsx(Index, { children: index + 1 }), _jsx(Question, { children: question.text }), _jsx(StyledToggleButtonGroup, { data: data, activeIndex: selectedLabelIndex, setActiveIndex: !onChange
|
|
13
|
+
? undefined
|
|
14
|
+
: (activeIndex) => {
|
|
15
|
+
const newEvaluations = [...evaluations];
|
|
16
|
+
newEvaluations[index].selectedLabelIndex = activeIndex;
|
|
17
|
+
onChange(newEvaluations);
|
|
18
|
+
} })] }, index)))] })));
|
|
25
19
|
}
|
|
26
20
|
const Container = styled.div(({ onClick }) => css `
|
|
27
21
|
${onClick && "cursor: pointer;"}
|
|
@@ -84,7 +78,7 @@ const Index = styled.div(({ theme }) => css `
|
|
|
84
78
|
font-weight: 800;
|
|
85
79
|
line-height: 16px;
|
|
86
80
|
`);
|
|
87
|
-
const
|
|
81
|
+
const Question = styled.div(({ theme }) => css `
|
|
88
82
|
display: flex;
|
|
89
83
|
padding: 4px 8px;
|
|
90
84
|
flex-direction: column;
|
|
@@ -1,25 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
import { ErrorWarningFillIcon, Input } from "../../../../../../../..";
|
|
2
|
+
import { useController } from "react-hook-form";
|
|
5
3
|
import styled from "@emotion/styled";
|
|
6
4
|
import { css } from "@emotion/react";
|
|
5
|
+
import { FormInput } from "../../../../../Form";
|
|
7
6
|
/** 레이블을 설정하는 Form입니다. */
|
|
8
7
|
export function FormLabel(props) {
|
|
9
8
|
const { control, index, rules } = props;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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 })) }));
|
|
9
|
+
// endIcon에 들어갈 value, error를 사용하기 위해 useController를 사용합니다.
|
|
10
|
+
const { field: { value }, fieldState: { error }, } = useController({
|
|
11
|
+
control,
|
|
12
|
+
name: `labels.${index}`,
|
|
13
|
+
rules,
|
|
14
|
+
});
|
|
15
|
+
return (_jsx(FormInput, { control: control, name: `labels.${index}`, rules: rules, size: "small", endIcon: (rules === null || rules === void 0 ? void 0 : rules.maxLength) && (_jsx(Counter, Object.assign({ error: Boolean(error) }, { children: value.length + "/" + rules.maxLength }))), fullWidth: true }));
|
|
23
16
|
}
|
|
24
17
|
const Counter = styled.span(({ theme, error }) => css `
|
|
25
18
|
color: ${error
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { Control } from "react-hook-form";
|
|
3
3
|
import { SettingFormData } from "./SettingForm";
|
|
4
4
|
/** 평가 항목을 설정하는 Form입니다. */
|
|
5
|
-
export declare function
|
|
5
|
+
export declare function FormQuestion(props: {
|
|
6
6
|
index: number;
|
|
7
7
|
control: Control<SettingFormData, any>;
|
|
8
8
|
rules: any;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
|
+
import styled from "@emotion/styled";
|
|
3
|
+
import { css } from "@emotion/react";
|
|
4
|
+
import { DeleteBinLineIcon, SquareButton } from "../../../../../../..";
|
|
5
|
+
import { FormInput } from "../../../../Form";
|
|
6
|
+
/** 평가 항목을 설정하는 Form입니다. */
|
|
7
|
+
export function FormQuestion(props) {
|
|
8
|
+
const { control, index, rules, onDelete } = props;
|
|
9
|
+
return (_jsxs(Item, { children: [_jsx(Index, { children: index + 1 }), _jsx(FormInput, { control: control, name: `evaluations.${index}.question.text`, rules: rules, size: "small", placeholder: `${index + 1}번 평가 항목`, multiline: true, fullWidth: true }), onDelete && (_jsx(SquareButton, { color: "white", size: "xsmall", icon: _jsx(DeleteBinLineIcon, {}), onClick: onDelete }))] }));
|
|
10
|
+
}
|
|
11
|
+
const Item = styled.div(({ theme }) => css `
|
|
12
|
+
display: flex;
|
|
13
|
+
padding: 4px 12px;
|
|
14
|
+
align-items: center;
|
|
15
|
+
gap: 4px;
|
|
16
|
+
align-self: stretch;
|
|
17
|
+
border-radius: 8px;
|
|
18
|
+
background: ${theme.color.background.neutralAlt};
|
|
19
|
+
`);
|
|
20
|
+
const Index = styled.div(({ theme }) => css `
|
|
21
|
+
display: flex;
|
|
22
|
+
width: 20px;
|
|
23
|
+
height: 20px;
|
|
24
|
+
padding: 4px;
|
|
25
|
+
|
|
26
|
+
user-select: none; /* Standard syntax */
|
|
27
|
+
|
|
28
|
+
border-radius: 4px;
|
|
29
|
+
background: ${theme.color.background.neutralBase};
|
|
30
|
+
border: 1px solid ${theme.color.background.neutralAltActive};
|
|
31
|
+
|
|
32
|
+
justify-content: center;
|
|
33
|
+
align-items: center;
|
|
34
|
+
|
|
35
|
+
color: ${theme.color.foreground.neutralBaseDisabled};
|
|
36
|
+
font-family: ${theme.fontFamily.ui};
|
|
37
|
+
font-size: 14px;
|
|
38
|
+
font-weight: 800;
|
|
39
|
+
line-height: 16px;
|
|
40
|
+
`);
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
/** @jsxImportSource @emotion/react */
|
|
2
2
|
import { NodeKey } from "lexical";
|
|
3
|
-
import { IconType } from "../../../SelfEvaluationNode";
|
|
4
|
-
export interface Description {
|
|
5
|
-
text: string;
|
|
6
|
-
}
|
|
3
|
+
import { EvaluationItem, IconType } from "../../../SelfEvaluationNode";
|
|
7
4
|
export interface SettingFormData {
|
|
8
5
|
iconType: IconType;
|
|
9
6
|
labels: string[];
|
|
10
|
-
|
|
7
|
+
evaluations: EvaluationItem[];
|
|
11
8
|
}
|
|
12
9
|
/** 자기평가 edit의 설정 컴포넌트입니다. */
|
|
13
10
|
export declare function SettingForm(props: {
|
|
11
|
+
className?: string;
|
|
14
12
|
iconType: IconType;
|
|
15
13
|
labels: string[];
|
|
16
|
-
|
|
14
|
+
evaluations: EvaluationItem[];
|
|
17
15
|
nodeKey: NodeKey;
|
|
18
16
|
onClose: () => void;
|
|
19
17
|
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
2
|
/** @jsxImportSource @emotion/react */
|
|
3
3
|
import { $getNodeByKey } from "lexical";
|
|
4
|
-
import { $isSelfEvaluationNode } from "../../../SelfEvaluationNode";
|
|
4
|
+
import { $isSelfEvaluationNode, } from "../../../SelfEvaluationNode";
|
|
5
5
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
6
6
|
import { useFieldArray, useForm } from "react-hook-form";
|
|
7
7
|
import styled from "@emotion/styled";
|
|
8
8
|
import { css } from "@emotion/react";
|
|
9
9
|
import { AddFillIcon, Button, EmojiStickerLineIcon, shadows, } from "../../../../../../..";
|
|
10
|
-
import { EVALUATION_EDIT_WIDTH } from "../../EvaluationComponent";
|
|
11
10
|
import { FormIconAndLabel } from "./FormIconAndLabel";
|
|
12
|
-
import {
|
|
11
|
+
import { FormQuestion } from "./FormQuestion";
|
|
13
12
|
/** 자기평가 edit의 설정 컴포넌트입니다. */
|
|
14
13
|
export function SettingForm(props) {
|
|
15
|
-
const { iconType, labels,
|
|
14
|
+
const { className, iconType, labels, evaluations, nodeKey, onClose } = props;
|
|
16
15
|
const [editor] = useLexicalComposerContext();
|
|
17
16
|
const { control, handleSubmit } = useForm({
|
|
18
17
|
mode: "all",
|
|
19
18
|
defaultValues: {
|
|
20
19
|
iconType,
|
|
21
20
|
labels,
|
|
22
|
-
|
|
21
|
+
evaluations,
|
|
23
22
|
},
|
|
24
23
|
});
|
|
25
24
|
const { fields, append, remove } = useFieldArray({
|
|
26
25
|
control,
|
|
27
|
-
name: "
|
|
26
|
+
name: "evaluations",
|
|
28
27
|
keyName: "uid",
|
|
29
28
|
});
|
|
30
29
|
const onSubmit = (data) => {
|
|
@@ -35,26 +34,28 @@ export function SettingForm(props) {
|
|
|
35
34
|
}
|
|
36
35
|
node.setIconType(data.iconType);
|
|
37
36
|
node.setLabels(data.labels);
|
|
38
|
-
node.
|
|
37
|
+
node.setEvaluations(data.evaluations);
|
|
39
38
|
});
|
|
40
39
|
onClose();
|
|
41
40
|
};
|
|
42
|
-
return (_jsxs(Form, Object.assign({ onSubmit: handleSubmit(onSubmit) }, { children: [_jsxs(Title, { children: [_jsx(EmojiStickerLineIcon, { css: css `
|
|
41
|
+
return (_jsxs(Form, Object.assign({ className: className, onSubmit: handleSubmit(onSubmit) }, { children: [_jsxs(Title, { children: [_jsx(EmojiStickerLineIcon, { css: css `
|
|
43
42
|
width: 12px;
|
|
44
43
|
height: 12px;
|
|
45
|
-
` }), "3\uB2E8\uACC4 \uD3C9\uAC00"] }), _jsxs(Content, { children: [_jsxs(Left, { children: [_jsx(Label, { children: "\uD3C9\uAC00 \uD56D\uBAA9" }), fields.map((field, index) => (_jsx(
|
|
44
|
+
` }), "3\uB2E8\uACC4 \uD3C9\uAC00"] }), _jsxs(Content, { children: [_jsxs(Left, { children: [_jsx(Label, { children: "\uD3C9\uAC00 \uD56D\uBAA9" }), fields.map((field, index) => (_jsx(FormQuestion, { index: index, control: control, rules: {
|
|
46
45
|
required: "필수 입력 항목입니다.",
|
|
47
46
|
}, onDelete: index !== 0
|
|
48
47
|
? () => {
|
|
49
48
|
remove(index);
|
|
50
49
|
}
|
|
51
50
|
: undefined }, field.uid))), _jsx(Button, { color: "grey", size: "small", startIcon: _jsx(AddFillIcon, {}), label: "\uD3C9\uAC00 \uD56D\uBAA9 \uCD94\uAC00", onClick: () => {
|
|
52
|
-
append({
|
|
51
|
+
append({
|
|
52
|
+
question: { text: "평가 항목을 입력하세요." },
|
|
53
|
+
selectedLabelIndex: null,
|
|
54
|
+
});
|
|
53
55
|
} })] }), _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" })] })] })));
|
|
54
56
|
}
|
|
55
57
|
const Form = styled.form(({ theme }) => css `
|
|
56
58
|
display: flex;
|
|
57
|
-
width: ${EVALUATION_EDIT_WIDTH}px;
|
|
58
59
|
flex-direction: column;
|
|
59
60
|
border-radius: 6px;
|
|
60
61
|
border: 1px solid ${theme.color.background.neutralAltActive};
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
/** @jsxImportSource @emotion/react */
|
|
2
2
|
import { NodeKey } from "lexical";
|
|
3
|
-
import { IconType } from "../SelfEvaluationNode";
|
|
4
|
-
export declare const EVALUATION_EDIT_WIDTH = 620;
|
|
3
|
+
import { EvaluationItem, IconType } from "../SelfEvaluationNode";
|
|
5
4
|
/** SelfEvaluationNode를 그리는 컴포넌트입니다. */
|
|
6
5
|
export declare function EvaluationComponent(props: {
|
|
7
6
|
iconType: IconType;
|
|
8
7
|
labels: string[];
|
|
9
|
-
|
|
10
|
-
selectedLabelIndexes: (number | null)[];
|
|
8
|
+
evaluations: EvaluationItem[];
|
|
11
9
|
nodeKey: NodeKey;
|
|
12
10
|
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@emotion/react/jsx-runtime";
|
|
2
2
|
/** @jsxImportSource @emotion/react */
|
|
3
3
|
import { $getNodeByKey } from "lexical";
|
|
4
|
-
import { $isSelfEvaluationNode } from "../SelfEvaluationNode";
|
|
4
|
+
import { $isSelfEvaluationNode, } from "../SelfEvaluationNode";
|
|
5
5
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
6
6
|
import { useContext, useState } from "react";
|
|
7
7
|
import useLexicalEditable from "@lexical/react/useLexicalEditable";
|
|
@@ -11,27 +11,35 @@ import { ICON_DATA } from "../iconData";
|
|
|
11
11
|
import { css, useTheme } from "@emotion/react";
|
|
12
12
|
import SquareButton from "../../../../../components/SquareButton";
|
|
13
13
|
import { Settings3FillIcon } from "../../../../../icons";
|
|
14
|
-
|
|
14
|
+
import _ from "lodash";
|
|
15
|
+
const EVALUATION_EDIT_WIDTH = 620;
|
|
15
16
|
/** SelfEvaluationNode를 그리는 컴포넌트입니다. */
|
|
16
17
|
export function EvaluationComponent(props) {
|
|
17
|
-
const { iconType, labels,
|
|
18
|
+
const { iconType, labels, evaluations, nodeKey } = props;
|
|
18
19
|
const theme = useTheme();
|
|
19
20
|
const [editor] = useLexicalComposerContext();
|
|
20
21
|
const isEditable = useLexicalEditable();
|
|
21
22
|
const { freezeProblemNode } = useContext(LexicalCustomConfigContext);
|
|
22
23
|
const [settingOpen, setSettingOpen] = useState(false);
|
|
23
24
|
const { icons } = ICON_DATA(theme)[iconType];
|
|
25
|
+
// label과 icons를 매핑하여 ToggelButtonData로 변환합니다.
|
|
26
|
+
// 만약 둘의 길이가 같지 않다면 짧은 길이에 맞춰집니다.
|
|
27
|
+
const data = _.zipWith(labels, icons, (label, { onIcon, offIcon }) => ({
|
|
28
|
+
label,
|
|
29
|
+
onIcon,
|
|
30
|
+
offIcon,
|
|
31
|
+
}));
|
|
24
32
|
// view
|
|
25
33
|
if (!isEditable) {
|
|
26
|
-
return (_jsx(Evaluation, {
|
|
34
|
+
return (_jsx(Evaluation, { data: data, evaluations: evaluations, onChange: freezeProblemNode
|
|
27
35
|
? undefined
|
|
28
|
-
: (
|
|
36
|
+
: (evaluations) => {
|
|
29
37
|
editor.update(() => {
|
|
30
38
|
const node = $getNodeByKey(nodeKey);
|
|
31
39
|
if (!$isSelfEvaluationNode(node)) {
|
|
32
40
|
return;
|
|
33
41
|
}
|
|
34
|
-
node.
|
|
42
|
+
node.setEvaluations(evaluations);
|
|
35
43
|
});
|
|
36
44
|
} }));
|
|
37
45
|
}
|
|
@@ -41,7 +49,9 @@ export function EvaluationComponent(props) {
|
|
|
41
49
|
gap: 4px;
|
|
42
50
|
` }, { children: [_jsx(Evaluation, { css: css `
|
|
43
51
|
width: ${EVALUATION_EDIT_WIDTH}px;
|
|
44
|
-
`,
|
|
52
|
+
`, data: data, evaluations: evaluations, onClick: () => setSettingOpen((open) => !open) }), _jsx(SquareButton, { size: "small", color: "icon", icon: _jsx(Settings3FillIcon, {}), onClick: () => {
|
|
45
53
|
setSettingOpen((open) => !open);
|
|
46
|
-
} })] })), settingOpen && (_jsx(SettingForm, {
|
|
54
|
+
} })] })), settingOpen && (_jsx(SettingForm, { css: css `
|
|
55
|
+
width: ${EVALUATION_EDIT_WIDTH}px;
|
|
56
|
+
`, iconType: iconType, labels: labels, evaluations: evaluations, nodeKey: nodeKey, onClose: () => setSettingOpen(false) }))] }));
|
|
47
57
|
}
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { DecoratorNode, EditorConfig, LexicalNode, NodeKey, SerializedLexicalNode, Spread } from "lexical";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
3
|
export type IconType = "emoji" | "ascendingNumber" | "descendingNumber";
|
|
4
|
+
/** 자기 평가 항목 */
|
|
5
|
+
export interface EvaluationItem {
|
|
6
|
+
question: {
|
|
7
|
+
text: string;
|
|
8
|
+
};
|
|
9
|
+
selectedLabelIndex: number | null;
|
|
10
|
+
}
|
|
4
11
|
export interface SelfEvaluationPayload {
|
|
5
12
|
/** 아이콘 프리셋 타입 */
|
|
6
13
|
iconType: IconType;
|
|
7
14
|
/** 자기평가 레이블 */
|
|
8
15
|
labels: string[];
|
|
9
16
|
/** 자기평가 항목들 */
|
|
10
|
-
|
|
11
|
-
/** 선택된 레이블 인덱스 */
|
|
12
|
-
selectedLabelIndexes: (number | null)[];
|
|
17
|
+
evaluations: EvaluationItem[];
|
|
13
18
|
key?: NodeKey;
|
|
14
19
|
}
|
|
15
20
|
export type SerializedSelfEvaluationNode = Spread<SelfEvaluationPayload, SerializedLexicalNode>;
|
|
@@ -19,20 +24,17 @@ export type SerializedSelfEvaluationNode = Spread<SelfEvaluationPayload, Seriali
|
|
|
19
24
|
export declare class SelfEvaluationNode extends DecoratorNode<ReactNode> {
|
|
20
25
|
__iconType: IconType;
|
|
21
26
|
__labels: string[];
|
|
22
|
-
|
|
23
|
-
__selectedLabelIndexes: (number | null)[];
|
|
27
|
+
__evaluations: EvaluationItem[];
|
|
24
28
|
isInline(): boolean;
|
|
25
29
|
static getType(): string;
|
|
26
30
|
getIconType(): IconType;
|
|
27
31
|
getLabels(): string[];
|
|
28
|
-
|
|
29
|
-
getSelectedLabelIndexes(): (number | null)[];
|
|
32
|
+
getEvaluations(): EvaluationItem[];
|
|
30
33
|
setIconType(iconType: IconType): void;
|
|
31
34
|
setLabels(labels: string[]): void;
|
|
32
|
-
|
|
33
|
-
setSelectedLabelIndexes(selectedLabelIndexes: (number | null)[]): void;
|
|
35
|
+
setEvaluations(evaluations: EvaluationItem[]): void;
|
|
34
36
|
static clone(node: SelfEvaluationNode): SelfEvaluationNode;
|
|
35
|
-
constructor(iconType: IconType, labels: string[],
|
|
37
|
+
constructor(iconType: IconType, labels: string[], evaluations: EvaluationItem[], key?: NodeKey);
|
|
36
38
|
createDOM(config: EditorConfig): HTMLElement;
|
|
37
39
|
updateDOM(): boolean;
|
|
38
40
|
static importJSON(serializedNode: SerializedSelfEvaluationNode): SelfEvaluationNode;
|
|
@@ -18,11 +18,8 @@ export class SelfEvaluationNode extends DecoratorNode {
|
|
|
18
18
|
getLabels() {
|
|
19
19
|
return this.__labels;
|
|
20
20
|
}
|
|
21
|
-
|
|
22
|
-
return this.
|
|
23
|
-
}
|
|
24
|
-
getSelectedLabelIndexes() {
|
|
25
|
-
return this.__selectedLabelIndexes;
|
|
21
|
+
getEvaluations() {
|
|
22
|
+
return this.__evaluations;
|
|
26
23
|
}
|
|
27
24
|
setIconType(iconType) {
|
|
28
25
|
const self = this.getWritable();
|
|
@@ -32,23 +29,18 @@ export class SelfEvaluationNode extends DecoratorNode {
|
|
|
32
29
|
const self = this.getWritable();
|
|
33
30
|
self.__labels = labels;
|
|
34
31
|
}
|
|
35
|
-
|
|
36
|
-
const self = this.getWritable();
|
|
37
|
-
self.__descriptions = descriptions;
|
|
38
|
-
}
|
|
39
|
-
setSelectedLabelIndexes(selectedLabelIndexes) {
|
|
32
|
+
setEvaluations(evaluations) {
|
|
40
33
|
const self = this.getWritable();
|
|
41
|
-
self.
|
|
34
|
+
self.__evaluations = evaluations;
|
|
42
35
|
}
|
|
43
36
|
static clone(node) {
|
|
44
|
-
return new SelfEvaluationNode(node.__iconType, node.__labels, node.
|
|
37
|
+
return new SelfEvaluationNode(node.__iconType, node.__labels, node.__evaluations);
|
|
45
38
|
}
|
|
46
|
-
constructor(iconType, labels,
|
|
39
|
+
constructor(iconType, labels, evaluations, key) {
|
|
47
40
|
super(key);
|
|
48
41
|
this.__iconType = iconType;
|
|
49
42
|
this.__labels = labels;
|
|
50
|
-
this.
|
|
51
|
-
this.__selectedLabelIndexes = selectedLabelIndexes;
|
|
43
|
+
this.__evaluations = evaluations;
|
|
52
44
|
}
|
|
53
45
|
createDOM(config) {
|
|
54
46
|
// Define the DOM element here
|
|
@@ -64,8 +56,7 @@ export class SelfEvaluationNode extends DecoratorNode {
|
|
|
64
56
|
key: serializedNode.key,
|
|
65
57
|
iconType: serializedNode.iconType,
|
|
66
58
|
labels: serializedNode.labels,
|
|
67
|
-
|
|
68
|
-
selectedLabelIndexes: serializedNode.selectedLabelIndexes,
|
|
59
|
+
evaluations: serializedNode.evaluations,
|
|
69
60
|
});
|
|
70
61
|
}
|
|
71
62
|
exportJSON() {
|
|
@@ -74,17 +65,16 @@ export class SelfEvaluationNode extends DecoratorNode {
|
|
|
74
65
|
version: 1,
|
|
75
66
|
iconType: this.__iconType,
|
|
76
67
|
labels: this.__labels,
|
|
77
|
-
|
|
78
|
-
selectedLabelIndexes: this.__selectedLabelIndexes,
|
|
68
|
+
evaluations: this.__evaluations,
|
|
79
69
|
};
|
|
80
70
|
}
|
|
81
71
|
decorate() {
|
|
82
|
-
return (_jsx(EvaluationComponent, { iconType: this.__iconType, labels: this.__labels,
|
|
72
|
+
return (_jsx(EvaluationComponent, { iconType: this.__iconType, labels: this.__labels, evaluations: this.__evaluations, nodeKey: this.getKey() }));
|
|
83
73
|
}
|
|
84
74
|
}
|
|
85
75
|
export function $createSelfEvaluationNode(payload) {
|
|
86
|
-
const { iconType, labels,
|
|
87
|
-
return $applyNodeReplacement(new SelfEvaluationNode(iconType, labels,
|
|
76
|
+
const { iconType, labels, evaluations, key } = payload;
|
|
77
|
+
return $applyNodeReplacement(new SelfEvaluationNode(iconType, labels, evaluations, key));
|
|
88
78
|
}
|
|
89
79
|
export function $isSelfEvaluationNode(node) {
|
|
90
80
|
return node instanceof SelfEvaluationNode;
|
package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.js
CHANGED
|
@@ -95,8 +95,14 @@ function getSheetContextOptions(editor, theme) {
|
|
|
95
95
|
onSelect: () => editor.dispatchCommand(INSERT_SELF_EVALUATION_COMMAND, {
|
|
96
96
|
iconType: "emoji",
|
|
97
97
|
labels: ["아주 잘했어요!", "보통이에요.", "잘 모르겠어요."],
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
evaluations: [
|
|
99
|
+
{
|
|
100
|
+
question: {
|
|
101
|
+
text: "평가 항목을 입력하세요.",
|
|
102
|
+
},
|
|
103
|
+
selectedLabelIndex: null,
|
|
104
|
+
},
|
|
105
|
+
],
|
|
100
106
|
}),
|
|
101
107
|
}),
|
|
102
108
|
new ComponentPickerOption("단답형 입력 칸", {
|
|
@@ -24,6 +24,6 @@ export declare function ToggleButtonGroup(props: {
|
|
|
24
24
|
data: ToggleButtonDatum[];
|
|
25
25
|
/** 활성화될 ToggleButton의 index */
|
|
26
26
|
activeIndex: number | null;
|
|
27
|
-
/** activeIndex set
|
|
28
|
-
setActiveIndex
|
|
29
|
-
}): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
/** activeIndex set 함수. 전달하지 않으면 pointer-events: none이 됩니다. */
|
|
28
|
+
setActiveIndex?: (index: number | null) => void;
|
|
29
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx } from "@emotion/react/jsx-runtime";
|
|
2
|
+
/** @jsxImportSource @emotion/react */
|
|
2
3
|
import styled from "@emotion/styled";
|
|
3
4
|
import { ToggleButton } from "./ToggleButton";
|
|
5
|
+
import { css } from "@emotion/react";
|
|
4
6
|
export const toggleButtonGroupClasses = {
|
|
5
7
|
container: "ToggleButtonGroup-container",
|
|
6
8
|
toggleButton: {
|
|
@@ -14,13 +16,16 @@ export const toggleButtonGroupClasses = {
|
|
|
14
16
|
*/
|
|
15
17
|
export function ToggleButtonGroup(props) {
|
|
16
18
|
const { className, data, activeIndex, setActiveIndex } = props;
|
|
17
|
-
return (_jsx(Container, Object.assign({ className: `${className} ${toggleButtonGroupClasses.container}
|
|
19
|
+
return (_jsx(Container, Object.assign({ className: `${className} ${toggleButtonGroupClasses.container}`, css: !setActiveIndex &&
|
|
20
|
+
css `
|
|
21
|
+
pointer-events: none;
|
|
22
|
+
` }, { children: data.map((datum, index) => {
|
|
18
23
|
const isFirst = index === 0;
|
|
19
24
|
const isLast = index === data.length - 1;
|
|
20
25
|
const isFirstAndLast = isFirst && isLast;
|
|
21
26
|
const isActive = activeIndex === index;
|
|
22
27
|
return (_jsx(ToggleButton, { isActive: isActive, onClick: () => {
|
|
23
|
-
setActiveIndex(isActive ? null : index);
|
|
28
|
+
setActiveIndex === null || setActiveIndex === void 0 ? void 0 : setActiveIndex(isActive ? null : index);
|
|
24
29
|
}, datum: datum, placement: isFirstAndLast
|
|
25
30
|
? "bottom"
|
|
26
31
|
: isFirst
|
package/package.json
CHANGED
|
@@ -1,55 +0,0 @@
|
|
|
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
|
-
`);
|