diff options
Diffstat (limited to 'src/client/util')
-rw-r--r-- | src/client/util/DropConverter.ts | 8 | ||||
-rw-r--r-- | src/client/util/RichTextRules.ts | 62 | ||||
-rw-r--r-- | src/client/util/RichTextSchema.tsx | 97 |
3 files changed, 96 insertions, 71 deletions
diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 9c068d2d7..8d92de28f 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -4,7 +4,9 @@ import { DocumentType } from "../documents/DocumentTypes"; import { ObjectField } from "../../new_fields/ObjectField"; import { StrCast } from "../../new_fields/Types"; import { Docs } from "../documents/Documents"; -import { ScriptField } from "../../new_fields/ScriptField"; +import { ScriptField, ComputedField } from "../../new_fields/ScriptField"; +import { RichTextField } from "../../new_fields/RichTextField"; +import { Compute } from "google-auth-library"; export function makeTemplate(doc: Doc): boolean { const layoutDoc = doc.layout instanceof Doc && doc.layout.isTemplateForField ? doc.layout : doc; @@ -18,6 +20,10 @@ export function makeTemplate(doc: Doc): boolean { Doc.MakeMetadataFieldTemplate(d, Doc.GetProto(layoutDoc)); } else if (d.type === DocumentType.COL) { any = makeTemplate(d) || any; + } else if (d.data instanceof RichTextField) { + d._textTemplate = ComputedField.MakeFunction("copyField(this.data)", { this: Doc.name }); + d.isTemplateForField = "data"; + any = true; } }); return any; diff --git a/src/client/util/RichTextRules.ts b/src/client/util/RichTextRules.ts index bc8a0abb1..0de0e21fd 100644 --- a/src/client/util/RichTextRules.ts +++ b/src/client/util/RichTextRules.ts @@ -2,14 +2,15 @@ import { textblockTypeInputRule, smartQuotes, emDash, ellipsis, InputRule } from import { schema } from "./RichTextSchema"; import { wrappingInputRule } from "./prosemirrorPatches"; import { NodeSelection, TextSelection } from "prosemirror-state"; -import { StrCast, Cast } from "../../new_fields/Types"; -import { Doc } from "../../new_fields/Doc"; +import { StrCast, Cast, NumCast } from "../../new_fields/Types"; +import { Doc, DataSym } from "../../new_fields/Doc"; import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; import { Docs, DocUtils } from "../documents/Documents"; import { Id } from "../../new_fields/FieldSymbols"; import { DocServer } from "../DocServer"; import { returnFalse, Utils } from "../../Utils"; import RichTextMenu from "./RichTextMenu"; +import { RichTextField } from "../../new_fields/RichTextField"; export const inpRules = { rules: [ @@ -70,36 +71,26 @@ export const inpRules = { return state.tr.deleteRange(start, end).addStoredMark(schema.marks.pFontSize.create({ fontSize: size })); }), - // make current selection a hyperlink portal (assumes % was used to initiate an EnteringStyle mode) + // create a text display of a metadata field new InputRule( - new RegExp(/!$/), + new RegExp(/\[\[([a-zA-Z_ \-0-9]+)\]\]$/), (state, match, start, end) => { - if (state.selection.to === state.selection.from || !(schema as any).EnteringStyle) return null; - const value = state.doc.textBetween(start, end); - - const node = (state.doc.resolve(start) as any).nodeAfter; - const sm = state.storedMarks || undefined; - const fieldView = state.schema.nodes.dashField.create({ fieldKey: StrCast(value) }); - const replaced = node ? state.tr.replaceRangeWith(start, end, fieldView).setStoredMarks([...node.marks, ...(sm ? sm : [])]) : state.tr; - return replaced.doc.nodeSize > end - 2 ? replaced.setSelection(new TextSelection(replaced.doc.resolve(end - 2))) : replaced; + const fieldKey = match[1]; + const fieldView = state.schema.nodes.dashField.create({ fieldKey: StrCast(fieldKey) }); + return state.tr.deleteRange(start, end).insert(start, fieldView); }), - // make current selection a hyperlink portal (assumes % was used to initiate an EnteringStyle mode) + // create a hyperlink portal new InputRule( - new RegExp(/@$/), + new RegExp(/\[\[:([a-zA-Z_ \-0-9]+)\]\]$/), (state, match, start, end) => { - if (state.selection.to === state.selection.from || !(schema as any).EnteringStyle) return null; - - const value = state.doc.textBetween(start, end); - if (value) { - DocServer.GetRefField(value).then(docx => { - const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([], { title: value, _width: 500, _height: 500, }, value); - DocUtils.Publish(target, value, returnFalse, returnFalse); - DocUtils.MakeLink({ doc: (schema as any).Document }, { doc: target }, "portal link", ""); - }); - const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + value), location: "onRight", title: value, targetId: value }); - return state.tr.addMark(start, end, link); - } - return state.tr; + const docId = match[1].substring(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", ""); + }); + const link = state.schema.marks.link.create({ href: Utils.prepend("/doc/" + docId), location: "onRight", title: docId, targetId: docId }); + return state.tr.addMark(start, end, link); }), // stop using active style new InputRule( @@ -209,10 +200,21 @@ export const inpRules = { new InputRule( new RegExp(/%#$/), (state, match, start, end) => { - const target = Docs.Create.TextDocument("", { _width: 75, _height: 35, backgroundColor: "yellow", annotationOn: FormattedTextBox.FocusedBox!.dataDoc, _autoHeight: true, fontSize: 9, title: "inline comment" }); + const textDoc = Doc.GetProto(Cast((schema as any).Document[DataSym], Doc, null)!); + const numInlines = NumCast(textDoc.inlineTextCount); + textDoc.inlineTextCount = numInlines + 1; + const inlineFieldKey = "inline" + numInlines; + const textDocInline = Docs.Create.TextDocument("", { _width: 75, _height: 35, backgroundColor: "yellow", annotationOn: textDoc, _autoHeight: true, fontSize: 9, title: "inline comment" }); + textDocInline.layoutKey = "layout_" + inlineFieldKey; + textDocInline.customTitle = true; + textDocInline.title = "inline"; + textDocInline.isTemplateForField = inlineFieldKey; + textDocInline.proto = textDoc; + textDoc[textDocInline.layoutKey] = FormattedTextBox.LayoutString(inlineFieldKey); + textDoc[inlineFieldKey] = "-inline-"; const node = (state.doc.resolve(start) as any).nodeAfter; - const newNode = schema.nodes.dashComment.create({ docid: target[Id] }); - const dashDoc = schema.nodes.dashDoc.create({ width: 75, height: 35, title: "dashDoc", docid: target[Id], float: "right" }); + 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; diff --git a/src/client/util/RichTextSchema.tsx b/src/client/util/RichTextSchema.tsx index 21916e3d6..0deedbe39 100644 --- a/src/client/util/RichTextSchema.tsx +++ b/src/client/util/RichTextSchema.tsx @@ -18,6 +18,7 @@ import { Transform } from "./Transform"; import React = require("react"); import { BoolCast, NumCast, StrCast } from "../../new_fields/Types"; import { FormattedTextBox } from "../views/nodes/FormattedTextBox"; +import { ObjectField } from "../../new_fields/ObjectField"; const blockquoteDOM: DOMOutputSpecArray = ["blockquote", 0], hrDOM: DOMOutputSpecArray = ["hr"], preDOM: DOMOutputSpecArray = ["pre", ["code", 0]], brDOM: DOMOutputSpecArray = ["br"], ulDOM: DOMOutputSpecArray = ["ul", 0]; @@ -682,7 +683,7 @@ export class DashDocCommentView { if (target) { const expand = target.hidden; const tr = view.state.tr.setNodeMarkup(target.pos, undefined, { ...target.node.attrs, hidden: target.node.attrs.hidden ? false : true }); - view.dispatch(tr.setSelection(TextSelection.create(tr.doc, getPos() + (expand ? 2 : 1)))); // update the attrs + view.dispatch(tr.setSelection(TextSelection.create(tr.doc, getPos() + (expand ? 2 : 1)))); // update the attrs setTimeout(() => { expand && DocServer.GetRefField(node.attrs.docid).then(async dashDoc => dashDoc instanceof Doc && Doc.linkFollowHighlight(dashDoc)); try { view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.tr.doc, getPos() + (expand ? 2 : 1)))); } catch (e) { } @@ -734,12 +735,6 @@ export class DashDocView { this._dashSpan.style.height = node.attrs.height; this._dashSpan.style.position = "absolute"; this._dashSpan.style.display = "inline-block"; - const removeDoc = () => { - const pos = getPos(); - const ns = new NodeSelection(view.state.doc.resolve(pos)); - view.dispatch(view.state.tr.setSelection(ns).deleteSelection()); - return true; - }; this._dashSpan.onpointerleave = () => { const ele = document.getElementById("DashDocCommentView-" + node.attrs.docid); if (ele) { @@ -752,46 +747,24 @@ export class DashDocView { (ele as HTMLDivElement).style.backgroundColor = "orange"; } }; + const removeDoc = () => { + const pos = getPos(); + const ns = new NodeSelection(view.state.doc.resolve(pos)); + 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") { + 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" })); + view.dispatch(view.state.tr.setNodeMarkup(getPos(), null, { ...node.attrs, width: dashDoc._width + "px", height: dashDoc._height + "px" })); } catch (e) { console.log(e); } } - this._reactionDisposer && this._reactionDisposer(); - this._reactionDisposer = reaction(() => dashDoc[HeightSym]() + dashDoc[WidthSym](), () => { - this._dashSpan.style.height = this._outer.style.height = dashDoc[HeightSym]() + "px"; - this._dashSpan.style.width = this._outer.style.width = dashDoc[WidthSym]() + "px"; - }); - ReactDOM.render(<DocumentView - Document={dashDoc} - LibraryPath={tbox.props.LibraryPath} - fitToBox={BoolCast(dashDoc._fitToBox)} - addDocument={returnFalse} - removeDocument={removeDoc} - ScreenToLocalTransform={this.getDocTransform} - addDocTab={self._textBox.props.addDocTab} - pinToPres={returnFalse} - renderDepth={1} - PanelWidth={self._dashDoc[WidthSym]} - PanelHeight={self._dashDoc[HeightSym]} - focus={self.outerFocus} - backgroundColor={returnEmptyString} - parentActive={returnFalse} - whenActiveChanged={returnFalse} - bringToFront={emptyFunction} - zoomToScale={emptyFunction} - getScale={returnOne} - dontRegisterView={false} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - ContentScaling={this.contentScaling} - />, this._dashSpan); + self.doRender(dashDoc, removeDoc); } }); const self = this; @@ -807,6 +780,49 @@ export class DashDocView { this._outer.appendChild(this._dashSpan); (this as any).dom = this._outer; } + doRender(dashDoc: Doc, removeDoc: any) { + const self = this; + const finalLayout = Doc.expandTemplateLayout(dashDoc, this._textBox.dataDoc); + if (!finalLayout) setTimeout(() => self.doRender(dashDoc, removeDoc), 0); + else { + const layoutKey = StrCast(finalLayout.layoutKey); + const finalKey = layoutKey && StrCast(finalLayout[layoutKey]).split("'")?.[1]; + if (finalLayout !== dashDoc && finalKey) { + const finalLayoutField = finalLayout[finalKey] + finalLayoutField instanceof ObjectField && (finalLayout._textTemplate = ObjectField.MakeCopy(finalLayoutField)); + } + this._reactionDisposer && this._reactionDisposer(); + this._reactionDisposer = reaction(() => [finalLayout[WidthSym](), finalLayout[HeightSym]()], (dim) => { + this._dashSpan.style.width = this._outer.style.width = dim[0] + "px"; + this._dashSpan.style.height = this._outer.style.height = dim[1] + "px"; + }, { fireImmediately: true }); + ReactDOM.render(<DocumentView + Document={finalLayout} + DataDoc={this._textBox.dataDoc} + LibraryPath={this._textBox.props.LibraryPath} + fitToBox={BoolCast(dashDoc._fitToBox)} + addDocument={returnFalse} + removeDocument={removeDoc} + ScreenToLocalTransform={this.getDocTransform} + addDocTab={this._textBox.props.addDocTab} + pinToPres={returnFalse} + renderDepth={1} + PanelWidth={finalLayout[WidthSym]} + PanelHeight={finalLayout[HeightSym]} + focus={this.outerFocus} + backgroundColor={returnEmptyString} + parentActive={returnFalse} + whenActiveChanged={returnFalse} + bringToFront={emptyFunction} + zoomToScale={emptyFunction} + getScale={returnOne} + dontRegisterView={false} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + ContentScaling={this.contentScaling} + />, this._dashSpan); + } + } destroy() { this._reactionDisposer && this._reactionDisposer(); } @@ -837,8 +853,9 @@ export class DashFieldView { this._labelSpan.style.fontWeight = "bold"; this._labelSpan.style.fontSize = "larger"; this._labelSpan.innerHTML = `${node.attrs.fieldKey}: `; + const ddoc = tbox.props.DataDoc || tbox.dataDoc; this._reactionDisposer && this._reactionDisposer(); - this._reactionDisposer = reaction(() => this._textBoxDoc[DataSym][node.attrs.fieldKey], fval => this._fieldSpan.innerHTML = Field.toString(fval as Field), { fireImmediately: true }); + this._reactionDisposer = reaction(() => ddoc[node.attrs.fieldKey], fval => this._fieldSpan.innerHTML = Field.toString(fval as Field), { fireImmediately: true }); this._fieldWrapper.appendChild(this._labelSpan); this._fieldWrapper.appendChild(this._fieldSpan); (this as any).dom = this._fieldWrapper; @@ -1012,7 +1029,7 @@ export class SummaryView { view.dispatch(view.state.tr. setSelection(textSelection). // select the current summarized text (or where it will be if its collapsed) replaceSelection(!visible ? new Slice(Fragment.fromArray([]), 0, 0) : node.attrs.text). // collapse/expand it - setNodeMarkup(getPos(), undefined, attrs)); // update the attrs + setNodeMarkup(getPos(), undefined, attrs)); // update the attrs e.preventDefault(); e.stopPropagation(); this._collapsed.className = this.className(visible); |