From e8fcbbf57b2a2f443d9c280ce10558cf9d51c632 Mon Sep 17 00:00:00 2001 From: vellichora Date: Sun, 9 Feb 2020 17:11:24 -0500 Subject: can upload collection from mobile to desktop --- src/client/views/collections/CollectionView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/collections/CollectionView.tsx') diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 88023783b..1d399e26f 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -138,7 +138,7 @@ export class CollectionView extends Touchable { let index = value.reduce((p, v, i) => (v instanceof Doc && v === doc) ? i : p, -1); index = index !== -1 ? index : value.reduce((p, v, i) => (v instanceof Doc && Doc.AreProtosEqual(v, doc)) ? i : p, -1); - ContextMenu.Instance.clearItems(); + ContextMenu.Instance && ContextMenu.Instance.clearItems(); if (index !== -1) { value.splice(index, 1); return true; -- cgit v1.2.3-70-g09d2 From 6886bf8c0549f09e9d911ab105fcf4b2d2f051de Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 13 Mar 2020 09:47:36 -0400 Subject: added reification of view menu items. tweaked sizing of collection chrome display --- src/client/util/DragManager.ts | 2 +- src/client/views/collections/CollectionView.tsx | 2 +- .../views/collections/CollectionViewChromes.scss | 2 - .../views/collections/CollectionViewChromes.tsx | 68 +++++++++++++++------- src/client/views/nodes/DocumentView.tsx | 2 +- 5 files changed, 49 insertions(+), 27 deletions(-) (limited to 'src/client/views/collections/CollectionView.tsx') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index a09d78a06..f4461d2f7 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -216,7 +216,7 @@ export namespace DragManager { // drag a button template and drop a new button export function StartButtonDrag(eles: HTMLElement[], script: string, title: string, vars: { [name: string]: Field }, params: string[], initialize: (button: Doc) => void, downX: number, downY: number, options?: DragOptions) { const finishDrag = (e: DragCompleteEvent) => { - const bd = Docs.Create.ButtonDocument({ _width: 150, _height: 50, title, dontSelect: true, onClick: ScriptField.MakeScript(script) }); + const bd = Docs.Create.ButtonDocument({ _width: 150, _height: 50, title, isButton: true, onClick: ScriptField.MakeScript(script) }); params.map(p => Object.keys(vars).indexOf(p) !== -1 && (Doc.GetProto(bd)[p] = new PrefetchProxy(vars[p] as Doc))); initialize?.(bd); bd.buttonParams = new List(params); diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 2d56f00d5..c74b9ad6d 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -9,7 +9,6 @@ import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be im import { DateField } from '../../../new_fields/DateField'; import { Doc, DocListCast, DataSym } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; -import { listSpec } from '../../../new_fields/Schema'; import { BoolCast, Cast, StrCast, NumCast } from '../../../new_fields/Types'; import { ImageField } from '../../../new_fields/URLField'; import { TraceMobx } from '../../../new_fields/util'; @@ -76,6 +75,7 @@ export namespace CollectionViewType { ]); export const valueOf = (value: string) => stringMapping.get(value.toLowerCase()); + export const stringFor = (value: number) => Array.from(stringMapping.entries()).find(entry => entry[1] === value)[0]; } export interface CollectionRenderProps { diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss index 19a14976c..428897ac4 100644 --- a/src/client/views/collections/CollectionViewChromes.scss +++ b/src/client/views/collections/CollectionViewChromes.scss @@ -27,7 +27,6 @@ outline-color: black; border: none; padding: 12px 10px 11px 10px; - margin-left: 50px; } .collectionViewBaseChrome-viewPicker:active { @@ -62,7 +61,6 @@ // margin-top: 10px; } .collectionViewBaseChrome-template { - margin-left: 10px; display: grid; background: rgb(238, 238, 238); color:grey; diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 689d3674f..5ab61645f 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -8,7 +8,7 @@ import { List } from "../../../new_fields/List"; import { listSpec } from "../../../new_fields/Schema"; import { ScriptField } from "../../../new_fields/ScriptField"; import { BoolCast, Cast, NumCast, StrCast } from "../../../new_fields/Types"; -import { Utils, emptyFunction } from "../../../Utils"; +import { Utils, emptyFunction, setupMoveUpEvents } from "../../../Utils"; import { DragManager } from "../../util/DragManager"; import { undoBatch } from "../../util/UndoManager"; import { EditableView } from "../EditableView"; @@ -82,6 +82,7 @@ export class CollectionViewBaseChrome extends React.Component(); + private _viewRef = React.createRef(); private _autosuggestRef = React.createRef(); @observable private _currentKey: string = ""; @observable private _viewSpecsOpen: boolean = false; @@ -344,8 +345,21 @@ export class CollectionViewBaseChrome extends React.Component { + setupMoveUpEvents(this, e, (e, down, delta) => { + const vtype = NumCast(this.props.CollectionView.props.Document._viewType) as CollectionViewType; + const c = { + params: ["target"], title: CollectionViewType.stringFor(vtype), + script: `this.target._viewType = ${NumCast(this.props.CollectionView.props.Document._viewType)}`, + immediate: (source: Doc[]) => Doc.setChildLayout(this.target, source?.[0]), + initialize: emptyFunction, + }; + DragManager.StartButtonDrag([this._viewRef.current!], c.script, c.title, + { target: this.props.CollectionView.props.Document }, c.params, c.initialize, e.clientX, e.clientY); + return true; + }, emptyFunction, emptyFunction); + } dragCommandDown = (e: React.PointerEvent) => { - this._startDragPosition = { x: e.clientX, y: e.clientY }; document.addEventListener("pointermove", this.dragPointerMove); document.addEventListener("pointerup", this.dragPointerUp); @@ -368,13 +382,17 @@ export class CollectionViewBaseChrome extends React.Component { document.removeEventListener("pointermove", this.dragPointerMove); document.removeEventListener("pointerup", this.dragPointerUp); - } render() { const collapsed = this.props.CollectionView.props.Document._chromeStatus !== "enabled"; return ( -
+
- +
+
+
+ +
+ +
+
@@ -449,7 +473,7 @@ export class CollectionViewBaseChrome extends React.Component
- +
{ }} checked={DocListCast((this.props.Document._facetCollection as Doc)?.data).some(d => d.title === facet)} /> - - {facet} - )} -
- ); - return
-
e.stopPropagation()}> - -
- Facet Filters - -
-
-
-
- -
+ return
+
; } @@ -234,8 +92,30 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { ContextMenu.Instance.addItem({ description: "Pivot/Time Options ...", subitems: layoutItems, icon: "eye" }); } + @computed get _allFacets() { + const facets = new Set(); + this.childDocs.forEach(child => Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key))); + Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.forEach(child => Object.keys(child).forEach(key => facets.add(key))); + return Array.from(facets); + } + menuCallback = (x: number, y: number) => { + ContextMenu.Instance.clearItems(); + const docItems: ContextMenuProps[] = []; + const keySet: Set = new Set(); - render() { + this.childLayoutPairs.map(pair => this._allFacets.filter(fieldKey => + pair.layout[fieldKey] instanceof RichTextField || + typeof (pair.layout[fieldKey]) === "number" || + typeof (pair.layout[fieldKey]) === "string").map(fieldKey => keySet.add(fieldKey))); + Array.from(keySet).map(fieldKey => + docItems.push({ description: ":" + fieldKey, event: () => this.props.Document._pivotField = fieldKey, icon: "compress-arrows-alt" })); + docItems.push({ description: ":(null)", event: () => this.props.Document._pivotField = undefined, icon: "compress-arrows-alt" }); + ContextMenu.Instance.addItem({ description: "Pivot Fields ...", subitems: docItems, icon: "eye" }); + const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(x, y); + ContextMenu.Instance.displayMenu(x, y, ":"); + } + + @computed get pivotKeyUI() { const newEditableViewProps = { GetValue: () => "", SetValue: (value: any) => { @@ -250,7 +130,26 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { toggle: this.toggleVisibility, color: "#f1efeb" // this.props.headingObject ? this.props.headingObject.color : "#f1efeb"; }; + return
+ + +
+ } + render() { let nonNumbers = 0; this.childDocs.map(doc => { const num = NumCast(doc[StrCast(this.props.Document._pivotField)], Number(StrCast(doc[StrCast(this.props.Document._pivotField)]))); @@ -270,41 +169,16 @@ export class CollectionTimeView extends CollectionSubView(doc => doc) { } } - - const facetCollection = Cast(this.props.Document?._facetCollection, Doc, null); - return !facetCollection ? (null) : -
-
- - -
- {!this.props.isSelected() || this.props.PanelHeight() < 100 ? (null) : -
- -
- } - {this.filterView} - {this.contents} - {!this.props.isSelected() || !doTimeline ? (null) : <> -
-
-
- } -
; + return
+ {this.pivotKeyUI} + {this.contents} + {!this.props.isSelected() || !doTimeline ? (null) : <> +
+
+
+ } +
; } } diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss index 1c46081a1..0246abccc 100644 --- a/src/client/views/collections/CollectionView.scss +++ b/src/client/views/collections/CollectionView.scss @@ -10,6 +10,60 @@ width: 100%; height: 100%; overflow: hidden; // bcz: used to be 'auto' which would create scrollbars when there's a floating doc that's not visible. not sure if that's better, but the scrollbars are annoying... + + + .collectionTimeView-dragger { + background-color: lightgray; + height: 40px; + width: 20px; + position: absolute; + border-radius: 10px; + top: 55%; + border: 1px black solid; + z-index: 2; + left: -10px; + } + .collectionTimeView-treeView { + display: flex; + flex-direction: column; + width: 200px; + height: 100%; + position: absolute; + left: 0; + top: 0; + + .collectionTimeView-addfacet { + display: inline-block; + width: 200px; + height: 30px; + background: darkGray; + text-align: left; + + .collectionTimeView-button { + align-items: center; + display: flex; + width: 100%; + height: 100%; + + .collectionTimeView-span { + margin: auto; + } + } + + >div, + >div>div { + width: 100%; + height: 100%; + text-align: center; + } + } + + .collectionTimeView-tree { + display: inline-block; + width: 100%; + height: calc(100% - 30px); + } + } } #google-tags { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 7683e1550..a1f173746 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,18 +1,19 @@ import { library } from '@fortawesome/fontawesome-svg-core'; -import { faEye } from '@fortawesome/free-regular-svg-icons'; +import { faEye, faEdit } from '@fortawesome/free-regular-svg-icons'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faColumns, faCopy, faEllipsisV, faFingerprint, faImage, faProjectDiagram, faSignature, faSquare, faTh, faThList, faTree } from '@fortawesome/free-solid-svg-icons'; -import { action, observable } from 'mobx'; +import { action, observable, computed } from 'mobx'; import { observer } from "mobx-react"; import * as React from 'react'; import Lightbox from 'react-image-lightbox-with-rotate'; import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app import { DateField } from '../../../new_fields/DateField'; -import { DataSym, Doc, DocListCast } from '../../../new_fields/Doc'; +import { DataSym, Doc, DocListCast, Field } from '../../../new_fields/Doc'; import { List } from '../../../new_fields/List'; import { BoolCast, Cast, NumCast, StrCast } from '../../../new_fields/Types'; import { ImageField } from '../../../new_fields/URLField'; import { TraceMobx } from '../../../new_fields/util'; -import { Utils } from '../../../Utils'; +import { Utils, setupMoveUpEvents, returnFalse } from '../../../Utils'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { ImageUtils } from '../../util/Import & Export/ImageUtils'; @@ -38,6 +39,12 @@ import './CollectionView.scss'; import { CollectionViewBaseChrome } from './CollectionViewChromes'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; import { Id } from '../../../new_fields/FieldSymbols'; +import { listSpec } from '../../../new_fields/Schema'; +import { Docs } from '../../documents/Documents'; +import { ScriptField, ComputedField } from '../../../new_fields/ScriptField'; +const higflyout = require("@hig/flyout"); +export const { anchorPoints } = higflyout; +export const Flyout = higflyout.default; export const COLLECTION_BORDER_WIDTH = 2; const path = require('path'); library.add(faTh, faTree, faSquare, faProjectDiagram, faSignature, faThList, faFingerprint, faColumns, faEllipsisV, faImage, faEye as any, faCopy); @@ -84,6 +91,7 @@ export interface CollectionRenderProps { moveDocument: (document: Doc, targetCollection: Doc | undefined, addDocument: (document: Doc) => boolean) => boolean; active: () => boolean; whenActiveChanged: (isActive: boolean) => void; + PanelWidth: () => number; } @observer @@ -268,6 +276,158 @@ export class CollectionView extends Touchable { onMovePrevRequest={action(() => this._curLightboxImg = (this._curLightboxImg + images.length - 1) % images.length)} onMoveNextRequest={action(() => this._curLightboxImg = (this._curLightboxImg + 1) % images.length)} />); } + @observable _facetWidth = 0; + + bodyPanelWidth = () => this.props.PanelWidth() - this._facetWidth; + getTransform = () => this.props.ScreenToLocalTransform().translate(-this._facetWidth, 0); + facetWidth = () => this._facetWidth; + + @computed get dataDoc() { + return (this.props.DataDoc && this.props.Document.isTemplateForField ? Doc.GetProto(this.props.DataDoc) : + this.props.Document.resolvedDataDoc ? this.props.Document : Doc.GetProto(this.props.Document)); // if the layout document has a resolvedDataDoc, then we don't want to get its parent which would be the unexpanded template + } + // The data field for rendering this collection will be on the this.props.Document unless we're rendering a template in which case we try to use props.DataDoc. + // When a document has a DataDoc but it's not a template, then it contains its own rendering data, but needs to pass the DataDoc through + // to its children which may be templates. + // If 'annotationField' is specified, then all children exist on that field of the extension document, otherwise, they exist directly on the data document under 'fieldKey' + @computed get dataField() { + return this.dataDoc[this.props.fieldKey]; + } + + get childLayoutPairs(): { layout: Doc; data: Doc; }[] { + const { Document, DataDoc } = this.props; + const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, DataDoc, doc)).filter(pair => pair.layout); + return validPairs.map(({ data, layout }) => ({ data, layout: layout! })); // this mapping is a bit of a hack to coerce types + } + get childDocList() { + return Cast(this.dataField, listSpec(Doc)); + } + get childDocs() { + const dfield = this.dataField; + const rawdocs = (dfield instanceof Doc) ? [dfield] : Cast(dfield, listSpec(Doc), this.props.Document.expandedTemplate ? [Cast(this.props.Document.expandedTemplate, Doc, null)] : []); + const docs = rawdocs.filter(d => !(d instanceof Promise)).map(d => d as Doc); + const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField); + return viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs; + } + @computed get _allFacets() { + const facets = new Set(); + this.childDocs.forEach(child => Object.keys(Doc.GetProto(child)).forEach(key => facets.add(key))); + Doc.AreProtosEqual(this.dataDoc, this.props.Document) && this.childDocs.forEach(child => Object.keys(child).forEach(key => facets.add(key))); + return Array.from(facets); + } + + /** + * Responds to clicking the check box in the flyout menu + */ + facetClick = (facetHeader: string) => { + const facetCollection = this.props.Document._facetCollection; + if (facetCollection instanceof Doc) { + const found = DocListCast(facetCollection.data).findIndex(doc => doc.title === facetHeader); + if (found !== -1) { + (facetCollection.data as List).splice(found, 1); + const docFilter = Cast(this.props.Document._docFilters, listSpec("string")); + if (docFilter) { + let index: number; + while ((index = docFilter.findIndex(item => item === facetHeader)) !== -1) { + docFilter.splice(index, 3); + } + } + const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string")); + if (docRangeFilters) { + let index: number; + while ((index = docRangeFilters.findIndex(item => item === facetHeader)) !== -1) { + docRangeFilters.splice(index, 3); + } + } + } else { + const allCollectionDocs = DocListCast(this.dataDoc[this.props.fieldKey]); + const facetValues = Array.from(allCollectionDocs.reduce((set, child) => + set.add(Field.toString(child[facetHeader] as Field)), new Set())); + + let nonNumbers = 0; + let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE; + facetValues.map(val => { + const num = Number(val); + if (Number.isNaN(num)) { + nonNumbers++; + } else { + minVal = Math.min(num, minVal); + maxVal = Math.max(num, maxVal); + } + }); + if (nonNumbers / allCollectionDocs.length < .1) { + const ranged = Doc.readDocRangeFilter(this.props.Document, facetHeader); + const newFacet = Docs.Create.SliderDocument({ title: facetHeader }); + Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox + newFacet.treeViewExpandedView = "layout"; + newFacet.treeViewOpen = true; + newFacet._sliderMin = ranged === undefined ? minVal : ranged[0]; + newFacet._sliderMax = ranged === undefined ? maxVal : ranged[1]; + newFacet._sliderMinThumb = minVal; + newFacet._sliderMaxThumb = maxVal; + newFacet.target = this.props.Document; + const scriptText = `setDocFilterRange(this.target, "${facetHeader}", range)`; + newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" }); + + Doc.AddDocToList(facetCollection, "data", newFacet); + } else { + const newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true }); + const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc }; + const params = { layoutDoc: Doc.name, dataDoc: Doc.name, }; + newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, params, capturedVariables); + Doc.AddDocToList(facetCollection, "data", newFacet); + } + } + } + } + + + onPointerDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { + this._facetWidth = Math.max(this.props.ScreenToLocalTransform().transformPoint(e.clientX, 0)[0], 0); + return false; + }), returnFalse, action(() => this._facetWidth = this._facetWidth < 15 ? 200 : 0)); + } + @computed get filterView() { + const facetCollection = Cast(this.props.Document?._facetCollection, Doc); + if (this._facetWidth && facetCollection === undefined) setTimeout(() => { + const scriptText = "setDocFilter(containingTreeView.target, heading, this.title, checked)"; + const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", _yMargin: 0, treeViewHideTitle: true, treeViewHideHeaderFields: true }); + facetCollection.target = this.props.Document; + facetCollection.onCheckedClick = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name }); + this.props.Document.excludeFields = new List(["_facetCollection", "_docFilters"]); + + this.props.Document._facetCollection = facetCollection; + }, 0); + const flyout = ( +
e.stopPropagation()}> + {this._allFacets.map(facet => )} +
+ ); + return !facetCollection ? (null) : +
+
e.stopPropagation()}> + +
+ Facet Filters + +
+
+
+
+ false} + removeDocument={(doc: Doc) => false} + addDocument={(doc: Doc) => false} /> +
+
; + } + render() { TraceMobx(); const props: CollectionRenderProps = { @@ -276,6 +436,7 @@ export class CollectionView extends Touchable { moveDocument: this.moveDocument, active: this.active, whenActiveChanged: this.whenActiveChanged, + PanelWidth: this.bodyPanelWidth }; return (
{ Utils.CorsProxy(Cast(d.data, ImageField)!.url.href) : Cast(d.data, ImageField)!.url.href : ""))} + {!this.props.isSelected() || this.props.PanelHeight() < 100 ? (null) : +
+ +
+ } + {this.filterView}
); } } \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index da44d0d59..a0dd4f2de 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -903,12 +903,12 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { }.bind(this)); doTimelineLayout(poolData: Map) { - return computeTimelineLayout(poolData, this.props.Document, this.childDocs, this.filterDocs, + return computeTimelineLayout(poolData, this.props.Document, this.childDocs, this.childDocs, this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX); } doPivotLayout(poolData: Map) { - return computePivotLayout(poolData, this.props.Document, this.childDocs, this.filterDocs, + return computePivotLayout(poolData, this.props.Document, this.childDocs, this.childDocs, this.childLayoutPairs, [this.props.PanelWidth(), this.props.PanelHeight()], this.viewDefsToJSX); } @@ -934,42 +934,6 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { return { newPool, computedElementData: this.doFreeformLayout(newPool) }; } - @computed get filterDocs() { - const docFilters = Cast(this.props.Document._docFilters, listSpec("string"), []); - const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string"), []); - const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields - for (let i = 0; i < docFilters.length; i += 3) { - const [key, value, modifiers] = docFilters.slice(i, i + 3); - if (!filterFacets[key]) { - filterFacets[key] = {}; - } - filterFacets[key][value] = modifiers; - } - const filteredDocs = docFilters.length ? this.childDocs.filter(d => { - for (const facetKey of Object.keys(filterFacets)) { - const facet = filterFacets[facetKey]; - const satisfiesFacet = Object.keys(facet).some(value => - (facet[value] === "x") !== Doc.matchFieldValue(d, facetKey, value)); - if (!satisfiesFacet) { - return false; - } - } - return true; - }) : this.childDocs; - const rangeFilteredDocs = filteredDocs.filter(d => { - for (let i = 0; i < docRangeFilters.length; i += 3) { - const key = docRangeFilters[i]; - const min = Number(docRangeFilters[i + 1]); - const max = Number(docRangeFilters[i + 2]); - const val = Cast(d[key], "number", null); - if (val !== undefined && (val < min || val > max)) { - return false; - } - } - return true; - }); - return rangeFilteredDocs; - } childLayoutDocFunc = () => this.props.childLayoutTemplate?.() || Cast(this.props.Document.childLayoutTemplate, Doc, null); get doLayoutComputation() { const { newPool, computedElementData } = this.doInternalLayoutComputation; -- cgit v1.2.3-70-g09d2 From a75ce06979b17457e6ccbdda8fb2217815757036 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 19 Mar 2020 21:31:45 -0400 Subject: cleaned up facet collections to not require an additional document --- src/Utils.ts | 2 +- src/client/views/collections/CollectionSubView.tsx | 8 +- .../views/collections/CollectionTimeView.scss | 1 + .../views/collections/CollectionTreeView.tsx | 24 ++-- src/client/views/collections/CollectionView.tsx | 143 +++++++++++---------- src/client/views/nodes/FieldView.tsx | 3 +- 6 files changed, 99 insertions(+), 82 deletions(-) (limited to 'src/client/views/collections/CollectionView.tsx') diff --git a/src/Utils.ts b/src/Utils.ts index 3786b4f6f..e3ec10dcd 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -498,7 +498,7 @@ export function setupMoveUpEvents( }; const _upEvent = (e: PointerEvent): void => { upEvent(e); - if (Math.abs(e.clientX - (target as any)._downX) < 4 || Math.abs(e.clientY - (target as any)._downY) < 4) { + if (Math.abs(e.clientX - (target as any)._downX) < 4 && Math.abs(e.clientY - (target as any)._downY) < 4) { clickEvent(e); } document.removeEventListener("pointermove", _moveEvent); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index a5480d567..11f214625 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -48,8 +48,8 @@ export interface SubCollectionViewProps extends CollectionViewProps { layoutEngine?: () => string; } -export function CollectionSubView(schemaCtor: (doc: Doc) => T) { - class CollectionSubView extends DocComponent(schemaCtor) { +export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?:X) { + class CollectionSubView extends DocComponent(schemaCtor) { private dropDisposer?: DragManager.DragDropDisposer; private gestureDisposer?: GestureUtils.GestureEventDisposer; protected multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; @@ -130,7 +130,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField); const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs; - const filteredDocs = docFilters.length ? childDocs.filter(d => { + const filteredDocs = docFilters.length && !this.props.dontRegisterView ? childDocs.filter(d => { for (const facetKey of Object.keys(filterFacets)) { const facet = filterFacets[facetKey]; const satisfiesFacet = Object.keys(facet).some(value => @@ -195,7 +195,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T) { @undoBatch @action protected onInternalDrop(e: Event, de: DragManager.DropEvent): boolean { - const docDragData = de.complete.docDragData; + const docDragData = de.complete.docDragData; (this.props.Document.dropConverter instanceof ScriptField) && this.props.Document.dropConverter.script.run({ dragData: docDragData }); /// bcz: check this if (docDragData) { diff --git a/src/client/views/collections/CollectionTimeView.scss b/src/client/views/collections/CollectionTimeView.scss index be745000e..fa7c87f4e 100644 --- a/src/client/views/collections/CollectionTimeView.scss +++ b/src/client/views/collections/CollectionTimeView.scss @@ -48,6 +48,7 @@ .collectionTimeView-flyout { width: 400px; display: block; + text-align: left; .collectionTimeView-flyout-item { background-color: lightgray; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index f8f8a0943..c02c4ac3f 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -6,7 +6,7 @@ import { observer } from "mobx-react"; import { Doc, DocListCast, Field, HeightSym, WidthSym, DataSym, Opt } from '../../../new_fields/Doc'; import { Id } from '../../../new_fields/FieldSymbols'; import { List } from '../../../new_fields/List'; -import { Document, listSpec } from '../../../new_fields/Schema'; +import { Document, listSpec, createSchema, makeInterface } from '../../../new_fields/Schema'; import { ComputedField, ScriptField } from '../../../new_fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../new_fields/Types'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; @@ -29,7 +29,7 @@ import { ImageBox } from '../nodes/ImageBox'; import { KeyValueBox } from '../nodes/KeyValueBox'; import { ScriptBox } from '../ScriptBox'; import { Templates } from '../Templates'; -import { CollectionSubView } from "./CollectionSubView"; +import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); import { CollectionViewType } from './CollectionView'; @@ -46,7 +46,7 @@ export interface TreeViewProps { renderDepth: number; deleteDoc: (doc: Doc) => boolean; moveDocument: DragManager.MoveFunction; - dropAction: "alias" | "copy" | undefined; + dropAction: dropActionType; addDocTab: (doc: Doc, where: string, libraryPath?: Doc[]) => boolean; pinToPres: (document: Doc) => void; panelWidth: () => number; @@ -632,8 +632,16 @@ class TreeView extends React.Component { } } +export type collectionTreeViewProps = { + treeViewHideTitle?: boolean; + treeViewHideHeaderFields?: boolean; + onCheckedClick?: ScriptField; +}; + +let xx: collectionTreeViewProps = {}; + @observer -export class CollectionTreeView extends CollectionSubView(Document) { +export class CollectionTreeView extends CollectionSubView(Document, xx) { private treedropDisposer?: DragManager.DragDropDisposer; private _mainEle?: HTMLDivElement; @@ -665,7 +673,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { const doAddDoc = () => Doc.AddDocToList(this.props.Document[DataSym], this.props.fieldKey, doc, relativeTo, before, false, false, false); if (this.props.Document.resolvedDataDoc instanceof Promise) { - this.props.Document.resolvedDataDoc.then(resolved => doAddDoc()); + this.props.Document.resolvedDataDoc.then((resolved: any) => doAddDoc()); } else { doAddDoc(); } @@ -773,7 +781,7 @@ export class CollectionTreeView extends CollectionSubView(Document) { onWheel={(e: React.WheelEvent) => this._mainEle && this._mainEle.scrollHeight > this._mainEle.clientHeight && e.stopPropagation()} onDrop={this.onTreeDrop} ref={this.createTreeDropTarget}> - {(this.props.Document.treeViewHideTitle ? (null) : BoolCast(this.props.Document.treeViewHideHeaderFields), - BoolCast(this.props.Document.treeViewPreventOpen), [], this.props.LibraryPath, ScriptCast(this.props.Document.onCheckedClick), this.props.ignoreFields) + this.outerXf, this.props.active, this.props.PanelWidth, this.props.ChromeHeight, this.props.renderDepth, () => this.props.treeViewHideHeaderFields || BoolCast(this.props.Document.treeViewHideHeaderFields), + BoolCast(this.props.Document.treeViewPreventOpen), [], this.props.LibraryPath, this.props.onCheckedClick || ScriptCast(this.props.Document.onCheckedClick), this.props.ignoreFields) }
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index a1f173746..ab553b206 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -8,7 +8,7 @@ import * as React from 'react'; import Lightbox from 'react-image-lightbox-with-rotate'; import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app import { DateField } from '../../../new_fields/DateField'; -import { DataSym, Doc, DocListCast, Field } from '../../../new_fields/Doc'; +import { DataSym, Doc, DocListCast, Field, Opt } from '../../../new_fields/Doc'; import { List } from '../../../new_fields/List'; import { BoolCast, Cast, NumCast, StrCast } from '../../../new_fields/Types'; import { ImageField } from '../../../new_fields/URLField'; @@ -320,64 +320,63 @@ export class CollectionView extends Touchable { * Responds to clicking the check box in the flyout menu */ facetClick = (facetHeader: string) => { - const facetCollection = this.props.Document._facetCollection; - if (facetCollection instanceof Doc) { - const found = DocListCast(facetCollection.data).findIndex(doc => doc.title === facetHeader); - if (found !== -1) { - (facetCollection.data as List).splice(found, 1); - const docFilter = Cast(this.props.Document._docFilters, listSpec("string")); - if (docFilter) { - let index: number; - while ((index = docFilter.findIndex(item => item === facetHeader)) !== -1) { - docFilter.splice(index, 3); - } + const facetCollection = this.props.Document; + const found = DocListCast(facetCollection[this.props.fieldKey + "-filter"]).findIndex(doc => doc.title === facetHeader); + if (found !== -1) { + (facetCollection[this.props.fieldKey + "-filter"] as List).splice(found, 1); + const docFilter = Cast(this.props.Document._docFilters, listSpec("string")); + if (docFilter) { + let index: number; + while ((index = docFilter.findIndex(item => item === facetHeader)) !== -1) { + docFilter.splice(index, 3); } - const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string")); - if (docRangeFilters) { - let index: number; - while ((index = docRangeFilters.findIndex(item => item === facetHeader)) !== -1) { - docRangeFilters.splice(index, 3); - } + } + const docRangeFilters = Cast(this.props.Document._docRangeFilters, listSpec("string")); + if (docRangeFilters) { + let index: number; + while ((index = docRangeFilters.findIndex(item => item === facetHeader)) !== -1) { + docRangeFilters.splice(index, 3); } - } else { - const allCollectionDocs = DocListCast(this.dataDoc[this.props.fieldKey]); - const facetValues = Array.from(allCollectionDocs.reduce((set, child) => - set.add(Field.toString(child[facetHeader] as Field)), new Set())); - - let nonNumbers = 0; - let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE; - facetValues.map(val => { - const num = Number(val); - if (Number.isNaN(num)) { - nonNumbers++; - } else { - minVal = Math.min(num, minVal); - maxVal = Math.max(num, maxVal); - } - }); - if (nonNumbers / allCollectionDocs.length < .1) { - const ranged = Doc.readDocRangeFilter(this.props.Document, facetHeader); - const newFacet = Docs.Create.SliderDocument({ title: facetHeader }); - Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox - newFacet.treeViewExpandedView = "layout"; - newFacet.treeViewOpen = true; - newFacet._sliderMin = ranged === undefined ? minVal : ranged[0]; - newFacet._sliderMax = ranged === undefined ? maxVal : ranged[1]; - newFacet._sliderMinThumb = minVal; - newFacet._sliderMaxThumb = maxVal; - newFacet.target = this.props.Document; - const scriptText = `setDocFilterRange(this.target, "${facetHeader}", range)`; - newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" }); - - Doc.AddDocToList(facetCollection, "data", newFacet); + } + } else { + const allCollectionDocs = DocListCast(this.dataDoc[this.props.fieldKey]); + const facetValues = Array.from(allCollectionDocs.reduce((set, child) => + set.add(Field.toString(child[facetHeader] as Field)), new Set())); + + let nonNumbers = 0; + let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE; + facetValues.map(val => { + const num = Number(val); + if (Number.isNaN(num)) { + nonNumbers++; } else { - const newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true }); - const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc }; - const params = { layoutDoc: Doc.name, dataDoc: Doc.name, }; - newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, params, capturedVariables); - Doc.AddDocToList(facetCollection, "data", newFacet); + minVal = Math.min(num, minVal); + maxVal = Math.max(num, maxVal); } + }); + let newFacet: Opt; + if (nonNumbers / allCollectionDocs.length < .1) { + newFacet = Docs.Create.SliderDocument({ title: facetHeader }); + const ranged = Doc.readDocRangeFilter(this.props.Document, facetHeader); + Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox + newFacet.treeViewExpandedView = "layout"; + newFacet.treeViewOpen = true; + newFacet._sliderMin = ranged === undefined ? minVal : ranged[0]; + newFacet._sliderMax = ranged === undefined ? maxVal : ranged[1]; + newFacet._sliderMinThumb = minVal; + newFacet._sliderMaxThumb = maxVal; + newFacet.target = this.props.Document; + const scriptText = `setDocFilterRange(this.target, "${facetHeader}", range)`; + newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: "number" }); + + Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet); + } else { + newFacet = Docs.Create.TreeDocument([], { title: facetHeader, treeViewOpen: true, isFacetFilter: true }); + const capturedVariables = { layoutDoc: this.props.Document, dataDoc: this.dataDoc }; + const params = { layoutDoc: Doc.name, dataDoc: Doc.name, }; + newFacet.data = ComputedField.MakeFunction(`readFacetData(layoutDoc, dataDoc, "${this.props.fieldKey}", "${facetHeader}")`, params, capturedVariables); } + Doc.AddDocToList(facetCollection, this.props.fieldKey + "-filter", newFacet); } } @@ -388,27 +387,24 @@ export class CollectionView extends Touchable { return false; }), returnFalse, action(() => this._facetWidth = this._facetWidth < 15 ? 200 : 0)); } + filterBackground = () => "dimGray"; + @computed get scriptField() { + const scriptText = "setDocFilter(containingTreeView.target, heading, this.title, checked)"; + return ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name }); + } @computed get filterView() { - const facetCollection = Cast(this.props.Document?._facetCollection, Doc); - if (this._facetWidth && facetCollection === undefined) setTimeout(() => { - const scriptText = "setDocFilter(containingTreeView.target, heading, this.title, checked)"; - const facetCollection = Docs.Create.TreeDocument([], { title: "facetFilters", _yMargin: 0, treeViewHideTitle: true, treeViewHideHeaderFields: true }); - facetCollection.target = this.props.Document; - facetCollection.onCheckedClick = ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name }); - this.props.Document.excludeFields = new List(["_facetCollection", "_docFilters"]); - - this.props.Document._facetCollection = facetCollection; - }, 0); + const facetCollection = this.props.Document; + this._facetWidth && setTimeout(() => facetCollection.target = this.props.Document, 0); const flyout = (
e.stopPropagation()}> {this._allFacets.map(facet => )}
); - return !facetCollection ? (null) : + return !this._facetWidth || this.props.dontRegisterView ? (null) :
e.stopPropagation()}> @@ -419,8 +415,19 @@ export class CollectionView extends Touchable {
- false} removeDocument={(doc: Doc) => false} addDocument={(doc: Doc) => false} /> diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 6619330a1..0305f43d5 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -10,6 +10,7 @@ import { Transform } from "../../util/Transform"; import { CollectionView } from "../collections/CollectionView"; import { AudioBox } from "./AudioBox"; import { VideoBox } from "./VideoBox"; +import { dropActionType } from "../../util/DragManager"; // // these properties get assigned through the render() method of the DocumentView when it creates this node. @@ -25,7 +26,7 @@ export interface FieldViewProps { DataDoc?: Doc; LibraryPath: Doc[]; onClick?: ScriptField; - dropAction: dropAction; + dropAction: dropActionType; isSelected: (outsideReaction?: boolean) => boolean; select: (isCtrlPressed: boolean) => void; renderDepth: number; -- cgit v1.2.3-70-g09d2 From 34f5992fa1d489b410f476cdb24cf28faed96632 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Thu, 19 Mar 2020 21:38:21 -0400 Subject: from last --- src/client/views/collections/CollectionView.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/client/views/collections/CollectionView.tsx') diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index ab553b206..2036a0ba9 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -389,12 +389,11 @@ export class CollectionView extends Touchable { } filterBackground = () => "dimGray"; @computed get scriptField() { - const scriptText = "setDocFilter(containingTreeView.target, heading, this.title, checked)"; + const scriptText = "setDocFilter(containingTreeView, heading, this.title, checked)"; return ScriptField.MakeScript(scriptText, { this: Doc.name, heading: "string", checked: "string", containingTreeView: Doc.name }); } @computed get filterView() { const facetCollection = this.props.Document; - this._facetWidth && setTimeout(() => facetCollection.target = this.props.Document, 0); const flyout = (
e.stopPropagation()}> {this._allFacets.map(facet =>