aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2023-11-07 13:48:26 -0500
committerbobzel <zzzman@gmail.com>2023-11-07 13:48:26 -0500
commita6cc25e5d03ffed16bfaa32e48e9cc2eaff7deaf (patch)
tree1322ef9a743457f23851ba7c9d5d3dd090f1f75d /src
parenta4e3b645317c4589cf49f8007f6e6b57cf2c12d3 (diff)
Changed how selection works to avoid invalidations. Fixed Cast problem with ProxyFields that caused renameEmbedding to infinite loop.. Changed brushing for the same reason. Cleaned up a few things with filter code.
Diffstat (limited to 'src')
-rw-r--r--src/Utils.ts24
-rw-r--r--src/client/documents/Documents.ts2
-rw-r--r--src/client/util/CurrentUserUtils.ts2
-rw-r--r--src/client/util/SelectionManager.ts59
-rw-r--r--src/client/views/DocComponent.tsx8
-rw-r--r--src/client/views/StyleProvider.tsx6
-rw-r--r--src/client/views/collections/CollectionMenu.tsx19
-rw-r--r--src/client/views/collections/CollectionSubView.tsx2
-rw-r--r--src/client/views/collections/TabDocView.tsx4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx20
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx12
-rw-r--r--src/client/views/nodes/DocumentView.tsx18
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx4
-rw-r--r--src/client/views/nodes/MapBox/MapBox2.tsx4
-rw-r--r--src/client/views/nodes/WebBox.tsx4
-rw-r--r--src/client/views/pdf/Annotation.tsx2
-rw-r--r--src/client/views/pdf/PDFViewer.tsx12
-rw-r--r--src/fields/Doc.ts95
-rw-r--r--src/fields/DocSymbols.ts2
-rw-r--r--src/fields/Types.ts3
20 files changed, 119 insertions, 183 deletions
diff --git a/src/Utils.ts b/src/Utils.ts
index 330ca59f9..9499aaf2f 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -157,23 +157,19 @@ export namespace Utils {
const isTransparentFunctionHack = 'isTransparent(__value__)';
export const noRecursionHack = '__noRecursion';
- export const noDragsDocFilter = 'noDragDocs::any::check';
+
+ // special case filters
+ export const noDragDocsFilter = 'noDragDocs::any::check';
+ export const TransparentBackgroundFilter = `backgroundColor::${isTransparentFunctionHack},${noRecursionHack}::check`; // bcz: hack. noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
+ export const OpaqueBackgroundFilter = `backgroundColor::${isTransparentFunctionHack},${noRecursionHack}::x`; // bcz: hack. noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
+
export function IsRecursiveFilter(val: string) {
return !val.includes(noRecursionHack);
}
- export function HasTransparencyFilter(val: string) {
- return val.includes(isTransparentFunctionHack);
- }
- export function IsTransparentFilter() {
- // bcz: isTransparent(__value__) is a hack. it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one
- return `backgroundColor::${isTransparentFunctionHack},${noRecursionHack}::check`; // bcz: hack. noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
- }
- export function IsOpaqueFilter() {
- // bcz: isTransparent(__value__) is a hack. it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one
- return `backgroundColor::${isTransparentFunctionHack},${noRecursionHack}::x`; // bcz: hack. noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field
- }
- export function IsPropUnsetFilter(prop: string) {
- return `${prop}::any,${noRecursionHack}::unset`;
+ export function HasFunctionFilter(val: string) {
+ if (val.includes(isTransparentFunctionHack)) return (color: string) => color !== '' && DashColor(color).alpha() !== 1;
+ // add other function filters here...
+ return undefined;
}
export function toRGBAstr(col: { r: number; g: number; b: number; a?: number }) {
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index 4086ede20..c5a42aadc 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -1284,7 +1284,7 @@ export namespace DocUtils {
if (d.cookies && (!filterFacets.cookies || !Object.keys(filterFacets.cookies).some(key => d.cookies === key))) {
return false;
}
- for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== Utils.noDragsDocFilter.split(Doc.FilterSep)[0])) {
+ for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== Utils.noDragDocsFilter.split(Doc.FilterSep)[0])) {
const facet = filterFacets[facetKey];
// facets that match some value in the field of the document (e.g. some text field)
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 87ee1b252..ba3c26b42 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -339,7 +339,7 @@ export class CurrentUserUtils {
/// returns descriptions needed to buttons for the left sidebar to open up panes displaying different collections of documents
static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, toolTip: string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}, hidden?: boolean}[] {
- const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length.toString())";
+ const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(self.target?.data).filter(doc => !docList(self.target.viewed).includes(doc)).length.toString())";
const getActiveDashTrails = "Doc.ActiveDashboard?.myTrails";
return [
{ title: "Dashboards", toolTip: "Dashboards", target: this.setupDashboards(doc, "myDashboards"), ignoreClick: true, icon: "desktop", funcs: {hidden: "IsNoviceMode()"} },
diff --git a/src/client/util/SelectionManager.ts b/src/client/util/SelectionManager.ts
index d0f66d124..fcf705ac0 100644
--- a/src/client/util/SelectionManager.ts
+++ b/src/client/util/SelectionManager.ts
@@ -1,6 +1,6 @@
-import { action, observable, ObservableMap } from 'mobx';
-import { computedFn } from 'mobx-utils';
+import { action, observable } from 'mobx';
import { Doc, Opt } from '../../fields/Doc';
+import { DocViews } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
import { listSpec } from '../../fields/Schema';
import { Cast, DocCast } from '../../fields/Types';
@@ -12,9 +12,8 @@ import { UndoManager } from './UndoManager';
export namespace SelectionManager {
class Manager {
- @observable IsDragging: boolean = false;
- SelectedViewsMap: ObservableMap<DocumentView, Doc> = new ObservableMap();
@observable SelectedViews: DocumentView[] = [];
+ @observable IsDragging: boolean = false;
@observable SelectedSchemaDocument: Doc | undefined;
@action
@@ -24,27 +23,21 @@ export namespace SelectionManager {
@action
SelectView(docView: DocumentView, ctrlPressed: boolean): void {
// if doc is not in SelectedDocuments, add it
- if (!manager.SelectedViewsMap.get(docView)) {
- if (!ctrlPressed) {
- this.DeselectAll();
- }
-
+ if (!docView.SELECTED) {
+ if (!ctrlPressed) this.DeselectAll();
manager.SelectedViews.push(docView);
- manager.SelectedViewsMap.set(docView, docView.rootDoc);
- docView.props.whenChildContentsActiveChanged(true);
- } else if (!ctrlPressed && (Array.from(manager.SelectedViewsMap.entries()).length > 1 || manager.SelectedSchemaDocument)) {
- Array.from(manager.SelectedViewsMap.keys()).map(dv => dv !== docView && dv.props.whenChildContentsActiveChanged(false));
+ } else if (!ctrlPressed && (manager.SelectedViews.length > 1 || manager.SelectedSchemaDocument)) {
+ manager.SelectedViews.filter(dv => dv !== docView).forEach(dv => dv.props.whenChildContentsActiveChanged(false));
manager.SelectedSchemaDocument = undefined;
manager.SelectedViews.length = 0;
- manager.SelectedViewsMap.clear();
- manager.SelectedViews.push(docView);
- manager.SelectedViewsMap.set(docView, docView.rootDoc);
}
+ docView.SELECTED = true;
+ docView.props.whenChildContentsActiveChanged(true);
}
@action
DeselectView(docView?: DocumentView): void {
- if (docView && manager.SelectedViewsMap.get(docView)) {
- manager.SelectedViewsMap.delete(docView);
+ if (docView && manager.SelectedViews.includes(docView)) {
+ docView.SELECTED = false;
manager.SelectedViews.splice(manager.SelectedViews.indexOf(docView), 1);
docView.props.whenChildContentsActiveChanged(false);
}
@@ -54,8 +47,10 @@ export namespace SelectionManager {
LinkManager.currentLink = undefined;
LinkManager.currentLinkAnchor = undefined;
manager.SelectedSchemaDocument = undefined;
- Array.from(manager.SelectedViewsMap.keys()).forEach(dv => dv.props.whenChildContentsActiveChanged(false));
- manager.SelectedViewsMap.clear();
+ manager.SelectedViews.forEach(dv => {
+ dv.SELECTED = false;
+ dv.props.whenChildContentsActiveChanged(false);
+ });
manager.SelectedViews.length = 0;
}
}
@@ -74,45 +69,27 @@ export namespace SelectionManager {
manager.SelectSchemaViewDoc(document);
}
- const IsSelectedCache = computedFn(function isSelected(doc: DocumentView) {
- // wrapping get() in a computedFn only generates mobx() invalidations when the return value of the function for the specific get parameters has changed
- return manager.SelectedViewsMap.get(doc) ? true : false;
- });
// computed functions, such as used in IsSelected generate errors if they're called outside of a
// reaction context. Specifying the context with 'outsideReaction' allows an efficiency feature
// to avoid unnecessary mobx invalidations when running inside a reaction.
- export function IsSelected(doc: DocumentView | undefined, outsideReaction?: boolean): boolean {
- return !doc
- ? false
- : outsideReaction
- ? manager.SelectedViewsMap.get(doc)
- ? true
- : false // get() accesses a hashtable -- setting anything in the hashtable generates a mobx invalidation for every get()
- : IsSelectedCache(doc);
+ export function IsSelected(dv?: DocumentView | Doc): boolean {
+ return (dv instanceof Doc ? Array.from(dv[DocViews]) : dv ? [dv] : []).some(dv => dv?.SELECTED);
}
export function DeselectAll(except?: Doc): void {
- let found: DocumentView | undefined = undefined;
- if (except) {
- for (const view of Array.from(manager.SelectedViewsMap.keys())) {
- if (view.props.Document === except) found = view;
- }
- }
-
+ const found = manager.SelectedViews.find(dv => dv.Document === except);
manager.DeselectAll();
if (found) manager.SelectView(found, false);
}
export function Views(): Array<DocumentView> {
return manager.SelectedViews;
- // Array.from(manager.SelectedViewsMap.keys()); //.filter(dv => manager.SelectedViews.get(dv)?._type_collection !== CollectionViewType.Docking);
}
export function SelectedSchemaDoc(): Doc | undefined {
return manager.SelectedSchemaDocument;
}
export function Docs(): Doc[] {
return manager.SelectedViews.map(dv => dv.rootDoc).filter(doc => doc?._type_collection !== CollectionViewType.Docking);
- // Array.from(manager.SelectedViewsMap.values()).filter(doc => doc?._type_collection !== CollectionViewType.Docking);
}
}
ScriptingGlobals.add(function SelectionManager_selectedDocType(type: string, expertMode: boolean, checkContext?: boolean) {
diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx
index 483b92957..57cea77c9 100644
--- a/src/client/views/DocComponent.tsx
+++ b/src/client/views/DocComponent.tsx
@@ -1,10 +1,10 @@
import { action, computed, observable } from 'mobx';
import { DateField } from '../../fields/DateField';
-import { Doc, DocListCast, HierarchyMapping, Opt, ReverseHierarchyMap } from '../../fields/Doc';
-import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocAcl, DocData } from '../../fields/DocSymbols';
+import { Doc, DocListCast, Opt } from '../../fields/Doc';
+import { AclAdmin, AclAugment, AclEdit, AclPrivate, AclReadonly, DocData } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
-import { Cast, DocCast, StrCast } from '../../fields/Types';
-import { distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util';
+import { Cast } from '../../fields/Types';
+import { GetEffectiveAcl, inheritParentAcls } from '../../fields/util';
import { returnFalse } from '../../Utils';
import { DocUtils } from '../documents/Documents';
import { DocumentType } from '../documents/DocumentTypes';
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index c6d3efd0c..72d7cd1c5 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -7,13 +7,13 @@ import { extname } from 'path';
import { BsArrowDown, BsArrowDownUp, BsArrowUp } from 'react-icons/bs';
import { FaFilter } from 'react-icons/fa';
import { Doc, Opt, StrListCast } from '../../fields/Doc';
+import { DocViews } from '../../fields/DocSymbols';
import { BoolCast, Cast, DocCast, ImageCast, NumCast, StrCast } from '../../fields/Types';
import { DashColor, lightOrDark, Utils } from '../../Utils';
import { CollectionViewType, DocumentType } from '../documents/DocumentTypes';
import { DocFocusOrOpen, DocumentManager } from '../util/DocumentManager';
import { IsFollowLinkScript } from '../util/LinkFollower';
import { LinkManager } from '../util/LinkManager';
-import { SelectionManager } from '../util/SelectionManager';
import { SettingsManager } from '../util/SettingsManager';
import { undoBatch, UndoManager } from '../util/UndoManager';
import { TreeSort } from './collections/TreeSort';
@@ -116,7 +116,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
case StyleProp.Highlighting:
if (doc && (Doc.IsSystem(doc) || doc.type === DocumentType.FONTICON)) return undefined;
if (doc && !doc.layout_disableBrushing && !props?.disableBrushing) {
- const selected = SelectionManager.Views().some(dv => dv.rootDoc === doc);
+ const selected = Array.from(doc?.[DocViews]??[]).filter(dv => dv.SELECTED).length;
const highlightIndex = Doc.isBrushedHighlightedDegree(doc) || (selected ? Doc.DocBrushStatus.selfBrushed : 0);
const highlightColor = ['transparent', 'rgb(68, 118, 247)', selected ? "black" : 'rgb(68, 118, 247)', 'orange', 'lightBlue'][highlightIndex];
const highlightStyle = ['solid', 'dashed', 'solid', 'solid', 'solid'][highlightIndex];
@@ -282,7 +282,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps
const showFilterIcon =
StrListCast(doc?._childFilters).length || StrListCast(doc?._childFiltersByRanges).length
? 'green' // #18c718bd' //'hasFilter'
- : docProps?.childFilters?.().filter(f => Utils.IsRecursiveFilter(f) && f !== Utils.noDragsDocFilter).length || docProps?.childFiltersByRanges().length
+ : docProps?.childFilters?.().filter(f => Utils.IsRecursiveFilter(f) && f !== Utils.noDragDocsFilter).length || docProps?.childFiltersByRanges().length
? 'orange' //'inheritsFilter'
: undefined;
return !showFilterIcon ? null : (
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 52cf40635..d0eadd9aa 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -557,25 +557,6 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
return this.props.type === CollectionViewType.Docking || (typeof layoutField === 'string' && !layoutField?.includes('CollectionView'));
} else return false;
}
- @computed
- get pinButton() {
- const targetDoc = this.selectedDoc;
- const isPinned = targetDoc && Doc.isDocPinned(targetDoc);
- return !targetDoc ? null : (
- <Tooltip key="pin" title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? 'Unpin from presentation' : 'Pin to presentation'}</div>} placement="top">
- <button
- className="antimodeMenu-button"
- style={{ backgroundColor: isPinned ? '121212' : undefined, borderLeft: '1px solid gray' }}
- onClick={e =>
- TabDocView.PinDoc(targetDoc, {
- /* unpin: isPinned*/
- })
- }>
- <FontAwesomeIcon className="colMenu-icon" size="lg" icon="map-pin" />
- </button>
- </Tooltip>
- );
- }
@undoBatch
@action
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 8a1ba0df1..e9192ebbe 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -125,7 +125,7 @@ export function CollectionSubView<X>(moreProps?: X) {
const docsforFilter: Doc[] = [];
childDocs.forEach(d => {
// dragging facets
- const dragged = this.props.childFilters?.().some(f => f.includes(Utils.noDragsDocFilter));
+ const dragged = this.props.childFilters?.().some(f => f.includes(Utils.noDragDocsFilter));
if (dragged && SnappingManager.GetCanEmbed() && DragManager.docsBeingDragged.includes(d)) return false;
let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), childFiltersByRanges, this.props.Document).length > 0;
if (notFiltered) {
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index 26aa5a121..41f3b2603 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -53,7 +53,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
@observable _isActive: boolean = false;
@observable _isAnyChildContentActive = false;
@computed get _isUserActivated() {
- return SelectionManager.Views().some(view => view.rootDoc === this._document) || this._isAnyChildContentActive;
+ return SelectionManager.IsSelected(this._document) || this._isAnyChildContentActive;
}
@computed get _isContentActive() {
return this._isUserActivated || this._hovering;
@@ -203,7 +203,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
}
});
tab._disposers.selectionDisposer = reaction(
- () => SelectionManager.Views().some(view => view.rootDoc === this._document),
+ () => SelectionManager.IsSelected(this._document),
action(selected => {
if (selected) this._activated = true;
const toggle = tab.element[0].children[2].children[0] as HTMLInputElement;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 24a758d8c..aca6df3c9 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -1,7 +1,7 @@
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, Field } from '../../../../fields/Doc';
-import { DocCss } from '../../../../fields/DocSymbols';
+import { Brushed, DocCss } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { List } from '../../../../fields/List';
import { Cast, NumCast, StrCast } from '../../../../fields/Types';
@@ -223,8 +223,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const pt2normlen = Math.sqrt(pt2norm[0] * pt2norm[0] + pt2norm[1] * pt2norm[1]) || 1;
const pt1normalized = [pt1norm[0] / pt1normlen, pt1norm[1] / pt1normlen];
const pt2normalized = [pt2norm[0] / pt2normlen, pt2norm[1] / pt2normlen];
- const aActive = A.isSelected() || Doc.IsBrushed(A.rootDoc);
- const bActive = B.isSelected() || Doc.IsBrushed(B.rootDoc);
+ const aActive = A.isSelected() || A.rootDoc[Brushed];
+ const bActive = B.isSelected() || B.rootDoc[Brushed];
const textX = (Math.min(pt1[0], pt2[0]) + Math.max(pt1[0], pt2[0])) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetX);
const textY = (pt1[1] + pt2[1]) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetY);
@@ -239,11 +239,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
textX,
textY,
// fully connected
- // pt1,
- // pt2,
+ // pt1,
+ // pt2,
// this code adds space between links
- pt1: link.link_displayArrow ? [pt1[0] + pt1normalized[0] * 3*NumCast(link.link_displayArrow_scale, 4), pt1[1] + pt1normalized[1] * 3*NumCast(link.link_displayArrow_scale, 3)] : pt1,
- pt2: link.link_displayArrow ? [pt2[0] + pt2normalized[0] * 3*NumCast(link.link_displayArrow_scale, 4), pt2[1] + pt2normalized[1] * 3*NumCast(link.link_displayArrow_scale, 3)] : pt2,
+ pt1: link.link_displayArrow ? [pt1[0] + pt1normalized[0] * 3 * NumCast(link.link_displayArrow_scale, 4), pt1[1] + pt1normalized[1] * 3 * NumCast(link.link_displayArrow_scale, 3)] : pt1,
+ pt2: link.link_displayArrow ? [pt2[0] + pt2normalized[0] * 3 * NumCast(link.link_displayArrow_scale, 4), pt2[1] + pt2normalized[1] * 3 * NumCast(link.link_displayArrow_scale, 3)] : pt2,
};
}
@@ -269,12 +269,12 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
//thickness varies linearly from 3px to 12px for increasing link count
const strokeWidth = linkSize === -1 ? '3px' : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + 'px';
- const arrowScale = NumCast(link.link_displayArrow_scale, 3)
+ const arrowScale = NumCast(link.link_displayArrow_scale, 3);
return link.opacity === 0 || !a.width || !b.width || (!(Doc.UserDoc().showLinkLines || link.link_displayLine) && !aActive && !bActive) ? null : (
<>
<defs>
- <marker id={`${link[Id] + 'arrowhead'}`} markerWidth={`${4*arrowScale}`} markerHeight={`${3*arrowScale}`} refX="0" refY={`${1.5*arrowScale}`} orient="auto">
- <polygon points={`0 0, ${3*arrowScale} ${1.5*arrowScale}, 0 ${3*arrowScale}`} fill={stroke} />
+ <marker id={`${link[Id] + 'arrowhead'}`} markerWidth={`${4 * arrowScale}`} markerHeight={`${3 * arrowScale}`} refX="0" refY={`${1.5 * arrowScale}`} orient="auto">
+ <polygon points={`0 0, ${3 * arrowScale} ${1.5 * arrowScale}, 0 ${3 * arrowScale}`} fill={stroke} />
</marker>
<filter id="outline">
<feMorphology in="SourceAlpha" result="expanded" operator="dilate" radius="1" />
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index f73c037f4..ce63a2cf2 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -86,7 +86,17 @@ export class CollectionSchemaView extends CollectionSubView() {
}
@computed get _selectedDocs() {
- return SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.rootDoc));
+ const selected = SelectionManager.Docs().filter(doc => Doc.AreProtosEqual(DocCast(doc.embedContainer), this.rootDoc));
+ if (!selected.length) {
+ for (const sel of SelectionManager.Docs()) {
+ const contextPath = DocumentManager.GetContextPath(sel, true);
+ if (contextPath.includes(this.rootDoc)) {
+ const parentInd = contextPath.indexOf(this.rootDoc);
+ return parentInd < contextPath.length - 1 ? [contextPath[parentInd + 1]] : [];
+ }
+ }
+ }
+ return selected;
}
@computed get documentKeys() {
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 98b13f90f..d87efb7b1 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -5,7 +5,7 @@ import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal';
import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
-import { AclPrivate, Animation, AudioPlay, DocData, Width } from '../../../fields/DocSymbols';
+import { AclPrivate, Animation, AudioPlay, DocData, DocViews, Width } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
@@ -900,7 +900,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@computed get _rootSelected() {
return this.props.isSelected(false) || (this.props.Document.rootDocument && this.props.rootSelected?.(false)) || false;
}
- rootSelected = (outsideReaction?: boolean) => this._rootSelected;
+ rootSelected = () => this._rootSelected;
panelHeight = () => this.props.PanelHeight() - this.headerMargin;
screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin);
onClickFunc: any = () => (this.disableClickScriptFunc ? undefined : this.onClickHandler);
@@ -1345,6 +1345,13 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps
@observer
export class DocumentView extends React.Component<DocumentViewProps> {
public static ROOT_DIV = 'documentView-effectsWrapper';
+ @observable _selected = false;
+ public get SELECTED() {
+ return this._selected;
+ }
+ public set SELECTED(val) {
+ this._selected = val;
+ }
@observable public static Interacting = false;
@observable public static LongPress = false;
@observable public static ExploreMode = false;
@@ -1579,7 +1586,7 @@ export class DocumentView extends React.Component<DocumentViewProps> {
scaleToScreenSpace = () => (1 / (this.props.NativeDimScaling?.() || 1)) * this.screenToLocalTransform().Scale;
docViewPathFunc = () => this.docViewPath;
- isSelected = (outsideReaction?: boolean) => SelectionManager.IsSelected(this, outsideReaction);
+ isSelected = () => SelectionManager.IsSelected(this);
select = (extendSelection: boolean, focusSelection?: boolean) => {
SelectionManager.SelectView(this, extendSelection);
if (focusSelection) {
@@ -1602,7 +1609,10 @@ export class DocumentView extends React.Component<DocumentViewProps> {
.ScreenToLocalTransform()
.translate(-this.centeringX, -this.centeringY)
.scale(this.trueNativeWidth() ? 1 / this.nativeScaling : 1);
+
+ @action
componentDidMount() {
+ this.rootDoc[DocViews].add(this);
this._disposers.updateContentsScript = reaction(
() => ScriptCast(this.rootDoc.updateContentsScript)?.script?.run({ this: this.props.Document, self: Cast(this.rootDoc, Doc, null) || this.props.Document }).result,
output => output
@@ -1617,7 +1627,9 @@ export class DocumentView extends React.Component<DocumentViewProps> {
);
!BoolCast(this.props.Document.dontRegisterView, this.props.dontRegisterView) && DocumentManager.Instance.AddView(this);
}
+ @action
componentWillUnmount() {
+ this.rootDoc[DocViews].delete(this);
Object.values(this._disposers).forEach(disposer => disposer?.());
!BoolCast(this.props.Document.dontRegisterView, this.props.dontRegisterView) && DocumentManager.Instance.RemoveView(this);
}
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index 50b070e7f..08dda2e1f 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -307,8 +307,8 @@ export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
- transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
- opaqueFilter = () => [...this.props.childFilters(), Utils.IsOpaqueFilter()];
+ transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
+ opaqueFilter = () => [...this.props.childFilters(), Utils.OpaqueBackgroundFilter];
infoWidth = () => this.props.PanelWidth() / 5;
infoHeight = () => this.props.PanelHeight() / 5;
anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
diff --git a/src/client/views/nodes/MapBox/MapBox2.tsx b/src/client/views/nodes/MapBox/MapBox2.tsx
index 407a91dd0..d38857d90 100644
--- a/src/client/views/nodes/MapBox/MapBox2.tsx
+++ b/src/client/views/nodes/MapBox/MapBox2.tsx
@@ -547,8 +547,8 @@ export class MapBox2 extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
- transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
- opaqueFilter = () => [...this.props.childFilters(), Utils.IsOpaqueFilter()];
+ transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
+ opaqueFilter = () => [...this.props.childFilters(), Utils.OpaqueBackgroundFilter];
infoWidth = () => this.props.PanelWidth() / 5;
infoHeight = () => this.props.PanelHeight() / 5;
anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 2aca314da..5c526fe38 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -1053,8 +1053,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps
panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
- transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
- opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(SnappingManager.GetCanEmbed() ? [] : [Utils.IsOpaqueFilter()])];
+ transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
+ opaqueFilter = () => [...this.props.childFilters(), Utils.noDragDocsFilter, ...(SnappingManager.GetCanEmbed() ? [] : [Utils.OpaqueBackgroundFilter])];
childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
if (doc instanceof Doc && property === StyleProp.PointerEvents) {
if (this.inlineTextAnnotations.includes(doc)) return 'none';
diff --git a/src/client/views/pdf/Annotation.tsx b/src/client/views/pdf/Annotation.tsx
index 52904b852..17a8048e9 100644
--- a/src/client/views/pdf/Annotation.tsx
+++ b/src/client/views/pdf/Annotation.tsx
@@ -90,7 +90,7 @@ class RegionAnnotation extends React.Component<IRegionAnnotationProps> {
@computed get linkHighlighted() {
for (const link of LinkManager.Instance.getAllDirectLinks(this.props.document)) {
const a1 = LinkManager.getOppositeAnchor(link, this.props.document);
- if (a1 && Doc.IsBrushedDegreeUnmemoized(DocCast(a1.annotationOn, this.props.document))) return true;
+ if (a1 && Doc.IsBrushedDegree(DocCast(a1.annotationOn, this.props.document))) return true;
}
}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx
index 43b662f0f..c1b2749fb 100644
--- a/src/client/views/pdf/PDFViewer.tsx
+++ b/src/client/views/pdf/PDFViewer.tsx
@@ -110,13 +110,7 @@ export class PDFViewer extends React.Component<IViewerProps> {
this._disposers.selected = reaction(
() => this.props.isSelected(),
- selected => {
- // if (!selected) {
- // Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove()));
- // Array.from(this._savedAnnotations.keys()).forEach(k => this._savedAnnotations.set(k, []));
- // }
- SelectionManager.Views().length === 1 && this.setupPdfJsViewer();
- },
+ selected => SelectionManager.Views().length === 1 && this.setupPdfJsViewer(),
{ fireImmediately: true }
);
this._disposers.curPage = reaction(
@@ -509,8 +503,8 @@ export class PDFViewer extends React.Component<IViewerProps> {
overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._freeform_scale, 1));
panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1);
panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
- transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
- opaqueFilter = () => [...this.props.childFilters(), Utils.noDragsDocFilter, ...(SnappingManager.GetCanEmbed() && this.props.isContentActive() ? [] : [Utils.IsOpaqueFilter()])];
+ transparentFilter = () => [...this.props.childFilters(), Utils.TransparentBackgroundFilter];
+ opaqueFilter = () => [...this.props.childFilters(), Utils.noDragDocsFilter, ...(SnappingManager.GetCanEmbed() && this.props.isContentActive() ? [] : [Utils.OpaqueBackgroundFilter])];
childStyleProvider = (doc: Doc | undefined, props: Opt<DocumentViewProps>, property: string): any => {
if (doc instanceof Doc && property === StyleProp.PointerEvents) {
if (this.inlineTextAnnotations.includes(doc) || this.props.isContentActive() === false) return 'none';
diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts
index feacdc9c5..2f9eea492 100644
--- a/src/fields/Doc.ts
+++ b/src/fields/Doc.ts
@@ -11,7 +11,7 @@ import { afterDocDeserialize, autoObject, Deserializable, SerializationHelper }
import { undoable } from '../client/util/UndoManager';
import { decycle } from '../decycler/decycler';
import * as JSZipUtils from '../JSZipUtils';
-import { DashColor, incrementTitleCopy, intersectRect, Utils } from '../Utils';
+import { incrementTitleCopy, intersectRect, Utils } from '../Utils';
import { DateField } from './DateField';
import {
AclAdmin,
@@ -21,6 +21,7 @@ import {
AclReadonly,
Animation,
AudioPlay,
+ Brushed,
CachedUpdates,
DirectLinks,
DocAcl,
@@ -28,6 +29,7 @@ import {
DocData,
DocFields,
DocLayout,
+ DocViews,
FieldKeys,
FieldTuples,
ForceServerWrite,
@@ -52,6 +54,7 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, StrCast, ToConstructor }
import { AudioField, CsvField, ImageField, PdfField, VideoField, WebField } from './URLField';
import { containedFieldChangedHandler, deleteProperty, GetEffectiveAcl, getField, getter, makeEditable, makeReadOnly, normalizeEmail, setter, SharingPermissions, TraceMobx } from './util';
import JSZip = require('jszip');
+import { DocumentView } from '../client/views/nodes/DocumentView';
export const LinkedTo = '-linkedTo';
export namespace Field {
export function toKeyValueString(doc: Doc, key: string): string {
@@ -354,6 +357,8 @@ export class Doc extends RefField {
@observable public [AudioPlay]: any; // meant to store sound object from Howl
@observable public [Animation]: Opt<Doc>;
@observable public [Highlight]: boolean = false;
+ @observable public [Brushed]: boolean = false;
+ @observable public [DocViews] = new ObservableSet<DocumentView>();
static __Anim(Doc: Doc) {
// for debugging to print AnimationSym field easily.
return Doc[Animation];
@@ -808,9 +813,7 @@ export namespace Doc {
const docAtKey = DocCast(clone[key]);
if (docAtKey && !Doc.IsSystem(docAtKey)) {
if (!Array.from(cloneMap.values()).includes(docAtKey)) {
- if (cloneMap.has(docAtKey[Id])) {
- clone[key] = cloneMap.get(docAtKey[Id]);
- } else clone[key] = undefined;
+ clone[key] = cloneMap.get(docAtKey[Id]);
} else {
repairClone(docAtKey, cloneMap, visited);
}
@@ -1020,7 +1023,7 @@ export namespace Doc {
references.add(doc);
return;
}
- const excludeLists = ['My Recently Closed', 'My Header Bar', 'My Dashboards'].includes(StrCast(doc.title));
+ const excludeLists = [Doc.MyRecentlyClosed, Doc.MyHeaderBar, Doc.MyDashboards].includes(doc);
if (system !== undefined && ((system && !Doc.IsSystem(doc)) || (!system && Doc.IsSystem(doc)))) return;
references.add(doc);
Object.keys(doc).forEach(key => {
@@ -1202,9 +1205,6 @@ export namespace Doc {
// change it to render the target metadata field instead of what it was rendering before and assign it to the template field layout document.
Doc.Layout(templateField).layout = templateFieldLayoutString.replace(/fieldKey={'[^']*'}/, `fieldKey={'${metadataFieldKey}'}`);
- // assign the template field doc a delegate of any extension document that was previously used to render the template field (since extension doc's carry rendering informatino)
- Doc.Layout(templateField)[metadataFieldKey + '_ext'] = Doc.MakeDelegate(templateField[templateFieldLayoutString?.split("'")[1] + '_ext'] as Doc);
-
return true;
}
@@ -1234,13 +1234,13 @@ export namespace Doc {
export function isBrushedHighlightedDegree(doc: Doc) {
return Doc.IsHighlighted(doc) ? DocBrushStatus.highlighted : Doc.IsBrushedDegree(doc);
}
- export function isBrushedHighlightedDegreeUnmemoized(doc: Doc) {
- return Doc.IsHighlighted(doc) ? DocBrushStatus.highlighted : Doc.IsBrushedDegreeUnmemoized(doc);
- }
-
export class DocBrush {
- BrushedDoc: ObservableMap<Doc, boolean> = new ObservableMap();
+ BrushedDoc = new Set<Doc>();
SearchMatchDoc: ObservableMap<Doc, { searchMatch: number }> = new ObservableMap();
+ brushDoc = action((doc: Doc, unbrush: boolean) => {
+ unbrush ? this.BrushedDoc.delete(doc) : this.BrushedDoc.add(doc);
+ doc[Brushed] = !unbrush;
+ });
}
export const brushManager = new DocBrush();
@@ -1330,45 +1330,25 @@ export namespace Doc {
brushManager.SearchMatchDoc.clear();
}
- const isBrushedCache = computedFn(function IsBrushed(doc: Doc) {
- return brushManager.BrushedDoc.has(doc) || brushManager.BrushedDoc.has(Doc.GetProto(doc));
- });
- export function IsBrushed(doc: Doc) {
- return isBrushedCache(doc);
- }
-
export enum DocBrushStatus {
unbrushed = 0,
protoBrushed = 1,
selfBrushed = 2,
highlighted = 3,
}
- // don't bother memoizing (caching) the result if called from a non-reactive context. (plus this avoids a warning message)
- export function IsBrushedDegreeUnmemoized(doc: Doc) {
- if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return DocBrushStatus.unbrushed;
- const status = brushManager.BrushedDoc.has(doc) ? DocBrushStatus.selfBrushed : brushManager.BrushedDoc.has(Doc.GetProto(doc)) ? DocBrushStatus.protoBrushed : DocBrushStatus.unbrushed;
- return status;
- }
+ // returns 'how' a Doc has been brushed over - whether the document itself was brushed, it's prototype, or neither
export function IsBrushedDegree(doc: Doc) {
- return computedFn(function IsBrushDegree(doc: Doc) {
- return Doc.IsBrushedDegreeUnmemoized(doc);
- })(doc);
+ if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate || doc.opacity === 0) return DocBrushStatus.unbrushed;
+ return doc[Brushed] ? DocBrushStatus.selfBrushed : Doc.GetProto(doc)[Brushed] ? DocBrushStatus.protoBrushed : DocBrushStatus.unbrushed;
}
- export function BrushDoc(doc: Doc) {
+ export function BrushDoc(doc: Doc, unbrush = false) {
if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc;
- runInAction(() => {
- brushManager.BrushedDoc.set(doc, true);
- brushManager.BrushedDoc.set(Doc.GetProto(doc), true);
- });
+ brushManager.brushDoc(doc, unbrush);
+ brushManager.brushDoc(Doc.GetProto(doc), unbrush);
return doc;
}
export function UnBrushDoc(doc: Doc) {
- if (!doc || GetEffectiveAcl(doc) === AclPrivate || GetEffectiveAcl(Doc.GetProto(doc)) === AclPrivate) return doc;
- runInAction(() => {
- brushManager.BrushedDoc.delete(doc);
- brushManager.BrushedDoc.delete(Doc.GetProto(doc));
- });
- return doc;
+ return BrushDoc(doc, true);
}
export function LinkEndpoint(linkDoc: Doc, anchorDoc: Doc) {
@@ -1436,7 +1416,7 @@ export namespace Doc {
});
}
export function UnBrushAllDocs() {
- runInAction(() => brushManager.BrushedDoc.clear());
+ Array.from(brushManager.BrushedDoc).forEach(action(doc => (doc[Brushed] = false)));
}
export function getDocTemplate(doc?: Doc) {
@@ -1454,9 +1434,9 @@ export namespace Doc {
}
export function matchFieldValue(doc: Doc, key: string, value: any): boolean {
- if (Utils.HasTransparencyFilter(value)) {
- const isTransparent = (color: string) => color !== '' && DashColor(color).alpha() !== 1;
- return isTransparent(StrCast(doc[key]));
+ const hasFunctionFilter = Utils.HasFunctionFilter(value);
+ if (hasFunctionFilter) {
+ return hasFunctionFilter(StrCast(doc[key]));
}
if (key === LinkedTo) {
// links are not a field value, so handled here. value is an expression of form ([field=]idToDoc("..."))
@@ -1481,11 +1461,9 @@ export namespace Doc {
(value === Doc.FilterNone && fieldVal === undefined)) {
return true;
}
- if (Cast(fieldVal, listSpec('string'), []).length) {
- const vals = StrListCast(fieldVal);
- const docs = vals.some(v => (v as any) instanceof Doc);
- if (docs) return value === Field.toString(fieldVal as Field);
- return vals.some(v => v.includes(value)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
+ const vals = StrListCast(fieldVal); // list typing is very imperfect. casting to a string list doesn't mean that the entries will actually be strings
+ if (vals.length) {
+ return vals.some(v => typeof v === 'string' && v.includes(value)); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
}
return Field.toString(fieldVal as Field).includes(value); // bcz: arghh: Todo: comparison should be parameterized as exact, or substring
}
@@ -1501,34 +1479,27 @@ export namespace Doc {
doc.layout_fieldKey = deiconify || 'layout';
}
export function setDocRangeFilter(container: Opt<Doc>, key: string, range?: readonly number[], modifiers?: 'remove') {
- //, modifiers: 'remove' | 'set'
if (!container) return;
const childFiltersByRanges = Cast(container._childFiltersByRanges, listSpec('string'), []);
for (let i = 0; i < childFiltersByRanges.length; i += 3) {
if (childFiltersByRanges[i] === key) {
- console.log('this is key inside childfilters by range ' + key);
childFiltersByRanges.splice(i, 3);
- console.log('this is child filters by range ' + childFiltersByRanges);
break;
}
}
if (range !== undefined) {
- console.log('in doc.ts in set range filter');
childFiltersByRanges.push(key);
childFiltersByRanges.push(range[0].toString());
childFiltersByRanges.push(range[1].toString());
container._childFiltersByRanges = new List<string>(childFiltersByRanges);
- console.log('this is child filters by range ' + childFiltersByRanges[0] + ',' + childFiltersByRanges[1] + ',' + childFiltersByRanges[2]);
- console.log('this is new list ' + container._childFiltersByRange);
}
if (modifiers) {
childFiltersByRanges.splice(0, 3);
container._childFiltersByRanges = new List<string>(childFiltersByRanges);
}
- console.log('this is child filters by range END' + childFiltersByRanges[0] + ',' + childFiltersByRanges[1] + ',' + childFiltersByRanges[2]);
}
export const FilterSep = '::';
@@ -1599,12 +1570,6 @@ export namespace Doc {
});
}
- export function isDocPinned(doc: Doc) {
- //add this new doc to props.Document
- const curPres = Doc.ActivePresentation;
- return !curPres ? false : DocListCast(curPres.data).findIndex(val => Doc.AreProtosEqual(val, doc)) !== -1;
- }
-
export function styleFromLayoutString(rootDoc: Doc, layoutDoc: Doc, props: any, scale: number) {
const style: { [key: string]: any } = {};
const divKeys = ['width', 'height', 'fontSize', 'transform', 'left', 'backgroundColor', 'left', 'right', 'top', 'bottom', 'pointerEvents', 'position'];
@@ -1628,7 +1593,7 @@ export namespace Doc {
if (ptx !== undefined && pty !== undefined && newPoint !== undefined) {
const firstx = list.length ? NumCast(list[0].x) + ptx - newPoint[0] : 0;
const firsty = list.length ? NumCast(list[0].y) + pty - newPoint[1] : 0;
- docs.map(doc => {
+ docs.forEach(doc => {
doc.x = NumCast(doc.x) - firstx;
doc.y = NumCast(doc.y) - firsty;
});
@@ -1871,10 +1836,6 @@ ScriptingGlobals.add(function setInPlace(doc: any, field: any, value: any) {
ScriptingGlobals.add(function sameDocs(doc1: any, doc2: any) {
return Doc.AreProtosEqual(doc1, doc2);
});
-ScriptingGlobals.add(function DOC(id: string) {
- console.log("Can't parse a document id in a script");
- return 'invalid';
-});
ScriptingGlobals.add(function assignDoc(doc: Doc, field: string, id: string) {
return Doc.assignDocToField(doc, field, id);
});
diff --git a/src/fields/DocSymbols.ts b/src/fields/DocSymbols.ts
index df74cc9fe..87f186a23 100644
--- a/src/fields/DocSymbols.ts
+++ b/src/fields/DocSymbols.ts
@@ -8,6 +8,8 @@ export const Width = Symbol('DocWidth');
export const Height = Symbol('DocHeight');
export const Animation = Symbol('DocAnimation');
export const Highlight = Symbol('DocHighlight');
+export const DocViews = Symbol('DocViews');
+export const Brushed = Symbol('DocBrushed');
export const DocData = Symbol('DocData');
export const DocLayout = Symbol('DocLayout');
export const DocFields = Symbol('DocFields');
diff --git a/src/fields/Types.ts b/src/fields/Types.ts
index 69dbe9756..337e8ca21 100644
--- a/src/fields/Types.ts
+++ b/src/fields/Types.ts
@@ -1,6 +1,7 @@
import { DateField } from './DateField';
import { Doc, Field, FieldResult, Opt } from './Doc';
import { List } from './List';
+import { ProxyField } from './Proxy';
import { RefField } from './RefField';
import { RichTextField } from './RichTextField';
import { ScriptField } from './ScriptField';
@@ -72,6 +73,8 @@ export function Cast<T extends CastCtor>(field: FieldResult, ctor: T, defaultVal
}
} else if (field instanceof (ctor as any)) {
return field as ToType<T>;
+ } else if (field instanceof ProxyField && field.value instanceof (ctor as any)) {
+ return field.value as ToType<T>;
}
}
return defaultVal === null ? undefined : defaultVal;