@team-monolith/cds 1.56.0 → 1.56.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/CodleDesignSystemProvider.js +4 -4
- package/dist/icons/Custom.d.ts +27 -0
- package/dist/icons/Custom.js +36 -0
- package/dist/icons/custom/emo-good-color.svg +69 -0
- package/dist/icons/custom/emo-good.svg +13 -0
- package/dist/icons/custom/emo-great-color.svg +68 -0
- package/dist/icons/custom/emo-great.svg +13 -0
- package/dist/icons/custom/emo-neutral-color.svg +77 -0
- package/dist/icons/custom/emo-neutral.svg +15 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/patterns/LexicalEditor/LexicalEditor.js +2 -0
- package/dist/patterns/LexicalEditor/Plugins.js +2 -1
- 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 +16 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/Evaluation.js +103 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormIconAndLabel/FormIconAndLabel.d.ts +7 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormIconAndLabel/FormIconAndLabel.js +52 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormIconAndLabel/FormLabel.d.ts +7 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormIconAndLabel/FormLabel.js +30 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormIconAndLabel/index.d.ts +1 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormIconAndLabel/index.js +1 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormQuestion.d.ts +9 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/FormQuestion.js +42 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/SettingForm.d.ts +17 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/SettingForm.js +110 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/index.d.ts +1 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/SettingForm/index.js +1 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/index.d.ts +2 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/Evaluation/index.js +2 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/EvaluationComponent.d.ts +10 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/EvaluationComponent.js +57 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/index.d.ts +1 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/EvaluationComponent/index.js +1 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/SelfEvaluationNode.d.ts +45 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/SelfEvaluationNode.js +81 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/iconData.d.ts +14 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/iconData.js +61 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/index.d.ts +1 -0
- package/dist/patterns/LexicalEditor/nodes/SelfEvaluationNode/index.js +1 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/useContextMenuOptions.js +15 -0
- package/dist/patterns/LexicalEditor/plugins/ComponentPickerMenuPlugin/ComponentPickerMenuPlugin.js +18 -1
- package/dist/patterns/LexicalEditor/plugins/SelfEvaluationPlugin/index.d.ts +5 -0
- package/dist/patterns/LexicalEditor/plugins/SelfEvaluationPlugin/index.js +20 -0
- package/dist/patterns/LexicalEditor/theme.d.ts +1 -0
- package/dist/patterns/LexicalEditor/theme.js +7 -0
- package/dist/patterns/ToggleButtonGroup/ToggleButton.d.ts +9 -0
- package/dist/patterns/ToggleButtonGroup/ToggleButton.js +54 -0
- package/dist/patterns/ToggleButtonGroup/ToggleButtonGroup.d.ts +29 -0
- package/dist/patterns/ToggleButtonGroup/ToggleButtonGroup.js +47 -0
- package/dist/patterns/ToggleButtonGroup/index.d.ts +1 -0
- package/dist/patterns/ToggleButtonGroup/index.js +1 -0
- package/package.json +1 -1
|
@@ -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";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ToggleButtonDatum } from "../../../../../ToggleButtonGroup";
|
|
2
|
+
import { EvaluationItem } from "../../SelfEvaluationNode";
|
|
3
|
+
/** 데이터를 받아 자기평가 항목들과 ToggleButtonGroup을 그리는 컴포넌트입니다.
|
|
4
|
+
* 비지니스 로직이 포함되어 있지 않습니다.
|
|
5
|
+
*/
|
|
6
|
+
export declare function Evaluation(props: {
|
|
7
|
+
className?: string;
|
|
8
|
+
/** 자기 평가 항목들 */
|
|
9
|
+
evaluations: EvaluationItem[];
|
|
10
|
+
/** ToggleButton의 데이터 배열 */
|
|
11
|
+
data: ToggleButtonDatum[];
|
|
12
|
+
/** 블럭을 클릭했을 때 실행될 콜백. 전달하지 않으면 cursor: pointer가 적용되지 않습니다. */
|
|
13
|
+
onClick?: () => void;
|
|
14
|
+
/** 전달하지 않으면 ToggleButtonGroup 호버, 클릭 동작이 없습니다. (pointer-events: none) */
|
|
15
|
+
onChange?: (evaluations: EvaluationItem[]) => void;
|
|
16
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,103 @@
|
|
|
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
|
+
import _ from "lodash";
|
|
7
|
+
const TOGGLE_BUTTON_GROUP_WIDTH = 320;
|
|
8
|
+
/** 데이터를 받아 자기평가 항목들과 ToggleButtonGroup을 그리는 컴포넌트입니다.
|
|
9
|
+
* 비지니스 로직이 포함되어 있지 않습니다.
|
|
10
|
+
*/
|
|
11
|
+
export function Evaluation(props) {
|
|
12
|
+
const { className, evaluations, data, onClick, onChange } = props;
|
|
13
|
+
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
|
|
14
|
+
? undefined
|
|
15
|
+
: (activeIndex) => {
|
|
16
|
+
const newEvaluations = _.cloneDeep(evaluations);
|
|
17
|
+
newEvaluations[index].selectedLabelIndex = activeIndex;
|
|
18
|
+
onChange(newEvaluations);
|
|
19
|
+
} })] }, index)))] })));
|
|
20
|
+
}
|
|
21
|
+
const Container = styled.div(({ onClick }) => css `
|
|
22
|
+
${onClick && "cursor: pointer;"}
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
align-items: flex-start;
|
|
26
|
+
align-self: stretch;
|
|
27
|
+
`);
|
|
28
|
+
const Header = styled.div(({ theme }) => css `
|
|
29
|
+
border-bottom: 1px solid ${theme.color.background.neutralAltActive};
|
|
30
|
+
display: flex;
|
|
31
|
+
padding: 8px 0px;
|
|
32
|
+
justify-content: flex-end;
|
|
33
|
+
align-items: center;
|
|
34
|
+
gap: 8px;
|
|
35
|
+
align-self: stretch;
|
|
36
|
+
`);
|
|
37
|
+
const LabelArea = styled.div `
|
|
38
|
+
display: flex;
|
|
39
|
+
width: ${TOGGLE_BUTTON_GROUP_WIDTH}px;
|
|
40
|
+
justify-content: space-between;
|
|
41
|
+
align-items: flex-start;
|
|
42
|
+
`;
|
|
43
|
+
const Label = styled.span(({ theme }) => css `
|
|
44
|
+
color: ${theme.color.foreground.neutralBaseDisabled};
|
|
45
|
+
text-align: center;
|
|
46
|
+
/* Default/Label/12px-Md */
|
|
47
|
+
font-family: Pretendard;
|
|
48
|
+
font-size: 12px;
|
|
49
|
+
font-style: normal;
|
|
50
|
+
font-weight: 500;
|
|
51
|
+
line-height: 16px; /* 133.333% */
|
|
52
|
+
`);
|
|
53
|
+
const Item = styled.div(({ theme }) => css `
|
|
54
|
+
display: flex;
|
|
55
|
+
padding: 4px 0px;
|
|
56
|
+
align-items: center;
|
|
57
|
+
gap: 8px;
|
|
58
|
+
align-self: stretch;
|
|
59
|
+
border-bottom: 1px solid ${theme.color.background.neutralAltActive};
|
|
60
|
+
`);
|
|
61
|
+
const Index = styled.div(({ theme }) => css `
|
|
62
|
+
display: flex;
|
|
63
|
+
width: 20px;
|
|
64
|
+
height: 20px;
|
|
65
|
+
padding: 4px;
|
|
66
|
+
|
|
67
|
+
user-select: none; /* Standard syntax */
|
|
68
|
+
|
|
69
|
+
border-radius: 4px;
|
|
70
|
+
background: ${theme.color.background.neutralBase};
|
|
71
|
+
border: 1px solid ${theme.color.background.neutralAltActive};
|
|
72
|
+
|
|
73
|
+
justify-content: center;
|
|
74
|
+
align-items: center;
|
|
75
|
+
|
|
76
|
+
color: ${theme.color.foreground.neutralBaseDisabled};
|
|
77
|
+
font-family: ${theme.fontFamily.ui};
|
|
78
|
+
font-size: 14px;
|
|
79
|
+
font-weight: 800;
|
|
80
|
+
line-height: 16px;
|
|
81
|
+
`);
|
|
82
|
+
const Question = styled.div(({ theme }) => css `
|
|
83
|
+
display: flex;
|
|
84
|
+
padding: 4px 8px;
|
|
85
|
+
flex-direction: column;
|
|
86
|
+
justify-content: center;
|
|
87
|
+
align-items: flex-start;
|
|
88
|
+
gap: 8px;
|
|
89
|
+
flex: 1 0 0;
|
|
90
|
+
align-self: stretch;
|
|
91
|
+
|
|
92
|
+
color: ${theme.color.foreground.neutralBase};
|
|
93
|
+
/* Default/Paragraph/16px-Rg */
|
|
94
|
+
font-family: ${theme.fontFamily.ui};
|
|
95
|
+
font-size: 16px;
|
|
96
|
+
font-style: normal;
|
|
97
|
+
font-weight: 400;
|
|
98
|
+
line-height: 24px; /* 150% */
|
|
99
|
+
`);
|
|
100
|
+
const StyledToggleButtonGroup = styled(ToggleButtonGroup) `
|
|
101
|
+
flex: none;
|
|
102
|
+
width: ${TOGGLE_BUTTON_GROUP_WIDTH}px;
|
|
103
|
+
`;
|
|
@@ -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,52 @@
|
|
|
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 })] }, index)))] }));
|
|
34
|
+
} }));
|
|
35
|
+
}
|
|
36
|
+
const LabelLine = styled.div `
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: flex-start;
|
|
39
|
+
gap: 4px;
|
|
40
|
+
align-self: stretch;
|
|
41
|
+
`;
|
|
42
|
+
const Icon = styled.div(({ theme }) => css `
|
|
43
|
+
display: flex;
|
|
44
|
+
min-width: 36px;
|
|
45
|
+
min-height: 36px;
|
|
46
|
+
justify-content: center;
|
|
47
|
+
align-items: center;
|
|
48
|
+
gap: 10px;
|
|
49
|
+
|
|
50
|
+
border-radius: 8px;
|
|
51
|
+
border: 0.5px solid ${theme.color.background.neutralAltActive};
|
|
52
|
+
`);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Control } 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
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useController } from "react-hook-form";
|
|
3
|
+
import styled from "@emotion/styled";
|
|
4
|
+
import { css } from "@emotion/react";
|
|
5
|
+
import { FormInput } from "../../../../../Form";
|
|
6
|
+
const LABEL_RULES = {
|
|
7
|
+
required: "필수 입력 항목입니다.",
|
|
8
|
+
maxLength: 12,
|
|
9
|
+
};
|
|
10
|
+
/** 레이블을 설정하는 Form입니다. */
|
|
11
|
+
export function FormLabel(props) {
|
|
12
|
+
const { control, index } = props;
|
|
13
|
+
// endIcon에 들어갈 value, error를 사용하기 위해 useController를 사용합니다.
|
|
14
|
+
const { field: { value }, fieldState: { error }, } = useController({
|
|
15
|
+
control,
|
|
16
|
+
name: `labels.${index}`,
|
|
17
|
+
rules: LABEL_RULES,
|
|
18
|
+
});
|
|
19
|
+
return (_jsx(FormInput, { control: control, name: `labels.${index}`, rules: LABEL_RULES, size: "small", endIcon: _jsx(Counter, Object.assign({ error: Boolean(error) }, { children: value.length + "/" + LABEL_RULES.maxLength })), fullWidth: true }));
|
|
20
|
+
}
|
|
21
|
+
const Counter = styled.span(({ theme, error }) => css `
|
|
22
|
+
color: ${error
|
|
23
|
+
? theme.color.foreground.dangerActive
|
|
24
|
+
: theme.color.foreground.neutralBase};
|
|
25
|
+
font-family: ${theme.fontFamily.ui};
|
|
26
|
+
font-size: 14px;
|
|
27
|
+
font-style: normal;
|
|
28
|
+
font-weight: 700;
|
|
29
|
+
line-height: 20px; /* 142.857% */
|
|
30
|
+
`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./FormIconAndLabel";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./FormIconAndLabel";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** @jsxImportSource @emotion/react */
|
|
2
|
+
import { Control } from "react-hook-form";
|
|
3
|
+
import { SettingFormData } from "./SettingForm";
|
|
4
|
+
/** 평가 항목을 설정하는 Form입니다. */
|
|
5
|
+
export declare function FormQuestion(props: {
|
|
6
|
+
index: number;
|
|
7
|
+
control: Control<SettingFormData, any>;
|
|
8
|
+
onDelete?: () => void;
|
|
9
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,42 @@
|
|
|
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, onDelete } = props;
|
|
9
|
+
return (_jsxs(Item, { children: [_jsx(Index, { children: index + 1 }), _jsx(FormInput, { control: control, name: `evaluations.${index}.question.text`, rules: {
|
|
10
|
+
required: "필수 입력 항목입니다.",
|
|
11
|
+
}, size: "small", placeholder: `${index + 1}번 평가 항목`, multiline: true, fullWidth: true }), onDelete && (_jsx(SquareButton, { color: "white", size: "xsmall", icon: _jsx(DeleteBinLineIcon, {}), onClick: onDelete }))] }));
|
|
12
|
+
}
|
|
13
|
+
const Item = styled.div(({ theme }) => css `
|
|
14
|
+
display: flex;
|
|
15
|
+
padding: 4px 12px;
|
|
16
|
+
align-items: center;
|
|
17
|
+
gap: 4px;
|
|
18
|
+
align-self: stretch;
|
|
19
|
+
border-radius: 8px;
|
|
20
|
+
background: ${theme.color.background.neutralAlt};
|
|
21
|
+
`);
|
|
22
|
+
const Index = styled.div(({ theme }) => css `
|
|
23
|
+
display: flex;
|
|
24
|
+
width: 20px;
|
|
25
|
+
height: 20px;
|
|
26
|
+
padding: 4px;
|
|
27
|
+
|
|
28
|
+
user-select: none; /* Standard syntax */
|
|
29
|
+
|
|
30
|
+
border-radius: 4px;
|
|
31
|
+
background: ${theme.color.background.neutralBase};
|
|
32
|
+
border: 1px solid ${theme.color.background.neutralAltActive};
|
|
33
|
+
|
|
34
|
+
justify-content: center;
|
|
35
|
+
align-items: center;
|
|
36
|
+
|
|
37
|
+
color: ${theme.color.foreground.neutralBaseDisabled};
|
|
38
|
+
font-family: ${theme.fontFamily.ui};
|
|
39
|
+
font-size: 14px;
|
|
40
|
+
font-weight: 800;
|
|
41
|
+
line-height: 16px;
|
|
42
|
+
`);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** @jsxImportSource @emotion/react */
|
|
2
|
+
import { NodeKey } from "lexical";
|
|
3
|
+
import { EvaluationItem, IconType } from "../../../SelfEvaluationNode";
|
|
4
|
+
export interface SettingFormData {
|
|
5
|
+
iconType: IconType;
|
|
6
|
+
labels: string[];
|
|
7
|
+
evaluations: EvaluationItem[];
|
|
8
|
+
}
|
|
9
|
+
/** 자기평가 edit의 설정 컴포넌트입니다. */
|
|
10
|
+
export declare function SettingForm(props: {
|
|
11
|
+
className?: string;
|
|
12
|
+
iconType: IconType;
|
|
13
|
+
labels: string[];
|
|
14
|
+
evaluations: EvaluationItem[];
|
|
15
|
+
nodeKey: NodeKey;
|
|
16
|
+
onClose: () => void;
|
|
17
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,110 @@
|
|
|
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 { FormQuestion } from "./FormQuestion";
|
|
12
|
+
/** 자기평가 edit의 설정 컴포넌트입니다. */
|
|
13
|
+
export function SettingForm(props) {
|
|
14
|
+
const { className, iconType, labels, evaluations, nodeKey, onClose } = props;
|
|
15
|
+
const [editor] = useLexicalComposerContext();
|
|
16
|
+
const { control, handleSubmit } = useForm({
|
|
17
|
+
mode: "all",
|
|
18
|
+
defaultValues: {
|
|
19
|
+
iconType,
|
|
20
|
+
labels,
|
|
21
|
+
evaluations,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
const { fields, append, remove } = useFieldArray({
|
|
25
|
+
control,
|
|
26
|
+
name: "evaluations",
|
|
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.setEvaluations(data.evaluations);
|
|
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(FormQuestion, { index: index, control: control, onDelete: index !== 0
|
|
45
|
+
? () => {
|
|
46
|
+
remove(index);
|
|
47
|
+
}
|
|
48
|
+
: undefined }, field.uid))), _jsx(Button, { color: "grey", size: "small", startIcon: _jsx(AddFillIcon, {}), label: "\uD3C9\uAC00 \uD56D\uBAA9 \uCD94\uAC00", onClick: () => {
|
|
49
|
+
append({
|
|
50
|
+
question: { text: "평가 항목을 입력하세요." },
|
|
51
|
+
selectedLabelIndex: null,
|
|
52
|
+
});
|
|
53
|
+
} })] }), _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
|
+
}
|
|
55
|
+
const Form = styled.form(({ theme }) => css `
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: column;
|
|
58
|
+
border-radius: 6px;
|
|
59
|
+
border: 1px solid ${theme.color.background.neutralAltActive};
|
|
60
|
+
background: ${theme.color.background.neutralBase};
|
|
61
|
+
box-shadow: ${shadows.shadow08};
|
|
62
|
+
`);
|
|
63
|
+
const Title = styled.div(({ theme }) => css `
|
|
64
|
+
display: flex;
|
|
65
|
+
padding: 8px 12px;
|
|
66
|
+
gap: 4px;
|
|
67
|
+
align-items: center;
|
|
68
|
+
color: ${theme.color.foreground.neutralBase};
|
|
69
|
+
/* Default/Label/12px-Md */
|
|
70
|
+
font-family: ${theme.fontFamily.ui};
|
|
71
|
+
font-size: 12px;
|
|
72
|
+
font-style: normal;
|
|
73
|
+
font-weight: 500;
|
|
74
|
+
line-height: 16px; /* 133.333% */
|
|
75
|
+
`);
|
|
76
|
+
const Content = styled.div(({ theme }) => css `
|
|
77
|
+
display: flex;
|
|
78
|
+
border-top: 1px solid ${theme.color.background.neutralAltActive};
|
|
79
|
+
border-bottom: 1px solid ${theme.color.background.neutralAltActive};
|
|
80
|
+
`);
|
|
81
|
+
const Left = styled.div `
|
|
82
|
+
flex: 1;
|
|
83
|
+
display: flex;
|
|
84
|
+
flex-direction: column;
|
|
85
|
+
gap: 12px;
|
|
86
|
+
padding: 12px;
|
|
87
|
+
`;
|
|
88
|
+
const Right = styled.div `
|
|
89
|
+
width: 240px;
|
|
90
|
+
display: flex;
|
|
91
|
+
flex-direction: column;
|
|
92
|
+
gap: 8px;
|
|
93
|
+
padding: 12px;
|
|
94
|
+
`;
|
|
95
|
+
const Label = styled.div(({ theme }) => css `
|
|
96
|
+
color: ${theme.color.foreground.neutralBaseDisabled};
|
|
97
|
+
/* Default/Label/12px-Md */
|
|
98
|
+
font-family: ${theme.fontFamily.ui};
|
|
99
|
+
font-size: 12px;
|
|
100
|
+
font-style: normal;
|
|
101
|
+
font-weight: 500;
|
|
102
|
+
line-height: 16px; /* 133.333% */
|
|
103
|
+
`);
|
|
104
|
+
const Buttons = styled.div `
|
|
105
|
+
display: flex;
|
|
106
|
+
padding: 12px;
|
|
107
|
+
justify-content: flex-end;
|
|
108
|
+
align-items: center;
|
|
109
|
+
gap: 8px;
|
|
110
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./SettingForm";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./SettingForm";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** @jsxImportSource @emotion/react */
|
|
2
|
+
import { NodeKey } from "lexical";
|
|
3
|
+
import { EvaluationItem, IconType } from "../SelfEvaluationNode";
|
|
4
|
+
/** SelfEvaluationNode를 그리는 컴포넌트입니다. */
|
|
5
|
+
export declare function EvaluationComponent(props: {
|
|
6
|
+
iconType: IconType;
|
|
7
|
+
labels: string[];
|
|
8
|
+
evaluations: EvaluationItem[];
|
|
9
|
+
nodeKey: NodeKey;
|
|
10
|
+
}): import("@emotion/react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,57 @@
|
|
|
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
|
+
import _ from "lodash";
|
|
15
|
+
const EVALUATION_EDIT_WIDTH = 620;
|
|
16
|
+
/** SelfEvaluationNode를 그리는 컴포넌트입니다. */
|
|
17
|
+
export function EvaluationComponent(props) {
|
|
18
|
+
const { iconType, labels, evaluations, nodeKey } = props;
|
|
19
|
+
const theme = useTheme();
|
|
20
|
+
const [editor] = useLexicalComposerContext();
|
|
21
|
+
const isEditable = useLexicalEditable();
|
|
22
|
+
const { freezeProblemNode } = useContext(LexicalCustomConfigContext);
|
|
23
|
+
const [settingOpen, setSettingOpen] = useState(false);
|
|
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
|
+
}));
|
|
32
|
+
// view
|
|
33
|
+
if (!isEditable) {
|
|
34
|
+
return (_jsx(Evaluation, { data: data, evaluations: evaluations, onChange: freezeProblemNode
|
|
35
|
+
? undefined
|
|
36
|
+
: (evaluations) => {
|
|
37
|
+
editor.update(() => {
|
|
38
|
+
const node = $getNodeByKey(nodeKey);
|
|
39
|
+
if (!$isSelfEvaluationNode(node)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
node.setEvaluations(evaluations);
|
|
43
|
+
});
|
|
44
|
+
} }));
|
|
45
|
+
}
|
|
46
|
+
// edit
|
|
47
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", Object.assign({ css: css `
|
|
48
|
+
display: flex;
|
|
49
|
+
gap: 4px;
|
|
50
|
+
` }, { children: [_jsx(Evaluation, { css: css `
|
|
51
|
+
width: ${EVALUATION_EDIT_WIDTH}px;
|
|
52
|
+
`, data: data, evaluations: evaluations, onClick: () => setSettingOpen((open) => !open) }), _jsx(SquareButton, { size: "small", color: "icon", icon: _jsx(Settings3FillIcon, {}), onClick: () => {
|
|
53
|
+
setSettingOpen((open) => !open);
|
|
54
|
+
} })] })), settingOpen && (_jsx(SettingForm, { css: css `
|
|
55
|
+
width: ${EVALUATION_EDIT_WIDTH}px;
|
|
56
|
+
`, iconType: iconType, labels: labels, evaluations: evaluations, nodeKey: nodeKey, onClose: () => setSettingOpen(false) }))] }));
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./EvaluationComponent";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./EvaluationComponent";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { DecoratorNode, EditorConfig, LexicalNode, NodeKey, SerializedLexicalNode, Spread } from "lexical";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
export type IconType = "emoji" | "ascendingNumber" | "descendingNumber";
|
|
4
|
+
/** 자기 평가 항목 */
|
|
5
|
+
export interface EvaluationItem {
|
|
6
|
+
question: {
|
|
7
|
+
text: string;
|
|
8
|
+
};
|
|
9
|
+
selectedLabelIndex: number | null;
|
|
10
|
+
}
|
|
11
|
+
export interface SelfEvaluationPayload {
|
|
12
|
+
/** 아이콘 프리셋 타입 */
|
|
13
|
+
iconType: IconType;
|
|
14
|
+
/** 자기평가 레이블 */
|
|
15
|
+
labels: string[];
|
|
16
|
+
/** 자기평가 항목들 */
|
|
17
|
+
evaluations: EvaluationItem[];
|
|
18
|
+
key?: NodeKey;
|
|
19
|
+
}
|
|
20
|
+
export type SerializedSelfEvaluationNode = Spread<SelfEvaluationPayload, SerializedLexicalNode>;
|
|
21
|
+
/** 3단계 평가 입력칸을 나타내는 노드입니다.
|
|
22
|
+
* 도구 이름으로서는 "3단계 평가 입력 칸"으로 나타나지만, n단계로 확장 가능합니다.
|
|
23
|
+
*/
|
|
24
|
+
export declare class SelfEvaluationNode extends DecoratorNode<ReactNode> {
|
|
25
|
+
__iconType: IconType;
|
|
26
|
+
__labels: string[];
|
|
27
|
+
__evaluations: EvaluationItem[];
|
|
28
|
+
isInline(): boolean;
|
|
29
|
+
static getType(): string;
|
|
30
|
+
getIconType(): IconType;
|
|
31
|
+
getLabels(): string[];
|
|
32
|
+
getEvaluations(): EvaluationItem[];
|
|
33
|
+
setIconType(iconType: IconType): void;
|
|
34
|
+
setLabels(labels: string[]): void;
|
|
35
|
+
setEvaluations(evaluations: EvaluationItem[]): void;
|
|
36
|
+
static clone(node: SelfEvaluationNode): SelfEvaluationNode;
|
|
37
|
+
constructor(iconType: IconType, labels: string[], evaluations: EvaluationItem[], key?: NodeKey);
|
|
38
|
+
createDOM(config: EditorConfig): HTMLElement;
|
|
39
|
+
updateDOM(): boolean;
|
|
40
|
+
static importJSON(serializedNode: SerializedSelfEvaluationNode): SelfEvaluationNode;
|
|
41
|
+
exportJSON(): SerializedSelfEvaluationNode;
|
|
42
|
+
decorate(): ReactNode;
|
|
43
|
+
}
|
|
44
|
+
export declare function $createSelfEvaluationNode(payload: SelfEvaluationPayload): SelfEvaluationNode;
|
|
45
|
+
export declare function $isSelfEvaluationNode(node: LexicalNode | null | undefined): node is SelfEvaluationNode;
|