diff options
| author | Andrew Kim <andrewdkim@users.noreply.github.com> | 2019-08-19 00:27:40 -0400 |
|---|---|---|
| committer | Andrew Kim <andrewdkim@users.noreply.github.com> | 2019-08-19 00:27:40 -0400 |
| commit | a4bfacd9f494df6698d12d408d736e878c1189eb (patch) | |
| tree | b3af89bc30eaf48fd983711c00cd4c28eb662bbc /src/client/views/collections | |
| parent | 1b7e8873ead3cc15349bd4c9e669f3b1edcbbc2b (diff) | |
| parent | b037aa89fb564812f880994453ce002054a0ad82 (diff) | |
merge from master
Diffstat (limited to 'src/client/views/collections')
8 files changed, 344 insertions, 83 deletions
diff --git a/src/client/views/collections/CollectionBaseView.tsx b/src/client/views/collections/CollectionBaseView.tsx index 4a296493a..b6ed6aaa0 100644 --- a/src/client/views/collections/CollectionBaseView.tsx +++ b/src/client/views/collections/CollectionBaseView.tsx @@ -11,6 +11,7 @@ import { SelectionManager } from '../../util/SelectionManager'; import { ContextMenu } from '../ContextMenu'; import { FieldViewProps } from '../nodes/FieldView'; import './CollectionBaseView.scss'; +import { DateField } from '../../../new_fields/DateField'; export enum CollectionViewType { Invalid, @@ -113,6 +114,7 @@ export class CollectionBaseView extends React.Component<CollectionViewProps> { } else { Doc.GetProto(targetDataDoc)[targetField] = new List([doc]); } + Doc.GetProto(doc).lastOpened = new DateField; return true; } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 7332a490a..a8e723379 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -28,6 +28,7 @@ import { library } from '@fortawesome/fontawesome-svg-core'; import { faFile, faUnlockAlt } from '@fortawesome/free-solid-svg-icons'; import { CurrentUserUtils } from '../../../server/authentication/models/current_user_utils'; import { Docs } from '../../documents/Documents'; +import { DateField } from '../../../new_fields/DateField'; library.add(faFile); @observer @@ -204,6 +205,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp } @action public AddTab = (stack: any, document: Doc, dataDocument: Doc | undefined) => { + Doc.GetProto(document).lastOpened = new DateField; let docs = Cast(this.props.Document.data, listSpec(Doc)); if (docs) { docs.push(document); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 5713cc770..2e4f6aff5 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -29,6 +29,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { _masonryGridRef: HTMLDivElement | null = null; _draggerRef = React.createRef<HTMLDivElement>(); _heightDisposer?: IReactionDisposer; + _childLayoutDisposer?: IReactionDisposer; _sectionFilterDisposer?: IReactionDisposer; _docXfs: any[] = []; _columnStart: number = 0; @@ -56,12 +57,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } get Sections() { - if (!this.sectionFilter) return new Map<SchemaHeaderField, Doc[]>(); + if (!this.sectionFilter || this.sectionHeaders instanceof Promise) return new Map<SchemaHeaderField, Doc[]>(); if (this.sectionHeaders === undefined) { - this.props.Document.sectionHeaders = new List<SchemaHeaderField>(); + setTimeout(() => this.props.Document.sectionHeaders = new List<SchemaHeaderField>(), 0); + return new Map<SchemaHeaderField, Doc[]>(); } - const sectionHeaders = this.sectionHeaders!; + const sectionHeaders = this.sectionHeaders; let fields = new Map<SchemaHeaderField, Doc[]>(sectionHeaders.map(sh => [sh, []] as [SchemaHeaderField, []])); this.filteredChildren.map(d => { let sectionValue = (d[this.sectionFilter] ? d[this.sectionFilter] : `NO ${this.sectionFilter.toUpperCase()} VALUE`) as object; @@ -84,12 +86,16 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { } componentDidMount() { + this._childLayoutDisposer = reaction(() => [this.childDocs, Cast(this.props.Document.childLayout, Doc)], + async (args) => args[1] instanceof Doc && + this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc), undefined))); + // is there any reason this needs to exist? -syip. yes, it handles autoHeight for stacking views (masonry isn't yet supported). this._heightDisposer = reaction(() => { if (this.isStackingView && BoolCast(this.props.Document.autoHeight)) { let sectionsList = Array.from(this.Sections.size ? this.Sections.values() : [this.filteredChildren]); return this.props.ContentScaling() * sectionsList.reduce((maxHght, s) => Math.max(maxHght, - 50 + s.reduce((height, d, i) => height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap), this.yMargin) + (this.Sections.size ? 50 : 0) + s.reduce((height, d, i) => height + this.childDocHeight(d) + (i === s.length - 1 ? this.yMargin : this.gridGap), this.yMargin) ), 0); } return -1; @@ -108,6 +114,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { ); } componentWillUnmount() { + this._childLayoutDisposer && this._childLayoutDisposer(); this._heightDisposer && this._heightDisposer(); this._sectionFilterDisposer && this._sectionFilterDisposer(); } @@ -356,7 +363,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) { }; Doc.UpdateDocumentExtensionForField(this.props.DataDoc ? this.props.DataDoc : this.props.Document, this.props.fieldKey); - let sections = (this.sectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc) : [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]]) + let sections = (this.sectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc) : [[undefined, this.filteredChildren] as [SchemaHeaderField | undefined, Doc[]]]); return ( <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"} ref={this.createRef} onDrop={this.onDrop.bind(this)} onContextMenu={this.onContextMenu} onWheel={(e: React.WheelEvent) => e.stopPropagation()} > diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 24bd24d11..4b1fca18a 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -25,7 +25,7 @@ import { CollectionSchemaPreview } from './CollectionSchemaView'; import { CollectionSubView } from "./CollectionSubView"; import "./CollectionTreeView.scss"; import React = require("react"); -import { ComputedField } from '../../../new_fields/ScriptField'; +import { ComputedField, ScriptField } from '../../../new_fields/ScriptField'; import { KeyValueBox } from '../nodes/KeyValueBox'; @@ -400,21 +400,56 @@ class TreeView extends React.Component<TreeViewProps> { panelWidth: () => number, renderDepth: number ) { - let docList = docs.filter(child => !child.excludeFromLibrary); + let viewSpecScript = Cast(containingCollection.viewSpecScript, ScriptField); + if (viewSpecScript) { + let script = viewSpecScript.script; + docs = docs.filter(d => { + let res = script.run({ doc: d }); + if (res.success) { + return res.result; + } + else { + console.log(res.error); + } + }); + } + + let descending = BoolCast(containingCollection.stackingHeadersSortDescending); + docs.sort(function (a, b): 1 | -1 { + let descA = descending ? b : a; + let descB = descending ? a : b; + let first = descA[String(containingCollection.sectionFilter)]; + let second = descB[String(containingCollection.sectionFilter)]; + // TODO find better way to sort how to sort.................. + if (typeof first === 'number' && typeof second === 'number') { + return (first - second) > 0 ? 1 : -1; + } + if (typeof first === 'string' && typeof second === 'string') { + return first > second ? 1 : -1; + } + if (typeof first === 'boolean' && typeof second === 'boolean') { + // if (first === second) { // bugfixing?: otherwise, the list "flickers" because the list is resorted during every load + // return Number(descA.x) > Number(descB.x) ? 1 : -1; + // } + return first > second ? 1 : -1; + } + return descending ? 1 : -1; + }); + let rowWidth = () => panelWidth() - 20; - return docList.map((child, i) => { + return docs.map((child, i) => { let pair = Doc.GetLayoutDataDocPair(containingCollection, dataDoc, key, child); let indent = i === 0 ? undefined : () => { - if (StrCast(docList[i - 1].layout).indexOf("CollectionView") !== -1) { - let fieldKeysub = StrCast(docList[i - 1].layout).split("fieldKey")[1]; + if (StrCast(docs[i - 1].layout).indexOf("CollectionView") !== -1) { + let fieldKeysub = StrCast(docs[i - 1].layout).split("fieldKey")[1]; let fieldKey = fieldKeysub.split("\"")[1]; - Doc.AddDocToList(docList[i - 1], fieldKey, child); + Doc.AddDocToList(docs[i - 1], fieldKey, child); remove(child); } }; let addDocument = (doc: Doc, relativeTo?: Doc, before?: boolean) => { - return add(doc, relativeTo ? relativeTo : docList[i], before !== undefined ? before : false); + return add(doc, relativeTo ? relativeTo : docs[i], before !== undefined ? before : false); }; let rowHeight = () => { let aspect = NumCast(child.nativeWidth, 0) / NumCast(child.nativeHeight, 0); diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss index 74f0dffd4..2427c8721 100644 --- a/src/client/views/collections/CollectionViewChromes.scss +++ b/src/client/views/collections/CollectionViewChromes.scss @@ -54,6 +54,14 @@ transform-origin: top left; // margin-top: 10px; } + .collectionViewBaseChrome-template { + margin-left: 10px; + display: grid; + background: rgb(238, 238, 238); + color:grey; + margin-top:auto; + margin-bottom:auto; + } .collectionViewBaseChrome-viewSpecs { margin-left: 10px; @@ -109,7 +117,7 @@ .collectionViewBaseChrome-viewSpecsMenu-lastRow { display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: 1fr 1fr 1fr; grid-gap: 10px; margin: 10px; } @@ -118,17 +126,20 @@ } - .collectionStackingViewChrome-cont { + .collectionStackingViewChrome-cont, + .collectionTreeViewChrome-cont { display: flex; justify-content: space-between; } - .collectionStackingViewChrome-sort { + .collectionStackingViewChrome-sort, + .collectionTreeViewChrome-sort { display: flex; align-items: center; justify-content: space-between; - .collectionStackingViewChrome-sortIcon { + .collectionStackingViewChrome-sortIcon, + .collectionTreeViewChrome-sortIcon { transition: transform .5s; margin-left: 10px; } @@ -139,18 +150,21 @@ } - .collectionStackingViewChrome-sectionFilter-cont { + .collectionStackingViewChrome-sectionFilter-cont, + .collectionTreeViewChrome-sectionFilter-cont { justify-self: right; display: flex; font-size: 75%; letter-spacing: 2px; - .collectionStackingViewChrome-sectionFilter-label { + .collectionStackingViewChrome-sectionFilter-label, + .collectionTreeViewChrome-sectionFilter-label { vertical-align: center; padding: 10px; } - .collectionStackingViewChrome-sectionFilter { + .collectionStackingViewChrome-sectionFilter, + .collectionTreeViewChrome-sectionFilter { color: white; width: 100px; text-align: center; @@ -177,7 +191,8 @@ } } - .collectionStackingViewChrome-sectionFilter:hover { + .collectionStackingViewChrome-sectionFilter:hover, + .collectionTreeViewChrome-sectionFilter:hover { cursor: text; } } diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx index 08a1f6902..6ea718330 100644 --- a/src/client/views/collections/CollectionViewChromes.tsx +++ b/src/client/views/collections/CollectionViewChromes.tsx @@ -5,7 +5,7 @@ import { CollectionViewType } from "./CollectionBaseView"; import { undoBatch } from "../../util/UndoManager"; import { action, observable, runInAction, computed, IObservable, IObservableValue, reaction, autorun } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast } from "../../../new_fields/Doc"; +import { Doc, DocListCast, FieldResult } from "../../../new_fields/Doc"; import { DocLike } from "../MetadataEntryMenu"; import * as Autosuggest from 'react-autosuggest'; import { EditableView } from "../EditableView"; @@ -21,7 +21,10 @@ import { listSpec } from "../../../new_fields/Schema"; import { List } from "../../../new_fields/List"; import { Id } from "../../../new_fields/FieldSymbols"; import { threadId } from "worker_threads"; +import { DragManager } from "../../util/DragManager"; const datepicker = require('js-datepicker'); +import * as $ from 'jquery'; +import { firebasedynamiclinks } from "googleapis/build/src/apis/firebasedynamiclinks"; interface CollectionViewChromeProps { CollectionView: CollectionView; @@ -29,10 +32,18 @@ interface CollectionViewChromeProps { collapse?: (value: boolean) => any; } +interface Filter { + key: string; + value: string; + contains: boolean; +} + let stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation(); @observer export class CollectionViewBaseChrome extends React.Component<CollectionViewChromeProps> { + //(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\) + @observable private _viewSpecsOpen: boolean = false; @observable private _dateWithinValue: string = ""; @observable private _dateValue: Date | string = ""; @@ -42,6 +53,43 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro private _picker: any; private _datePickerElGuid = Utils.GenerateGuid(); + getFilters = (script: string) => { + let re: any = /(!)?\(\(\(doc\.(\w+)\s+&&\s+\(doc\.\w+\s+as\s+\w+\)\.includes\(\"(\w+)\"\)/g; + let arr: any[] = re.exec(script); + let toReturn: Filter[] = []; + if (arr !== null) { + let filter: Filter = { + key: arr[2], + value: arr[3], + contains: (arr[1] === "!") ? false : true, + }; + toReturn.push(filter); + script = script.replace(arr[0], ""); + if (re.exec(script) !== null) { + toReturn.push(...this.getFilters(script)); + } + else { return toReturn; } + } + return toReturn; + } + + addKeyRestrictions = (fields: Filter[]) => { + + if (fields.length !== 0) { + for (let i = 0; i < fields.length; i++) { + this._keyRestrictions.push([<KeyRestrictionRow field={fields[i].key} value={fields[i].value} key={Utils.GenerateGuid()} contains={fields[i].contains} script={(value: string) => runInAction(() => this._keyRestrictions[i][1] = value)} />, ""]); + + } + if (this._keyRestrictions.length === 1) { + this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[1][1] = value)} />, ""]); + } + } + else { + this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[0][1] = value)} />, ""]); + this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={false} script={(value: string) => runInAction(() => this._keyRestrictions[1][1] = value)} />, ""]); + } + } + componentDidMount = () => { setTimeout(() => this._picker = datepicker("#" + this._datePickerElGuid, { disabler: (date: Date) => date > new Date(), @@ -49,10 +97,14 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro dateSelected: new Date() }), 1000); - runInAction(() => { - this._keyRestrictions.push([<KeyRestrictionRow key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[0][1] = value)} />, ""]); - this._keyRestrictions.push([<KeyRestrictionRow key={Utils.GenerateGuid()} contains={false} script={(value: string) => runInAction(() => this._keyRestrictions[1][1] = value)} />, ""]); + let fields: Filter[] = []; + if (this.filterValue) { + let string = this.filterValue.script.originalScript; + fields = this.getFilters(string); + } + runInAction(() => { + this.addKeyRestrictions(fields); // chrome status is one of disabled, collapsed, or visible. this determines initial state from document let chromeStatus = this.props.CollectionView.props.Document.chromeStatus; if (chromeStatus) { @@ -111,17 +163,19 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro @action addKeyRestriction = (e: React.MouseEvent) => { let index = this._keyRestrictions.length; - this._keyRestrictions.push([<KeyRestrictionRow key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[index][1] = value)} />, ""]); + this._keyRestrictions.push([<KeyRestrictionRow field="" value="" key={Utils.GenerateGuid()} contains={true} script={(value: string) => runInAction(() => this._keyRestrictions[index][1] = value)} />, ""]); this.openViewSpecs(e); } - @action + @action.bound applyFilter = (e: React.MouseEvent) => { + this.openViewSpecs(e); - let keyRestrictionScript = `${this._keyRestrictions.map(i => i[1]) - .reduce((acc: string, value: string, i: number) => value ? `${acc} && ${value}` : acc)}`; + console.log(this._keyRestrictions) + + let keyRestrictionScript = "(" + this._keyRestrictions.map(i => i[1]).filter(i => i.length > 0).join(" && ") + ")"; let yearOffset = this._dateWithinValue[1] === 'y' ? 1 : 0; let monthOffset = this._dateWithinValue[1] === 'm' ? parseInt(this._dateWithinValue[0]) : 0; let weekOffset = this._dateWithinValue[1] === 'w' ? parseInt(this._dateWithinValue[0]) : 0; @@ -141,9 +195,10 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro } } let fullScript = dateRestrictionScript.length || keyRestrictionScript.length ? dateRestrictionScript.length ? - `return ${dateRestrictionScript} ${keyRestrictionScript.length ? "&&" : ""} ${keyRestrictionScript}` : - `return ${keyRestrictionScript} ${dateRestrictionScript.length ? "&&" : ""} ${dateRestrictionScript}` : + `return ${dateRestrictionScript} ${keyRestrictionScript.length ? "&&" : ""} (${keyRestrictionScript})` : + `return (${keyRestrictionScript}) ${dateRestrictionScript.length ? "&&" : ""} ${dateRestrictionScript}` : "return true"; + let compiled = CompileScript(fullScript, { params: { doc: Doc.name }, typecheck: false }); if (compiled.compiled) { this.props.CollectionView.props.Document.viewSpecScript = new ScriptField(compiled); @@ -180,6 +235,12 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro CollectionView={this.props.CollectionView} type={this.props.type} />); + case CollectionViewType.Tree: return ( + <CollectionTreeViewChrome + key="collchrome" + CollectionView={this.props.CollectionView} + type={this.props.type} + />); default: return null; } @@ -215,6 +276,38 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro })} />); } + @action.bound + clearFilter = () => { + let compiled = CompileScript("return true", { params: { doc: Doc.name }, typecheck: false }); + if (compiled.compiled) { + this.props.CollectionView.props.Document.viewSpecScript = new ScriptField(compiled); + } + + this._keyRestrictions = []; + this.addKeyRestrictions([]); + } + + private dropDisposer?: DragManager.DragDropDisposer; + protected createDropTarget = (ele: HTMLDivElement) => { + this.dropDisposer && this.dropDisposer(); + if (ele) { + this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.drop.bind(this) } }); + } + } + + @undoBatch + @action + protected drop(e: Event, de: DragManager.DropEvent): boolean { + if (de.data instanceof DragManager.DocumentDragData) { + if (de.data.draggedDocuments.length) { + this.props.CollectionView.props.Document.childLayout = de.data.draggedDocuments[0]; + e.stopPropagation(); + return true; + } + } + return true; + } + render() { let collapsed = this.props.CollectionView.props.Document.chromeStatus !== "enabled"; return ( @@ -245,9 +338,10 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro <div className="collectionViewBaseChrome-viewSpecs" style={{ display: collapsed ? "none" : "grid" }}> <input className="collectionViewBaseChrome-viewSpecsInput" placeholder="FILTER DOCUMENTS" - value={this.filterValue ? this.filterValue.script.originalScript : ""} + value={this.filterValue ? this.filterValue.script.originalScript === "return true" ? "" : this.filterValue.script.originalScript : ""} onChange={(e) => { }} - onPointerDown={this.openViewSpecs} /> + onPointerDown={this.openViewSpecs} + id="viewSpecsInput" /> {this.getPivotInput()} <div className="collectionViewBaseChrome-viewSpecsMenu" onPointerDown={this.openViewSpecs} @@ -287,13 +381,15 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.applyFilter}> APPLY FILTER </button> - + <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.clearFilter}> + CLEAR + </button> </div> </div> </div> - {/* <button className="collectionViewBaseChrome-button" > - SHOW TIMELINE - </button> */} + <div className="collectionViewBaseChrome-template" ref={this.createDropTarget} style={{}}> + TEMPLATE + </div> </div> {this.subChrome()} </div> @@ -471,4 +567,109 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionViewCh </div > ); } -}
\ No newline at end of file +} + +@observer +export class CollectionTreeViewChrome extends React.Component<CollectionViewChromeProps> { + @observable private _currentKey: string = ""; + @observable private suggestions: string[] = []; + + @computed private get descending() { return BoolCast(this.props.CollectionView.props.Document.stackingHeadersSortDescending); } + @computed get sectionFilter() { return StrCast(this.props.CollectionView.props.Document.sectionFilter); } + + getKeySuggestions = async (value: string): Promise<string[]> => { + value = value.toLowerCase(); + let docs: Doc | Doc[] | Promise<Doc> | Promise<Doc[]> | (() => DocLike) + = () => DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldExt ? this.props.CollectionView.props.fieldExt : this.props.CollectionView.props.fieldKey]); + if (typeof docs === "function") { + docs = docs(); + } + docs = await docs; + if (docs instanceof Doc) { + return Object.keys(docs).filter(key => key.toLowerCase().startsWith(value)); + } else { + const keys = new Set<string>(); + docs.forEach(doc => Doc.allKeys(doc).forEach(key => keys.add(key))); + return Array.from(keys).filter(key => key.toLowerCase().startsWith(value)); + } + } + + @action + onKeyChange = (e: React.ChangeEvent, { newValue }: { newValue: string }) => { + this._currentKey = newValue; + } + + getSuggestionValue = (suggestion: string) => suggestion; + + renderSuggestion = (suggestion: string) => { + return <p>{suggestion}</p>; + } + + onSuggestionFetch = async ({ value }: { value: string }) => { + const sugg = await this.getKeySuggestions(value); + runInAction(() => { + this.suggestions = sugg; + }); + } + + @action + onSuggestionClear = () => { + this.suggestions = []; + } + + setValue = (value: string) => { + this.props.CollectionView.props.Document.sectionFilter = value; + return true; + } + + @action toggleSort = () => { this.props.CollectionView.props.Document.stackingHeadersSortDescending = !this.props.CollectionView.props.Document.stackingHeadersSortDescending; }; + @action resetValue = () => { this._currentKey = this.sectionFilter; }; + + render() { + return ( + <div className="collectionTreeViewChrome-cont"> + <button className="collectionTreeViewChrome-sort" onClick={this.toggleSort}> + <div className="collectionTreeViewChrome-sortLabel"> + Sort + </div> + <div className="collectionTreeViewChrome-sortIcon" style={{ transform: `rotate(${this.descending ? "180" : "0"}deg)` }}> + <FontAwesomeIcon icon="caret-up" size="2x" color="white" /> + </div> + </button> + <div className="collectionTreeViewChrome-sectionFilter-cont"> + <div className="collectionTreeViewChrome-sectionFilter-label"> + GROUP ITEMS BY: + </div> + <div className="collectionTreeViewChrome-sectionFilter"> + <EditableView + GetValue={() => this.sectionFilter} + autosuggestProps={ + { + resetValue: this.resetValue, + value: this._currentKey, + onChange: this.onKeyChange, + autosuggestProps: { + inputProps: + { + value: this._currentKey, + onChange: this.onKeyChange + }, + getSuggestionValue: this.getSuggestionValue, + suggestions: this.suggestions, + alwaysRenderSuggestions: true, + renderSuggestion: this.renderSuggestion, + onSuggestionsFetchRequested: this.onSuggestionFetch, + onSuggestionsClearRequested: this.onSuggestionClear + } + }} + oneLine + SetValue={this.setValue} + contents={this.sectionFilter ? this.sectionFilter : "N/A"} + /> + </div> + </div> + </div> + ); + } +} + diff --git a/src/client/views/collections/KeyRestrictionRow.tsx b/src/client/views/collections/KeyRestrictionRow.tsx index 1b59547d8..e35b7d7d3 100644 --- a/src/client/views/collections/KeyRestrictionRow.tsx +++ b/src/client/views/collections/KeyRestrictionRow.tsx @@ -7,12 +7,14 @@ import { Doc } from "../../../new_fields/Doc"; interface IKeyRestrictionProps { contains: boolean; script: (value: string) => void; + field: string; + value: string; } @observer export default class KeyRestrictionRow extends React.Component<IKeyRestrictionProps> { - @observable private _key = ""; - @observable private _value = ""; + @observable private _key = this.props.field; + @observable private _value = this.props.value; @observable private _contains = this.props.contains; render() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index b932db424..1c6393795 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1,7 +1,7 @@ import { library } from "@fortawesome/fontawesome-svg-core"; import { faEye } from "@fortawesome/free-regular-svg-icons"; import { faCompass, faCompressArrowsAlt, faExpandArrowsAlt, faPaintBrush, faTable, faUpload, faChalkboard, faBraille } from "@fortawesome/free-solid-svg-icons"; -import { action, computed, observable } from "mobx"; +import { action, computed, observable, IReactionDisposer, reaction } from "mobx"; import { observer } from "mobx-react"; import { Doc, DocListCastAsync, HeightSym, WidthSym, DocListCast, FieldResult, Field, Opt } from "../../../../new_fields/Doc"; import { Id } from "../../../../new_fields/FieldSymbols"; @@ -80,7 +80,6 @@ export namespace PivotView { let collection = target.Document; const field = StrCast(collection.pivotField) || "title"; const width = NumCast(collection.pivotWidth) || 200; - const groups = new Map<FieldResult<Field>, Doc[]>(); for (const doc of target.childDocs) { @@ -93,14 +92,11 @@ export namespace PivotView { } else { groups.set(val, [doc]); } - } let minSize = Infinity; - groups.forEach((val, key) => { - minSize = Math.min(minSize, val.length); - }); + groups.forEach((val, key) => minSize = Math.min(minSize, val.length)); const numCols = NumCast(collection.pivotNumColumns) || Math.ceil(Math.sqrt(minSize)); const fontSize = NumCast(collection.pivotFontSize); @@ -137,42 +133,36 @@ export namespace PivotView { }); let elements = target.viewDefsToJSX(groupNames); - let curPage = FieldValue(target.Document.curPage, -1); - - let docViews = target.childDocs.filter(doc => doc instanceof Doc).reduce((prev, doc) => { - var page = NumCast(doc.page, -1); - if ((Math.abs(Math.round(page) - Math.round(curPage)) < 3) || page === -1) { - let minim = BoolCast(doc.isMinimized); - if (minim === undefined || !minim) { - let defaultPosition = (): ViewDefBounds => { - return { - x: NumCast(doc.x), - y: NumCast(doc.y), - z: NumCast(doc.z), - width: NumCast(doc.width), - height: NumCast(doc.height) - }; + let docViews = target.childDocs.reduce((prev, doc) => { + let minim = BoolCast(doc.isMinimized); + if (minim === undefined || !minim) { + let defaultPosition = (): ViewDefBounds => { + return { + x: NumCast(doc.x), + y: NumCast(doc.y), + z: NumCast(doc.z), + width: NumCast(doc.width), + height: NumCast(doc.height) }; - const pos = docMap.get(doc) || defaultPosition(); - prev.push({ - ele: ( - <CollectionFreeFormDocumentView - key={doc[Id]} - x={pos.x} - y={pos.y} - width={pos.width} - height={pos.height} - {...target.getChildDocumentViewProps(doc)} - />), - bounds: { - x: pos.x, - y: pos.y, - z: pos.z, - width: NumCast(pos.width), - height: NumCast(pos.height) - } - }); - } + }; + const pos = docMap.get(doc) || defaultPosition(); + prev.push({ + ele: <CollectionFreeFormDocumentView + key={doc[Id]} + x={pos.x} + y={pos.y} + width={pos.width} + height={pos.height} + {...target.getChildDocumentViewProps(doc)} + />, + bounds: { + x: pos.x, + y: pos.y, + z: pos.z, + width: NumCast(pos.width), + height: NumCast(pos.height) + } + }); } return prev; }, elements); @@ -196,9 +186,15 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { private get _pheight() { return this.props.PanelHeight(); } private _timelineRef = React.createRef<Timeline>(); private inkKey = "ink"; + private _childLayoutDisposer?: IReactionDisposer; - constructor(props: any) { - super(props); + componentDidMount() { + this._childLayoutDisposer = reaction(() => [this.childDocs, Cast(this.props.Document.childLayout, Doc)], + async (args) => args[1] instanceof Doc && + this.childDocs.map(async doc => !Doc.AreProtosEqual(args[1] as Doc, (await doc).layout as Doc) && Doc.ApplyTemplateTo(args[1] as Doc, (await doc), undefined))); + } + componentWillUnmount() { + this._childLayoutDisposer && this._childLayoutDisposer(); } get parentScaling() { @@ -726,6 +722,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @computed.struct get elements() { + if (this.Document.usePivotLayout) return PivotView.elements(this); let curPage = FieldValue(this.Document.curPage, -1); const initScript = this.Document.arrangeInit; const script = this.Document.arrangeScript; @@ -769,7 +766,7 @@ export class CollectionFreeFormView extends CollectionSubView(PanZoomDocument) { @computed.struct get views() { - let source = this.Document.usePivotLayout === true ? PivotView.elements(this) : this.elements; + let source = this.elements; return source.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); } @computed.struct |
