@team-monolith/cds 1.9.9 → 1.9.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/ProblemSelectNode.d.ts +2 -2
  2. package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/ProblemSelectNode.js +1 -1
  3. package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SelectBox.d.ts +1 -1
  4. package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SelectBox.js +1 -1
  5. package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SelectComponent.js +2 -7
  6. package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/FormSelection.d.ts +3 -6
  7. package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/FormSelection.js +37 -45
  8. package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/InsertImageDialog.d.ts +2 -2
  9. package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/InsertImageDialog.js +20 -12
  10. package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/InsertImageUploadedDialogBody.d.ts +4 -5
  11. package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/InsertImageUploadedDialogBody.js +5 -6
  12. package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/InsertImageUriDialogBody.d.ts +3 -5
  13. package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/InsertImageUriDialogBody.js +4 -7
  14. package/dist/patterns/LexicalEditor/nodes/ProblemSelectNode/SettingForm/SettingForm.js +22 -11
  15. package/dist/patterns/LexicalEditor/plugins/ImagesPlugin/InsertImageDialog.js +12 -11
  16. package/dist/patterns/LexicalEditor/plugins/ImagesPlugin/InsertImageUploadedDialogBody.d.ts +13 -6
  17. package/dist/patterns/LexicalEditor/plugins/ImagesPlugin/InsertImageUploadedDialogBody.js +5 -6
  18. package/dist/patterns/LexicalEditor/plugins/ImagesPlugin/InsertImageUriDialogBody.d.ts +9 -6
  19. package/dist/patterns/LexicalEditor/plugins/ImagesPlugin/InsertImageUriDialogBody.js +4 -7
  20. package/package.json +1 -1
@@ -7,7 +7,7 @@ export interface ImageProps {
7
7
  export interface Selection {
8
8
  isAnswer: boolean;
9
9
  show: {
10
- image?: ImageProps;
10
+ image?: ImageProps | null;
11
11
  text: string;
12
12
  };
13
13
  value: string;
@@ -31,7 +31,7 @@ export declare class ProblemSelectNode extends DecoratorNode<ReactNode> {
31
31
  static getType(): string;
32
32
  getSelections(): Selection[];
33
33
  getSelected(): string[];
34
- setSolutions(selections: Selection[]): void;
34
+ setSelections(selections: Selection[]): void;
35
35
  setSelected(selected: string[]): void;
36
36
  static clone(node: ProblemSelectNode): ProblemSelectNode;
37
37
  constructor(selections: Selection[], selected: string[], hasMultipleAnswers?: boolean, key?: NodeKey);
@@ -19,7 +19,7 @@ export class ProblemSelectNode extends DecoratorNode {
19
19
  getSelected() {
20
20
  return this.__selected;
21
21
  }
22
- setSolutions(selections) {
22
+ setSelections(selections) {
23
23
  const self = this.getWritable();
24
24
  self.__selections = selections;
25
25
  }
@@ -3,7 +3,7 @@ export interface SelectBoxProps {
3
3
  index: number;
4
4
  isSelected?: boolean;
5
5
  isAnswer?: boolean;
6
- image?: ImageProps;
6
+ image?: ImageProps | null;
7
7
  text: string;
8
8
  onClick?: () => void;
9
9
  fullWidth?: boolean;
@@ -12,7 +12,7 @@ export default function SelectBox(props) {
12
12
  ` })) : (index) })), _jsxs(Content, { children: [image && (_jsx("img", { src: image.src, alt: image.altText, css: css `
13
13
  height: auto;
14
14
  // 이미지로 인해 좌우로 스크롤이 생기는 것을 방지
15
- max-width: min(328px, 100%);
15
+ max-width: 100%;
16
16
  width: fit-content;
17
17
  border-radius: 6px;
18
18
  ` })), text] }), isAnswer && (_jsx(CheckboxCircleFillIcon, { color: theme.color.foreground.success, css: css `
@@ -45,10 +45,7 @@ export function SelectComponent(props) {
45
45
  if (!$isProblemSelectNode(node)) {
46
46
  return;
47
47
  }
48
- const newSelected = [...selected];
49
- const index = newSelected.indexOf(selection.value);
50
- newSelected.splice(index, 1);
51
- node.setSelected(newSelected);
48
+ node.setSelected(selected.filter((v) => v !== selection.value));
52
49
  });
53
50
  }
54
51
  else {
@@ -57,9 +54,7 @@ export function SelectComponent(props) {
57
54
  if (!$isProblemSelectNode(node)) {
58
55
  return;
59
56
  }
60
- const newSelected = [...selected];
61
- newSelected.push(selection.value);
62
- node.setSelected(newSelected);
57
+ node.setSelected([...selected, selection.value]);
63
58
  });
64
59
  }
65
60
  }, fullWidth: true }, index)))] }));
