1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
import { Node, DOMOutputSpec } from 'prosemirror-model';
import clamp from '../../../util/clamp';
import convertToCSSPTValue from '../../../util/convertToCSSPTValue';
import toCSSLineSpacing from '../../../util/toCSSLineSpacing';
// import type { NodeSpec } from './Types';
type NodeSpec = {
attrs?: { [key: string]: any };
content?: string;
draggable?: boolean;
group?: string;
inline?: boolean;
name?: string;
parseDOM?: Array<any>;
toDOM?: (node: any) => DOMOutputSpec;
};
// This assumes that every 36pt maps to one indent level.
export const INDENT_MARGIN_PT_SIZE = 36;
export const MIN_INDENT_LEVEL = 0;
export const MAX_INDENT_LEVEL = 7;
export const ATTRIBUTE_INDENT = 'data-indent';
export const EMPTY_CSS_VALUE = new Set(['', '0%', '0pt', '0px']);
const ALIGN_PATTERN = /(left|right|center|justify)/;
function convertMarginLeftToIndentValue(marginLeft: string): number {
const ptValue = convertToCSSPTValue(marginLeft);
return clamp(MIN_INDENT_LEVEL, Math.floor(ptValue / INDENT_MARGIN_PT_SIZE), MAX_INDENT_LEVEL);
}
function getAttrs(dom: HTMLElement): Object {
const { lineHeight, textAlign, marginLeft, paddingTop, paddingBottom } = dom.style;
let align = dom.getAttribute('align') || textAlign || '';
align = ALIGN_PATTERN.test(align) ? align : '';
let indent = parseInt(dom.getAttribute(ATTRIBUTE_INDENT) || '', 10);
if (!indent && marginLeft) {
indent = convertMarginLeftToIndentValue(marginLeft);
}
indent = indent || MIN_INDENT_LEVEL;
const lineSpacing = lineHeight ? toCSSLineSpacing(lineHeight) : null;
const id = dom.getAttribute('id') || '';
return { align, indent, lineSpacing, paddingTop, paddingBottom, id };
}
function toDOM(node: Node): DOMOutputSpec {
const { align, indent, inset, lineSpacing, paddingTop, paddingBottom, id } = node.attrs;
const attrs: { [key: string]: any } | null = {};
let style = '';
if (align && align !== 'left') {
style += `text-align: ${align};`;
}
if (lineSpacing) {
const cssLineSpacing = toCSSLineSpacing(lineSpacing);
style +=
`line-height: ${cssLineSpacing};` +
// This creates the local css variable `--czi-content-line-height`
// that its children may apply.
`--czi-content-line-height: ${cssLineSpacing}`;
}
if (paddingTop && !EMPTY_CSS_VALUE.has(paddingTop)) {
style += `padding-top: ${paddingTop};`;
}
if (paddingBottom && !EMPTY_CSS_VALUE.has(paddingBottom)) {
style += `padding-bottom: ${paddingBottom};`;
}
if (indent) {
style += `text-indent: ${indent}; padding-left: ${indent < 0 ? -indent : undefined};`;
}
if (inset) {
style += `margin-left: ${inset}; margin-right: ${inset};`;
}
style && (attrs.style = style);
if (indent) {
attrs[ATTRIBUTE_INDENT] = String(indent);
}
if (id) {
attrs.id = id;
}
return ['p', attrs, 0];
}
// https://github.com/ProseMirror/prosemirror-schema-basic/blob/master/src/schema-basic.js
// :: NodeSpec A plain paragraph textblock. Represented in the DOM
// as a `<p>` element.
export const ParagraphNodeSpec: NodeSpec = {
attrs: {
align: { default: null },
color: { default: null },
id: { default: null },
indent: { default: null },
inset: { default: null },
lineSpacing: { default: null },
// TODO: Add UI to let user edit / clear padding.
paddingBottom: { default: null },
// TODO: Add UI to let user edit / clear padding.
paddingTop: { default: null },
},
content: 'inline*',
group: 'block',
parseDOM: [{ tag: 'p', getAttrs }],
toDOM,
};
export const toParagraphDOM = toDOM;
export const getParagraphNodeAttrs = getAttrs;
export default ParagraphNodeSpec;
|