@team-monolith/cds 1.9.0 → 1.9.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/ProblemSelectNode/ProblemSelectNode.d.ts +5 -1
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SelectBox.d.ts +2 -3
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SelectBox.js +8 -1
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SelectComponent.js +27 -29
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/FormSelection.js +48 -17
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/InsertImageDialog.d.ts +7 -0
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/InsertImageDialog.js +96 -0
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/InsertImageUploadedDialogBody.d.ts +9 -0
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/InsertImageUploadedDialogBody.js +51 -0
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/InsertImageUriDialogBody.d.ts +9 -0
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/InsertImageUriDialogBody.js +20 -0
- package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/SettingForm.js +17 -2
- package/dist/patterns/LexicalEditor/plugins/ComponentAdderPlugin/useContextMenuOptions.js +15 -0
- package/package.json +1 -1
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { DecoratorNode, EditorConfig, LexicalNode, NodeKey, SerializedLexicalNode, Spread } from "lexical";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
|
+
export interface ImageProps {
|
|
4
|
+
src: string;
|
|
5
|
+
altText: string;
|
|
6
|
+
}
|
|
3
7
|
export interface Selection {
|
|
4
8
|
isAnswer: boolean;
|
|
5
9
|
show: {
|
|
6
|
-
image?:
|
|
10
|
+
image?: ImageProps;
|
|
7
11
|
text: string;
|
|
8
12
|
};
|
|
9
13
|
value: string;
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
import { ImagePayload } from "../ImageNode";
|
|
1
|
+
import { ImageProps } from "./ProblemSelectNode";
|
|
3
2
|
export interface SelectBoxProps {
|
|
4
3
|
index: number;
|
|
5
4
|
isSelected?: boolean;
|
|
6
5
|
isAnswer?: boolean;
|
|
7
|
-
image?:
|
|
6
|
+
image?: ImageProps;
|
|
8
7
|
text: string;
|
|
9
8
|
onClick: () => void;
|
|
10
9
|
fullWidth?: boolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
2
|
+
/** @jsxImportSource @emotion/react */
|
|
2
3
|
import styled from "@emotion/styled";
|
|
3
4
|
import { css, useTheme } from "@emotion/react";
|
|
4
5
|
import { CheckFillIcon, CheckboxCircleFillIcon } from "../../../../icons";
|
|
@@ -8,7 +9,13 @@ export default function SelectBox(props) {
|
|
|
8
9
|
return (_jsxs(Container, Object.assign({ isSelected: isSelected, fullWidth: fullWidth, onClick: onClick }, { children: [_jsx(Index, Object.assign({ isSelected: isSelected }, { children: isSelected ? (_jsx(CheckFillIcon, { css: css `
|
|
9
10
|
width: 12px;
|
|
10
11
|
height: 12px;
|
|
11
|
-
` })) : (index) })),
|
|
12
|
+
` })) : (index) })), _jsxs(Content, { children: [image && (_jsx("img", { src: image.src, alt: image.altText, css: css `
|
|
13
|
+
height: auto;
|
|
14
|
+
// 이미지로 인해 좌우로 스크롤이 생기는 것을 방지
|
|
15
|
+
max-width: min(328px, 100%);
|
|
16
|
+
width: fit-content;
|
|
17
|
+
border-radius: 6px;
|
|
18
|
+
` })), text] }), isAnswer && (_jsx(CheckboxCircleFillIcon, { color: theme.color.foreground.success, css: css `
|
|
12
19
|
width: 16px;
|
|
13
20
|
height: 16px;
|
|
14
21
|
` }))] })));
|
|
@@ -30,34 +30,32 @@ export function SelectComponent(props) {
|
|
|
30
30
|
});
|
|
31
31
|
// 학생 view
|
|
32
32
|
if (!isEditable) {
|
|
33
|
-
return (_jsx(_Fragment, { children:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}, fullWidth: true }, index));
|
|
60
|
-
}) }));
|
|
33
|
+
return (_jsx(_Fragment, { children: fields.map((field, index) => (_jsx(SelectBox, { index: index + 1, isSelected: selected.includes(field.value), image: field.show.image, text: field.show.text, onClick: () => {
|
|
34
|
+
const isSelected = selected.includes(field.value);
|
|
35
|
+
if (isSelected) {
|
|
36
|
+
editor.update(() => {
|
|
37
|
+
const node = $getNodeByKey(nodeKey);
|
|
38
|
+
if (!$isProblemSelectNode(node)) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const newSelected = [...selected];
|
|
42
|
+
const index = newSelected.indexOf(field.value);
|
|
43
|
+
newSelected.splice(index, 1);
|
|
44
|
+
node.setSelected(newSelected);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
editor.update(() => {
|
|
49
|
+
const node = $getNodeByKey(nodeKey);
|
|
50
|
+
if (!$isProblemSelectNode(node)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const newSelected = [...selected];
|
|
54
|
+
newSelected.push(field.value);
|
|
55
|
+
node.setSelected(newSelected);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}, fullWidth: true }, index))) }));
|
|
61
59
|
}
|
|
62
60
|
// 교사 edit view
|
|
63
61
|
return (_jsxs(_Fragment, { children: [_jsxs("div", Object.assign({ css: css `
|
|
@@ -67,7 +65,7 @@ export function SelectComponent(props) {
|
|
|
67
65
|
display: flex;
|
|
68
66
|
flex-direction: column;
|
|
69
67
|
gap: 4px;
|
|
70
|
-
` }, { children: fields.map((field, index) => (_jsx(SelectBox, { index: index + 1, isAnswer: field.isAnswer, text: field.show.text || `${index + 1}번 선택지`, onClick: () => setSettingOpen(true) }, index))) })), _jsx(SquareButton, { size: "small", color: "icon", icon: _jsx(Settings3FillIcon, {}), onClick: () => {
|
|
68
|
+
` }, { children: fields.map((field, index) => (_jsx(SelectBox, { index: index + 1, isAnswer: field.isAnswer, image: field.show.image, text: field.show.text || `${index + 1}번 선택지`, onClick: () => setSettingOpen(true) }, index))) })), _jsx(SquareButton, { size: "small", color: "icon", icon: _jsx(Settings3FillIcon, {}), onClick: () => {
|
|
71
69
|
setSettingOpen(true);
|
|
72
70
|
} })] })), settingOpen && (_jsx(SettingForm, { control: control, handleSubmit: handleSubmit, fields: fields, append: append, remove: remove, update: update, nodeKey: nodeKey, onClose: () => setSettingOpen(false) }))] }));
|
|
73
71
|
}
|
|
@@ -1,34 +1,63 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@emotion/react/jsx-runtime";
|
|
2
2
|
/** @jsxImportSource @emotion/react */
|
|
3
3
|
import styled from "@emotion/styled";
|
|
4
4
|
import { Controller, } from "react-hook-form";
|
|
5
5
|
import { css } from "@emotion/react";
|
|
6
6
|
import Input from "../../../../../components/Input";
|
|
7
|
-
import { DeleteBinLineIcon, ErrorWarningFillIcon, ImageAddFillIcon, } from "../../../../../icons";
|
|
7
|
+
import { DeleteBinLineIcon, ErrorWarningFillIcon, ImageAddFillIcon, ImageEditFillIcon, } from "../../../../../icons";
|
|
8
8
|
import SquareButton from "../../../../../components/SquareButton";
|
|
9
9
|
import Switch from "../../../../../components/Switch";
|
|
10
|
+
import { useState } from "react";
|
|
11
|
+
import { InsertImageDialog } from "./InsertImageDialog";
|
|
10
12
|
export function FormSelection(props) {
|
|
11
13
|
const { index, control, field, update, rules, onDelete } = props;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
14
|
+
const [imageOpen, setImageOpen] = useState(false);
|
|
15
|
+
const [inputFocused, setInputFocused] = useState(false);
|
|
16
|
+
return (_jsxs(_Fragment, { children: [_jsx(InsertImageDialog, { open: imageOpen, onClose: () => setImageOpen(false), updateImg: (props) => {
|
|
17
|
+
update(index, Object.assign(Object.assign({}, field), { show: Object.assign(Object.assign({}, field.show), { image: props }) }));
|
|
18
|
+
} }), _jsxs(Container, { children: [_jsx(Index, { children: index + 1 }), _jsxs("div", Object.assign({ css: css `
|
|
19
|
+
display: flex;
|
|
20
|
+
flex: 1;
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
gap: 4px;
|
|
23
|
+
` }, { children: [field.show.image && field.show.image.src && (_jsx("img", { src: field.show.image.src, alt: field.show.image.altText, css: css `
|
|
24
|
+
height: auto;
|
|
25
|
+
// 이미지로 인해 좌우로 스크롤이 생기는 것을 방지
|
|
26
|
+
max-width: min(400px, 100%);
|
|
27
|
+
width: fit-content;
|
|
28
|
+
border-radius: 6px;
|
|
29
|
+
`, 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
|
|
30
|
+
? "activeDanger"
|
|
31
|
+
: inputFocused
|
|
32
|
+
? "activePrimary"
|
|
33
|
+
: "default", value: value, onChange: onChange, inputProps: {
|
|
34
|
+
onFocus: (_e) => {
|
|
35
|
+
setInputFocused(true);
|
|
36
|
+
},
|
|
37
|
+
onBlur: (_e) => {
|
|
38
|
+
setInputFocused(false);
|
|
39
|
+
// onBlur시에 선택지 미리보기에 반영합니다.
|
|
40
|
+
update(index, Object.assign(Object.assign({}, field), { show: Object.assign(Object.assign({}, field.show), { text: value }) }));
|
|
41
|
+
},
|
|
42
|
+
}, placeholder: `${index + 1}번 선택지`, hintIcon: invalid ? _jsx(ErrorWarningFillIcon, {}) : undefined, hintText: error === null || error === void 0 ? void 0 : error.message, multiline: true, fullWidth: true, css: css `
|
|
43
|
+
flex: 1;
|
|
44
|
+
` })) })] })), _jsxs("div", Object.assign({ css: css `
|
|
45
|
+
display: flex;
|
|
46
|
+
height: 36px;
|
|
47
|
+
gap: 8px;
|
|
48
|
+
align-items: center;
|
|
49
|
+
` }, { children: [_jsx(SquareButton, { color: "icon", size: "xsmall", icon: field.show.image ? _jsx(ImageEditFillIcon, {}) : _jsx(ImageAddFillIcon, {}), onClick: () => {
|
|
50
|
+
setImageOpen(true);
|
|
51
|
+
} }), _jsx(Controller, { name: `selections.${index}.isAnswer`, control: control, render: ({ field: { value, onChange } }) => (_jsxs(Answer, Object.assign({ onClick: () => {
|
|
52
|
+
onChange(!value);
|
|
53
|
+
// 선택지 미리보기에 정답여부를 반영합니다.
|
|
54
|
+
update(index, Object.assign(Object.assign({}, field), { isAnswer: !value }));
|
|
55
|
+
} }, { children: ["\uC815\uB2F5", _jsx(Switch, { checked: value, size: "small" })] }))) }), onDelete && (_jsx(SquareButton, { color: "white", size: "xsmall", icon: _jsx(DeleteBinLineIcon, {}), onClick: onDelete }))] }))] })] }));
|
|
26
56
|
}
|
|
27
57
|
const Container = styled.div(({ theme }) => css `
|
|
28
58
|
display: flex;
|
|
29
59
|
padding: 4px 12px;
|
|
30
60
|
gap: 8px;
|
|
31
|
-
align-items: center;
|
|
32
61
|
border-radius: 8px;
|
|
33
62
|
background: ${theme.color.background.neutralAlt};
|
|
34
63
|
`);
|
|
@@ -38,6 +67,7 @@ const Index = styled.div(({ theme }) => css `
|
|
|
38
67
|
width: 20px;
|
|
39
68
|
height: 20px;
|
|
40
69
|
padding: 4px;
|
|
70
|
+
margin-top: 8px;
|
|
41
71
|
justify-content: center;
|
|
42
72
|
align-items: center;
|
|
43
73
|
border-radius: 4px;
|
|
@@ -51,6 +81,7 @@ const Index = styled.div(({ theme }) => css `
|
|
|
51
81
|
`);
|
|
52
82
|
const Answer = styled.div(({ theme }) => css `
|
|
53
83
|
display: flex;
|
|
84
|
+
align-items: center;
|
|
54
85
|
padding-right: 4px;
|
|
55
86
|
gap: 8px;
|
|
56
87
|
color: ${theme.color.foreground.neutralBase};
|
package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/InsertImageDialog.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ImageProps } from "../ProblemSelectNode";
|
|
2
|
+
export interface InsertImageDialogProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onClose: () => void;
|
|
5
|
+
updateImg: (props: ImageProps) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function InsertImageDialog(props: InsertImageDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
+
import { useContext, useRef, useState } from "react";
|
|
12
|
+
import styled from "@emotion/styled";
|
|
13
|
+
import { useTheme } from "@emotion/react";
|
|
14
|
+
import { ImageFillIcon, LinkIcon, UploadLineIcon } from "../../../../../icons";
|
|
15
|
+
import { AlertDialog, AlertDialogContent, AlertDialogTitle, } from "../../../../../components/AlertDialog";
|
|
16
|
+
import Button from "../../../../../components/Button";
|
|
17
|
+
import { CodleDesignSystemContext } from "../../../../../CodleDesignSystemProvider";
|
|
18
|
+
import { InsertImageUriDialogBody } from "./InsertImageUriDialogBody";
|
|
19
|
+
import { InsertImageUploadedDialogBody } from "./InsertImageUploadedDialogBody";
|
|
20
|
+
export function InsertImageDialog(props) {
|
|
21
|
+
const { open, onClose, updateImg } = props;
|
|
22
|
+
const theme = useTheme();
|
|
23
|
+
const [mode, setMode] = useState(null);
|
|
24
|
+
const inputRef = useRef(null);
|
|
25
|
+
const cdsContext = useContext(CodleDesignSystemContext);
|
|
26
|
+
const [src, setSrc] = useState("");
|
|
27
|
+
const [altText, setAltText] = useState("");
|
|
28
|
+
const onClick = (props) => {
|
|
29
|
+
updateImg(props);
|
|
30
|
+
handleOnClose();
|
|
31
|
+
};
|
|
32
|
+
const handleOnClose = () => {
|
|
33
|
+
setMode(null);
|
|
34
|
+
setSrc("");
|
|
35
|
+
setAltText("");
|
|
36
|
+
onClose();
|
|
37
|
+
};
|
|
38
|
+
return (_jsxs(StyledAlertDialog, Object.assign({ icon: _jsx(ImageFillIcon, { color: theme.color.background.primary }), open: open, onClose: handleOnClose, disableIconPadding: true }, { children: [_jsx(StyledAlertDialogTitle, Object.assign({ onClose: handleOnClose }, { children: "\uC774\uBBF8\uC9C0 \uC0BD\uC785\uD558\uAE30" })), !mode && (_jsx(AlertDialogContent, { children: _jsxs(Buttons, { children: [_jsxs(ButtonAndDescription, { children: [_jsx(Description, { children: "\uC774\uBBF8\uC9C0 URL\uC744 \uC54C\uACE0 \uC788\uB2E4\uBA74" }), _jsx(Button, { color: "grey", size: "medium", fullWidth: true, label: "URL\uB85C \uC0BD\uC785\uD558\uAE30", startIcon: _jsx(LinkIcon, {}), onClick: () => setMode("url") })] }), _jsxs(ButtonAndDescription, { children: [_jsx(Description, { children: "\uAC16\uACE0 \uC788\uB294 \uC774\uBBF8\uC9C0\uB97C \uC0BD\uC785\uD558\uACE0 \uC2F6\uB2E4\uBA74" }), _jsx(Button, { color: "grey", size: "medium", fullWidth: true, label: "\uD30C\uC77C \uC120\uD0DD\uD558\uAE30", startIcon: _jsx(UploadLineIcon, {}), onClick: () => {
|
|
39
|
+
var _a;
|
|
40
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
41
|
+
} }), _jsx(HiddenInput, { ref: inputRef, type: "file", accept: "image/*", onChange: (event) => __awaiter(this, void 0, void 0, function* () {
|
|
42
|
+
var _a, _b;
|
|
43
|
+
const file = (_a = event.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
44
|
+
if (!file)
|
|
45
|
+
return;
|
|
46
|
+
const uploadByFile = (_b = cdsContext.lexical) === null || _b === void 0 ? void 0 : _b.uploadByFile;
|
|
47
|
+
if (uploadByFile) {
|
|
48
|
+
setSrc(yield uploadByFile(file));
|
|
49
|
+
setMode("file");
|
|
50
|
+
}
|
|
51
|
+
}) })] })] }) })), mode === "url" && (_jsx(InsertImageUriDialogBody, { src: src, setSrc: setSrc, altText: altText, setAltText: setAltText, onClick: onClick })), mode === "file" && (_jsx(InsertImageUploadedDialogBody, { src: src, setSrc: setSrc, altText: altText, setAltText: setAltText, onClick: onClick }))] })));
|
|
52
|
+
}
|
|
53
|
+
const StyledAlertDialog = styled(AlertDialog) `
|
|
54
|
+
gap: 16px;
|
|
55
|
+
`;
|
|
56
|
+
const StyledAlertDialogTitle = styled(AlertDialogTitle) `
|
|
57
|
+
color: ${({ theme }) => theme.color.foreground.neutralBase};
|
|
58
|
+
text-align: center;
|
|
59
|
+
|
|
60
|
+
/* Default/Heading/20px-Bd */
|
|
61
|
+
font-family: ${({ theme }) => theme.fontFamily.ui};
|
|
62
|
+
font-size: 20px;
|
|
63
|
+
font-style: normal;
|
|
64
|
+
font-weight: 700;
|
|
65
|
+
line-height: 28px; /* 140% */
|
|
66
|
+
letter-spacing: 0.25px;
|
|
67
|
+
`;
|
|
68
|
+
const Buttons = styled.div `
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: flex-start;
|
|
71
|
+
gap: 8px;
|
|
72
|
+
|
|
73
|
+
// Actions가 없는 경우 하단이 디자인과 맞지 않는다
|
|
74
|
+
margin-bottom: -16px;
|
|
75
|
+
`;
|
|
76
|
+
const ButtonAndDescription = styled.div `
|
|
77
|
+
display: flex;
|
|
78
|
+
flex-direction: column;
|
|
79
|
+
align-items: center;
|
|
80
|
+
gap: 8px;
|
|
81
|
+
flex: 1 0 0;
|
|
82
|
+
`;
|
|
83
|
+
const Description = styled.div `
|
|
84
|
+
color: ${({ theme }) => theme.color.foreground.neutralBaseDisabled};
|
|
85
|
+
text-align: center;
|
|
86
|
+
|
|
87
|
+
/* Default/Label/12px-Md */
|
|
88
|
+
font-family: ${({ theme }) => theme.fontFamily.ui};
|
|
89
|
+
font-size: 12px;
|
|
90
|
+
font-style: normal;
|
|
91
|
+
font-weight: 500;
|
|
92
|
+
line-height: 16px; /* 133.333% */
|
|
93
|
+
`;
|
|
94
|
+
const HiddenInput = styled.input `
|
|
95
|
+
display: none;
|
|
96
|
+
`;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ImageProps } from "../ProblemSelectNode";
|
|
2
|
+
export interface InsertImageUploadedDialogBodyProps {
|
|
3
|
+
src: string;
|
|
4
|
+
setSrc: (src: string) => void;
|
|
5
|
+
altText: string;
|
|
6
|
+
setAltText: (altText: string) => void;
|
|
7
|
+
onClick: (props: ImageProps) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function InsertImageUploadedDialogBody(props: InsertImageUploadedDialogBodyProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
11
|
+
import { useContext, useRef } from "react";
|
|
12
|
+
import styled from "@emotion/styled";
|
|
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
|
+
export function InsertImageUploadedDialogBody(props) {
|
|
19
|
+
const { src, setSrc, altText, setAltText, onClick } = props;
|
|
20
|
+
const inputRef = useRef(null);
|
|
21
|
+
const cdsContext = useContext(CodleDesignSystemContext);
|
|
22
|
+
const isDisabled = src === "";
|
|
23
|
+
return (_jsxs(_Fragment, { children: [_jsx(AlertDialogContent, { children: _jsxs(Inputs, { children: [_jsx(Button, { fullWidth: true, color: "grey", size: "medium", label: "\uD30C\uC77C \uBC14\uAFB8\uAE30", onClick: () => {
|
|
24
|
+
var _a;
|
|
25
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
26
|
+
}, startIcon: _jsx(RefreshLineIcon, {}) }), _jsx(HiddenInput, { ref: inputRef, type: "file", accept: "image/*", onChange: (event) => __awaiter(this, void 0, void 0, function* () {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
const file = (_a = event.target.files) === null || _a === void 0 ? void 0 : _a[0];
|
|
29
|
+
if (!file)
|
|
30
|
+
return;
|
|
31
|
+
const uploadByFile = (_b = cdsContext.lexical) === null || _b === void 0 ? void 0 : _b.uploadByFile;
|
|
32
|
+
if (uploadByFile) {
|
|
33
|
+
setSrc(yield uploadByFile(file));
|
|
34
|
+
}
|
|
35
|
+
}) }), _jsxs(ImagePreview, { children: ["\uC774\uBBF8\uC9C0 \uBBF8\uB9AC\uBCF4\uAE30", _jsx("img", { src: src, alt: altText })] }), _jsx(Input, { fullWidth: true, label: "\uB300\uCCB4 \uD14D\uC2A4\uD2B8", placeholder: "\uC0BD\uC785\uD558\uB294 \uC774\uBBF8\uC9C0\uC5D0 \uAD00\uD55C \uC124\uBA85", color: "default", size: "medium", onChange: (e) => {
|
|
36
|
+
setAltText(e.target.value);
|
|
37
|
+
}, value: altText })] }) }), _jsx(AlertDialogActions, { children: _jsx(Button, { fullWidth: true, label: "\uC0BD\uC785\uD558\uAE30", size: "medium", color: "primary", disabled: isDisabled, onClick: () => onClick({ altText, src }) }) })] }));
|
|
38
|
+
}
|
|
39
|
+
const Inputs = styled.div `
|
|
40
|
+
display: flex;
|
|
41
|
+
flex-direction: column;
|
|
42
|
+
gap: 16px;
|
|
43
|
+
`;
|
|
44
|
+
const HiddenInput = styled.input `
|
|
45
|
+
display: none;
|
|
46
|
+
`;
|
|
47
|
+
const ImagePreview = styled.div `
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
gap: 8px;
|
|
51
|
+
`;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ImageProps } from "../ProblemSelectNode";
|
|
2
|
+
export interface InsertImageUriDialogBodyProps {
|
|
3
|
+
src: string;
|
|
4
|
+
setSrc: (src: string) => void;
|
|
5
|
+
altText: string;
|
|
6
|
+
setAltText: (altText: string) => void;
|
|
7
|
+
onClick: (props: ImageProps) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function InsertImageUriDialogBody(props: InsertImageUriDialogBodyProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/InsertImageUriDialogBody.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import styled from "@emotion/styled";
|
|
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
|
+
export function InsertImageUriDialogBody(props) {
|
|
8
|
+
const { src, setSrc, altText, setAltText, onClick } = props;
|
|
9
|
+
const isDisabled = src === "";
|
|
10
|
+
return (_jsxs(_Fragment, { children: [" ", _jsx(AlertDialogContent, { children: _jsxs(Inputs, { children: [_jsx(Input, { fullWidth: true, label: "URL", placeholder: "https://www.pexels.com/photo/n-2848492/", color: "default", size: "medium", onChange: (e) => {
|
|
11
|
+
setSrc(e.target.value);
|
|
12
|
+
}, value: src, startIcon: _jsx(LinkIcon, {}) }), _jsx(Input, { fullWidth: true, label: "\uB300\uCCB4 \uD14D\uC2A4\uD2B8", placeholder: "\uC0BD\uC785\uD558\uB294 \uC774\uBBF8\uC9C0\uC5D0 \uAD00\uD55C \uC124\uBA85", color: "default", size: "medium", onChange: (e) => {
|
|
13
|
+
setAltText(e.target.value);
|
|
14
|
+
}, value: altText })] }) }), _jsx(AlertDialogActions, { children: _jsx(Button, { fullWidth: true, label: "\uC0BD\uC785\uD558\uAE30", size: "medium", color: "primary", disabled: isDisabled, onClick: () => onClick({ altText, src }) }) })] }));
|
|
15
|
+
}
|
|
16
|
+
const Inputs = styled.div `
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
gap: 16px;
|
|
20
|
+
`;
|
|
@@ -5,7 +5,7 @@ import { css } from "@emotion/react";
|
|
|
5
5
|
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
|
|
6
6
|
import styled from "@emotion/styled";
|
|
7
7
|
import shadows from "../../../../../foundation/shadows";
|
|
8
|
-
import { AddFillIcon, ListRadioIcon } from "../../../../../icons";
|
|
8
|
+
import { AddFillIcon, AlarmWarningFillIcon, ListRadioIcon, } from "../../../../../icons";
|
|
9
9
|
import Button from "../../../../../components/Button";
|
|
10
10
|
import { FormSelection } from "./FormSelection";
|
|
11
11
|
export default function SettingForm(props) {
|
|
@@ -31,6 +31,7 @@ export default function SettingForm(props) {
|
|
|
31
31
|
return true;
|
|
32
32
|
return `${duplicatedIndex + 1}번 선택지와 같은 내용입니다.`;
|
|
33
33
|
}
|
|
34
|
+
const hasMultipleAnswers = fields.filter((field) => field.isAnswer).length > 1;
|
|
34
35
|
return (_jsxs(Form, Object.assign({ onSubmit: handleSubmit(onSettingSubmit) }, { children: [_jsxs(Title, { children: [_jsx(ListRadioIcon, { css: css `
|
|
35
36
|
width: 12px;
|
|
36
37
|
height: 12px;
|
|
@@ -49,7 +50,10 @@ export default function SettingForm(props) {
|
|
|
49
50
|
},
|
|
50
51
|
value: (fields.length + 1).toString(),
|
|
51
52
|
});
|
|
52
|
-
} })
|
|
53
|
+
} }), hasMultipleAnswers && (_jsxs(Alert, { children: [_jsx(AlarmWarningFillIcon, { css: css `
|
|
54
|
+
width: 14px;
|
|
55
|
+
height: 14px;
|
|
56
|
+
` }), "\uC815\uB2F5\uC774 \uC5EC\uB7EC \uAC1C\uC778 \uBB38\uC81C\uC5D0\uB294 \uC815\uB2F5\uC744 \uBAA8\uB450 \uC120\uD0DD\uD574\uC57C \uD55C\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", type: "submit" })] })] })));
|
|
53
57
|
}
|
|
54
58
|
const Form = styled.form(({ theme }) => css `
|
|
55
59
|
display: flex;
|
|
@@ -102,3 +106,14 @@ const Buttons = styled.div `
|
|
|
102
106
|
align-items: center;
|
|
103
107
|
gap: 8px;
|
|
104
108
|
`;
|
|
109
|
+
const Alert = styled.div(({ theme }) => css `
|
|
110
|
+
display: flex;
|
|
111
|
+
gap: 4px;
|
|
112
|
+
color: ${theme.color.foreground.neutralBaseDisabled};
|
|
113
|
+
/* Default/Label/12px-Md */
|
|
114
|
+
font-family: ${theme.fontFamily.ui};
|
|
115
|
+
font-size: 12px;
|
|
116
|
+
font-style: normal;
|
|
117
|
+
font-weight: 500;
|
|
118
|
+
line-height: 16px; /* 133.333% */
|
|
119
|
+
`);
|
|
@@ -14,6 +14,7 @@ import { $isColoredQuoteNode, } from "../../nodes/ColoredQuoteNode";
|
|
|
14
14
|
import { useTheme } from "@emotion/react";
|
|
15
15
|
import { css } from "@emotion/css";
|
|
16
16
|
import { $isProblemInputNode } from "../../nodes";
|
|
17
|
+
import { $isProblemSelectNode, } from "../../nodes/ProblemSelectNode";
|
|
17
18
|
function getParagraphContextMenuOptions(editor, node, setOpen) {
|
|
18
19
|
return [
|
|
19
20
|
new ComponentPickerOption("본문", {
|
|
@@ -185,6 +186,17 @@ function getProblemInputContextMenuOptions(node) {
|
|
|
185
186
|
}),
|
|
186
187
|
];
|
|
187
188
|
}
|
|
189
|
+
function getProblemSelectContextMenuOptions(node) {
|
|
190
|
+
return [
|
|
191
|
+
new ComponentPickerOption("블록 삭제", {
|
|
192
|
+
icon: _jsx(CloseFillIcon, {}),
|
|
193
|
+
keywords: [],
|
|
194
|
+
onSelect: () => {
|
|
195
|
+
node.remove();
|
|
196
|
+
},
|
|
197
|
+
}),
|
|
198
|
+
];
|
|
199
|
+
}
|
|
188
200
|
function getColoredQuoteContextMenuOptions(editor, theme, node) {
|
|
189
201
|
return [
|
|
190
202
|
new ComponentPickerOption("회색", {
|
|
@@ -264,6 +276,9 @@ export function useContextMenuOptions() {
|
|
|
264
276
|
if ($isProblemInputNode(node)) {
|
|
265
277
|
return getProblemInputContextMenuOptions(node);
|
|
266
278
|
}
|
|
279
|
+
else if ($isProblemSelectNode(node)) {
|
|
280
|
+
return getProblemSelectContextMenuOptions(node);
|
|
281
|
+
}
|
|
267
282
|
else if (node instanceof ParagraphNode) {
|
|
268
283
|
return getParagraphContextMenuOptions(editor, node, setImageOpen);
|
|
269
284
|
}
|