@@ -1,16 +1,13 @@
1
1
  import { Selection } from "../ProblemSelectNode";
2
- import { Control, FieldArrayWithId, UseFieldArrayUpdate } from "react-hook-form";
2
+ import { Control, UseFormWatch } from "react-hook-form";
3
3
  export interface FormSelectionProps {
4
4
  index: number;
5
5
  control: Control<{
6
6
  selections: Selection[];
7
7
  }, any>;
8
- field: FieldArrayWithId<{
8
+ watch: UseFormWatch<{
9
9
  selections: Selection[];
10
- }, "selections", "uid">;
11
- update: UseFieldArrayUpdate<{
12
- selections: Selection[];
13
- }, "selections">;
10
+ }>;
14
11
  rules?: any;
15
12
  onDelete?: () => void;
16
13
  }
@@ -1,7 +1,7 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@emotion/react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
2
  /** @jsxImportSource @emotion/react */
3
3
  import styled from "@emotion/styled";
4
- import { Controller, } from "react-hook-form";
4
+ import { Controller } from "react-hook-form";
5
5
  import { css } from "@emotion/react";
6
6
  import Input from "../../../../../components/Input";
7
7
  import { DeleteBinLineIcon, ErrorWarningFillIcon, ImageAddFillIcon, ImageEditFillIcon, } from "../../../../../icons";
@@ -10,51 +10,43 @@ import Switch from "../../../../../components/Switch";
10
10
  import { useState } from "react";
11
11
  import { InsertImageDialog } from "./InsertImageDialog";
