Skip to content

Commit 4cbaced

Browse files
author
Zaydek Michels-Gualtieri
committed
Extracted ReactRenderer (and parseSpans)
1 parent 84ab4d4 commit 4cbaced

3 files changed

Lines changed: 81 additions & 77 deletions

File tree

src/Editor/Editor.js

Lines changed: 1 addition & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import keyDownTypesEnum from "./keyDownTypesEnum"
44
import noopTextContent from "./noopTextContent"
55
import React from "react"
66
import ReactDOM from "react-dom"
7-
import toReact from "./toReact"
7+
import ReactRenderer from "./ReactRenderer"
88
import useEditor from "./useEditor"
99
import uuidv4 from "uuid/v4"
1010
import { computeCursors } from "./cursors"
@@ -27,81 +27,6 @@ import "./Editor.css"
2727
noopTextContent()
2828
})()
2929

30-
// Computes an array of types and type map for a pseudo-
31-
// React element.
32-
function computeTypeInfo(element) {
33-
const types = []
34-
const typeMap = {}
35-
if (typeof element === "string") {
36-
return [types, typeMap]
37-
}
38-
let ref = element.type !== undefined && // NOTE: "type" can be 0
39-
element
40-
while (ref) {
41-
types.push(ref.type)
42-
typeMap[ref.type] = ref
43-
ref = ref.props.children.type !== undefined && // NOTE: "type" can be 0
44-
ref.props.children
45-
}
46-
return [types, typeMap]
47-
}
48-
49-
// Decorates pseudo-React elements; sets element.pos to
50-
// "at-start", "at-center", or "at-end".
51-
function decoratePos(elements) {
52-
for (let x = 0; x < elements.length; x++) {
53-
if (!x || typeof elements[x - 1] === "string" || typeof elements[x] === "string") {
54-
// No-op
55-
continue
56-
}
57-
const [types1, typeMap1] = computeTypeInfo(elements[x - 1])
58-
const [types2, typeMap2] = computeTypeInfo(elements[x])
59-
const common = types1.filter(a => types2.some(b => a === b))
60-
for (const type of common) {
61-
typeMap1[type].props.pos = !typeMap1[type].props.pos ? "at-start" : "at-center"
62-
typeMap2[type].props.pos = "at-end"
63-
}
64-
}
65-
}
66-
67-
// Parses spans to pseudo-React elements.
68-
function parseSpans(spans) {
69-
const elements = []
70-
for (const span of spans) {
71-
if (!span.formats.length) {
72-
elements.push(span.content)
73-
continue
74-
}
75-
const element = {}
76-
let lastRef = element
77-
let ref = lastRef
78-
for (const format of span.formats.sort()) {
79-
Object.assign(ref, { // <- lastRef
80-
type: format,
81-
props: {
82-
...span[format],
83-
children: {}, // <- ref
84-
},
85-
})
86-
lastRef = ref
87-
ref = ref.props.children
88-
}
89-
lastRef.props.children = span.content
90-
elements.push(element)
91-
}
92-
decoratePos(elements)
93-
return elements
94-
}
95-
96-
const ReactRenderer = ({ state, dispatch, renderableMap }) => (
97-
state.elements.map(({ type: T, spans, ...props }) => (
98-
React.createElement(T, {
99-
key: props.uuid,
100-
...props,
101-
}, toReact(parseSpans(spans), renderableMap))
102-
))
103-
)
104-
10530
const Editor = () => {
10631

10732
// Maps enum types to renderable React components.

src/Editor/ReactRenderer.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import React from "react"
2+
import toReact from "./toReact"
3+
4+
// Computes an array of types and type map for a pseudo-
5+
// React element.
6+
function computeTypeInfo(element) {
7+
const types = []
8+
const typeMap = {}
9+
if (typeof element === "string") {
10+
return [types, typeMap]
11+
}
12+
let ref = element.type !== undefined && // NOTE: "type" can be 0
13+
element
14+
while (ref) {
15+
types.push(ref.type)
16+
typeMap[ref.type] = ref
17+
ref = ref.props.children.type !== undefined && // NOTE: "type" can be 0
18+
ref.props.children
19+
}
20+
return [types, typeMap]
21+
}
22+
23+
// Decorates pseudo-React elements; sets element.pos to
24+
// "at-start", "at-center", or "at-end".
25+
function decoratePos(elements) {
26+
for (let x = 0; x < elements.length; x++) {
27+
if (!x || typeof elements[x - 1] === "string" || typeof elements[x] === "string") {
28+
// No-op
29+
continue
30+
}
31+
const [types1, typeMap1] = computeTypeInfo(elements[x - 1])
32+
const [types2, typeMap2] = computeTypeInfo(elements[x])
33+
const common = types1.filter(a => types2.some(b => a === b))
34+
for (const type of common) {
35+
typeMap1[type].props.pos = !typeMap1[type].props.pos ? "at-start" : "at-center"
36+
typeMap2[type].props.pos = "at-end"
37+
}
38+
}
39+
}
40+
41+
// Parses pseudo-React elements from spans.
42+
function parseSpans(spans) {
43+
const elements = []
44+
for (const span of spans) {
45+
if (!span.formats.length) {
46+
elements.push(span.content)
47+
continue
48+
}
49+
const element = {}
50+
let lastRef = element
51+
let ref = lastRef
52+
for (const format of span.formats.sort()) {
53+
Object.assign(ref, { // <- lastRef
54+
type: format,
55+
props: {
56+
...span[format],
57+
children: {}, // <- ref
58+
},
59+
})
60+
lastRef = ref
61+
ref = ref.props.children
62+
}
63+
lastRef.props.children = span.content
64+
elements.push(element)
65+
}
66+
decoratePos(elements)
67+
return elements
68+
}
69+
70+
const ReactRenderer = ({ state, dispatch, renderableMap }) => (
71+
state.elements.map(({ type: T, spans, ...props }) => (
72+
React.createElement(T, {
73+
key: props.uuid,
74+
...props,
75+
}, toReact(parseSpans(spans), renderableMap))
76+
))
77+
)
78+
79+
export default ReactRenderer

src/Editor/toReact.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function toReact(elements, renderableMap) {
1616
}, toReact(props.children, renderableMap)))
1717
}
1818
// NOTE: Does not return an empty array -- uses null for
19-
// {children || <br />} case
19+
// {children || <br />}
2020
if (!renderable.length) {
2121
return null
2222
}

0 commit comments

Comments
 (0)