From 5ed063e0e39cc1dbdffc21b162d1acff0db65ba1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 2 Oct 2020 21:35:45 -0400 Subject: cleaned up CollectionLinkView and fixed an issue with link lines flopping around --- .../CollectionFreeFormLinkView.tsx | 145 +++++++++------------ 1 file changed, 64 insertions(+), 81 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 9dcfde7f9..3e9c0b485 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -22,74 +22,63 @@ export class CollectionFreeFormLinkView extends React.Component (Date.now() < this._start++ + 1000) && (this._timeout = setTimeout(this.timeout, 25))); + componentWillUnmount() { this._anchorDisposer?.(); } + @action timeout = action(() => (Date.now() < this._start++ + 1000) && (this._timeout = setTimeout(this.timeout, 25))); componentDidMount() { - this._anchorDisposer = reaction(() => [this.props.A.props.ScreenToLocalTransform(), this.props.B.props.ScreenToLocalTransform(), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document), this.props.A.isSelected() || Doc.IsBrushed(this.props.A.props.Document)], + this._anchorDisposer = reaction(() => [this.props.A.props.ScreenToLocalTransform(), this.props.B.props.ScreenToLocalTransform()], action(() => { this._start = Date.now(); this._timeout && clearTimeout(this._timeout); this._timeout = setTimeout(this.timeout, 25); - if (SnappingManager.GetIsDragging() || !this.props.A.ContentDiv || !this.props.B.ContentDiv) return; - setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render() - setTimeout(action(() => (!this.props.LinkDocs.length || !this.props.LinkDocs[0].linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line. - const acont = this.props.A.props.Document.type === DocumentType.LINK ? this.props.A.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : []; - const bcont = this.props.B.props.Document.type === DocumentType.LINK ? this.props.B.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : []; - const adiv = (acont.length ? acont[0] : this.props.A.ContentDiv); - const bdiv = (bcont.length ? bcont[0] : this.props.B.ContentDiv); - const a = adiv.getBoundingClientRect(); - const b = bdiv.getBoundingClientRect(); - const abounds = adiv.parentElement!.getBoundingClientRect(); - const bbounds = bdiv.parentElement!.getBoundingClientRect(); - const apt = Utils.closestPtBetweenRectangles(abounds.left, abounds.top, abounds.width, abounds.height, - bbounds.left, bbounds.top, bbounds.width, bbounds.height, - a.left + a.width / 2, a.top + a.height / 2); - const bpt = Utils.closestPtBetweenRectangles(bbounds.left, bbounds.top, bbounds.width, bbounds.height, - abounds.left, abounds.top, abounds.width, abounds.height, - apt.point.x, apt.point.y); - const afield = this.props.A.props.LayoutTemplateString?.indexOf("anchor1") === -1 ? "anchor2" : "anchor1"; - const bfield = afield === "anchor1" ? "anchor2" : "anchor1"; - - // really hacky stuff to make the LinkAnchorBox display where we want it to: - // if there's an element in the DOM with a classname containing the link's id and a data-targetids attribute containing the other end of the link, - // then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right - // otherwise, we just use the computed nearest point on the document boundary to the target Document - const linkId = this.props.LinkDocs[0][Id]; // this link's Id - const AanchorId = (this.props.LinkDocs[0][afield] as Doc)[Id]; // anchor a's id - const BanchorId = (this.props.LinkDocs[0][bfield] as Doc)[Id]; // anchor b's id - const linkEles = Array.from(window.document.getElementsByClassName(linkId)); - const targetAhyperlink = linkEles.find((ele: any) => ele.dataset.targetids?.includes(AanchorId)); - const targetBhyperlink = linkEles.find((ele: any) => ele.dataset.targetids?.includes(BanchorId)); - if (!targetBhyperlink) { - this.props.A.rootDoc[afield + "_x"] = (apt.point.x - abounds.left) / abounds.width * 100; - this.props.A.rootDoc[afield + "_y"] = (apt.point.y - abounds.top) / abounds.height * 100; - } else { - setTimeout(() => { - (this.props.A.rootDoc[(this.props.A.props as any).fieldKey] as Doc); - const m = targetBhyperlink.getBoundingClientRect(); - const mp = this.props.A.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5); - this.props.A.rootDoc[afield + "_x"] = Math.min(1, mp[0] / this.props.A.props.PanelWidth()) * 100; - this.props.A.rootDoc[afield + "_y"] = Math.min(1, mp[1] / this.props.A.props.PanelHeight()) * 100; - }, 0); - } - if (!targetAhyperlink) { - this.props.A.rootDoc[bfield + "_x"] = (bpt.point.x - bbounds.left) / bbounds.width * 100; - this.props.A.rootDoc[bfield + "_y"] = (bpt.point.y - bbounds.top) / bbounds.height * 100; - } else { - setTimeout(() => { - (this.props.B.rootDoc[(this.props.B.props as any).fieldKey] as Doc); - const m = targetAhyperlink.getBoundingClientRect(); - const mp = this.props.B.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5); - this.props.B.rootDoc[bfield + "_x"] = Math.min(1, mp[0] / this.props.B.props.PanelWidth()) * 100; - this.props.B.rootDoc[bfield + "_y"] = Math.min(1, mp[1] / this.props.B.props.PanelHeight()) * 100; - }, 0); - } + setTimeout(this.placeAnchors); }) , { fireImmediately: true }); } + placeAnchors = () => { + const { A, B, LinkDocs } = this.props; + const linkDoc = LinkDocs[0]; + if (SnappingManager.GetIsDragging() || !A.ContentDiv || !B.ContentDiv) return; + setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render() + setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line. + const acont = A.props.Document.type === DocumentType.LINK ? A.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : []; + const bcont = B.props.Document.type === DocumentType.LINK ? B.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : []; + const adiv = (acont.length ? acont[0] : A.ContentDiv); + const bdiv = (bcont.length ? bcont[0] : B.ContentDiv); + const a = adiv.getBoundingClientRect(); + const b = bdiv.getBoundingClientRect(); + const { left: aleft, top: atop, width: awidth, height: aheight } = adiv.parentElement!.getBoundingClientRect(); + const { left: bleft, top: btop, width: bwidth, height: bheight } = bdiv.parentElement!.getBoundingClientRect(); + const apt = Utils.closestPtBetweenRectangles(aleft, atop, awidth, aheight, bleft, btop, bwidth, bheight, a.left + a.width / 2, a.top + a.height / 2); + const bpt = Utils.closestPtBetweenRectangles(bleft, btop, bwidth, bheight, aleft, atop, awidth, aheight, apt.point.x, apt.point.y); + const afield = A.props.LayoutTemplateString?.indexOf("anchor1") === -1 ? "anchor2" : "anchor1"; + const bfield = afield === "anchor1" ? "anchor2" : "anchor1"; + + // really hacky stuff to make the LinkAnchorBox display where we want it to: + // if there's an element in the DOM with a classname containing the link's id and a data-targetids attribute containing the other end of the link, + // then that DOM element is a hyperlink source for the current anchor and we want to place our link box at it's top right + // otherwise, we just use the computed nearest point on the document boundary to the target Document + const linkEles = Array.from(window.document.getElementsByClassName(linkDoc[Id])); + const targetAhyperlink = linkEles.find((ele: any) => ele.dataset.targetids?.includes((linkDoc[afield] as Doc)[Id])); + const targetBhyperlink = linkEles.find((ele: any) => ele.dataset.targetids?.includes((linkDoc[bfield] as Doc)[Id])); + if (!targetBhyperlink) { + A.rootDoc[afield + "_x"] = (apt.point.x - aleft) / awidth * 100; + A.rootDoc[afield + "_y"] = (apt.point.y - atop) / aheight * 100; + } else { + const m = targetBhyperlink.getBoundingClientRect(); + const mp = A.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5); + A.rootDoc[afield + "_x"] = Math.min(1, mp[0] / A.props.PanelWidth()) * 100; + A.rootDoc[afield + "_y"] = Math.min(1, mp[1] / A.props.PanelHeight()) * 100; + } + if (!targetAhyperlink) { + B.rootDoc[bfield + "_x"] = (bpt.point.x - bleft) / bwidth * 100; + B.rootDoc[bfield + "_y"] = (bpt.point.y - btop) / bheight * 100; + } else { + const m = targetAhyperlink.getBoundingClientRect(); + const mp = B.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5); + B.rootDoc[bfield + "_x"] = Math.min(1, mp[0] / B.props.PanelWidth()) * 100; + B.rootDoc[bfield + "_y"] = Math.min(1, mp[1] / B.props.PanelHeight()) * 100; + } + } pointerDown = (e: React.PointerEvent) => { @@ -138,14 +127,12 @@ export class CollectionFreeFormLinkView extends React.Component Date: Fri, 2 Oct 2020 21:40:38 -0400 Subject: from last --- .../views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index 3e9c0b485..f051d5f8d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -102,7 +102,7 @@ export class CollectionFreeFormLinkView extends React.Component Date: Sat, 3 Oct 2020 09:49:54 -0400 Subject: style fix for schema view to see dropdown list items in a cell --- src/client/views/collections/CollectionSchemaView.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss index 8d2f645d9..b0b5f995a 100644 --- a/src/client/views/collections/CollectionSchemaView.scss +++ b/src/client/views/collections/CollectionSchemaView.scss @@ -500,7 +500,7 @@ button.add-column { border: grey; border-style: solid; border-width: 1px; - height: 100%; + height: 30px; .collectionSchemaView-dropdownButton { -- cgit v1.2.3-70-g09d2 From 83aaea5320257ce15d68a5299c00977684ddab8e Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 6 Oct 2020 11:07:28 -0400 Subject: restored ability to open document that has been moved from another document's annotation overlay --- src/client/util/DocumentManager.ts | 8 +++++--- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 3 ++- src/client/views/nodes/FieldView.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 6 +++--- 4 files changed, 11 insertions(+), 8 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 2ca29cb7e..e2518d05f 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -193,11 +193,13 @@ export class DocumentManager { if (retryDocView) { // we found the target in the context retryDocView.props.focus(targetDoc, willZoom, undefined, focusAndFinish); // focus on the target in the context highlight(); - } - if (delay > 2500) { + } else if (delay > 1500) { // we didn't find the target, so it must have moved out of the context. Go back to just creating it. if (closeContextIfNotFound) targetDocContextView.props.removeDocument?.(targetDocContextView.props.Document); - // targetDoc.layout && createViewFunc(Doc.BrushDoc(targetDoc), finished); // create a new view of the target + if (targetDoc.layout) { + Doc.SetInPlace(targetDoc, "annotationOn", undefined, false); + createViewFunc(Doc.BrushDoc(targetDoc), finished); // create a new view of the target + } } else { setTimeout(() => findView(delay + 250), 250); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 2783011cf..fc3fd3a7c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -891,9 +891,10 @@ export class CollectionFreeFormView extends CollectionSubView { // ); } else if (field instanceof List) { - return
{field.length ? field.map(f => Field.toString(f)).join(", ") : "[]"}
; + return
{field.length ? field.map(f => Field.toString(f)).join(", ") : ""}
; } // bcz: this belongs here, but it doesn't render well so taking it out for now else if (field instanceof WebField) { diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 582e4ba84..4c5f72b97 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -180,9 +180,9 @@ export class PDFViewer extends ViewBoxAnnotatableComponent this._mainCont.current && smoothScroll(1000, this._mainCont.current, scrollY || 0), 250); } setTimeout(() => this.Document._scrollY = undefined, 1000); } -- cgit v1.2.3-70-g09d2 From 68916ee8daafb03ad2aff3338e18ca419ce888de Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 6 Oct 2020 12:15:49 -0400 Subject: fixed adding documents to check acl of added document(s) correctly --- src/client/views/DocComponent.tsx | 5 +++-- src/client/views/MainView.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 8 ++++---- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index b3fbe418b..d6f8c49df 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -123,8 +123,9 @@ export function ViewBoxAnnotatableComponent

effectiveAcl === AclEdit || effectiveAcl === AclAdmin || GetEffectiveAcl(doc) === AclAdmin); + if (docs.length) { const docs = doc instanceof Doc ? [doc] : doc; docs.map(doc => doc.isPushpin = doc.annotationOn = undefined); const targetDataDoc = this.dataDoc; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index cb84409cb..69354020b 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -86,7 +86,7 @@ export class MainView extends React.Component { document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!)); new InkStrokeProperties(); this._sidebarContent.proto = undefined; - DocServer.setPlaygroundFields(["dataTransition", "_delayAutoHeight", "_autoHeight", "_showSidebar", "_sidebarWidthPercent", "_width", "_height", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollY", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's + DocServer.setPlaygroundFields(["x", "y", "dataTransition", "_delayAutoHeight", "_autoHeight", "_showSidebar", "_sidebarWidthPercent", "_width", "_height", "_viewTransition", "_panX", "_panY", "_viewScale", "_scrollY", "_scrollTop", "hidden", "_curPage", "_viewType", "_chromeStatus"]); // can play with these fields on someone else's DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg))); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 2bdc8e2f3..0ef3dd6cd 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -157,7 +157,7 @@ export class CollectionView extends Touchable { + added.filter(doc => [AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))).map(doc => { // only make a pushpin if we have acl's to edit the document const context = Cast(doc.context, Doc, null); if (context && (context.type === DocumentType.VID || context.type === DocumentType.WEB || context.type === DocumentType.PDF || context.type === DocumentType.IMG)) { const pushpin = Docs.Create.FontIconDocument({ @@ -186,9 +186,9 @@ export class CollectionView extends Touchable { const effectiveAcl = GetEffectiveAcl(this.props.Document[DataSym]); - const docAcl = GetEffectiveAcl(doc); - if (effectiveAcl === AclEdit || effectiveAcl === AclAdmin || docAcl === AclAdmin) { - const docs = doc instanceof Doc ? [doc] : doc as Doc[]; + const indocs = doc instanceof Doc ? [doc] : doc as Doc[]; + const docs = indocs.filter(doc => effectiveAcl === AclEdit || effectiveAcl === AclAdmin || GetEffectiveAcl(doc) === AclAdmin); + if (docs.length) { const targetDataDoc = this.props.Document[DataSym]; const value = DocListCast(targetDataDoc[this.props.fieldKey]); const toRemove = value.filter(v => docs.includes(v)); -- cgit v1.2.3-70-g09d2 From e66380d8a99c3ee61faeaaebf6e28395332e1d64 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 6 Oct 2020 14:12:06 -0400 Subject: fixed list additions to be truly incremental to allow undo to work --- src/client/views/collections/CollectionView.tsx | 1 - src/fields/List.ts | 2 +- src/fields/util.ts | 28 ++++++++++++++++--------- src/server/websocket.ts | 10 +++++---- 4 files changed, 25 insertions(+), 16 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 0ef3dd6cd..80e9b41ad 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -196,7 +196,6 @@ export class CollectionView extends Touchable { const ind = (targetDataDoc[this.props.fieldKey] as List).indexOf(doc); - (targetDataDoc[this.props.fieldKey] as List).splice(ind, 0); if (ind !== -1) { Doc.RemoveDocFromList(targetDataDoc, this.props.fieldKey, doc); recent && Doc.AddDocToList(recent, "data", doc, undefined, true, true); diff --git a/src/fields/List.ts b/src/fields/List.ts index dca8d111c..d9bd54673 100644 --- a/src/fields/List.ts +++ b/src/fields/List.ts @@ -43,7 +43,7 @@ const listHandlers: any = { } } const res = list.__fields.push(...items); - this[Update]("$addToSet"); + this[Update]({ op: "$addToSet", items }); return res; }), reverse() { diff --git a/src/fields/util.ts b/src/fields/util.ts index 90446f531..b6128f5e6 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -11,6 +11,8 @@ import { ComputedField } from "./ScriptField"; import { ScriptCast, StrCast } from "./Types"; import { returnZero } from "../Utils"; import CursorField from "./CursorField"; +import { undo } from "prosemirror-history"; +import { List } from "./List"; function _readOnlySetter(): never { @@ -371,20 +373,26 @@ export function deleteProperty(target: any, prop: string | number | symbol) { export function updateFunction(target: any, prop: any, value: any, receiver: any) { let current = ObjectField.MakeCopy(value); return (diff?: any) => { - if (diff === "$addToSet") { - diff = { '$addToSet': { ["fields." + prop]: SerializationHelper.Serialize(value) } }; - } else { - diff = { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } }; - } + const op = diff?.op === "$addToSet" ? + { '$addToSet': { ["fields." + prop]: SerializationHelper.Serialize(new List(diff.items)) } } + : { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } }; const oldValue = current; const newValue = ObjectField.MakeCopy(value); current = newValue; if (!(value instanceof CursorField) && !(value?.some?.((v: any) => v instanceof CursorField))) { - UndoManager.AddEvent({ - redo() { receiver[prop] = newValue; }, - undo() { receiver[prop] = oldValue; } - }); + UndoManager.AddEvent(diff?.op === "$addToSet" ? + { + redo: () => receiver[prop].push(...(newValue as List)), + undo: action(() => (newValue as List).forEach(doc => { + const ind = receiver[prop].indexOf(doc); + ind !== -1 && receiver[prop].splice(ind, 1); + })) + } + : { + redo: () => receiver[prop] = newValue, + undo: () => receiver[prop] = oldValue + }); } - target[Update](diff); + target[Update](op); }; } \ No newline at end of file diff --git a/src/server/websocket.ts b/src/server/websocket.ts index 3687ef876..221a01308 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -271,14 +271,16 @@ export namespace WebSocket { return typeof value === "string" ? value : value[0]; } - function updateListField(socket: Socket, diff: Diff, results?: Transferable): void { + function updateListField(socket: Socket, diff: Diff, curListItems?: Transferable): void { diff.diff.$set = diff.diff.$addToSet; // convert add to set to a query of the current fields, and then a set of the composition of the new fields with the old ones delete diff.diff.$addToSet; const updatefield = Array.from(Object.keys(diff.diff.$set))[0]; - const list = (results as any).fields?.[updatefield.replace("fields.", "")]?.fields; + const newListItems = diff.diff.$set[updatefield].fields; + const curList = (curListItems as any).fields?.[updatefield.replace("fields.", "")]?.fields; const prelen = diff.diff.$set[updatefield].fields.length; - list?.forEach((item: any) => !diff.diff.$set[updatefield].fields.some((x: any) => x.fieldId === item.fieldId) && diff.diff.$set[updatefield].fields.push(item)); - const sendBack = diff.diff.$set[updatefield].fields.length !== prelen; + let insInd = 0; + curList?.forEach((curItem: any) => !newListItems.some((newItem: any) => newItem.fieldId === curItem.fieldId) && newListItems.splice(insInd++, 0, curItem)); + const sendBack = curList.length !== prelen; Database.Instance.update(diff.id, diff.diff, () => { if (sendBack) { -- cgit v1.2.3-70-g09d2 From c65ca9da4ccdbb525ca7c4e50e7403cf6b543cbe Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 6 Oct 2020 19:14:06 -0400 Subject: fixes for undo with multi-users --- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/fields/List.ts | 3 +- src/fields/util.ts | 55 +++++++++++++++------- src/server/websocket.ts | 35 ++++++++++---- 4 files changed, 66 insertions(+), 29 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index fc3fd3a7c..1ddb709ac 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1160,7 +1160,7 @@ export class CollectionFreeFormView extends CollectionSubView { - super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY)); + // super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY)); } diff --git a/src/fields/List.ts b/src/fields/List.ts index d9bd54673..ceb538b2d 100644 --- a/src/fields/List.ts +++ b/src/fields/List.ts @@ -66,6 +66,7 @@ const listHandlers: any = { this[Self].__realFields(); // coerce retrieving entire array items = items.map(toObjectField); const list = this[Self]; + const removed = list.__fields.filter((item: any, i: number) => i >= start && i < start + deleteCount); for (let i = 0; i < items.length; i++) { const item = items[i]; //TODO Error checking to make sure parent doesn't already exist @@ -76,7 +77,7 @@ const listHandlers: any = { } } const res = list.__fields.splice(start, deleteCount, ...items); - this[Update](); + this[Update](items.length === 0 && deleteCount ? { op: "$remFromSet", items: removed } : undefined); return res.map(toRealField); }), unshift(...items: any[]) { diff --git a/src/fields/util.ts b/src/fields/util.ts index 6e72675db..b251236d0 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -11,10 +11,8 @@ import { ComputedField } from "./ScriptField"; import { ScriptCast, StrCast } from "./Types"; import { returnZero } from "../Utils"; import CursorField from "./CursorField"; -import { undo } from "prosemirror-history"; import { List } from "./List"; - function _readOnlySetter(): never { throw new Error("Documents can't be modified in read-only mode"); } @@ -95,7 +93,10 @@ const _setterImpl = action(function (target: any, prop: string | symbol | number } else { DocServer.registerDocWithCachedUpdate(receiver, prop as string, curValue); } - UndoManager.AddEvent({ + if (prop === "data-annotations") { + console.log("Yukc") + } + !receiver[UpdatingFromServer] && UndoManager.AddEvent({ redo: () => receiver[prop] = value, undo: () => receiver[prop] = curValue }); @@ -373,25 +374,43 @@ export function deleteProperty(target: any, prop: string | number | symbol) { export function updateFunction(target: any, prop: any, value: any, receiver: any) { let current = ObjectField.MakeCopy(value); return (diff?: any) => { - const op = diff?.op === "$addToSet" ? - { '$addToSet': { ["fields." + prop]: SerializationHelper.Serialize(new List(diff.items)) } } - : { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } }; + const op = diff?.op === "$addToSet" ? { '$addToSet': { ["fields." + prop]: SerializationHelper.Serialize(new List(diff.items)) } } : + diff?.op === "$remFromSet" ? { '$remFromSet': { ["fields." + prop]: SerializationHelper.Serialize(new List(diff.items)) } } + : { '$set': { ["fields." + prop]: SerializationHelper.Serialize(value) } }; const oldValue = current; const newValue = ObjectField.MakeCopy(value); current = newValue; if (!(value instanceof CursorField) && !(value?.some?.((v: any) => v instanceof CursorField))) { - UndoManager.AddEvent(diff?.op === "$addToSet" ? - { - redo: () => receiver[prop].push(...diff.items), - undo: action(() => diff.items.forEach((doc: Doc) => { - const ind = receiver[prop].indexOf(doc); - ind !== -1 && receiver[prop].splice(ind, 1); - })) - } - : { - redo: () => receiver[prop] = newValue, - undo: () => receiver[prop] = oldValue - }); + !receiver[UpdatingFromServer] && UndoManager.AddEvent( + diff?.op === "$addToSet" ? + { + redo: () => { + receiver[prop].push(...diff.items); + }, + undo: action(() => { + let curList = receiver[prop]; + //while (curList[ForwardUpates]) curList = curList[ForwardUpates]; + diff.items.forEach((doc: any) => { + const ind = curList.indexOf(doc.value()); + ind !== -1 && curList.splice(ind, 1); + }); + }) + } : + diff?.op === "$remFromSet" ? + { + redo: action(() => { + let curList = receiver[prop]; + diff.items.forEach((doc: any) => { + const ind = curList.indexOf(doc.value()); + ind !== -1 && curList.splice(ind, 1); + }); + }), + undo: () => receiver[prop].push(...diff.items) + } + : { + redo: () => receiver[prop] = newValue, + undo: () => receiver[prop] = oldValue + }); } target[Update](op); }; diff --git a/src/server/websocket.ts b/src/server/websocket.ts index 221a01308..459fa520b 100644 --- a/src/server/websocket.ts +++ b/src/server/websocket.ts @@ -271,16 +271,13 @@ export namespace WebSocket { return typeof value === "string" ? value : value[0]; } - function updateListField(socket: Socket, diff: Diff, curListItems?: Transferable): void { - diff.diff.$set = diff.diff.$addToSet; // convert add to set to a query of the current fields, and then a set of the composition of the new fields with the old ones - delete diff.diff.$addToSet; + function addToListField(socket: Socket, diff: Diff, curListItems?: Transferable): void { + diff.diff.$set = diff.diff.$addToSet; delete diff.diff.$addToSet;// convert add to set to a query of the current fields, and then a set of the composition of the new fields with the old ones const updatefield = Array.from(Object.keys(diff.diff.$set))[0]; const newListItems = diff.diff.$set[updatefield].fields; - const curList = (curListItems as any).fields?.[updatefield.replace("fields.", "")]?.fields; - const prelen = diff.diff.$set[updatefield].fields.length; - let insInd = 0; - curList?.forEach((curItem: any) => !newListItems.some((newItem: any) => newItem.fieldId === curItem.fieldId) && newListItems.splice(insInd++, 0, curItem)); - const sendBack = curList.length !== prelen; + const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields || []; + diff.diff.$set[updatefield].fields = [...curList, ...newListItems.filter((newItem: any) => !curList.some((curItem: any) => curItem.fieldId === newItem.fieldId))]; + const sendBack = true;//curList.length !== prelen; Database.Instance.update(diff.id, diff.diff, () => { if (sendBack) { @@ -292,8 +289,28 @@ export namespace WebSocket { }, false); } + function remFromListField(socket: Socket, diff: Diff, curListItems?: Transferable): void { + diff.diff.$set = diff.diff.$remFromSet; delete diff.diff.$remFromSet; + const updatefield = Array.from(Object.keys(diff.diff.$set))[0]; + const remListItems = diff.diff.$set[updatefield].fields; + const curList = (curListItems as any)?.fields?.[updatefield.replace("fields.", "")]?.fields || []; + diff.diff.$set[updatefield].fields = curList?.filter((curItem: any) => !remListItems.some((remItem: any) => remItem.fieldId === curItem.fieldId)); + const sendBack = true;//curList.length + remListItems.length !== prelen; + Database.Instance.update(diff.id, diff.diff, + () => { + if (sendBack) { + const id = socket.id; + socket.id = ""; + socket.broadcast.emit(MessageStore.UpdateField.Message, diff); + socket.id = id; + } else socket.broadcast.emit(MessageStore.UpdateField.Message, diff); + }, false); + } + + function UpdateField(socket: Socket, diff: Diff) { - if (diff.diff.$addToSet) return GetRefField([diff.id, (result?: Transferable) => updateListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own + if (diff.diff.$addToSet) return GetRefField([diff.id, (result?: Transferable) => addToListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own + if (diff.diff.$remFromSet) return GetRefField([diff.id, (result?: Transferable) => remFromListField(socket, diff, result)]); // would prefer to have Mongo handle list additions direclty, but for now handle it on our own Database.Instance.update(diff.id, diff.diff, () => socket.broadcast.emit(MessageStore.UpdateField.Message, diff), false); const docfield = diff.diff.$set || diff.diff.$unset; -- cgit v1.2.3-70-g09d2 From c6e407d7ab92244964c2fad05b6a68d124bf18df Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 7 Oct 2020 11:32:23 -0400 Subject: fixed following links to not center outer document if originating anchor is in that document. fixed scroling to happen instantly if it's already scrolled there. fixed clicking on pdf text anchor to select the pdf. --- src/client/util/DocumentManager.ts | 5 +++-- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 11 +++++++---- src/client/views/pdf/Annotation.tsx | 7 +++++-- src/client/views/pdf/PDFViewer.tsx | 7 ++++--- 4 files changed, 19 insertions(+), 11 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index cb9aa35d0..4becdf4a3 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -151,7 +151,7 @@ export class DocumentManager { }; const docView = getFirstDocView(targetDoc, originatingDoc); let annotatedDoc = await Cast(targetDoc.annotationOn, Doc); - if (annotatedDoc && annotatedDoc !== docContext && !targetDoc?.isPushpin) { + if (annotatedDoc && annotatedDoc !== originatingDoc?.context && !targetDoc?.isPushpin) { const first = getFirstDocView(annotatedDoc); if (first) { annotatedDoc = first.props.Document; @@ -166,7 +166,8 @@ export class DocumentManager { else { docView.select(false); docView.props.Document.hidden && (docView.props.Document.hidden = undefined); - docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish); + // @ts-ignore + docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish, annotatedDoc && annotatedDoc === originatingDoc?.context); highlight(); } } else { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 1ddb709ac..35b4c8e98 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -867,7 +867,7 @@ export class CollectionFreeFormView extends CollectionSubView boolean) => { + focusDocument = (doc: Doc, willZoom: boolean, scale?: number, afterFocus?: () => boolean, dontCenter?: boolean) => { const state = HistoryUtil.getState(); // TODO This technically isn't correct if type !== "doc", as @@ -886,16 +886,19 @@ export class CollectionFreeFormView extends CollectionSubView 5 ? 1000 : 0; } - this.props.focus(this.props.Document); - afterFocus && setTimeout(afterFocus, 1000); + !dontCenter && this.props.focus(this.props.Document); + afterFocus && setTimeout(afterFocus, delay); } else { const layoutdoc = Doc.Layout(doc); const newPanX = NumCast(doc.x) + NumCast(layoutdoc._width) / 2; diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx index 84b14cd61..e7f901091 100644 --- a/src/client/views/pdf/Annotation.tsx +++ b/src/client/views/pdf/Annotation.tsx @@ -15,6 +15,7 @@ interface IAnnotationProps { addDocTab: (document: Doc, where: string) => boolean; pinToPres: (document: Doc, unpin?: boolean) => void; focus: (doc: Doc) => void; + select: (isCtrlPressed: boolean) => void; dataDoc: Doc; fieldKey: string; showInfo: (anno: Opt) => void; @@ -25,7 +26,7 @@ export class Annotation extends React.Component { render() { return DocListCast(this.props.anno.annotations).map(a => - ); + ); } } @@ -37,6 +38,7 @@ interface IRegionAnnotationProps { height: number; addDocTab: (document: Doc, where: string) => boolean; pinToPres: (document: Doc, unpin: boolean) => void; + select: (isCtrlPressed: boolean) => void; document: Doc; dataDoc: Doc; fieldKey: string; @@ -115,7 +117,8 @@ class RegionAnnotation extends React.Component { e.persist(); e.stopPropagation(); PromiseValue(this.props.document.group).then(annoGroup => annoGroup instanceof Doc && - DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation), false, undefined) + DocumentManager.Instance.FollowLink(undefined, annoGroup, (doc, followLinkLocation) => this.props.addDocTab(doc, e.ctrlKey ? "add" : followLinkLocation), false, undefined, + () => this.props.select(false)) ); } } diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 4c5f72b97..3570c565a 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -460,7 +460,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent { const hit = document.elementFromPoint(e.clientX, e.clientY); - if (hit && hit.localName === "span" && this.props.isSelected(true)) { // drag selecting text stops propagation + if (hit && hit.localName === "span" && this.annotationsActive(true)) { // drag selecting text stops propagation e.button === 0 && e.stopPropagation(); } // if alt+left click, drag and annotate @@ -557,6 +557,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent { clearStyleSheetRules(PDFViewer._annotationStyle); + this.props.select(false); this._savedAnnotations.clear(); if (this._marqueeing) { if (this._marqueeWidth > 10 || this._marqueeHeight > 10) { @@ -694,7 +695,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent {this.nonDocAnnotations.sort((a, b) => NumCast(a.y) - NumCast(b.y)).map(anno => - ) + ) } ; } @@ -763,7 +764,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent this._zoomed; render() { TraceMobx(); - return

Date: Wed, 7 Oct 2020 15:02:16 -0400 Subject: fixed following links to set the scollTop of PDFs corretion. Fixed following links to rich text anchors to scroll and highlight properly. fixed pushpin behavior to only toggle off when the target is in sight. --- src/client/util/DocumentManager.ts | 13 ++++++++++--- .../collectionFreeForm/CollectionFreeFormView.tsx | 22 ++++++++++++++++------ .../views/nodes/formattedText/FormattedTextBox.tsx | 12 +++++++----- src/client/views/pdf/PDFViewer.tsx | 2 +- src/fields/util.ts | 4 ++-- 5 files changed, 36 insertions(+), 17 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 4becdf4a3..ce4e1e378 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -159,17 +159,24 @@ export class DocumentManager { } } if (docView) { // we have a docView already and aren't forced to create a new one ... just focus on the document. TODO move into view if necessary otherwise just highlight? + const sameContext = annotatedDoc && annotatedDoc === originatingDoc?.context; if (originatingDoc?.isPushpin) { - docView.props.Document.hidden = !docView.props.Document.hidden; + const hide = !docView.props.Document.hidden; + (!hide || !sameContext) && (docView.props.Document.hidden = !docView.props.Document.hidden); + docView.props.focus(docView.props.Document, willZoom, undefined, (notfocused: boolean) => { // bcz: Argh! TODO: Need to add a notFocused argument to the after finish callback function that indicates whether the window had to scroll to show the target + notfocused && hide && (docView.props.Document.hidden = true); + return focusAndFinish(); + // @ts-ignore bcz: Argh TODO: Need to add a parameter to focus() everywhere for whether focus should center the target's container in the view or not. // here we don't want to focus the container if the source and target are in the same container + }, sameContext); finished?.(); } else { docView.select(false); docView.props.Document.hidden && (docView.props.Document.hidden = undefined); // @ts-ignore - docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish, annotatedDoc && annotatedDoc === originatingDoc?.context); - highlight(); + docView.props.focus(docView.props.Document, willZoom, undefined, focusAndFinish, sameContext); } + highlight(); } else { const contextDocs = docContext ? await DocListCastAsync(docContext.data) : undefined; const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc)) ? docContext : undefined; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 35b4c8e98..382929861 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -888,17 +888,27 @@ export class CollectionFreeFormView extends CollectionSubView 5 ? 1000 : 0; + const curScroll = NumCast(this.props.Document._scrollTop); + let scrollTo = curScroll; + if (curScroll + contextHgt < NumCast(doc.y)) { + scrollTo = NumCast(doc.y) + NumCast(doc._height) - contextHgt; + } else if (curScroll > NumCast(doc.y)) { + scrollTo = NumCast(doc.y); + } + if (curScroll !== scrollTo) { + this.props.Document._scrollY = scrollTo; + delay = Math.abs(scrollTo - curScroll) > 5 ? 1000 : 0; + !dontCenter && delay && this.props.focus(this.props.Document); + afterFocus && setTimeout(afterFocus, delay); + // @ts-ignore + } else afterFocus(true); // bcz: TODO Aragh -- need to add a parameter to afterFocus() functions to indicate whether the focus function didn't need to scroll } - !dontCenter && this.props.focus(this.props.Document); - afterFocus && setTimeout(afterFocus, delay); } else { const layoutdoc = Doc.Layout(doc); const newPanX = NumCast(doc.x) + NumCast(layoutdoc._width) / 2; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index ef39e6a90..0fccbd8ef 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -916,11 +916,13 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp async (scrollToLinkID) => { const findLinkFrag = (frag: Fragment, editor: EditorView) => { const nodes: Node[] = []; + let hadStart = start !== 0; frag.forEach((node, index) => { const examinedNode = findLinkNode(node, editor); - if (examinedNode?.textContent) { - nodes.push(examinedNode); - !start && (start = index); + if (examinedNode?.node.textContent) { + nodes.push(examinedNode.node); + !hadStart && (start = index + examinedNode.start); + hadStart = true; } }); return { frag: Fragment.fromArray(nodes), start }; @@ -928,11 +930,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const findLinkNode = (node: Node, editor: EditorView) => { if (!node.isText) { const content = findLinkFrag(node.content, editor); - return node.copy(content.frag); + return { node: node.copy(content.frag), start: content.start }; } const marks = [...node.marks]; const linkIndex = marks.findIndex(mark => mark.type === editor.state.schema.marks.linkAnchor); - return linkIndex !== -1 && marks[linkIndex].attrs.allLinks.find((item: { href: string }) => scrollToLinkID === item.href.replace(/.*\/doc\//, "")) ? node : undefined; + return linkIndex !== -1 && marks[linkIndex].attrs.allLinks.find((item: { href: string }) => scrollToLinkID === item.href.replace(/.*\/doc\//, "")) ? { node, start: 0 } : undefined; }; let start = 0; diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 3570c565a..606d3e550 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -184,7 +184,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent this._mainCont.current && smoothScroll(1000, this._mainCont.current, scrollY || 0), 250); } - setTimeout(() => this.Document._scrollY = undefined, 1000); + setTimeout(() => { this.Document._scrollTop = scrollY; this.Document._scrollY = undefined; }, 1000); } }, { fireImmediately: true } diff --git a/src/fields/util.ts b/src/fields/util.ts index 8a694de83..7293db0c2 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -385,7 +385,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any receiver[prop].push(...diff.items); }, undo: action(() => { - let curList = receiver[prop]; + const curList = receiver[prop]; //while (curList[ForwardUpates]) curList = curList[ForwardUpates]; diff.items.forEach((doc: any) => { const ind = curList.indexOf(doc.value()); @@ -396,7 +396,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any diff?.op === "$remFromSet" ? { redo: action(() => { - let curList = receiver[prop]; + const curList = receiver[prop]; diff.items.forEach((doc: any) => { const ind = curList.indexOf(doc.value()); ind !== -1 && curList.splice(ind, 1); -- cgit v1.2.3-70-g09d2 From 192e23a2ae335a5f0de361eed9a24a5d08547217 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 7 Oct 2020 17:49:20 -0400 Subject: fixed undoing changes to a textbox's fontsize/family. changed auto-page ref syntax to start with ':'. fixed making text menu appear when clicking on caption & other text boxes. --- src/client/views/collections/CollectionMenu.tsx | 45 +++++++++++----------- .../views/nodes/formattedText/FormattedTextBox.tsx | 5 +-- .../views/nodes/formattedText/RichTextMenu.tsx | 45 ++++++++++++---------- .../views/nodes/formattedText/RichTextRules.ts | 6 +-- 4 files changed, 50 insertions(+), 51 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 09ff3bb0c..4bdb233c9 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -529,12 +529,7 @@ export class CollectionFreeFormViewChrome extends React.Component - {!Doc.UserDoc().noviceMode && !this.isText && !this.props.isDoc ? Back Frame
} placement="bottom"> -
- -
- : null} - {!Doc.UserDoc().noviceMode && !this.isText && !this.props.isDoc ? Toggle View All} placement="bottom"> -
this.document.editing = !this.document.editing)} > - {NumCast(this.document._currentFrame)} -
-
: null} - {!Doc.UserDoc().noviceMode && !this.isText && !this.props.isDoc ? Forward Frame} placement="bottom"> -
- -
-
: null} + {!Doc.UserDoc().noviceMode && !this.isText && !this.props.isDoc ? + <> + Back Frame} placement="bottom"> +
+ +
+
+ Toggle View All} placement="bottom"> +
this.document.editing = !this.document.editing)} > + {NumCast(this.document._currentFrame)} +
+
+ Forward Frame} placement="bottom"> +
+ +
+
+ + : null} {!this.props.isOverlay || this.document.type !== DocumentType.WEB || this.isText || this.props.isDoc ? (null) : this.urlEditor @@ -872,7 +871,7 @@ export class CollectionFreeFormViewChrome extends React.Component : (null) } - {this.isText ? : null} + {
} ; } } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 0fccbd8ef..c6bd0cb81 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1321,6 +1321,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp if (coords && coords.left > x && coords.left < x + RichTextMenu.Instance.width && coords.top > y && coords.top < y + RichTextMenu.Instance.height + 50) { y = Math.min(bounds.bottom, window.innerHeight - RichTextMenu.Instance.height); } + this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props); setTimeout(() => window.document.activeElement === this.ProseRef?.children[0] && RichTextMenu.Instance.jumpTo(x, y), 250); } } @@ -1557,9 +1558,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp
{ - console.log(e); - }} />; } @@ -1608,7 +1606,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const scale = this.props.hideOnLeave ? 1 : this.props.ContentScaling() * NumCast(this.layoutDoc._viewScale, 1); const rounded = StrCast(this.layoutDoc.borderRounding) === "100%" ? "-rounded" : ""; const interactive = (Doc.GetSelectedTool() === InkTool.None || SnappingManager.GetIsDragging()) && !this.layoutDoc._isBackground; - selected && setTimeout(() => this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props)); // need to make sure that we update a text box that is selected after updating the one that was deselected if (!selected && FormattedTextBoxComment.textBox === this) { FormattedTextBoxComment.Hide(); } const minimal = this.props.ignoreAutoHeight; const margins = NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0); diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 68239a8f1..d492b561b 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -32,7 +32,7 @@ export class RichTextMenu extends AntimodeMenu { static Instance: RichTextMenu; public overMenu: boolean = false; // kind of hacky way to prevent selects not being selectable - public view?: EditorView; + @observable public view?: EditorView; public editorProps: FieldViewProps & FormattedTextBoxProps | undefined; public _brushMap: Map> = new Map(); @@ -154,7 +154,7 @@ export class RichTextMenu extends AntimodeMenu { @action public updateMenu(view: EditorView, lastState: EditorState | undefined, props: any) { - if (!view || !(view as any).TextView?.props.isSelected(true) || !view.hasFocus()) { + if (!view || !view.hasFocus()) { return; } this.view = view; @@ -366,7 +366,7 @@ export class RichTextMenu extends AntimodeMenu { ); } - createMarksDropdown(activeOption: string, options: { mark: Mark | null, title: string, label: string, command: (mark: Mark, view: EditorView) => void, hidden?: boolean, style?: {} }[], key: string, setter: (val: string) => {}): JSX.Element { + createMarksDropdown(activeOption: string, options: { mark: Mark | null, title: string, label: string, command: (mark: Mark, view: EditorView) => void, hidden?: boolean, style?: {} }[], key: string, setter: (val: string) => void): JSX.Element { const items = options.map(({ title, label, hidden, style }) => { if (hidden) { return ; @@ -378,19 +378,22 @@ export class RichTextMenu extends AntimodeMenu { function onChange(e: React.ChangeEvent) { e.stopPropagation(); e.preventDefault(); - self.TextView.endUndoTypingBatch(); - options.forEach(({ label, mark, command }) => { - if (e.target.value === label && mark) { - if (!self.TextView.props.isSelected(true)) { - switch (mark.type) { - case schema.marks.pFontFamily: setter(Doc.UserDoc().fontFamily = mark.attrs.family); break; - case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "px"); break; + self.TextView?.endUndoTypingBatch(); + UndoManager.RunInBatch(() => { + options.forEach(({ label, mark, command }) => { + if (e.target.value === label && mark) { + if (!self.TextView?.props.isSelected(true)) { + switch (mark.type) { + case schema.marks.pFontFamily: setter(Doc.UserDoc().fontFamily = mark.attrs.family); break; + case schema.marks.pFontSize: setter(Doc.UserDoc().fontSize = mark.attrs.fontSize.toString() + "px"); break; + } } + else self.view && mark && command(mark, self.view); } - else UndoManager.RunInBatch(() => self.view && mark && command(mark, self.view), "text mark dropdown"); - } - }); + }); + }, "text mark dropdown"); } + return {key}
} placement="bottom"> ; @@ -424,9 +427,6 @@ export class RichTextMenu extends AntimodeMenu { } changeFontSize = (mark: Mark, view: EditorView) => { - if ((this.view?.state.selection.$from.pos || 0) < 2) { - this.TextView.layoutDoc._fontSize = mark.attrs.fontSize === Number(mark.attrs.fontSize) ? `${mark.attrs.fontSize}pt` : mark.attrs.fontSize; - } const fmark = view.state.schema.marks.pFontSize.create({ fontSize: mark.attrs.fontSize }); this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true); view.focus(); @@ -434,9 +434,6 @@ export class RichTextMenu extends AntimodeMenu { } changeFontFamily = (mark: Mark, view: EditorView) => { - if ((this.view?.state.selection.$from.pos || 0) < 2) { - this.TextView.layoutDoc._fontFamily = mark.attrs.family; - } const fmark = view.state.schema.marks.pFontFamily.create({ family: mark.attrs.family }); this.setMark(fmark, view.state, (tx: any) => view.dispatch(tx.addStoredMark(fmark)), true); view.focus(); @@ -984,8 +981,14 @@ export class RichTextMenu extends AntimodeMenu { {this.collapsed ? this.getDragger() : (null)}
, - {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => this.activeFontSize = val)), - this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => this.activeFontFamily = val)), + {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => { + this.activeFontSize = val; + SelectionManager.SelectedDocuments().map(dv => dv.props.Document._fontSize = val); + })), + this.createMarksDropdown(this.activeFontFamily, this.fontFamilyOptions, "font family", action((val: string) => { + this.activeFontFamily = val; + SelectionManager.SelectedDocuments().map(dv => dv.props.Document._fontFamily = val); + })),
, this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})), this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer), diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 72b91c955..99334b6db 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -267,11 +267,11 @@ export class RichTextRules { // [[fieldKey]] => show field // [[fieldKey:Doc]] => show field of doc new InputRule( - new RegExp(/\[\[([a-zA-Z_@\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@\.\? \-0-9]+)?\]\]$/), + new RegExp(/\[\[([a-zA-Z_\? \-0-9]*)(=[a-zA-Z_@\? /\-0-9]*)?(:[a-zA-Z_@:\.\? \-0-9]+)?\]\]$/), (state, match, start, end) => { const fieldKey = match[1]; - const rawdocid = match[3]?.substring(1); - const docid = rawdocid ? (!rawdocid.includes("@") ? normalizeEmail(Doc.CurrentUserEmail) + "@" + rawdocid : rawdocid) : undefined; + const rawdocid = match[3]; + const docid = rawdocid ? normalizeEmail((!rawdocid.includes("@") ? Doc.CurrentUserEmail + rawdocid : rawdocid.substring(1))) : undefined; const value = match[2]?.substring(1); if (!fieldKey) { const linkId = Utils.GenerateGuid(); -- cgit v1.2.3-70-g09d2 From a722aab6a65ad8d222ec3d576497ce9687cac966 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 8 Oct 2020 00:44:43 -0400 Subject: fixed adding/removing schema columns. fixed showing text/ink tools in menu at right time. cleaned up schema view a bit and fixed drag bar from disappearing when shown in full tab. --- src/client/views/collections/CollectionMenu.tsx | 4 +- .../views/collections/CollectionSchemaView.scss | 5 +- .../views/collections/CollectionSchemaView.tsx | 148 +++++++-------------- src/client/views/collections/SchemaTable.tsx | 6 +- src/client/views/globalCssVariables.scss | 2 + src/client/views/globalCssVariables.scss.d.ts | 1 + .../views/nodes/formattedText/FormattedTextBox.tsx | 1 + .../views/nodes/formattedText/RichTextMenu.tsx | 4 +- src/server/websocket.ts | 4 +- 9 files changed, 63 insertions(+), 112 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 4bdb233c9..429764154 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -528,9 +528,7 @@ export class CollectionFreeFormViewChrome extends React.Component = new Map([ @observer export class CollectionSchemaView extends CollectionSubView(doc => doc) { private _previewCont?: HTMLDivElement; - private DIVIDER_WIDTH = 4; - - @observable previewDoc: Doc | undefined = undefined; - @observable private _focusedTable: Doc = this.props.Document; - - @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); } - @computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; } - @computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - this.DIVIDER_WIDTH - this.previewWidth(); } - @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } + @observable _previewDoc: Doc | undefined = undefined; + @observable _focusedTable: Doc = this.props.Document; + @observable _col: any = ""; @observable _menuWidth = 0; @observable _headerOpen = false; @observable _headerIsEditing = false; - @observable _col: any = ""; @observable _menuHeight = 0; @observable _pointerX = 0; @observable _pointerY = 0; @observable _openTypes: boolean = false; + + @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); } + @computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; } + @computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth(); } + @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } + @computed get scale() { return this.props.ScreenToLocalTransform().Scale; } + @computed get columns() { return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []); } + set columns(columns: SchemaHeaderField[]) { this.props.Document._schemaHeaders = new List(columns); } + @computed get menuCoordinates() { let searchx = 0; let searchy = 0; @@ -81,15 +83,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { return this.props.ScreenToLocalTransform().transformPoint(x, y); } - @computed get scale() { return this.props.ScreenToLocalTransform().Scale; } - - @computed get columns() { - return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []); - } - set columns(columns: SchemaHeaderField[]) { - this.props.Document._schemaHeaders = new List(columns); - } - get documentKeys() { const docs = this.childDocs; const keys: { [key: string]: boolean } = {}; @@ -104,27 +97,12 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { this.columns.forEach(key => keys[key.heading] = true); return Array.from(Object.keys(keys)); } - @computed get possibleKeys() { return this.documentKeys.filter(key => this.columns.findIndex(existingKey => existingKey.heading.toUpperCase() === key.toUpperCase()) === -1); } @action setHeaderIsEditing = (isEditing: boolean) => this._headerIsEditing = isEditing; - - @action - changeColumnType = (type: ColumnType, col: any): void => { - this._openTypes = false; - this.setColumnType(col, type); - } - - changeColumnSort = (desc: boolean | undefined, col: any): void => { - this.setColumnSort(col, desc); - } - - changeColumnColor = (color: string, col: any): void => { - this.setColumnColor(col, color); - } - @undoBatch setColumnType = (columnField: SchemaHeaderField, type: ColumnType): void => { + this._openTypes = false; if (columnTypes.get(columnField.heading)) return; const columns = this.columns; @@ -165,42 +143,42 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { const type = col.type; - const anyType =
this.changeColumnType(ColumnType.Any, col)}> + const anyType =
this.setColumnType(col, ColumnType.Any)}> Any
; - const numType =
this.changeColumnType(ColumnType.Number, col)}> + const numType =
this.setColumnType(col, ColumnType.Number)}> Number
; - const textType =
this.changeColumnType(ColumnType.String, col)}> + const textType =
this.setColumnType(col, ColumnType.String)}> Text
; - const boolType =
this.changeColumnType(ColumnType.Boolean, col)}> + const boolType =
this.setColumnType(col, ColumnType.Boolean)}> Checkbox
; - const listType =
this.changeColumnType(ColumnType.List, col)}> + const listType =
this.setColumnType(col, ColumnType.List)}> List
; - const docType =
this.changeColumnType(ColumnType.Doc, col)}> + const docType =
this.setColumnType(col, ColumnType.Doc)}> Document
; - const imageType =
this.changeColumnType(ColumnType.Image, col)}> + const imageType =
this.setColumnType(col, ColumnType.Image)}> Image
; - const dateType =
this.changeColumnType(ColumnType.Date, col)}> + const dateType =
this.setColumnType(col, ColumnType.Date)}> Date
; @@ -239,15 +217,15 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
-
this.changeColumnSort(true, col)}> +
this.setColumnSort(col, true)}> Sort descending
-
this.changeColumnSort(false, col)}> +
this.setColumnSort(col, false)}> Sort ascending
-
this.changeColumnSort(undefined, col)}> +
this.setColumnSort(col, undefined)}> Clear sorting
@@ -270,12 +248,12 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
-
this.changeColumnColor(pink!, col)}>
-
this.changeColumnColor(purple!, col)}>
-
this.changeColumnColor(blue!, col)}>
-
this.changeColumnColor(yellow!, col)}>
-
this.changeColumnColor(red!, col)}>
-
this.changeColumnColor(gray, col)}>
+
this.setColumnColor(col, pink!)}>
+
this.setColumnColor(col, purple!)}>
+
this.setColumnColor(col, blue!)}>
+
this.setColumnColor(col, yellow!)}>
+
this.setColumnColor(col, red!)}>
+
this.setColumnColor(col, gray)}>
); @@ -320,8 +298,6 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { @action closeHeader = () => { this._headerOpen = false; } - - @undoBatch @action deleteColumn = (key: string) => { @@ -351,15 +327,12 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { onWheel(e: React.WheelEvent) { const scale = this.props.ScreenToLocalTransform().Scale; this.props.active(true) && e.stopPropagation(); - //this.menuCoordinates[0] -= e.screenX / scale; - //this.menuCoordinates[1] -= e.screenY / scale; } @computed get renderMenuContent() { TraceMobx(); return
{this.renderTypes(this._col)} - {/* {this.renderSorting(this._col)} */} {this.renderColors(this._col)}