12
12
  export function FormSelection(props) {
13
- const { index, control, field, update, rules, onDelete } = props;
13
+ const { index, control, watch, rules, onDelete } = props;
14
14
  const [imageOpen, setImageOpen] = useState(false);
15
15
  const [inputFocused, setInputFocused] = useState(false);
16
- return (_jsxs(_Fragment, { children: [_jsx(InsertImageDialog, { title: field.show.image ? "이미지 바꾸기" : "이미지 삽입하기", 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
- }, deleteImg: field.show.image
19
- ? () => {
20
- update(index, Object.assign(Object.assign({}, field), { show: Object.assign(Object.assign({}, field.show), { image: undefined }) }));
21
- }
22
- : undefined }), _jsxs(Container, { children: [_jsx(Index, { children: index + 1 }), _jsxs("div", Object.assign({ css: css `
23
- display: flex;
24
- flex: 1;
25
- flex-direction: column;
26
- gap: 4px;
27
- ` }, { children: [field.show.image && field.show.image.src && (_jsx("img", { src: field.show.image.src, alt: field.show.image.altText, css: css `
28
- height: auto;
29
- // 이미지로 인해 좌우로 스크롤이 생기는 것을 방지
30
- max-width: min(400px, 100%);
31
- width: fit-content;
32
- border-radius: 6px;
33
- `, 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
34
- ? "activeDanger"
35
- : inputFocused
36
- ? "activePrimary"
37
- : "default", value: value, onChange: onChange, inputProps: {
38
- onFocus: (_e) => {
39
- setInputFocused(true);
40
- },
41
- onBlur: (_e) => {
42
- setInputFocused(false);
43
- update(index, Object.assign(Object.assign({}, field), { show: Object.assign(Object.assign({}, field.show), { text: value }) }));
44
- },
45
- }, placeholder: `${index + 1}번 선택지`, hintIcon: invalid ? _jsx(ErrorWarningFillIcon, {}) : undefined, hintText: error === null || error === void 0 ? void 0 : error.message, multiline: true, fullWidth: true, css: css `
46
- flex: 1;
47
- ` })) })] })), _jsxs("div", Object.assign({ css: css `
48
- display: flex;
49
- height: 36px;
50
- gap: 8px;
51
- align-items: center;
52
- ` }, { children: [_jsx(SquareButton, { color: "icon", size: "xsmall", icon: field.show.image ? _jsx(ImageEditFillIcon, {}) : _jsx(ImageAddFillIcon, {}), onClick: () => {
53
- setImageOpen(true);
54
- } }), _jsx(Controller, { name: `selections.${index}.isAnswer`, control: control, render: ({ field: { value, onChange } }) => (_jsxs(Answer, Object.assign({ onClick: () => {
55
- onChange(!value);
56
- update(index, Object.assign(Object.assign({}, field), { isAnswer: !value }));
57
- } }, { children: ["\uC815\uB2F5", _jsx(Switch, { checked: value, size: "small" })] }))) }), onDelete && (_jsx(SquareButton, { color: "white", size: "xsmall", icon: _jsx(DeleteBinLineIcon, {}), onClick: onDelete }))] }))] })] }));
16
+ return (_jsxs(Container, { children: [_jsx(Index, { children: index + 1 }), _jsxs("div", Object.assign({ css: css `
17
+ display: flex;
18
+ flex: 1;
19
+ flex-direction: column;
20
+ gap: 4px;
21
+ ` }, { children: [_jsx(Controller, { name: `selections.${index}.show.image`, control: control, render: ({ field: { value, onChange } }) => (_jsxs(_Fragment, { children: [_jsx(InsertImageDialog, { title: value ? "이미지 바꾸기" : "이미지 삽입하기", open: imageOpen, onClose: () => setImageOpen(false), updateImg: (props) => onChange(props), deleteButton: Boolean(value) }), value && value.src && (_jsx("img", { src: value.src, alt: value.altText, css: css `
22
+ height: auto;
23
+ // 이미지로 인해 좌우로 스크롤이 생기는 것을 방지
24
+ max-width: min(400px, 100%);
25
+ width: fit-content;
26
+ border-radius: 6px;
27
+ `, 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
28
+ ? "activeDanger"
29
+ : inputFocused
30
+ ? "activePrimary"
31
+ : "default", value: value, onChange: onChange, inputProps: {
32
+ onFocus: (_e) => {
33
+ setInputFocused(true);
34
+ },
35
+ onBlur: (_e) => {
36
+ setInputFocused(false);
37
+ },
38
+ }, placeholder: `${index + 1}번 선택지`, hintIcon: invalid ? _jsx(ErrorWarningFillIcon, {}) : undefined, hintText: error === null || error === void 0 ? void 0 : error.message, multiline: true, fullWidth: true, css: css `
39
+ flex: 1;
40
+ ` })) })] })), _jsxs("div", Object.assign({ css: css `
41
+ display: flex;
42
+ height: 36px;
43
+ gap: 8px;
44
+ align-items: center;
45
+ ` }, { children: [_jsx(SquareButton, { color: "icon", size: "xsmall", icon: watch(`selections.${index}.show.image`) ? (_jsx(ImageEditFillIcon, {})) : (_jsx(ImageAddFillIcon, {})), onClick: () => {
46
+ setImageOpen(true);
47
+ } }), _jsx(Controller, { name: `selections.${index}.isAnswer`, control: control, render: ({ field: { value, onChange } }) => (_jsxs(Answer, Object.assign({ onClick: () => {
48
+ onChange(!value);
49
+ } }, { children: ["\uC815\uB2F5", _jsx(Switch, { checked: value, size: "small" })] }))) }), onDelete && (_jsx(SquareButton, { color: "white", size: "xsmall", icon: _jsx(DeleteBinLineIcon, {}), onClick: onDelete }))] }))] }));
58
50
  }
59
51
  const Container = styled.div(({ theme }) => css `
60
52
  display: flex;
@@ -3,8 +3,8 @@ export interface InsertImageDialogProps {
3
3
  title: string;
4
4
  open: boolean;
5
5
  onClose: () => void;
6
- updateImg: (props: ImageProps) => void;
6
+ updateImg: (props: ImageProps | null) => void;
7
7
  /** 전달하면 "이미지 삭제하기" 버튼이 생깁니다. */
8
- deleteImg?: () => void;
8
+ deleteButton?: boolean;
9
9
  }
10
10
  export declare function InsertImageDialog(props: InsertImageDialogProps): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -18,25 +18,30 @@ import Button from "../../../../../components/Button";
18
18
  import { CodleDesignSystemContext } from "../../../../../CodleDesignSystemProvider";
19
19
  import { InsertImageUriDialogBody } from "./InsertImageUriDialogBody";
20
20
  import { InsertImageUploadedDialogBody } from "./InsertImageUploadedDialogBody";
21
+ import { useForm } from "react-hook-form";
21
22
  export function InsertImageDialog(props) {
22
- const { title, open, onClose, updateImg, deleteImg } = props;
23
+ const { title, open, onClose, updateImg, deleteButton } = props;
23
24
  const theme = useTheme();
24
25
  const [mode, setMode] = useState(null);
25
26
  const inputRef = useRef(null);
26
27
  const cdsContext = useContext(CodleDesignSystemContext);
27
- const [src, setSrc] = useState("");
28
- const [altText, setAltText] = useState("");
29
- const onClick = (props) => {
30
- updateImg(props);
31
- handleOnClose();
32
- };
28
+ const { control, setValue, watch, reset, handleSubmit } = useForm({
29
+ defaultValues: { src: "", altText: "" },
30
+ });
33
31
  const handleOnClose = () => {
34
32
  setMode(null);
35
- setSrc("");
36
- setAltText("");
33
+ reset();
37
34
  onClose();
38
35
  };
39
- 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: title })), !mode && (_jsx(AlertDialogContent, { children: _jsxs("div", Object.assign({ css: css `
36
+ const onSubmit = (data) => {
37
+ updateImg(data);
38
+ handleOnClose();
39
+ };
40
+ return (_jsxs(StyledAlertDialog, Object.assign({ component: "form", icon: _jsx(ImageFillIcon, { color: theme.color.background.primary }), open: open, onClose: handleOnClose, onSubmit: (e) => {
41
+ // nested form 구조라서 submit이벤트 전파를 막습니다.
42
+ e.stopPropagation();
43
+ handleSubmit(onSubmit)();
44
+ }, disableIconPadding: true }, { children: [_jsx(StyledAlertDialogTitle, Object.assign({ onClose: handleOnClose }, { children: title })), !mode && (_jsx(AlertDialogContent, { children: _jsxs("div", Object.assign({ css: css `
40
45
  display: flex;
41
46
  flex-direction: column;
42
47
  gap: 16px;
@@ -52,10 +57,13 @@ export function InsertImageDialog(props) {
52
57
  return;
53
58
  const uploadByFile = (_b = cdsContext.lexical) === null || _b === void 0 ? void 0 : _b.uploadByFile;
54
59
  if (uploadByFile) {
55
- setSrc(yield uploadByFile(file));
60
+ setValue("src", yield uploadByFile(file));
56
61
  setMode("file");
57
62
  }
58
- }) })] })] }), deleteImg && (_jsxs(_Fragment, { children: [_jsx(Divider, {}), _jsx(Button, { color: "textDanger", size: "medium", fullWidth: true, label: "\uC774\uBBF8\uC9C0 \uC0AD\uC81C\uD558\uAE30", onClick: deleteImg })] }))] })) })), 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 }))] })));
63
+ }) })] })] }), deleteButton && (_jsxs(_Fragment, { children: [_jsx(Divider, {}), _jsx(Button, { color: "textDanger", size: "medium", fullWidth: true, label: "\uC774\uBBF8\uC9C0 \uC0AD\uC81C\uD558\uAE30", onClick: () => {
64
+ updateImg(null);
65
+ handleOnClose();
66
+ } })] }))] })) })), mode === "url" && (_jsx(InsertImageUriDialogBody, { control: control, watch: watch })), mode === "file" && (_jsx(InsertImageUploadedDialogBody, { control: control, watch: watch, setValue: setValue }))] })));
59
67
  }
60
68
  const StyledAlertDialog = styled(AlertDialog) `
61
69
  gap: 16px;
@@ -1,9 +1,8 @@
1
1
  import { ImageProps } from "../ProblemSelectNode";
2
+ import { Control, UseFormSetValue, UseFormWatch } from "react-hook-form";
2
3
  export interface InsertImageUploadedDialogBodyProps {
3
- src: string;
4
- setSrc: (src: string) => void;
5
- altText: string;
6
- setAltText: (altText: string) => void;
7
- onClick: (props: ImageProps) => void;
4
+ control: Control<ImageProps, any>;
5
+ watch: UseFormWatch<ImageProps>;
6
+ setValue: UseFormSetValue<ImageProps>;
8
7
  }
9
8
  export declare function InsertImageUploadedDialogBody(props: InsertImageUploadedDialogBodyProps): import("react/jsx-runtime").JSX.Element;
@@ -15,11 +15,12 @@ import { AlertDialogActions, AlertDialogContent, } from "../../../../../componen
15
15
  import Button from "../../../../../components/Button";
16
16
  import { RefreshLineIcon } from "../../../../../icons";
17
17
  import Input from "../../../../../components/Input";
18
+ import { Controller, } from "react-hook-form";
18
19
  export function InsertImageUploadedDialogBody(props) {
19
- const { src, setSrc, altText, setAltText, onClick } = props;
20
+ const { control, watch, setValue } = props;
20
21
  const inputRef = useRef(null);
21
22
  const cdsContext = useContext(CodleDesignSystemContext);
22
- const isDisabled = src === "";
23
+ const isDisabled = watch("src") === "";
23
24
  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
25
  var _a;
25
26
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click();
@@ -30,11 +31,9 @@ export function InsertImageUploadedDialogBody(props) {
30
31
  return;
31
32
  const uploadByFile = (_b = cdsContext.lexical) === null || _b === void 0 ? void 0 : _b.uploadByFile;
32
33
  if (uploadByFile) {
33
- setSrc(yield uploadByFile(file));
34
+ setValue("src", yield uploadByFile(file));
34
35
  }
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 }) }) })] }));
36
+ }) }), _jsxs(ImagePreview, { children: ["\uC774\uBBF8\uC9C0 \uBBF8\uB9AC\uBCF4\uAE30", _jsx("img", { src: watch("src"), alt: watch("altText") })] }), _jsx(Controller, { name: "altText", control: control, render: ({ field: { value, onChange } }) => (_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: onChange, value: value })) })] }) }), _jsx(AlertDialogActions, { children: _jsx(Button, { type: "submit", fullWidth: true, label: "\uC0BD\uC785\uD558\uAE30", size: "medium", color: "primary", disabled: isDisabled }) })] }));
38
37
  }
