diff options
author | usodhi <61431818+usodhi@users.noreply.github.com> | 2020-09-22 13:51:49 +0530 |
---|---|---|
committer | usodhi <61431818+usodhi@users.noreply.github.com> | 2020-09-22 13:51:49 +0530 |
commit | a9dad1837ffa66b0da304dd2f6f5e35b3ab4a7b8 (patch) | |
tree | 69853de7a2a8faa597e12dd4e05a211b3c24b618 | |
parent | 8fad5a8e842aaed6540d37f6a3a4ba3a3c2abe9b (diff) | |
parent | 02029b5cad1c27e65630a63cd4d2b632ed6973cd (diff) |
Merge branch 'master' of https://github.com/browngraphicslab/Dash-Web into acls_uv
-rw-r--r-- | deploy/assets/pinWithView.png | bin | 0 -> 89557 bytes | |||
-rw-r--r-- | src/client/documents/Documents.ts | 3 | ||||
-rw-r--r-- | src/client/views/DocumentDecorations.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionSchemaCells.tsx | 18 | ||||
-rw-r--r-- | src/client/views/collections/CollectionTreeView.scss | 1 | ||||
-rw-r--r-- | src/client/views/collections/CollectionTreeView.tsx | 46 | ||||
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 3 | ||||
-rw-r--r-- | src/client/views/collections/TreeView.tsx | 21 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentContentsView.tsx | 19 | ||||
-rw-r--r-- | src/client/views/nodes/FilterBox.tsx | 48 | ||||
-rw-r--r-- | src/client/views/nodes/PresBox.tsx | 16 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBox.tsx | 82 | ||||
-rw-r--r-- | src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx | 13 | ||||
-rw-r--r-- | src/client/views/search/SearchBox.tsx | 6 | ||||
-rw-r--r-- | src/fields/util.ts | 2 |
15 files changed, 149 insertions, 131 deletions
diff --git a/deploy/assets/pinWithView.png b/deploy/assets/pinWithView.png Binary files differnew file mode 100644 index 000000000..eb0d09689 --- /dev/null +++ b/deploy/assets/pinWithView.png diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f7ab955f3..2d02742c5 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -52,6 +52,7 @@ import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; import { DocumentType } from "./DocumentTypes"; import { FilterBox } from "../views/nodes/FilterBox"; import { SharingPermissions } from "../../fields/util"; +import { SharingManager } from "../util/SharingManager"; const path = require('path'); const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", "")); @@ -601,7 +602,7 @@ export namespace Docs { viewDoc.author = Doc.CurrentUserEmail; viewDoc.type !== DocumentType.LINK && DocUtils.MakeLinkToActiveAudio(viewDoc); - if (Doc.UserDoc()?.defaultAclPrivate) viewDoc["acl-Public"] = dataDoc["acl-Public"] = "Not Shared"; + viewDoc["acl-Public"] = dataDoc["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Add; return Doc.assign(viewDoc, delegateProps, true); } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 795208c91..9432d1ffb 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -139,7 +139,7 @@ export class DocumentDecorations extends React.Component<{}, { value: string }> dragData.offset = dragDocView.props.ScreenToLocalTransform().scale(dragDocView.props.ContentScaling()).transformDirection(e.x - left, e.y - top); dragData.moveDocument = dragDocView.props.moveDocument; dragData.isSelectionMove = true; - dragData.dropAction = dragDocView.props.dropAction as dropActionType; + dragData.dropAction = dragDocView.props.dropAction; this.Interacting = true; this._hidden = true; DragManager.StartDocumentDrag(SelectionManager.SelectedDocuments().map(dv => dv.ContentDiv!), dragData, e.x, e.y, { diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx index 18ae260e8..2443bd34e 100644 --- a/src/client/views/collections/CollectionSchemaCells.tsx +++ b/src/client/views/collections/CollectionSchemaCells.tsx @@ -71,6 +71,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> { @observable protected _isEditing: boolean = false; protected _focusRef = React.createRef<HTMLDivElement>(); protected _rowDoc = this.props.rowProps.original; + protected _rowDataDoc = Doc.GetProto(this.props.rowProps.original); protected _dropDisposer?: DragManager.DragDropDisposer; @observable contents: string = ""; @@ -126,11 +127,11 @@ export class CollectionSchemaCell extends React.Component<CellProps> { private drop = (e: Event, de: DragManager.DropEvent) => { if (de.complete.docDragData) { if (de.complete.docDragData.draggedDocuments.length === 1) { - this._rowDoc[this.renderFieldKey] = de.complete.docDragData.draggedDocuments[0]; + this._rowDataDoc[this.renderFieldKey] = de.complete.docDragData.draggedDocuments[0]; } else { const coll = Docs.Create.SchemaDocument([new SchemaHeaderField("title", "#f1efeb")], de.complete.docDragData.draggedDocuments, {}); - this._rowDoc[this.renderFieldKey] = coll; + this._rowDataDoc[this.renderFieldKey] = coll; } e.stopPropagation(); } @@ -164,8 +165,7 @@ export class CollectionSchemaCell extends React.Component<CellProps> { @computed get renderFieldKey() { return CollectionSchemaCell.resolvedFieldKey(this.props.rowProps.column.id!, this.props.rowProps.original); } onItemDown = async (e: React.PointerEvent) => { if (this.props.Document._searchDoc) { - const doc = Doc.GetProto(this._rowDoc); - const aliasdoc = await SearchUtil.GetAliasesOfDocument(doc); + const aliasdoc = await SearchUtil.GetAliasesOfDocument(this._rowDataDoc); const targetContext = aliasdoc.length <= 0 ? undefined : Cast(aliasdoc[0].context, Doc, null); DocumentManager.Instance.jumpToDocument(this._rowDoc, false, () => undefined, targetContext); } @@ -238,10 +238,10 @@ export class CollectionSchemaCell extends React.Component<CellProps> { let retVal = false; if (value.startsWith(":=") || value.startsWith("=:=")) { const script = value.substring(value.startsWith("=:=") ? 3 : 2); - retVal = this.props.setComputed(script, value.startsWith(":=") ? Doc.GetProto(this.props.Document) : this.props.Document, this.renderFieldKey, this.props.row, this.props.col); + retVal = this.props.setComputed(script, value.startsWith(":=") ? this._rowDataDoc : this._rowDoc, this.renderFieldKey, this.props.row, this.props.col); } else { const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); - script.compiled && (retVal = this.applyToDoc(this._rowDoc, this.props.row, this.props.col, script.run)); + script.compiled && (retVal = this.applyToDoc(this._rowDataDoc, this.props.row, this.props.col, script.run)); } if (retVal) { this._isEditing = false; // need to set this here. otherwise, the assignment of the field will invalidate & cause render() to be called with the wrong value for 'editing' @@ -251,10 +251,10 @@ export class CollectionSchemaCell extends React.Component<CellProps> { })} OnFillDown={async (value: string) => { const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } }); - script.compiled && DocListCast(field). + script.compiled && DocListCast(this.props.Document[this.props.fieldKey]). forEach((doc, i) => value.startsWith(":=") ? - this.props.setComputed(value.substring(2), doc, this.renderFieldKey, i, this.props.col) : - this.applyToDoc(doc, i, this.props.col, script.run)); + this.props.setComputed(value.substring(2), Doc.GetProto(doc), this.renderFieldKey, i, this.props.col) : + this.applyToDoc(Doc.GetProto(doc), i, this.props.col, script.run)); }} /> : diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 20bfc0e9d..c5add7cfb 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -28,6 +28,7 @@ .no-indent { padding-left: 0; + width: max-content; } .editableView-container { diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index efbe5581b..257eaf7f0 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -41,7 +41,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll private _mainEle?: HTMLDivElement; public _uniqueId = Utils.GenerateGuid(); - @computed get doc() { return this.props.Document; } + @computed get doc() { TraceMobx(); return this.props.Document; } @computed get dataDoc() { return this.props.DataDoc || this.doc; } protected createTreeDropTarget = (ele: HTMLDivElement) => { @@ -82,7 +82,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll return true; } return false; - }) + }); @action addDoc = (doc: Doc | Doc[], relativeTo: Opt<Doc>, before?: boolean): boolean => { const doAddDoc = (doc: Doc | Doc[]) => @@ -117,7 +117,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll onTreeDrop = (e: React.DragEvent) => this.onExternalDrop(e, {}); @computed get renderClearButton() { - return <div key="toolbar"> + return !this.doc.allowClear ? (null) : <div key="toolbar"> <button className="toolbar-button round-button" title="Empty" onClick={undoBatch(action(() => Doc.GetProto(this.doc)[this.props.fieldKey] = undefined))}> <FontAwesomeIcon icon={"trash"} size="sm" /> </button> @@ -192,37 +192,45 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick); whenActiveChanged = (isActive: boolean) => { this.props.whenActiveChanged(this._isChildActive = isActive); }; active = (outsideReaction: boolean | undefined) => this.props.active(outsideReaction) || this._isChildActive; - render() { + @computed get treeChildren() { + TraceMobx(); + return this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs; + } + @computed get treeViewElements() { TraceMobx(); - if (!(this.doc instanceof Doc)) return (null); const dropAction = StrCast(this.doc.childDropAction) as dropActionType; const addDoc = (doc: Doc | Doc[], relativeTo?: Doc, before?: boolean) => this.addDoc(doc, relativeTo, before); const moveDoc = (d: Doc | Doc[], target: Doc | undefined, addDoc: (doc: Doc | Doc[]) => boolean) => this.props.moveDocument(d, target, addDoc); - const childDocs = this.props.overrideDocuments ? this.props.overrideDocuments : this.childDocs; - const childElements = childDocs && TreeView.GetChildElements(childDocs, this, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove, + return TreeView.GetChildElements(this.treeChildren, this, this.doc, this.props.DataDoc, this.props.fieldKey, this.props.ContainingCollectionDoc, undefined, addDoc, this.remove, moveDoc, dropAction, this.props.addDocTab, this.props.pinToPres, this.props.backgroundColor, this.props.ScreenToLocalTransform, this.outerXf, this.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.doc.treeViewHideHeaderFields), BoolCast(this.doc.treeViewPreventOpen), [], this.props.onCheckedClick, this.onChildClick, this.props.ignoreFields, true, this.whenActiveChanged); + } + @computed get titleBar() { const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle; - const backgroundColor = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.doc.backgroundColor) || this.props.backgroundColor?.(this.doc, this.props.renderDepth); + return hideTitle ? (null) : (this.doc.treeViewOutlineMode ? this.documentTitle : this.editableTitle)(this.treeChildren); + } + render() { + TraceMobx(); + if (!(this.doc instanceof Doc)) return (null); + const background = StrCast(this.layoutDoc._backgroundColor) || StrCast(this.layoutDoc.backgroundColor) || StrCast(this.doc.backgroundColor) || this.props.backgroundColor?.(this.doc, this.props.renderDepth); + const paddingX = `${NumCast(this.doc._xPadding, 10)}px`; + const paddingTop = `${NumCast(this.doc._yPadding, 20)}px`; + const pointerEvents = !this.props.active() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined; - return !childDocs ? (null) : ( + return !this.treeChildren ? (null) : ( <div className="collectionTreeView-container" onContextMenu={this.onContextMenu}> <div className="collectionTreeView-dropTarget" - style={{ - background: backgroundColor, - paddingLeft: `${NumCast(this.doc._xPadding, 10)}px`, - paddingRight: `${NumCast(this.doc._xPadding, 10)}px`, - paddingTop: `${NumCast(this.doc._yPadding, 20)}px`, - pointerEvents: !this.props.active() && !SnappingManager.GetIsDragging() && !this._isChildActive ? "none" : undefined, - }} + style={{ background, paddingLeft: paddingX, paddingRight: paddingX, paddingTop, pointerEvents }} onWheel={(e) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()} onDrop={this.onTreeDrop} ref={this.createTreeDropTarget}> - {hideTitle ? (null) : (this.doc.treeViewOutlineMode ? this.documentTitle : this.editableTitle)(childDocs)} - {this.doc.allowClear ? this.renderClearButton : (null)} - <ul className="no-indent" style={{ width: "max-content" }} > {childElements} </ul> + {this.titleBar} + {this.renderClearButton} + <ul className="no-indent"> + {this.treeViewElements} + </ul> </div > </div> ); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index c9496d374..e35474b81 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -144,7 +144,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus added.forEach(d => { for (const [key, value] of Object.entries(this.props.Document[AclSym])) { if (d.author === key.substring(4).replace("_", ".") && !d.aliasOf) distributeAcls(key, SharingPermissions.Admin, d, true); - else distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true); + else if (this.props.Document[key] === SharingPermissions.Admin) distributeAcls(key, SharingPermissions.Add, d, true); + //else distributeAcls(key, this.AclMap.get(value) as SharingPermissions, d, true); } }); } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index edb703135..486e44f6b 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -83,9 +83,9 @@ export class TreeView extends React.Component<TreeViewProps> { private _uniqueId = Utils.GenerateGuid(); private _editMaxWidth: number | string = 0; - get doc() { return this.props.document; } + @computed get doc() { TraceMobx(); return this.props.document; } get noviceMode() { return BoolCast(Doc.UserDoc().noviceMode, false); } - get displayName() { return "TreeView(" + this.doc.title + ")"; } // this makes mobx trace() statements more descriptive + get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive get treeViewLockExpandedView() { return this.doc.treeViewLockExpandedView; } get defaultExpandedView() { return StrCast(this.doc.treeViewDefaultExpandedView, this.noviceMode || this.outlineMode ? "layout" : "fields"); } get treeViewDefaultExpandedView() { return this.treeViewLockExpandedView ? this.defaultExpandedView : (this.childDocs ? this.fieldKey : this.defaultExpandedView); } @@ -100,14 +100,14 @@ export class TreeView extends React.Component<TreeViewProps> { @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containingCollection.maxEmbedHeight, 200); } @computed get dataDoc() { return this.doc[DataSym]; } @computed get layoutDoc() { return Doc.Layout(this.doc); } - @computed get fieldKey() { const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; } + @computed get fieldKey() { TraceMobx(); const splits = StrCast(Doc.LayoutField(this.doc)).split("fieldKey={\'"); return splits.length > 1 ? splits[1].split("\'")[0] : "data"; } childDocList(field: string) { const layout = Doc.LayoutField(this.doc) instanceof Doc ? Doc.LayoutField(this.doc) as Doc : undefined; return ((this.props.dataDoc ? DocListCastOrNull(this.props.dataDoc[field]) : undefined) || // if there's a data doc for an expanded template, use it's data field (layout ? DocListCastOrNull(layout[field]) : undefined) || // else if there's a layout doc, display it's fields DocListCastOrNull(this.doc[field])); // otherwise use the document's data field } - @computed get childDocs() { return this.childDocList(this.fieldKey); } + @computed get childDocs() { TraceMobx(); return this.childDocList(this.fieldKey); } @computed get childLinks() { return this.childDocList("links"); } @computed get childAnnos() { return this.childDocList(this.fieldKey + "-annotations"); } @computed get boundsOfCollectionDocument() { @@ -130,13 +130,12 @@ export class TreeView extends React.Component<TreeViewProps> { constructor(props: any) { super(props); - console.log("Ttile = " + this.props.document.title); const titleScript = ScriptField.MakeScript(`{setInPlace(self, 'editTitle', '${this._uniqueId}'); documentView.select();} `, { documentView: "any" }); const openScript = ScriptField.MakeScript(`openOnRight(self)`); const treeOpenScript = ScriptField.MakeScript(`self.treeViewOpen = !self.treeViewOpen`); this._editTitleScript = !Doc.IsSystem(this.props.document) ? titleScript && (() => titleScript) : treeOpenScript && (() => treeOpenScript); this._openScript = !Doc.IsSystem(this.props.document) ? openScript && (() => openScript) : undefined; - if (Doc.GetT(this.doc, "editTitle", "string", true) === "*") Doc.SetInPlace(this.doc, "editTitle", this._uniqueId, false); + if (Doc.GetT(this.props.document, "editTitle", "string", true) === "*") Doc.SetInPlace(this.props.document, "editTitle", this._uniqueId, false); } protected createTreeDropTarget = (ele: HTMLDivElement) => { @@ -185,7 +184,7 @@ export class TreeView extends React.Component<TreeViewProps> { } public static makeTextBullet() { - const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10 }); + const bullet = Docs.Create.TextDocument("-text-", { title: "-title-", forceActive: true, _viewType: CollectionViewType.Tree, hideLinkButton: true, _showSidebar: true, treeViewOutlineMode: true, x: 0, y: 0, _xMargin: 0, _yMargin: 0, _autoHeight: true, _singleLine: true, _backgroundColor: "transparent", _width: 1000, _height: 10 }); Doc.GetProto(bullet).layout = CollectionView.LayoutString("data"); Doc.GetProto(bullet).title = ComputedField.MakeFunction('self.text?.Text'); Doc.GetProto(bullet).data = new List<Doc>([]); @@ -659,8 +658,7 @@ export class TreeView extends React.Component<TreeViewProps> { onChildClick: undefined | (() => ScriptField), ignoreFields: string[] | undefined, firstLevel: boolean, - whenActiveChanged: (isActive: boolean) => void - ) { + whenActiveChanged: (isActive: boolean) => void) { const viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField); if (viewSpecScript) { childDocs = childDocs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result); @@ -691,7 +689,7 @@ export class TreeView extends React.Component<TreeViewProps> { } const rowWidth = () => panelWidth() - 20; - return docs.map((child, i) => { + return docs.filter(child => child instanceof Doc).map((child, i) => { const pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, child); if (!pair.layout || pair.data instanceof Promise) { return (null); @@ -727,13 +725,12 @@ export class TreeView extends React.Component<TreeViewProps> { const aspect = NumCast(childLayout._nativeWidth, 0) / NumCast(childLayout._nativeHeight, 0); return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym](); }; - return !(child instanceof Doc) ? (null) : <TreeView + return <TreeView key={child[Id]} document={pair.layout} dataDoc={pair.data} containingCollection={containingCollection} prevSibling={docs[i]} treeView={treeView} - key={child[Id]} indentDocument={indent} outdentDocument={!parentCollectionDoc ? undefined : outdent} onCheckedClick={onCheckedClick} diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 67e7c1986..e8c3662aa 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -114,22 +114,13 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo }> { @computed get layout(): string { TraceMobx(); - if (!this.layoutDoc) return "<p>awaiting layout</p>"; - // const layout = Cast(this.layoutDoc[StrCast(this.layoutDoc.layoutKey, this.layoutDoc === this.props.Document ? this.props.layoutKey : "layout")], "string"); // bcz: replaced this with below... is it right? if (this.props.LayoutTemplateString) return this.props.LayoutTemplateString; + if (!this.layoutDoc) return "<p>awaiting layout</p>"; + if (this.props.layoutKey === "layout_keyValue") return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data")); const layout = Cast(this.layoutDoc[this.layoutDoc === this.props.Document && this.props.layoutKey ? this.props.layoutKey : StrCast(this.layoutDoc.layoutKey, "layout")], "string"); - if (this.props.layoutKey === "layout_keyValue") { - return StrCast(this.props.Document.layout_keyValue, KeyValueBox.LayoutString("data")); - } else - if (layout === undefined) { - return this.props.Document.data ? - "<FieldView {...props} fieldKey='data' />" : - KeyValueBox.LayoutString(this.layoutDoc.proto ? "proto" : ""); - } else if (typeof layout === "string") { - return layout; - } else { - return "<p>Loading layout</p>"; - } + if (layout === undefined) return this.props.Document.data ? "<FieldView {...props} fieldKey='data' />" : KeyValueBox.LayoutString(this.layoutDoc.proto ? "proto" : ""); + if (typeof layout === "string") return layout; + return "<p>Loading layout</p>"; } get dataDoc() { diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index 748af89ef..067477dcf 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -19,6 +19,7 @@ import { SearchBox } from "../search/SearchBox"; import { FieldView, FieldViewProps } from './FieldView'; import './FilterBox.scss'; import { Scripting } from "../../util/Scripting"; +import { values } from "lodash"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -49,6 +50,39 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key))); return Array.from(keys.keys()).filter(key => key[0] === "#" || key.indexOf("lastModified") !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith("_") && !key.startsWith("acl")) || noviceFields.includes(key)).sort(); } + + gatherFieldValues(dashboard: Doc, facetKey: string) { + const childDocs = DocListCast((dashboard.data as any)[0].data); + const valueSet = new Set<string>(); + let rtFields = 0; + childDocs.forEach((d) => { + const facetVal = d[facetKey]; + if (facetVal instanceof RichTextField) rtFields++; + valueSet.add(Field.toString(facetVal as Field)); + const fieldKey = Doc.LayoutFieldKey(d); + const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView"); + const data = d[annos ? fieldKey + "-annotations" : fieldKey]; + if (data !== undefined) { + let subDocs = DocListCast(data); + if (subDocs.length > 0) { + let newarray: Doc[] = []; + while (subDocs.length > 0) { + newarray = []; + subDocs.forEach((t) => { + const facetVal = t[facetKey]; + if (facetVal instanceof RichTextField) rtFields++; + valueSet.add(Field.toString(facetVal as Field)); + const fieldKey = Doc.LayoutFieldKey(t); + const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView"); + DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc)); + }); + subDocs = newarray; + } + } + } + }); + return { strings: Array.from(valueSet.keys()), rtFields }; + } /** * Responds to clicking the check box in the flyout menu */ @@ -73,17 +107,11 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc } } else { const allCollectionDocs = DocListCast((targetDoc.data as any)[0].data); - var rtfields = 0; - const facetValues = Array.from(allCollectionDocs.reduce((set, child) => { - const field = child[facetHeader] as Field; - const fieldStr = Field.toString(field); - if (field instanceof RichTextField || (typeof (field) === "string" && fieldStr.split(" ").length > 2)) rtfields++; - return set.add(fieldStr); - }, new Set<string>())); + const facetValues = this.gatherFieldValues(targetDoc, facetHeader); let nonNumbers = 0; let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE; - facetValues.map(val => { + facetValues.strings.map(val => { const num = Number(val); if (Number.isNaN(num)) { nonNumbers++; @@ -93,13 +121,13 @@ export class FilterBox extends ViewBoxBaseComponent<FieldViewProps, FilterBoxDoc } }); let newFacet: Opt<Doc>; - if (facetHeader === "text" || rtfields / allCollectionDocs.length > 0.1) { + if (facetHeader === "text" || facetValues.rtFields / allCollectionDocs.length > 0.1) { newFacet = Docs.Create.TextDocument("", { _width: 100, _height: 25, _stayInCollection: true, _hideContextMenu: true, treeViewExpandedView: "layout", title: facetHeader, treeViewOpen: true, forceActive: true, ignoreClick: true }); Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox newFacet._textBoxPadding = 4; const scriptText = `setDocFilter(this?.target, "${facetHeader}", text, "match")`; newFacet.onTextChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, text: "string" }); - } else if (facetHeader !== "tags" && nonNumbers / facetValues.length < .1) { + } else if (facetHeader !== "tags" && nonNumbers / facetValues.strings.length < .1) { newFacet = Docs.Create.SliderDocument({ title: facetHeader, _stayInCollection: true, _hideContextMenu: true, treeViewExpandedView: "layout", treeViewOpen: true }); const newFacetField = Doc.LayoutFieldKey(newFacet); const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader); diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/PresBox.tsx index c5a64af3e..7eca09f8c 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/PresBox.tsx @@ -265,19 +265,19 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> this.layoutDoc.presCollection = srcContext; } else if (targetDoc) this.layoutDoc.presCollection = targetDoc; } - if (collectionDocView) { - if (srcContext && srcContext !== presCollection) { - // Case 1: new srcContext inside of current collection so add a new tab to the current pres collection - collectionDocView.props.addDocTab(srcContext, "inPlace"); - } - } + // if (collectionDocView) { + // if (srcContext && srcContext !== presCollection) { + // // Case 1: new srcContext inside of current collection so add a new tab to the current pres collection + // collectionDocView.props.addDocTab(srcContext, "inPlace"); + // } + // } this.updateCurrentPresentation(); const docToJump = curDoc; const willZoom = false; // If openDocument is selected then it should open the document for the user if (activeItem.openDocument) { - this.props.addDocTab(activeItem, "replace:right"); + collectionDocView ? collectionDocView.props.addDocTab(activeItem, "inPlace") : this.props.addDocTab(activeItem, "replace:right"); } else //docToJump stayed same meaning, it was not in the group or was the last element in the group if (activeItem.zoomProgressivize && this.rootDoc.presStatus !== 'edit') { @@ -1047,7 +1047,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> activeItem.presPinViewX = x; activeItem.presPinViewY = y; activeItem.presPinViewScale = scale; - }}>Update</div> : (null)}; + }}>Update</div> : (null)} </div> <div style={{ display: activeItem.presPinView ? "block" : "none" }}> <div className="ribbon-doubleButton" style={{ marginRight: 10 }}> diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index ff2ce90b7..e92efd708 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1189,7 +1189,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this.endUndoTypingBatch(); Object.values(this._disposers).forEach(disposer => disposer?.()); this._editorView?.destroy(); - FormattedTextBoxComment.Hide(); + FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none"); } _downEvent: any; @@ -1522,16 +1522,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @computed get sidebarColor() { return StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], StrCast(this.layoutDoc[this.props.fieldKey + "-backgroundColor"], "transparent")); } render() { TraceMobx(); + const selected = this.props.isSelected(); + const active = this.active(); 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 && !this.layoutDoc._isBackground; - this.props.isSelected() && setTimeout(() => this._editorView && RichTextMenu.Instance?.updateMenu(this._editorView, undefined, this.props), 0); // need to make sure that we update a text box that is selected after updating the one that was deselected - if (!this.props.isSelected() && FormattedTextBoxComment.textBox === this) { - setTimeout(() => FormattedTextBoxComment.Hide(), 0); - } + 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 selPad = (this.props.isSelected() && !this.layoutDoc._singleLine) || minimal ? -10 : 0; - const selclass = this.props.isSelected() && !this.layoutDoc._singleLine ? "-selected" : ""; + const selPad = (selected && !this.layoutDoc._singleLine) || minimal ? -10 : 0; + const selclass = selected && !this.layoutDoc._singleLine ? "-selected" : ""; return ( <div className={"formattedTextBox-cont"} ref={this._boxRef} style={{ @@ -1564,65 +1564,53 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp onMouseUp={this.onMouseUp} onWheel={this.onPointerWheel} onPointerEnter={action(() => this._entered = true)} - onPointerLeave={action((e: React.PointerEvent<HTMLDivElement>) => { + onPointerLeave={action(e => { this._entered = false; const target = document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y); for (let child: any = target; child; child = child?.parentElement) { - if (child === this._ref.current!) { - this._entered = true; - } + child === this._ref.current! && (this._entered = true); } })} onDoubleClick={this.onDoubleClick} > <div className={`formattedTextBox-outer`} ref={this._scrollRef} - style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: !this.props.active() ? "none" : undefined }} + style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: !active ? "none" : undefined }} onScroll={this.onscrolled} onDrop={this.ondrop} > <div className={minimal ? "formattedTextBox-minimal" : `formattedTextBox-inner${rounded}${selclass}`} ref={this.createDropTarget} style={{ overflow: this.layoutDoc._singleLine ? "hidden" : undefined, padding: this.layoutDoc._textBoxPadding ? StrCast(this.layoutDoc._textBoxPadding) : `${Math.max(0, NumCast(this.layoutDoc._yMargin, this.props.yMargin || 0) + selPad)}px ${NumCast(this.layoutDoc._xMargin, this.props.xMargin || 0) + selPad}px`, - pointerEvents: !this.props.active() ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : undefined) : undefined + pointerEvents: !active ? ((this.layoutDoc.isLinkButton || this.props.onClick) ? "none" : undefined) : undefined }} /> </div> - {!this.layoutDoc._showSidebar ? (null) : <> - <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")} - style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}> - {this.sidebarWidthPercent === "0%" ? (null) : - <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} - PanelHeight={this.active() ? () => 1000 : this.props.PanelHeight} - PanelWidth={this.sidebarWidth} - scaleField={this.annotationKey + "-scale"} - annotationsKey={this.annotationKey} - isAnnotationOverlay={true} - focus={this.props.focus} - isSelected={this.props.isSelected} - select={emptyFunction} - active={this.annotationsActive} - ContentScaling={returnOne} - whenActiveChanged={this.whenActiveChanged} - removeDocument={this.removeDocument} - moveDocument={this.moveDocument} - addDocument={this.addDocument} - CollectionView={undefined} - ScreenToLocalTransform={this.sidebarScreenToLocal} - renderDepth={this.props.renderDepth + 1} - ContainingCollectionDoc={this.props.ContainingCollectionDoc} /> - } - </div> - {this.props.isSelected() ? <div className="formattedTextBox-sidebar-handle" style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 5px))` }} onPointerDown={this.sidebarDown} /> : (null)} - </>} + {!this.layoutDoc._showSidebar || this.sidebarWidthPercent === "0%" ? (null) : + <div className={"formattedTextBox-sidebar" + (Doc.GetSelectedTool() !== InkTool.None ? "-inking" : "")} style={{ width: `${this.sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}> + <CollectionFreeFormView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} + PanelHeight={active ? () => 1000 : this.props.PanelHeight} + PanelWidth={this.sidebarWidth} + scaleField={this.annotationKey + "-scale"} + annotationsKey={this.annotationKey} + isAnnotationOverlay={true} + focus={this.props.focus} + isSelected={this.props.isSelected} + select={emptyFunction} + active={this.annotationsActive} + ContentScaling={returnOne} + whenActiveChanged={this.whenActiveChanged} + removeDocument={this.removeDocument} + moveDocument={this.moveDocument} + addDocument={this.addDocument} + CollectionView={undefined} + ScreenToLocalTransform={this.sidebarScreenToLocal} + renderDepth={this.props.renderDepth + 1} + ContainingCollectionDoc={this.props.ContainingCollectionDoc} /> + </div>} + {selected ? <div className="formattedTextBox-sidebar-handle" style={{ left: `max(0px, calc(100% - ${this.sidebarWidthPercent} - 5px))` }} onPointerDown={this.sidebarDown} /> : (null)} {!this.layoutDoc._showAudio ? (null) : <div className="formattedTextBox-dictation" onClick={action(e => this._recording = !this._recording)} > - <FontAwesomeIcon className="formattedTextBox-audioFont" - style={{ - color: this._recording ? "red" : "blue", - transitionDelay: "0.6s", - opacity: this._recording ? 1 : 0.25, - }} - icon={"microphone"} size="sm" /> + <FontAwesomeIcon className="formattedTextBox-audioFont" style={{ color: this._recording ? "red" : "blue", transitionDelay: "0.6s", opacity: this._recording ? 1 : 0.25, }} icon={"microphone"} size="sm" /> </div>} </div> </div> diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index a37210de6..14bbee1ac 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -143,9 +143,12 @@ export class FormattedTextBoxComment { public static Hide() { FormattedTextBoxComment.textBox = undefined; - FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none"); - ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText); FormattedTextBoxComment.linkDoc = undefined; + FormattedTextBoxComment.tooltip && (FormattedTextBoxComment.tooltip.style.display = "none"); + try { + ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText); + FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText); + } catch (e) { } } public static SetState(textBox: any, start: number, end: number, mark: Mark) { FormattedTextBoxComment.textBox = textBox; @@ -228,17 +231,17 @@ export class FormattedTextBoxComment { if (forceUrl || (href && child && nbef && naft && mark?.attrs.showPreview)) { try { ReactDOM.unmountComponentAtNode(FormattedTextBoxComment.tooltipText); + FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText); } catch (e) { } - FormattedTextBoxComment.tooltip.removeChild(FormattedTextBoxComment.tooltipText); FormattedTextBoxComment.tooltipText = document.createElement("div"); FormattedTextBoxComment.tooltipText.style.width = "100%"; FormattedTextBoxComment.tooltipText.style.height = "100%"; FormattedTextBoxComment.tooltipText.style.textOverflow = "ellipsis"; FormattedTextBoxComment.tooltipText.style.cursor = "pointer"; - FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText); - FormattedTextBoxComment.tooltipText.textContent = "URL: " + href; (FormattedTextBoxComment.tooltipText as any).href = href; + FormattedTextBoxComment.tooltip.appendChild(FormattedTextBoxComment.tooltipText); + if (href.startsWith("https://en.wikipedia.org/wiki/")) { wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(summary => FormattedTextBoxComment.tooltipText.textContent = summary.substring(0, 500))); } else { diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 3238e4dc6..a6ac62ee4 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -530,10 +530,10 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc style={{ cursor: "hand", color: "black", padding: 1, position: "relative" }} /></div> </Tooltip> </div> - <div style={{ position: "absolute", left: 220, width: 30, zIndex: 9000, color: "grey", background: "white", }}> + <div style={{ position: "absolute", left: Doc.UserDoc().noviceMode ? 220 : 200, width: 30, zIndex: 9000, color: "grey", background: "white", }}> {`${this._results.length}` + " of " + `${this.realTotalResults}`} </div> - {/* <div style={{ cursor: "default", left: 235, position: "absolute", }}> + {Doc.UserDoc().noviceMode ? (null) : <div style={{ cursor: "default", left: 235, position: "absolute", }}> <Tooltip title={<div className="dash-tooltip" >only display documents matching search</div>} > <div> <FontAwesomeIcon icon={"filter"} size="lg" @@ -542,7 +542,7 @@ export class SearchBox extends ViewBoxBaseComponent<FieldViewProps, SearchBoxDoc onClick={action(() => this.setSearchFilter(this.currentSelectedCollection, this.filter ? undefined : this.docsforfilter))} /> </div> </Tooltip> - </div> */} + </div>} {this.scopeButtons} </div> </div > diff --git a/src/fields/util.ts b/src/fields/util.ts index 31d5cc5f2..a3849661d 100644 --- a/src/fields/util.ts +++ b/src/fields/util.ts @@ -361,7 +361,7 @@ export function updateFunction(target: any, prop: any, value: any, receiver: any const oldValue = current; const newValue = ObjectField.MakeCopy(value); current = newValue; - if (!(value instanceof CursorField) && !(value?.some((v: any) => v instanceof CursorField))) { + if (!(value instanceof CursorField) && !(value?.some?.((v: any) => v instanceof CursorField))) { UndoManager.AddEvent({ redo() { receiver[prop] = newValue; }, undo() { receiver[prop] = oldValue; } |