aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoryipstanley <stanley_yip@brown.edu>2019-07-24 17:18:15 -0400
committeryipstanley <stanley_yip@brown.edu>2019-07-24 17:18:15 -0400
commit828fdd80ab8af26f6e2ef521e05a71e7e500bf8c (patch)
treeb3d6f76b8362f12b6d1af1dc66d4001e995ecf03
parent0ebcadb80a89e7fe4f8f2a8a47570b19fd2f8711 (diff)
view specsss
-rw-r--r--package.json1
-rw-r--r--src/client/views/EditableView.tsx2
-rw-r--r--src/client/views/MainView.tsx4
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx9
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx44
-rw-r--r--src/client/views/collections/CollectionSubView.tsx15
-rw-r--r--src/client/views/collections/CollectionViewChromes.scss113
-rw-r--r--src/client/views/collections/CollectionViewChromes.tsx261
-rw-r--r--src/client/views/collections/KeyRestrictionRow.tsx45
9 files changed, 428 insertions, 66 deletions
diff --git a/package.json b/package.json
index 4a15cbb2f..37052fde3 100644
--- a/package.json
+++ b/package.json
@@ -136,6 +136,7 @@
"image-size": "^0.7.4",
"imagesloaded": "^4.1.4",
"jquery-awesome-cursor": "^0.3.1",
+ "js-datepicker": "^4.6.6",
"jsonwebtoken": "^8.5.0",
"jsx-to-string": "^1.4.0",
"lodash": "^4.17.11",
diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx
index a5bb40243..bd00d47b9 100644
--- a/src/client/views/EditableView.tsx
+++ b/src/client/views/EditableView.tsx
@@ -116,7 +116,7 @@ export class EditableView extends React.Component<EditableProps> {
return (
<div className={`editableView-container-editing${this.props.oneLine ? "-oneLine" : ""}`}
style={{ display: this.props.display, height: "auto", maxHeight: `${this.props.height}` }}
- onClick={this.onClick} >
+ onClick={this.onClick}>
<span style={{ fontStyle: this.props.fontStyle }}>{this.props.contents}</span>
</div>
);
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 94a4835a1..69602deed 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -1,5 +1,5 @@
import { IconName, library } from '@fortawesome/fontawesome-svg-core';
-import { faArrowDown, faCloudUploadAlt, faArrowUp, faClone, faCheck, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faPortrait, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faThumbtack, faTree, faUndoAlt, faCat } from '@fortawesome/free-solid-svg-icons';
+import { faArrowDown, faCaretUp, faLongArrowAltRight, faCloudUploadAlt, faArrowUp, faClone, faCheck, faCommentAlt, faCut, faExclamation, faFilePdf, faFilm, faFont, faGlobeAsia, faPortrait, faMusic, faObjectGroup, faPenNib, faRedoAlt, faTable, faThumbtack, faTree, faUndoAlt, faCat } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, configure, observable, runInAction, reaction, trace } from 'mobx';
import { observer } from 'mobx-react';
@@ -127,7 +127,9 @@ export class MainView extends React.Component {
library.add(faCut);
library.add(faCommentAlt);
library.add(faThumbtack);
+ library.add(faLongArrowAltRight);
library.add(faCheck);
+ library.add(faCaretUp);
library.add(faArrowDown);
library.add(faArrowUp);
library.add(faCloudUploadAlt);
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index e4e6f92e3..a78a47ffb 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -249,6 +249,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
return false;
}
+ sortFunc = (a: [SchemaHeaderField, Doc[]], b: [SchemaHeaderField, Doc[]]): 1 | -1 => {
+ let descending = BoolCast(this.props.Document.stackingHeadersSortDescending);
+ let firstEntry = descending ? b : a;
+ let secondEntry = descending ? a : b;
+ return firstEntry[0].heading > secondEntry[0].heading ? 1 : -1;
+ }
+
render() {
let headings = Array.from(this.Sections.keys());
let editableViewProps = {
@@ -264,7 +271,7 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
["width > height", this.filteredChildren.filter(f => f[WidthSym]() >= 1 + f[HeightSym]())],
["width = height", this.filteredChildren.filter(f => Math.abs(f[WidthSym]() - f[HeightSym]()) < 1)],
["height > width", this.filteredChildren.filter(f => f[WidthSym]() + 1 <= f[HeightSym]())]]. */}
- {this.props.Document.sectionFilter ? Array.from(this.Sections.entries()).sort((a, b) => a[0].toString() > b[0].toString() ? 1 : -1).
+ {this.props.Document.sectionFilter ? Array.from(this.Sections.entries()).sort(this.sortFunc).
map(section => this.section(section[0], section[1] as Doc[])) :
this.section(undefined, this.filteredChildren)}
{this.props.Document.sectionFilter ?
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index fe24c63c7..582adc418 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -16,6 +16,8 @@ import "./CollectionStackingView.scss";
import { Docs } from "../../documents/Documents";
import { SchemaHeaderField } from "../../../new_fields/SchemaHeaderField";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import { CompileScript } from "../../util/Scripting";
interface CSVFieldColumnProps {
@@ -35,6 +37,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
private _dropRef: HTMLDivElement | null = null;
private dropDisposer?: DragManager.DragDropDisposer;
+ private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
@observable _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading;
@@ -182,6 +185,43 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
}
}
+ startDrag = (e: PointerEvent) => {
+ let alias = Doc.MakeAlias(this.props.parent.props.Document);
+ let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ let value = this.getValue(this._heading);
+ value = typeof value === "string" ? `"${value}"` : value;
+ let script = `return doc.${key} === ${value}`;
+ let compiled = CompileScript(script, { params: { doc: Doc.name } });
+ if (compiled.compiled) {
+ let scriptField = new ScriptField(compiled);
+ alias.viewSpecScript = scriptField;
+ let dragData = new DragManager.DocumentDragData([alias], [alias.proto]);
+ DragManager.StartDocumentDrag([this._headerRef.current!], dragData, e.clientX, e.clientY);
+ }
+
+ e.stopPropagation();
+ document.removeEventListener("pointermove", this.startDrag);
+ document.removeEventListener("pointerup", this.pointerUp);
+ }
+
+ pointerUp = (e: PointerEvent) => {
+ e.stopPropagation();
+ e.preventDefault();
+
+ document.removeEventListener("pointermove", this.startDrag);
+ document.removeEventListener("pointerup", this.pointerUp);
+ }
+
+ headerDown = (e: React.PointerEvent<HTMLDivElement>) => {
+ e.stopPropagation();
+ e.preventDefault();
+
+ document.removeEventListener("pointermove", this.startDrag);
+ document.addEventListener("pointermove", this.startDrag);
+ document.removeEventListener("pointerup", this.pointerUp);
+ document.addEventListener("pointerup", this.pointerUp);
+ }
+
render() {
let cols = this.props.cols();
let key = StrCast(this.props.parent.props.Document.sectionFilter);
@@ -199,11 +239,11 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
oneLine: true
}
let headingView = this.props.headingObject ?
- <div key={heading} className="collectionStackingView-sectionHeader"
+ <div key={heading} className="collectionStackingView-sectionHeader" ref={this._headerRef}
style={{ width: (style.columnWidth) / (uniqueHeadings.length + 1) }}>
{/* the default bucket (no key value) has a tooltip that describes what it is.
Further, it does not have a color and cannot be deleted. */}
- <div className="collectionStackingView-sectionHeader-subCont"
+ <div className="collectionStackingView-sectionHeader-subCont" onPointerDown={this.headerDown}
title={evContents === `No ${key} value` ?
`Documents that don't have a ${key} value will go here. This column cannot be removed.` : ""}
style={{
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 2ddefb3c0..24c8adb35 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -21,6 +21,8 @@ import { CollectionView } from "./CollectionView";
import React = require("react");
import { MainView } from "../MainView";
import { Utils } from "../../../Utils";
+import { ScriptField } from "../../../new_fields/ScriptField";
+import { CompileScript } from "../../util/Scripting";
export interface CollectionViewProps extends FieldViewProps {
addDocument: (document: Doc, allowDuplicates?: boolean) => boolean;
@@ -54,7 +56,18 @@ export function CollectionSubView<T>(schemaCtor: (doc: Doc) => T) {
let self = this;
//TODO tfs: This might not be what we want?
//This linter error can't be fixed because of how js arguments work, so don't switch this to filter(FieldValue)
- return DocListCast(this.extensionDoc[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]);
+ let docs = DocListCast(this.extensionDoc[this.props.fieldExt ? this.props.fieldExt : this.props.fieldKey]);
+ let viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField);
+ if (viewSpecScript) {
+ let script = viewSpecScript.script;
+ docs = docs.filter(d => {
+ let res = script.run({ doc: d });
+ if (res.success) {
+ return res.result;
+ }
+ });
+ }
+ return docs;
}
get childDocList() {
//TODO tfs: This might not be what we want?
diff --git a/src/client/views/collections/CollectionViewChromes.scss b/src/client/views/collections/CollectionViewChromes.scss
index e5cb1b546..3103cd309 100644
--- a/src/client/views/collections/CollectionViewChromes.scss
+++ b/src/client/views/collections/CollectionViewChromes.scss
@@ -1,12 +1,120 @@
@import "../globalCssVariables";
+@import '~js-datepicker/dist/datepicker.min.css';
+
+.collectionViewBaseChrome {
+ display: flex;
+
+ .collectionViewBaseChrome-viewPicker {
+ font-size: 75%;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ background: rgb(238, 238, 238);
+ color: grey;
+ outline-color: black;
+ border: none;
+ padding: 12px 10px 11px 10px;
+ }
+
+ .collectionViewBaseChrome-viewPicker:active {
+ outline-color: black;
+ }
+
+ .collectionViewBaseChrome-collapse {
+ margin-right: 10px;
+ transition: all .5s;
+ }
+
+ .collectionViewBaseChrome-viewSpecs {
+ margin-left: 10px;
+ display: grid;
+
+ .collectionViewBaseChrome-viewSpecsInput {
+ padding: 12px 10px 11px 10px;
+ border: 0px;
+ color: grey;
+ text-align: center;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ outline-color: black;
+ font-size: 75%;
+ background: rgb(238, 238, 238);
+ height: 100%;
+ width: 150px;
+ }
+
+ .collectionViewBaseChrome-viewSpecsMenu {
+ overflow: hidden;
+ transition: height .5s, display .5s;
+ position: absolute;
+ top: 60px;
+ z-index: 100;
+ display: flex;
+ flex-direction: column;
+ background: rgb(238, 238, 238);
+ box-shadow: grey 2px 2px 4px;
+
+ .qs-datepicker {
+ left: unset;
+ right: 0;
+ }
+
+ .collectionViewBaseChrome-viewSpecsMenu-row {
+ display: grid;
+ grid-template-columns: 150px 200px 150px;
+ margin-top: 10px;
+ margin-right: 10px;
+
+ .collectionViewBaseChrome-viewSpecsMenu-rowLeft,
+ .collectionViewBaseChrome-viewSpecsMenu-rowMiddle,
+ .collectionViewBaseChrome-viewSpecsMenu-rowRight {
+ font-size: 75%;
+ letter-spacing: 2px;
+ color: grey;
+ margin-left: 10px;
+ padding: 5px;
+ border: none;
+ outline-color: black;
+ }
+ }
+
+ .collectionViewBaseChrome-viewSpecsMenu-lastRow {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-gap: 10px;
+ margin: 10px;
+ }
+ }
+ }
+}
.collectionStackingViewChrome {
display: grid;
- grid-template-columns: 1fr 1fr;
+ grid-template-columns: 1fr auto;
padding-bottom: 10px;
border-bottom: .5px solid lightgrey;
margin: 10px;
+ .collectionStackingViewChrome-cont {
+ display: flex;
+ justify-content: space-between;
+ }
+
+ .collectionStackingViewChrome-sort {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+
+ .collectionStackingViewChrome-sortIcon {
+ transition: transform .5s;
+ margin-left: 10px;
+ }
+ }
+
+ button:hover {
+ transform: scale(1);
+ }
+
+
.collectionStackingViewChrome-sectionFilter-cont {
justify-self: right;
display: flex;
@@ -21,10 +129,9 @@
.collectionStackingViewChrome-sectionFilter {
color: white;
- width: fit-content;
+ width: 100px;
text-align: center;
background: rgb(238, 238, 238);
- height: 37px;
.editable-view-input,
input,
diff --git a/src/client/views/collections/CollectionViewChromes.tsx b/src/client/views/collections/CollectionViewChromes.tsx
index 52fee26bf..2edac384d 100644
--- a/src/client/views/collections/CollectionViewChromes.tsx
+++ b/src/client/views/collections/CollectionViewChromes.tsx
@@ -3,55 +3,189 @@ import { CollectionView } from "./CollectionView";
import "./CollectionViewChromes.scss";
import { CollectionViewType } from "./CollectionBaseView";
import { undoBatch } from "../../util/UndoManager";
-import { action, observable, runInAction, computed } from "mobx";
+import { action, observable, runInAction, computed, IObservable, IObservableValue } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast } from "../../../new_fields/Doc";
import { DocLike } from "../MetadataEntryMenu";
-const higflyout = require("@hig/flyout");
-export const Flyout = higflyout.default;
import * as Autosuggest from 'react-autosuggest';
import { EditableView } from "../EditableView";
-import { StrCast } from "../../../new_fields/Types";
+import { StrCast, NumCast, BoolCast, Cast } from "../../../new_fields/Types";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { Utils } from "../../../Utils";
+import KeyRestrictionRow from "./KeyRestrictionRow";
+import { CompileScript } from "../../util/Scripting";
+import { ScriptField } from "../../../new_fields/ScriptField";
+const datepicker = require('js-datepicker');
interface CollectionViewChromeProps {
CollectionView: CollectionView;
}
+let stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();
+
+@observer
class CollectionViewBaseChrome extends React.Component<CollectionViewChromeProps> {
+ @observable private _viewSpecsOpen: boolean = false;
+ @observable private _dateWithinValue: string = "";
+ @observable private _dateValue: Date = new Date();
+ @observable private _keyRestrictions: [JSX.Element, string][] = [];
+ @computed private get filterValue() { return Cast(this.props.CollectionView.props.Document.viewSpecScript, ScriptField); }
+
+ private _picker: any;
+ private _datePickerElGuid = Utils.GenerateGuid();
+
+ componentDidMount = () => {
+ this._picker = datepicker("#" + this._datePickerElGuid, {
+ disabler: (date: Date) => date > new Date(),
+ onSelect: (instance: any, date: Date) => runInAction(() => this._dateValue = date),
+ dateSelected: new Date()
+ });
+
+ 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)} />, ""]);
+ });
+ }
+
@undoBatch
viewChanged = (e: React.ChangeEvent) => {
//@ts-ignore
- switch (e.target.selectedOptions[0].value) {
- case "freeform":
- this.props.CollectionView.props.Document.viewType = CollectionViewType.Freeform;
- break;
- case "schema":
- this.props.CollectionView.props.Document.viewType = CollectionViewType.Schema;
- break;
- case "treeview":
- this.props.CollectionView.props.Document.viewType = CollectionViewType.Tree;
- break;
- case "stacking":
- this.props.CollectionView.props.Document.viewType = CollectionViewType.Stacking;
- break;
- case "masonry":
- this.props.CollectionView.props.Document.viewType = CollectionViewType.Masonry;
- break;
- default:
- break;
+ this.props.CollectionView.props.Document.viewType = parseInt(e.target.selectedOptions[0].value);
+ }
+
+ @action
+ openViewSpecs = (e: React.SyntheticEvent) => {
+ this._viewSpecsOpen = true;
+
+ //@ts-ignore
+ if (!e.target.classList[0].startsWith("qs")) {
+ this.closeDatePicker();
+ }
+
+ e.stopPropagation();
+ document.removeEventListener("pointerdown", this.closeViewSpecs);
+ document.addEventListener("pointerdown", this.closeViewSpecs);
+ }
+
+ @action closeViewSpecs = () => { this._viewSpecsOpen = false; document.removeEventListener("pointerdown", this.closeViewSpecs); };
+
+ @action
+ openDatePicker = (e: React.PointerEvent) => {
+ this.openViewSpecs(e);
+ if (this._picker) {
+ this._picker.alwaysShow = true;
+ this._picker.show();
+ // TODO: calendar is offset when zoomed in/out
+ // this._picker.calendar.style.position = "absolute";
+ // let transform = this.props.CollectionView.props.ScreenToLocalTransform();
+ // let x = parseInt(this._picker.calendar.style.left) / transform.Scale;
+ // let y = parseInt(this._picker.calendar.style.top) / transform.Scale;
+ // this._picker.calendar.style.left = x;
+ // this._picker.calendar.style.top = y;
+
+ e.stopPropagation();
}
}
+ @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.openViewSpecs(e);
+ }
+
+ @action
+ 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)}`;
+ 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;
+ let dayOffset = (this._dateWithinValue[1] === 'd' ? parseInt(this._dateWithinValue[0]) : 0) + weekOffset * 7;
+ let lowerBound = new Date(this._dateValue.getFullYear() - yearOffset, this._dateValue.getMonth() - monthOffset, this._dateValue.getDate() - dayOffset);
+ let upperBound = new Date(this._dateValue.getFullYear() + yearOffset, this._dateValue.getMonth() + monthOffset, this._dateValue.getDate() + dayOffset + 1);
+ let dateRestrictionScript = `((doc.creationDate as any).date >= ${lowerBound.valueOf()} && (doc.creationDate as any).date <= ${upperBound.valueOf()})`;
+ let fullScript = `return ${dateRestrictionScript} && ${keyRestrictionScript}`;
+ let compiled = CompileScript(fullScript, { params: { doc: Doc.name } });
+ if (compiled.compiled) {
+ this.props.CollectionView.props.Document.viewSpecScript = new ScriptField(compiled);
+ }
+ }
+
+ @action
+ closeDatePicker = () => {
+ if (this._picker) {
+ this._picker.alwaysShow = false;
+ this._picker.hide();
+ }
+ document.removeEventListener("pointerdown", this.closeDatePicker);
+ }
+
render() {
return (
<div className="collectionViewBaseChrome">
- <select onChange={this.viewChanged}>
- <option value="freeform" selected={this.props.CollectionView.props.Document.viewType === CollectionViewType.Freeform}>Freeform View</option>
- <option value="schema" selected={this.props.CollectionView.props.Document.viewType === CollectionViewType.Schema}>Schema View</option>
- <option value="treeview" selected={this.props.CollectionView.props.Document.viewType === CollectionViewType.Tree}>Tree View</option>
- <option value="stacking" selected={this.props.CollectionView.props.Document.viewType === CollectionViewType.Stacking}>Stacking View</option>
- <option value="masonry" selected={this.props.CollectionView.props.Document.viewType === CollectionViewType.Masonry}>Masonry View</option>
+ <button className="collectionViewBaseChrome-collapse" title="Collapse collection chrome">
+ <FontAwesomeIcon icon="caret-up" size="2x" />
+ </button>
+ <select
+ className="collectionViewBaseChrome-viewPicker"
+ onPointerDown={stopPropagation}
+ onChange={this.viewChanged}
+ value={NumCast(this.props.CollectionView.props.Document.viewType)}>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="1">Freeform View</option>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="2">Schema View</option>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="4">Tree View</option>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="5">Stacking View</option>
+ <option className="collectionViewBaseChrome-viewOption" onPointerDown={stopPropagation} value="6">Masonry View></option>
</select>
+ <div className="collectionViewBaseChrome-viewSpecs">
+ <input className="collectionViewBaseChrome-viewSpecsInput"
+ placeholder="Filter Documents"
+ value={this.filterValue ? this.filterValue.script.originalScript : ""}
+ onPointerDown={this.openViewSpecs} />
+ <div className="collectionViewBaseChrome-viewSpecsMenu"
+ onPointerDown={this.openViewSpecs}
+ style={{
+ height: this._viewSpecsOpen ? "fit-content" : "0px",
+ overflow: this._viewSpecsOpen ? "initial" : "hidden"
+ }}>
+ {this._keyRestrictions.map(i => i[0])}
+ <div className="collectionViewBaseChrome-viewSpecsMenu-row">
+ <div className="collectionViewBaseChrome-viewSpecsMenu-rowLeft">
+ CREATED WITHIN:
+ </div>
+ <select className="collectionViewBaseChrome-viewSpecsMenu-rowMiddle"
+ style={{ textTransform: "uppercase", textAlign: "center" }}
+ value={this._dateWithinValue}
+ onChange={(e) => runInAction(() => this._dateWithinValue = e.target.value)}>
+ <option value="1d">1 day of</option>
+ <option value="3d">3 days of</option>
+ <option value="1w">1 week of</option>
+ <option value="2w">2 weeks of</option>
+ <option value="1m">1 month of</option>
+ <option value="2m">2 months of</option>
+ <option value="6m">6 months of</option>
+ <option value="1y">1 year of</option>
+ </select>
+ <input className="collectionViewBaseChrome-viewSpecsMenu-rowRight"
+ id={this._datePickerElGuid}
+ value={this._dateValue.toLocaleDateString()}
+ onPointerDown={this.openDatePicker}
+ placeholder="Value" />
+ </div>
+ <div className="collectionViewBaseChrome-viewSpecsMenu-lastRow">
+ <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.addKeyRestriction}>
+ ADD KEY RESTRICTION
+ </button>
+ <button className="collectonViewBaseChrome-viewSpecsMenu-lastRowButton" onClick={this.applyFilter}>
+ APPLY FILTER
+ </button>
+ </div>
+ </div>
+ </div>
</div>
)
}
@@ -62,6 +196,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
@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[]> => {
@@ -109,41 +244,53 @@ export class CollectionStackingViewChrome extends React.Component<CollectionView
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="collectionStackingViewChrome">
<CollectionViewBaseChrome CollectionView={this.props.CollectionView} />
- <div className="collectionStackingViewChrome-sectionFilter-cont">
- <div className="collectionStackingViewChrome-sectionFilter-label">
- Group items by:
- </div>
- <div className="collectionStackingViewChrome-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
- }
- }}
- SetValue={this.setValue}
- contents={this.sectionFilter ? this.sectionFilter : "N/A"}
- />
+ <div className="collectionStackingViewChrome-cont">
+ <button className="collectionStackingViewChrome-sort" onClick={this.toggleSort}>
+ <div className="collectionStackingViewChrome-sortLabel">
+ Sort
+ </div>
+ <div className="collectionStackingViewChrome-sortIcon" style={{ transform: `rotate(${this.descending ? "180" : "0"}deg)` }}>
+ <FontAwesomeIcon icon="caret-up" size="2x" color="white" />
+ </div>
+ </button>
+ <div className="collectionStackingViewChrome-sectionFilter-cont">
+ <div className="collectionStackingViewChrome-sectionFilter-label">
+ Group items by:
+ </div>
+ <div className="collectionStackingViewChrome-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>
</div>
diff --git a/src/client/views/collections/KeyRestrictionRow.tsx b/src/client/views/collections/KeyRestrictionRow.tsx
new file mode 100644
index 000000000..8051a8359
--- /dev/null
+++ b/src/client/views/collections/KeyRestrictionRow.tsx
@@ -0,0 +1,45 @@
+import * as React from "react";
+import { observable, runInAction } from "mobx";
+import { observer } from "mobx-react";
+import { PastelSchemaPalette } from "../../../new_fields/SchemaHeaderField";
+
+interface IKeyRestrictionProps {
+ contains: boolean;
+ script: (value: string) => void;
+}
+
+@observer
+export default class KeyRestrictionRow extends React.Component<IKeyRestrictionProps> {
+ @observable private _key = "";
+ @observable private _value = "";
+ @observable private _contains = this.props.contains;
+
+ render() {
+ if (this._key && this._value) {
+ let parsedValue: string | number = `"${this._value}"`;
+ let parsed = parseInt(this._value);
+ if (!isNaN(parsed)) {
+ parsedValue = parsed;
+ }
+ let scriptText = `(doc.${this._key} ${this._contains ? "===" : "!=="} ${parsedValue})`;
+ this.props.script(scriptText);
+ }
+ return (
+ <div className="collectionViewBaseChrome-viewSpecsMenu-row">
+ <input className="collectionViewBaseChrome-viewSpecsMenu-rowLeft"
+ value={this._key}
+ onChange={(e) => runInAction(() => this._key = e.target.value)}
+ placeholder="KEY" />
+ <button className="collectionViewBaseChrome-viewSpecsMenu-rowMiddle"
+ style={{ background: PastelSchemaPalette.get(this._contains ? "green" : "red") }}
+ onClick={() => runInAction(() => this._contains = !this._contains)}>
+ {this._contains ? "CONTAINS" : "DOES NOT CONTAIN"}
+ </button>
+ <input className="collectionViewBaseChrome-viewSpecsMenu-rowRight"
+ value={this._value}
+ onChange={(e) => runInAction(() => this._value = e.target.value)}
+ placeholder="VALUE" />
+ </div>
+ )
+ }
+} \ No newline at end of file