39
38
  const Inputs = styled.div `
40
39
  display: flex;
@@ -1,9 +1,7 @@
1
1
  import { ImageProps } from "../ProblemSelectNode";
2
+ import { Control, UseFormWatch } from "react-hook-form";
2
3
  export interface InsertImageUriDialogBodyProps {
3
- src: string;
4
- setSrc: (src: string) => void;
5
- altText: string;
6
- setAltText: (altText: string) => void;
7
- onClick: (props: ImageProps) => void;
4
+ control: Control<ImageProps, any>;
5
+ watch: UseFormWatch<ImageProps>;
8
6
  }
9
7
  export declare function InsertImageUriDialogBody(props: InsertImageUriDialogBodyProps): import("react/jsx-runtime").JSX.Element;
@@ -4,14 +4,11 @@ import { AlertDialogActions, AlertDialogContent, } from "../../../../../componen
4
4
  import Input from "../../../../../components/Input";
5
5
  import { LinkIcon } from "../../../../../icons";
6
6
  import Button from "../../../../../components/Button";
7
+ import { Controller } from "react-hook-form";
7
8
  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 }) }) })] }));
9
+ const { control, watch } = props;
10
+ const isDisabled = watch("src") === "";
11
+ return (_jsxs(_Fragment, { children: [" ", _jsx(AlertDialogContent, { children: _jsxs(Inputs, { children: [_jsx(Controller, { name: "src", control: control, render: ({ field: { value, onChange } }) => (_jsx(Input, { fullWidth: true, label: "URL", placeholder: "https://www.pexels.com/photo/n-2848492/", color: "default", size: "medium", onChange: onChange, value: value, startIcon: _jsx(LinkIcon, {}) })) }), _jsx(Controller, { name: "altText", control: control, render: ({ field: { value, onChange } }) => (_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: onChange, value: value })) })] }) }), _jsx(AlertDialogActions, { children: _jsx(Button, { type: "submit", fullWidth: true, label: "\uC0BD\uC785\uD558\uAE30", size: "medium", color: "primary", disabled: isDisabled }) })] }));
15
12
  }
16
13
  const Inputs = styled.div `
