diff options
author | bob <bcz@cs.brown.edu> | 2020-01-30 10:40:40 -0500 |
---|---|---|
committer | bob <bcz@cs.brown.edu> | 2020-01-30 10:40:40 -0500 |
commit | e5312b1b568512d744dd9e4829a3ece12620cbbc (patch) | |
tree | f180c836f4625ed7b5d7c6322a53c1fe3792542b /src | |
parent | 03c97e178c7c768cd37f24043d1804816fab6253 (diff) |
changed syntax to [[(doc)?(:field]] for embedding doc as text blocks. added {{doc}} for embedding a document
Diffstat (limited to 'src')
-rw-r--r-- | src/client/util/RichTextRules.ts | 90 | ||||
-rw-r--r-- | src/client/util/RichTextSchema.tsx | 37 | ||||
-rw-r--r-- | src/client/views/DocumentButtonBar.tsx | 2 | ||||
-rw-r--r-- | src/client/views/TemplateMenu.tsx | 20 | ||||
-rw-r--r-- | src/scraping/buxton/scraper.py | 5 |
5 files changed, 83 insertions, 71 deletions
diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts index 38cd5ca5c..00ce3ef73 100644 --- a/src/client/util/RichTextRules.ts +++ b/src/client/util/RichTextRules.ts @@ -72,32 +72,63 @@ export const inpRules = { return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: size })); }), - // create a text display of a metadata field + // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document new InputRule( - new RegExp(/\[\[([a-zA-Z_ \-0-9]+)\]\]$/), + new RegExp(/\[\[([a-zA-Z_ \-0-9]*)(:[a-zA-Z_ \-0-9]+)?\]\]$/), (state, match, start, end) => { - const fieldView = state.schema.nodes.dashField.create({ fieldKey: match[1] }); - return state.tr.deleteRange(start, end).insert(start, fieldView); - }), - // create a text display of a metadata field on another document - new InputRule( - new RegExp(/\[\[([a-zA-Z_ \-0-9]+):([a-zA-Z_ \-0-9]+)\]\]$/), - (state, match, start, end) => { - const fieldView = state.schema.nodes.dashField.create({ fieldKey: match[2], docid: match[1] }); + if (!match[2]) { + const docId = match[1]; + DocServer.GetRefField(docId).then(docx => { + const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docId, _width: 500, _height: 500, _LODdisable: true, }, docId); + DocUtils.Publish(target, docId, returnFalse, returnFalse); + DocUtils.MakeLink({ doc: (schema as any).Document }, { doc: target }, "portal link", ""); + }); + const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + docId), location: "onRight", title: docId, targetId: docId }); + return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 2).addMark(start, end - 3, link); + } + const fieldView = state.schema.nodes.dashField.create({ fieldKey: match[2]?.substring(1), docid: match[1] }); return state.tr.deleteRange(start, end).insert(start, fieldView); }), - // create a hyperlink portal + // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document new InputRule( - new RegExp(/@@([a-zA-Z_ \-0-9]+)@@$/), + new RegExp(/\{\{([a-zA-Z_ \-0-9]*)(:[a-zA-Z_ \-0-9]+)?\}\}$/), (state, match, start, end) => { const docId = match[1]; DocServer.GetRefField(docId).then(docx => { - const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: docId, _width: 500, _height: 500, }, docId); - DocUtils.Publish(target, docId, returnFalse, returnFalse); - DocUtils.MakeLink({ doc: (schema as any).Document }, { doc: target }, "portal link", ""); + if (!(docx instanceof Doc && docx)) { + const docx = Docs.Create.FreeformDocument([], { title: docId, _width: 500, _height: 500, _LODdisable: true }, docId); + DocUtils.Publish(docx, docId, returnFalse, returnFalse); + } }); - const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + docId), location: "onRight", title: docId, targetId: docId }); - return state.tr.deleteRange(end - 1, end).deleteRange(start, start + 2).addMark(start, end - 3, link); + const node = (state.doc.resolve(start) as any).nodeAfter; + const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 75, title: "dashDoc", docid: docId, float: "right", fieldKey: match[2], alias: Utils.GenerateGuid() }); + const sm = state.storedMarks || undefined; + return node ? state.tr.replaceRangeWith(start, end, dashDoc).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr; + }), + new InputRule( + new RegExp(/##$/), + (state, match, start, end) => { + const schemaDoc = Doc.GetDataDoc((schema as any).Document); + const textDoc = Doc.GetProto(Cast(schemaDoc[DataSym], Doc, null)!); + const numInlines = NumCast(textDoc.inlineTextCount); + textDoc.inlineTextCount = numInlines + 1; + const inlineFieldKey = "inline" + numInlines; // which field on the text document this annotation will write to + const inlineLayoutKey = "layout_" + inlineFieldKey; // the field holding the layout string that will render the inline annotation + const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, fontSize: 9, title: "inline comment" }); + textDocInline.title = inlineFieldKey; // give the annotation its own title + textDocInline.customTitle = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc + textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point + textDocInline.proto = textDoc; // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]] + textDocInline._textContext = ComputedField.MakeFunction(`copyField(this.${inlineFieldKey})`, { this: Doc.name }); + textDoc[inlineLayoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); // create a layout string for the layout key that will render the annotation text + textDoc[inlineFieldKey] = ""; // set a default value for the annotation + const node = (state.doc.resolve(start) as any).nodeAfter; + const newNode = schema.nodes.dashComment.create({ docid: textDocInline[Id] }); + const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: textDocInline[Id], float: "right" }); + const sm = state.storedMarks || undefined; + const replaced = node ? state.tr.insert(start, newNode).replaceRangeWith(start + 1, end + 1, dashDoc).insertText(" ", start + 2).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : + state.tr; + return replaced; }), // stop using active style new InputRule( @@ -205,31 +236,6 @@ export const inpRules = { return replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2))); }), new InputRule( - new RegExp(/##$/), - (state, match, start, end) => { - const schemaDoc = Doc.GetDataDoc((schema as any).Document); - const textDoc = Doc.GetProto(Cast(schemaDoc[DataSym], Doc, null)!); - const numInlines = NumCast(textDoc.inlineTextCount); - textDoc.inlineTextCount = numInlines + 1; - const inlineFieldKey = "inline" + numInlines; // which field on the text document this annotation will write to - const inlineLayoutKey = "layout_" + inlineFieldKey; // the field holding the layout string that will render the inline annotation - const textDocInline = Docs.Create.TextDocument("", { layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, fontSize: 9, title: "inline comment" }); - textDocInline.title = inlineFieldKey; // give the annotation its own title - textDocInline.customTitle = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc - textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point - textDocInline.proto = textDoc; // make the annotation inherit from the outer text doc so that it can resolve any nested field references, e.g., [[field]] - textDocInline._textContext = ComputedField.MakeFunction(`copyField(this.${inlineFieldKey})`, { this: Doc.name }); - textDoc[inlineLayoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); // create a layout string for the layout key that will render the annotation text - textDoc[inlineFieldKey] = ""; // set a default value for the annotation - const node = (state.doc.resolve(start) as any).nodeAfter; - const newNode = schema.nodes.dashComment.create({ docid: textDocInline[Id] }); - const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: textDocInline[Id], float: "right" }); - const sm = state.storedMarks || undefined; - const replaced = node ? state.tr.insert(start, newNode).replaceRangeWith(start + 1, end + 1, dashDoc).insertText(" ", start + 2).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : - state.tr; - return replaced; - }), - new InputRule( new RegExp(/%\(/), (state, match, start, end) => { const node = (state.doc.resolve(start) as any).nodeAfter; diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index f14e09a40..d1e0f07cb 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -168,7 +168,9 @@ export const nodes: { [index: string]: NodeSpec } = { float: { default: "right" }, location: { default: "onRight" }, hidden: { default: false }, + fieldKey: { default: "" }, docid: { default: "" }, + alias: { default: "" } }, group: "inline", draggable: false, @@ -756,18 +758,16 @@ export class DashDocView { view.dispatch(view.state.tr.setSelection(ns).deleteSelection()); return true; }; - DocServer.GetRefField(node.attrs.docid).then(async dashDoc => { - if (dashDoc instanceof Doc) { - self._dashDoc = dashDoc; - dashDoc._hideSidebar = true; - if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") { - try { // bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made - view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: dashDoc._width + "px", height: dashDoc._height + "px" })); - } catch (e) { - console.log(e); + const alias = node.attrs.alias; + DocServer.GetRefField(node.attrs.docid + alias).then(async dashDoc => { + if (!(dashDoc instanceof Doc)) { + alias && DocServer.GetRefField(node.attrs.docid).then(async dashDocBase => { + if (dashDocBase instanceof Doc) { + self.doRender(Doc.MakeAlias(dashDocBase), removeDoc, node, view, getPos); } - } - self.doRender(dashDoc, removeDoc); + }); + } else { + self.doRender(dashDoc, removeDoc, node, view, getPos); } }); const self = this; @@ -783,10 +783,19 @@ export class DashDocView { this._outer.appendChild(this._dashSpan); (this as any).dom = this._outer; } - doRender(dashDoc: Doc, removeDoc: any) { + doRender(dashDoc: Doc, removeDoc: any, node: any, view: any, getPos: any) { + this._dashDoc = dashDoc; + dashDoc._hideSidebar = true; + if (node.attrs.width !== dashDoc._width + "px" || node.attrs.height !== dashDoc._height + "px") { + try { // bcz: an exception will be thrown if two aliases are open at the same time when a doc view comment is made + view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: dashDoc._width + "px", height: dashDoc._height + "px" })); + } catch (e) { + console.log(e); + } + } const self = this; const finalLayout = Doc.expandTemplateLayout(dashDoc, !Doc.AreProtosEqual(this._textBox.dataDoc, this._textBox.Document) ? this._textBox.dataDoc : undefined); - if (!finalLayout) setTimeout(() => self.doRender(dashDoc, removeDoc), 0); + if (!finalLayout) setTimeout(() => self.doRender(dashDoc, removeDoc, node, view, getPos), 0); else { const layoutKey = StrCast(finalLayout.layoutKey); const finalKey = layoutKey && StrCast(finalLayout[layoutKey]).split("'")?.[1]; @@ -803,7 +812,7 @@ export class DashDocView { }, { fireImmediately: true }); ReactDOM.render(<DocumentView Document={finalLayout} - DataDoc={this._textBox.dataDoc} + DataDoc={Doc.AreProtosEqual(this._textBox.Document, dashDoc) ? this._textBox.dataDoc : undefined} LibraryPath={this._textBox.props.LibraryPath} fitToBox={BoolCast(dashDoc._fitToBox)} addDocument={returnFalse} diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 6abb7108f..65d1ade2a 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -280,7 +280,7 @@ export class DocumentButtonBar extends React.Component<{ views: (DocumentView | templates.set(template, this.props.views.reduce((checked, doc) => checked || doc?.getLayoutPropStr("show" + template.Name) ? true : false, false as boolean))); return !view0 ? (null) : <div title="Customize layout" className="documentButtonBar-linkFlyout" ref={this._dragRef}> <Flyout anchorPoint={anchorPoints.LEFT_TOP} - content={<TemplateMenu docs={this.props.views.filter(v => v).map(v => v as DocumentView)} templates={templates} />}> + content={<TemplateMenu docViews={this.props.views.filter(v => v).map(v => v as DocumentView)} templates={templates} />}> <div className={"documentButtonBar-linkButton-" + "empty"} ref={this._dragRef} onPointerDown={this.onAliasButtonDown} > {<FontAwesomeIcon className="documentdecorations-icon" icon="edit" size="sm" />} </div> diff --git a/src/client/views/TemplateMenu.tsx b/src/client/views/TemplateMenu.tsx index 2f36d4fbd..d5554fd4f 100644 --- a/src/client/views/TemplateMenu.tsx +++ b/src/client/views/TemplateMenu.tsx @@ -41,7 +41,7 @@ class OtherToggle extends React.Component<{ checked: boolean, name: string, togg } export interface TemplateMenuProps { - docs: DocumentView[]; + docViews: DocumentView[]; templates: Map<Template, boolean>; } @@ -51,12 +51,12 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> { @observable private _hidden: boolean = true; toggleLayout = (e: React.ChangeEvent<HTMLInputElement>, layout: string): void => { - this.props.docs.map(dv => dv.setCustomView(e.target.checked, layout)); + this.props.docViews.map(dv => dv.setCustomView(e.target.checked, layout)); } toggleFloat = (e: React.ChangeEvent<HTMLInputElement>): void => { SelectionManager.DeselectAll(); - const topDocView = this.props.docs[0]; + const topDocView = this.props.docViews[0]; const ex = e.target.getBoundingClientRect().left; const ey = e.target.getBoundingClientRect().top; DocumentView.FloatDoc(topDocView, ex, ey); @@ -67,9 +67,9 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> { @action toggleTemplate = (event: React.ChangeEvent<HTMLInputElement>, template: Template): void => { if (event.target.checked) { - this.props.docs.map(d => d.Document["show" + template.Name] = template.Name.toLowerCase()); + this.props.docViews.map(d => d.Document["show" + template.Name] = template.Name.toLowerCase()); } else { - this.props.docs.map(d => d.Document["show" + template.Name] = ""); + this.props.docViews.map(d => d.Document["show" + template.Name] = ""); } } @@ -81,7 +81,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> { @undoBatch @action toggleChrome = (): void => { - this.props.docs.map(dv => { + this.props.docViews.map(dv => { const layout = Doc.Layout(dv.Document); layout._chromeStatus = (layout._chromeStatus !== "disabled" ? "disabled" : "enabled"); }); @@ -95,7 +95,7 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> { } componentDidMount() { !TemplateMenu._addedKeys && (TemplateMenu._addedKeys = new ObservableSet(["narrative"])); - Array.from(Object.keys(Doc.GetProto(this.props.docs[0].props.Document))). + Array.from(Object.keys(Doc.GetProto(this.props.docViews[0].props.Document))). filter(key => key.startsWith("layout_")). map(key => runInAction(() => TemplateMenu._addedKeys.add(key.replace("layout_", "")))); DocListCast(Cast(CurrentUserUtils.UserDocument.expandingButtons, Doc, null)?.data)?.map(btnDoc => { @@ -108,14 +108,14 @@ export class TemplateMenu extends React.Component<TemplateMenuProps> { static _addedKeys = new ObservableSet(["narrative"]); _customRef = React.createRef<HTMLInputElement>(); render() { - const layout = Doc.Layout(this.props.docs[0].Document); + const layout = Doc.Layout(this.props.docViews[0].Document); const templateMenu: Array<JSX.Element> = []; this.props.templates.forEach((checked, template) => templateMenu.push(<TemplateToggle key={template.Name} template={template} checked={checked} toggle={this.toggleTemplate} />)); - templateMenu.push(<OtherToggle key={"float"} name={"Float"} checked={this.props.docs[0].Document.z ? true : false} toggle={this.toggleFloat} />); + templateMenu.push(<OtherToggle key={"float"} name={"Float"} checked={this.props.docViews[0].Document.z ? true : false} toggle={this.toggleFloat} />); templateMenu.push(<OtherToggle key={"chrome"} name={"Chrome"} checked={layout._chromeStatus !== "disabled"} toggle={this.toggleChrome} />); TemplateMenu._addedKeys && Array.from(TemplateMenu._addedKeys).map(layout => - templateMenu.push(<OtherToggle key={layout} name={layout} checked={StrCast(this.props.docs[0].Document.layoutKey, "layout") === "layout_" + layout} toggle={e => this.toggleLayout(e, layout)} />) + templateMenu.push(<OtherToggle key={layout} name={layout} checked={StrCast(this.props.docViews[0].Document.layoutKey, "layout") === "layout_" + layout} toggle={e => this.toggleLayout(e, layout)} />) ); return <ul className="template-list" style={{ display: "block" }}> {templateMenu} diff --git a/src/scraping/buxton/scraper.py b/src/scraping/buxton/scraper.py index 7cfa4696d..ec9c3f72c 100644 --- a/src/scraping/buxton/scraper.py +++ b/src/scraping/buxton/scraper.py @@ -100,8 +100,6 @@ def write_collection(parse_results, display_fields, storage_key, viewType): fields["proto"] = protofy(common_proto_id) fields[storage_key] = listify(proxify_guids(view_guids)) fields["schemaColumns"] = listify(display_fields) - fields["backgroundColor"] = "white" - fields["_viewType"] = 2 fields["author"] = "Bill Buxton" fields["creationDate"] = { "date": datetime.datetime.utcnow().microsecond, @@ -113,7 +111,6 @@ def write_collection(parse_results, display_fields, storage_key, viewType): "__type": "image" } fields["isPrototype"] = True - fields["page"] = -1 target_collection.insert_one(data_doc) target_collection.insert_one(view_doc) @@ -409,7 +406,7 @@ parent_guid = write_collection({ "__type": "Doc" }, "child_guids": schema_guids -}, ["title", "short_description", "original_price"], "data", 4) +}, ["title", "short_description", "original_price"], "data", 2) print("appending parent schema to main workspace...\n") target_collection.update_one( |