ソースを参照

dev: 开发图文简单富文本编辑嘴贱

Roxas 4 年 前
コミット
94aa88fe2d

+ 12
- 27
src/components/Editor/BraftEditor/index.tsx ファイルの表示

@@ -36,19 +36,19 @@ const emotionOptions = {
36 36
 
37 37
 BraftEditor.use(Emoticon(emotionOptions));
38 38
 
39
+export interface BaseEditorProps {
40
+  value: EditorState;
41
+  onChange: (editorState: EditorState) => void;
42
+  controls?: ControlType[];
43
+  FloatControls?: any;
44
+}
45
+
39 46
 export const BarftEditorPage = ({
40
-  initValue,
41 47
   value,
42 48
   onChange,
43 49
   controls,
44 50
   FloatControls
45
-}: {
46
-  initValue?: string;
47
-  value?: EditorState;
48
-  onChange?: (editorState: EditorState) => void;
49
-  controls?: ControlType[];
50
-  FloatControls?: any;
51
-}) => {
51
+}: BaseEditorProps) => {
52 52
   const optionsControls = controls
53 53
     ? controls
54 54
     : [
@@ -63,33 +63,18 @@ export const BarftEditorPage = ({
63 63
       ];
64 64
   const options: any = {
65 65
     controls: optionsControls,
66
-    uncontrolled: !value && !onChange,
67 66
     showControlsBar:
68 67
       optionsControls && optionsControls.length > 0 && !FloatControls,
69 68
     showFloatControls: FloatControls
70 69
   };
71 70
 
72
-  const [insideValue, setInsideValue] = useState(
73
-    BraftEditor.createEditorState(initValue || "<p>Hello <b>World!</b></p>")
74
-  );
75
-
76 71
   const [focusState, setFocusState] = useState(false);
77 72
 
78
-  let resultValue: EditorState | undefined;
79
-  let resultAction: ((editorState: EditorState) => void) | undefined;
80
-  if (options.uncontrolled) {
81
-    resultValue = insideValue;
82
-    resultAction = setInsideValue;
83
-  } else {
84
-    resultValue = value;
85
-    resultAction = onChange;
86
-  }
87
-
88 73
   return (
89 74
     <div className={styles.baseWrapper}>
90 75
       <BraftEditor
91
-        value={resultValue}
92
-        onChange={resultAction}
76
+        value={value}
77
+        onChange={onChange}
93 78
         controls={options.controls}
94 79
         controlBarClassName={classnames(styles.controlBar, {
95 80
           [styles.focus]: focusState
@@ -113,8 +98,8 @@ export const BarftEditorPage = ({
113 98
         <div className={classnames(styles.floatControls)}>
114 99
           {
115 100
             <FloatControls
116
-              editorState={resultValue}
117
-              setEditorState={resultAction}
101
+              editorState={value}
102
+              setEditorState={onChange}
118 103
             />
119 104
           }
120 105
         </div>

+ 54
- 0
src/components/Editor/CommonTool/ControlsToolBar.tsx ファイルの表示

@@ -0,0 +1,54 @@
1
+import React from "react";
2
+import { EditorState } from "braft-editor";
3
+
4
+export interface ToolListItem {
5
+  key: any;
6
+  color: string;
7
+  iconClass: string;
8
+  onClick: any;
9
+  text?: string;
10
+};
11
+interface ControlsToolBarProps {
12
+  editorState: EditorState;
13
+  setEditorState: any;
14
+  toolList: Array<ToolListItem>;
15
+  injectIconWrapStyle?: any;
16
+  injectIconStyle?: any;
17
+}
18
+
19
+export const ControlsToolBar = (props: ControlsToolBarProps) => {
20
+  const {
21
+    editorState,
22
+    setEditorState,
23
+    toolList = [],
24
+    injectIconWrapStyle = {},
25
+    injectIconStyle = {},
26
+  } = props;
27
+  const IconWrapStyle = {
28
+    cursor: "pointer",
29
+    textAlign: "center" as const,
30
+    width: "42px",
31
+    ...injectIconWrapStyle,
32
+  };
33
+  const IconStyle = { fontSize: "21px", ...injectIconStyle };
34
+
35
+  if (toolList.length < 1) {
36
+    return null;
37
+  }
38
+
39
+  return (
40
+    <>
41
+    {
42
+      toolList.map(i => (
43
+        <div
44
+          key={`toolList_${i.key}`}
45
+          style={{ color: i.color, ...IconWrapStyle }}
46
+          onClick={i.onClick}
47
+        >
48
+          <i className={i.iconClass} style={IconStyle} />
49
+        </div>
50
+      ))
51
+    }
52
+    </>
53
+  );
54
+};

+ 63
- 0
src/components/Editor/QAEditor/SimpleEditor.tsx ファイルの表示

@@ -0,0 +1,63 @@
1
+import React from 'react';
2
+import { BarftEditorPage, BaseEditorProps } from "@components/Editor/BraftEditor";
3
+import { ControlsToolBar, ToolListItem } from "@components/Editor/CommonTool/ControlsToolBar";
4
+import { ContentUtils } from "braft-utils";
5
+import styles from './index.less';
6
+
7
+interface QASimpleEditorProps extends BaseEditorProps {
8
+  toolAlign?: 'inner' | 'bottom';
9
+  toolList?: Array<ToolListItem>;
10
+  toolBarContainerStyle?: any;
11
+  appendToolBtn?: any;
12
+}
13
+
14
+const QASimpleEditor = (props: QASimpleEditorProps) => {
15
+  const {
16
+    value,
17
+    onChange, 
18
+    toolAlign = 'inner',
19
+    toolList = [],
20
+    toolBarContainerStyle = {},
21
+    appendToolBtn = null,
22
+  } = props;
23
+
24
+
25
+  if (toolAlign === 'inner') {
26
+    return (
27
+      <BarftEditorPage
28
+        value={value}
29
+        onChange={onChange}
30
+        controls={[]}
31
+        FloatControls={({ editorState, setEditorState }: any) => (
32
+          <ControlsToolBar
33
+            editorState={editorState}
34
+            setEditorState={setEditorState}
35
+            toolList={toolList}
36
+          />)
37
+        }
38
+      />
39
+    )
40
+  }
41
+
42
+  return (
43
+    <div>
44
+      <BarftEditorPage
45
+        value={value}
46
+        onChange={onChange}
47
+        controls={[]}
48
+      />
49
+      <div style={toolBarContainerStyle}>
50
+        <div className={styles.bottomToolBarWrapper}>
51
+          <ControlsToolBar
52
+            editorState={value}
53
+            setEditorState={onChange}
54
+            toolList={toolList}
55
+          />
56
+        </div>
57
+        {appendToolBtn}
58
+      </div>
59
+    </div>
60
+  );
61
+}
62
+
63
+export default QASimpleEditor;

+ 3
- 0
src/components/Editor/QAEditor/index.less ファイルの表示

@@ -0,0 +1,3 @@
1
+.bottomToolBarWrapper {
2
+  display: flex;
3
+}

+ 2
- 0
src/components/Editor/TextInputLogic/index.ts ファイルの表示

@@ -0,0 +1,2 @@
1
+import { useState } from 'react';
2
+import BraftEditor, { ControlType, EditorState } from "braft-editor";

+ 154
- 41
stories/Editor.stories.tsx ファイルの表示

@@ -6,12 +6,16 @@ import { withKnobs, number, boolean, select } from "@storybook/addon-knobs";
6 6
 import { addReadme } from "storybook-readme";
7 7
 
8 8
 import { BarftEditorPage } from "@components/Editor/BraftEditor";
9
+import SimpleEditor from "@components/Editor/QAEditor/SimpleEditor";
10
+import { ControlsToolBar } from "@components/Editor/CommonTool/ControlsToolBar";
9 11
 import { ContentUtils } from "braft-utils";
10 12
 
11 13
 import EditorData from "./data/articleData.json";
12
-import { Divider, Popover } from "antd";
14
+import { Divider, Popover, Checkbox } from "antd";
13 15
 import BraftEditor from "braft-editor";
14 16
 
17
+import styles from './index.less';
18
+
15 19
 const stories = storiesOf("Editor", module);
16 20
 stories.addDecorator(storyFn => (
17 21
   <div style={{ padding: "0px 40px" }}>{storyFn()}</div>
@@ -41,54 +45,162 @@ stories.add(
41 45
     );
42 46
     return (
43 47
       <>
44
-        <h3>非受控组件</h3>
45
-        <BarftEditorPage controls={[]} />
46
-        <Divider />
47
-        <h3>受控组件</h3>
48 48
         <BarftEditorPage
49 49
           value={editorState}
50 50
           onChange={setEditorState}
51 51
           controls={["bold"]}
52
-          FloatControls={({ editorState, setEditorState }: any) => {
53
-            const IconWrapStyle = {
54
-              cursor: "pointer",
55
-              textAlign: "center" as const,
56
-              width: "42px"
57
-            };
58
-            const IconStyle = { fontSize: "21px" };
59
-            return (
60
-              <>
61
-                <div
62
-                  style={{ color: "#f07977", ...IconWrapStyle }}
63
-                  onClick={() => {
52
+          FloatControls={({ editorState, setEditorState }: any) => (
53
+            <ControlsToolBar
54
+              editorState={editorState}
55
+              setEditorState={setEditorState}
56
+              toolList={[
57
+                {
58
+                  key: 1,
59
+                  color: "#f07977",
60
+                  iconClass: "schedule schedule-icon_story",
61
+                  onClick: () => {
64 62
                     setEditorState(
65 63
                       ContentUtils.insertText(editorState, "Hello World")
66 64
                     );
67
-                  }}
68
-                >
69
-                  <i
70
-                    className="schedule schedule-icon_story"
71
-                    style={IconStyle}
72
-                  />
73
-                </div>
74
-                <div style={{ ...IconWrapStyle, color: "#3d9bfe" }}>
75
-                  <i
76
-                    className="schedule schedule-icon_topic"
77
-                    style={IconStyle}
78
-                  />
79
-                </div>
80
-                <div style={{ ...IconWrapStyle, color: "#ffa405" }}>
81
-                  <i
82
-                    className="schedule schedule-icon_emoticon"
83
-                    style={IconStyle}
84
-                  />
85
-                </div>
86
-                <div style={{ ...IconWrapStyle, color: "#71c135" }}>
87
-                  <i className="schedule schedule-icon_img" style={IconStyle} />
88
-                </div>
89
-              </>
90
-            );
65
+                  }
66
+                },
67
+                {
68
+                  key: 2,
69
+                  color: "#3d9bfe",
70
+                  iconClass: "schedule schedule-icon_topic",
71
+                  onClick: () => {
72
+                    setEditorState(
73
+                      ContentUtils.insertText(editorState, "Hello World")
74
+                    );
75
+                  }
76
+                },
77
+                {
78
+                  key: 3,
79
+                  color: "#ffa405",
80
+                  iconClass: "schedule schedule-icon_emoticon",
81
+                  onClick: () => {
82
+                    setEditorState(
83
+                      ContentUtils.insertText(editorState, "Hello World")
84
+                    );
85
+                  }
86
+                },
87
+                {
88
+                  key: 4,
89
+                  color: "#71c135",
90
+                  iconClass: "schedule schedule-icon_img",
91
+                  onClick: () => {
92
+                    setEditorState(
93
+                      ContentUtils.insertText(editorState, "Hello World")
94
+                    );
95
+                  }
96
+                }
97
+              ]}
98
+            />
99
+          )}
100
+        />
101
+      </>
102
+    );
103
+  },
104
+  {
105
+    info: {
106
+      inline: true
107
+    },
108
+    notes: "A very simple example of addon notes"
109
+  }
110
+);
111
+
112
+stories.add(
113
+  "QASimpleEditor",
114
+  () => {
115
+    const [editorState, setEditorState] = useState(
116
+      BraftEditor.createEditorState("<p>Hello <b>World!</b></p>")
117
+    );
118
+    return (
119
+      <>
120
+        <SimpleEditor
121
+          value={editorState}
122
+          onChange={setEditorState}
123
+          controls={[]}
124
+          toolList={[
125
+            {
126
+              key: 1,
127
+              color: "#f07977",
128
+              iconClass: "schedule schedule-icon_story",
129
+              onClick: () => {
130
+                setEditorState(
131
+                  ContentUtils.insertText(editorState, "Hello World")
132
+                );
133
+              }
134
+            },
135
+            {
136
+              key: 2,
137
+              color: "#3d9bfe",
138
+              iconClass: "schedule schedule-icon_topic",
139
+              onClick: () => {
140
+                setEditorState(
141
+                  ContentUtils.insertText(editorState, "Hello World")
142
+                );
143
+              }
144
+            },
145
+            {
146
+              key: 3,
147
+              color: "#ffa405",
148
+              iconClass: "schedule schedule-icon_emoticon",
149
+              onClick: () => {
150
+                setEditorState(
151
+                  ContentUtils.insertText(editorState, "Hello World")
152
+                );
153
+              }
154
+            },
155
+            {
156
+              key: 4,
157
+              color: "#71c135",
158
+              iconClass: "schedule schedule-icon_img",
159
+              onClick: () => {
160
+                setEditorState(
161
+                  ContentUtils.insertText(editorState, "Hello World")
162
+                );
163
+              }
164
+            }
165
+          ]}
166
+          toolAlign={select("toolAlign", {
167
+            Inner: 'inner',
168
+            Bottom: 'bottom',
169
+          }, "bottom")}
170
+          toolBarContainerStyle={{
171
+            display: 'flex',
172
+            justifyContent: 'space-between',
173
+            height: '55px',
174
+            alignItems: 'center',
91 175
           }}
176
+          appendToolBtn={(
177
+            <div style={{ display: 'flex', alignItems: 'center', width: '400px', justifyContent: 'space-between'}}>
178
+              <div className={styles.QAAnonymous}>
179
+                <Popover
180
+                  trigger="hover"
181
+                  content={
182
+                    <div>
183
+                      警告
184
+                    </div>
185
+                  }
186
+                >
187
+                  <Checkbox>
188
+                    anonymous
189
+                  </Checkbox>
190
+                </Popover>
191
+              </div>
192
+              <div
193
+                className={styles.QASaveBtn}
194
+              >
195
+                Save
196
+              </div>
197
+              <div
198
+                className={styles.QASubmitBtn}
199
+              >
200
+                Submit
201
+              </div>
202
+            </div>
203
+          )}
92 204
         />
93 205
       </>
94 206
     );
@@ -100,3 +212,4 @@ stories.add(
100 212
     notes: "A very simple example of addon notes"
101 213
   }
102 214
 );
215
+

+ 28
- 0
stories/index.less ファイルの表示

@@ -0,0 +1,28 @@
1
+.QASubmitBtn {
2
+  width: 140px;
3
+  height: 40px;
4
+  line-height: 40px;
5
+  background: rgba(113, 193, 53, 1);
6
+  border-radius: 4px;
7
+  color: #fff;
8
+  font-size: 16px;
9
+  font-weight: 500;
10
+  text-align: center;
11
+  cursor: pointer;
12
+}
13
+
14
+.QASaveBtn {
15
+  font-size: 16px;
16
+  width: 91px;
17
+  height: 40px;
18
+  line-height: 40px;
19
+  text-align: center;
20
+  border-radius: 4px;
21
+  color: rgba(113, 193, 53, 1);
22
+  border: 1px solid rgba(113, 193, 53, 1);
23
+  cursor: pointer;
24
+}
25
+
26
+.QAAnonymous {
27
+  font-size: 16px;
28
+}