17
14
  display: flex;
@@ -16,13 +16,22 @@ export default function SettingForm(props) {
16
16
  const { selections, nodeKey, onClose } = props;
17
17
  const [editor] = useLexicalComposerContext();
18
18
  const [tooltipOpen, setTooltipOpen] = useState(false);
19
- const { control, handleSubmit } = useForm({
19
+ const { control, handleSubmit, watch } = useForm({
20
20
  mode: "all",
21
21
  defaultValues: {
22
- selections,
22
+ selections: selections.map((selection) => {
23
+ var _a;
24
+ return ({
25
+ isAnswer: selection.isAnswer,
26
+ show: {
27
+ image: (_a = selection.show.image) !== null && _a !== void 0 ? _a : null,
28
+ text: selection.show.text,
29
+ },
30
+ });
31
+ }),
23
32
  },
24
33
  });
25
- const { fields, append, remove, update } = useFieldArray({
34
+ const { fields, append, remove } = useFieldArray({
26
35
  control,
27
36
  name: "selections",
28
37
  keyName: "uid",
@@ -33,28 +42,30 @@ export default function SettingForm(props) {
33
42
  if (!$isProblemSelectNode(node)) {
34
43
  return;
35
44
  }
36
- node.setSolutions(data.selections);
45
+ node.setSelections(data.selections);
37
46
  });
38
47
  onClose();
39
48
  };
40
- function validateDuplicatedSelection(fields, index) {
49
+ function validateDuplicatedSelection(index) {
50
+ const selections = watch("selections");
41
51
  if (index === 0)
42
52
  return true;
43
- const duplicatedIndex = fields
53
+ const duplicatedIndex = selections
44
54
  .slice(0, index)
45
- .findIndex((prevField) => prevField.show.text === fields[index].show.text);
55
+ .findIndex((selection) => selection.show.text === selections[index].show.text);
46
56
  if (duplicatedIndex < 0)
47
57
  return true;
48
58
  return `${duplicatedIndex + 1}번 선택지와 같은 내용입니다.`;
49
59
  }
50
- const hasMultipleAnswers = fields.filter((field) => field.isAnswer).length > 1;
51
- const hasAnswer = fields.some((field) => field.isAnswer);
60
+ const answersCount = watch("selections").filter((selection) => selection.isAnswer).length;
61
+ const hasMultipleAnswers = answersCount > 1;
62
+ const hasAnswer = answersCount > 0;
52
63
  return (_jsxs(Form, Object.assign({ onSubmit: handleSubmit(onSettingSubmit) }, { children: [_jsxs(Title, { children: [_jsx(ListRadioIcon, { css: css `
53
64
  width: 12px;
54
65
  height: 12px;
55
- ` }), "\uAC1D\uAD00\uC2DD \uC785\uB825 \uCE78"] }), _jsxs(Content, { children: [_jsxs(FormArea, { children: [_jsx(Label, { children: "\uB2F5\uC548" }), fields.map((field, index) => (_jsx(FormSelection, { index: index, control: control, field: field, update: update, rules: {
66
+ ` }), "\uAC1D\uAD00\uC2DD \uC785\uB825 \uCE78"] }), _jsxs(Content, { children: [_jsxs(FormArea, { children: [_jsx(Label, { children: "\uB2F5\uC548" }), fields.map((field, index) => (_jsx(FormSelection, { index: index, control: control, watch: watch, rules: {
56
67
  required: "필수 입력 항목입니다.",
57
- validate: () => validateDuplicatedSelection(fields, index),
68
+ validate: () => validateDuplicatedSelection(index),
58
69
  }, onDelete: index !== 0
59
70
  ? () => {
60
71
  remove(index);
@@ -18,6 +18,7 @@ import { ImageFillIcon, LinkIcon, UploadLineIcon } from "../../../../icons";
18
18
  import Button from "../../../../components/Button";
19
19
  import { useTheme } from "@emotion/react";
20
20
  import { CodleDesignSystemContext } from "../../../../CodleDesignSystemProvider";
21
+ import { useForm } from "react-hook-form";
21
22
  export function InsertImageDialog(props) {
22
23
  const { open, activeEditor, onClose } = props;
23
24
  const theme = useTheme();
@@ -25,8 +26,13 @@ export function InsertImageDialog(props) {
25
26
  const hasModifier = useRef(false);
26
27
  const inputRef = useRef(null);
27
28
  const cdsContext = useContext(CodleDesignSystemContext);
28
- const [src, setSrc] = useState("");
29
- const [altText, setAltText] = useState("");
29
+ const { control, setValue, watch, reset, handleSubmit } = useForm({
30
+ defaultValues: { src: "", altText: "" },
31
+ });
32
+ const onSubmit = (data) => {
33
+ activeEditor.dispatchCommand(INSERT_IMAGE_COMMAND, data);
34
+ handleOnClose();
35
+ };
30
36
  useEffect(() => {
31
37
  hasModifier.current = false;
32
38
  const handler = (e) => {
@@ -37,17 +43,12 @@ export function InsertImageDialog(props) {
37
43
  document.removeEventListener("keydown", handler);
38
44
  };
39
45
  }, [activeEditor]);
40
- const onClick = (payload) => {
41
- activeEditor.dispatchCommand(INSERT_IMAGE_COMMAND, payload);
42
- handleOnClose();
43
- };
44
46
  const handleOnClose = () => {
45
47
  setMode(null);
46
- setSrc("");
47
- setAltText("");
48
+ reset();
48
49
  onClose();
49
50
  };
50
- 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: () => {
51
+ return (_jsxs(StyledAlertDialog, Object.assign({ component: "form", icon: _jsx(ImageFillIcon, { color: theme.color.background.primary }), open: open, onClose: handleOnClose, onSubmit: handleSubmit(onSubmit), 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: () => {
51
52
  var _a;
52
53
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click();
53
54
  } }), _jsx(HiddenInput, { ref: inputRef, type: "file", accept: "image/*", onChange: (event) => __awaiter(this, void 0, void 0, function* () {
@@ -57,10 +58,10 @@ export function InsertImageDialog(props) {
57
58
  return;
58
59
  const uploadByFile = (_b = cdsContext.lexical) === null || _b === void 0 ? void 0 : _b.uploadByFile;
59
60
  if (uploadByFile) {
60
- setSrc(yield uploadByFile(file));
61
+ setValue("src", yield uploadByFile(file));
61
62
  setMode("file");
62
63
  }
63
- }) })] })] }) })), 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 }))] })));
64
+ }) })] })] }) })), mode === "url" && (_jsx(InsertImageUriDialogBody, { control: control, watch: watch })), mode === "file" && (_jsx(InsertImageUploadedDialogBody, { control: control, watch: watch, setValue: setValue }))] })));
64
65
  }
65
66
  const StyledAlertDialog = styled(AlertDialog) `
66
67
  gap: 16px;
@@ -1,9 +1,16 @@
1
- import { InsertImagePayload } from ".";
1
+ import { Control, UseFormSetValue, UseFormWatch } from "react-hook-form";
2
2
  export interface InsertImageUploadedDialogBodyProps {
3
- src: string;
4
- setSrc: (src: string) => void;
5
- altText: string;
6
- setAltText: (altText: string) => void;
7
- onClick: (payload: InsertImagePayload) => void;
3
+ control: Control<{
4
+ src: string;
5
+ altText: string;
6
+ }, any>;
7
+ watch: UseFormWatch<{
8
+ src: string;
9
+ altText: string;
10
+ }>;
11
+ setValue: UseFormSetValue<{
12
+ src: string;
13
+ altText: string;
14
+ }>;
8
15
  }
9
16
  export declare function InsertImageUploadedDialogBody(props: InsertImageUploadedDialogBodyProps): import("react/jsx-runtime").JSX.Element;
@@ -15,11 +15,12 @@ import { AlertDialogContent, AlertDialogActions, } from "../../../../components/
15
15
  import Button from "../../../../components/Button";
16
16
  import Input from "../../../../components/Input";
17
17
  import { RefreshLineIcon } from "../../../../icons";
18
+ import { Controller, } from "react-hook-form";
18
19
  export function InsertImageUploadedDialogBody(props) {
19
- const { src, setSrc, altText, setAltText, onClick } = props;
20
+ const { control, watch, setValue } = props;
20
21
  const inputRef = useRef(null);
21
22
  const cdsContext = useContext(CodleDesignSystemContext);
22
- const isDisabled = src === "";
23
+ const isDisabled = watch("src") === "";
23
24
  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
25
  var _a;
25
26
  (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.click();
@@ -30,11 +31,9 @@ export function InsertImageUploadedDialogBody(props) {
30
31
  return;
31
32
  const uploadByFile = (_b = cdsContext.lexical) === null || _b === void 0 ? void 0 : _b.uploadByFile;
32
33
  if (uploadByFile) {
33
- setSrc(yield uploadByFile(file));
34
+ setValue("src", yield uploadByFile(file));
34
35
  }
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 }) }) })] }));
36
+ }) }), _jsxs(ImagePreview, { children: ["\uC774\uBBF8\uC9C0 \uBBF8\uB9AC\uBCF4\uAE30", _jsx("img", { src: watch("src"), alt: watch("altText") })] }), _jsx(Controller, { name: "altText", control: control, render: ({ field: { value, onChange } }) => (_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: onChange, value: value })) })] }) }), _jsx(AlertDialogActions, { children: _jsx(Button, { type: "submit", fullWidth: true, label: "\uC0BD\uC785\uD558\uAE30", size: "medium", color: "primary", disabled: isDisabled }) })] }));
38
37
  }
