aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSam Wilkins <samwilkins333@gmail.com>2019-08-31 15:12:22 -0400
committerSam Wilkins <samwilkins333@gmail.com>2019-08-31 15:12:22 -0400
commit420b17379afe3e3ba2c17628fd00ff524ec1a743 (patch)
treea03c291bb7631ffcba22bd012992f3dedb33c182
parente139441a1f3bbec9a51ef8594a9c785733d28415 (diff)
mostly functional bullet structure import
-rw-r--r--src/client/apis/google_docs/GoogleApiClientUtils.ts2
-rw-r--r--src/new_fields/RichTextUtils.ts84
2 files changed, 79 insertions, 7 deletions
diff --git a/src/client/apis/google_docs/GoogleApiClientUtils.ts b/src/client/apis/google_docs/GoogleApiClientUtils.ts
index 3026f6e17..828d4451a 100644
--- a/src/client/apis/google_docs/GoogleApiClientUtils.ts
+++ b/src/client/apis/google_docs/GoogleApiClientUtils.ts
@@ -123,7 +123,7 @@ export namespace GoogleApiClientUtils {
bullet = element.paragraph.bullet.nestingLevel || 0;
}
}
- runs.length && fragments.push({ runs, bullet });
+ (runs.length || !filterEmpty) && fragments.push({ runs, bullet });
}
}
return fragments;
diff --git a/src/new_fields/RichTextUtils.ts b/src/new_fields/RichTextUtils.ts
index 4d40040ac..5a16227ef 100644
--- a/src/new_fields/RichTextUtils.ts
+++ b/src/new_fields/RichTextUtils.ts
@@ -1,4 +1,4 @@
-import { EditorState } from "prosemirror-state";
+import { EditorState, Transaction, TextSelection } from "prosemirror-state";
import { Node, Fragment, Mark } from "prosemirror-model";
import { RichTextField } from "./RichTextField";
import { docs_v1 } from "googleapis";
@@ -6,6 +6,8 @@ import { GoogleApiClientUtils } from "../client/apis/google_docs/GoogleApiClient
import { FormattedTextBox } from "../client/views/nodes/FormattedTextBox";
import { Opt } from "./Doc";
import * as Color from "color";
+import { sinkListItem } from "prosemirror-schema-list";
+import { number } from "prop-types";
export namespace RichTextUtils {
@@ -96,6 +98,8 @@ export namespace RichTextUtils {
};
};
+ type BulletPosition = { value: number, sinks: number };
+
export const Import = async (documentId: GoogleApiClientUtils.Docs.DocumentId): Promise<Opt<GoogleApiClientUtils.Docs.ImportResult>> => {
const document = await GoogleApiClientUtils.Docs.retrieve({ documentId });
if (!document) {
@@ -105,17 +109,85 @@ export namespace RichTextUtils {
const title = document.title!;
const { text, paragraphs } = GoogleApiClientUtils.Docs.Utils.extractText(document);
let state = FormattedTextBox.blankState();
+ let structured = parseLists(paragraphs);
- const nodes = paragraphs.map(paragraph => paragraphNode(state.schema, paragraph));
+ let position = 3;
+ let lists: ListGroup[] = [];
+ const indentMap = new Map<ListGroup, BulletPosition[]>();
+ let globalOffset = 0;
+ const nodes = structured.map(element => {
+ if (Array.isArray(element)) {
+ lists.push(element);
+ let positions: BulletPosition[] = [];
+ let items = element.map(paragraph => {
+ let item = listItem(state.schema, paragraph.runs);
+ let sinks = paragraph.bullet!;
+ positions.push({
+ value: position + globalOffset,
+ sinks
+ });
+ position += item.nodeSize;
+ globalOffset += 2 * sinks;
+ return item;
+ });
+ indentMap.set(element, positions);
+ return list(state.schema, items);
+ } else {
+ let paragraph = paragraphNode(state.schema, element.runs);
+ position += paragraph.nodeSize;
+ return paragraph;
+ }
+ });
state = state.apply(state.tr.replaceWith(0, 2, nodes));
+ let sink = sinkListItem(state.schema.nodes.list_item);
+ let dispatcher = (tr: Transaction) => state = state.apply(tr);
+ for (let list of lists) {
+ for (let pos of indentMap.get(list)!) {
+ let resolved = state.doc.resolve(pos.value);
+ state = state.apply(state.tr.setSelection(new TextSelection(resolved)));
+ for (let i = 0; i < pos.sinks; i++) {
+ sink(state, dispatcher);
+ }
+ }
+ }
+
return { title, text, state };
};
- const paragraphNode = (schema: any, content: GoogleApiClientUtils.Docs.Utils.DeconstructedParagraph) => {
- let children = content.runs.map(run => textNode(schema, run));
- let complete = children.every(child => child !== undefined);
- let fragment = complete ? Fragment.from(children) : undefined;
+ type Paragraph = GoogleApiClientUtils.Docs.Utils.DeconstructedParagraph;
+ type ListGroup = Paragraph[];
+ type PreparedParagraphs = (ListGroup | Paragraph)[];
+
+ const parseLists = (paragraphs: ListGroup) => {
+ let groups: PreparedParagraphs = [];
+ let group: ListGroup = [];
+ for (let paragraph of paragraphs) {
+ if (paragraph.bullet !== undefined) {
+ group.push(paragraph);
+ } else {
+ if (group.length) {
+ groups.push(group);
+ group = [];
+ }
+ groups.push(paragraph);
+ }
+ }
+ group.length && groups.push(group);
+ return groups;
+ };
+
+ const listItem = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => {
+ return schema.node("list_item", null, paragraphNode(schema, runs));
+ };
+
+ const list = (schema: any, items: Node[]): Node => {
+ return schema.node("bullet_list", null, items);
+ };
+
+ const paragraphNode = (schema: any, runs: docs_v1.Schema$TextRun[]): Node => {
+ let children = runs.map(run => textNode(schema, run)).filter(child => child !== undefined);
+ let fragment = children.length ? Fragment.from(children) : undefined;
return schema.node("paragraph", null, fragment);
};