39
38
  const Inputs = styled.div `
40
39
  display: flex;
@@ -1,9 +1,12 @@
1
- import { InsertImagePayload } from ".";
1
+ import { Control, UseFormWatch } from "react-hook-form";
2
2
  export interface InsertImageUriDialogBodyProps {
3
- src: string;
4
- setSrc: (src: string) => void;
5
- altText: string;
6
- setAltText: (altText: string) => void;
7
- onClick: (payload: InsertImagePayload) => void;
3
+ control: Control<{
4
+ src: string;
5
+ altText: string;
6
+ }, any>;
7
+ watch: UseFormWatch<{
8
+ src: string;
9
+ altText: string;
10
+ }>;
8
11
  }
9
12
  export declare function InsertImageUriDialogBody(props: InsertImageUriDialogBodyProps): import("react/jsx-runtime").JSX.Element;
@@ -4,14 +4,11 @@ import { AlertDialogActions, AlertDialogContent, } from "../../../../components/
4
4
  import Input from "../../../../components/Input";
5
5
  import { LinkIcon } from "../../../../icons";
6
6
  import Button from "../../../../components/Button";
7
+ import { Controller } from "react-hook-form";
7
8
  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 }) }) })] }));
9
+ const { control, watch } = props;
10
+ const isDisabled = watch("src") === "";
11
+ return (_jsxs(_Fragment, { children: [" ", _jsx(AlertDialogContent, { children: _jsxs(Inputs, { children: [_jsx(Controller, { name: "src", control: control, render: ({ field: { value, onChange } }) => (_jsx(Input, { fullWidth: true, label: "URL", placeholder: "https://www.pexels.com/photo/n-2848492/", color: "default", size: "medium", onChange: onChange, value: value, startIcon: _jsx(LinkIcon, {}) })) }), _jsx(Controller, { name: "altText", control: control, render: ({ field: { value, onChange } }) => (_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: onChange, value: value })) })] }) }), _jsx(AlertDialogActions, { children: _jsx(Button, { type: "submit", fullWidth: true, label: "\uC0BD\uC785\uD558\uAE30", size: "medium", color: "primary", disabled: isDisabled }) })] }));
15
12
  }
16
13
  const Inputs = styled.div `
17
14
  display: flex;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-monolith/cds",
3
- "version": "1.9.9",
3
+ "version": "1.9.10",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "sideEffects": false,