aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/MetadataEntryMenu.tsx1
-rw-r--r--src/client/views/collections/CollectionStackingView.scss55
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx113
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx202
-rw-r--r--src/client/views/nodes/DocumentView.tsx3
5 files changed, 286 insertions, 88 deletions
diff --git a/src/client/views/MetadataEntryMenu.tsx b/src/client/views/MetadataEntryMenu.tsx
index bd5a307b3..7fce3dd0c 100644
--- a/src/client/views/MetadataEntryMenu.tsx
+++ b/src/client/views/MetadataEntryMenu.tsx
@@ -74,6 +74,7 @@ export class MetadataEntryMenu extends React.Component<MetadataEntryProps>{
this.userModified = e.target.value.trim() !== "";
}
+ @action
onValueKeyDown = async (e: React.KeyboardEvent) => {
if (e.key === "Enter") {
const script = KeyValueBox.CompileKVPScript(this._currentValue);
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 7ebf5f77c..3e389225a 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -1,9 +1,12 @@
@import "../globalCssVariables";
+
.collectionStackingView {
height: 100%;
width: 100%;
position: absolute;
+ display: flex;
overflow-y: auto;
+
.collectionStackingView-docView-container {
width: 45%;
margin: 5% 2.5%;
@@ -18,21 +21,23 @@
align-items: center;
}
- .collectionStackingView-masonrySingle, .collectionStackingView-masonryGrid {
- width:100%;
- height:100%;
+ .collectionStackingView-masonrySingle,
+ .collectionStackingView-masonryGrid {
+ width: 100%;
+ height: 100%;
position: absolute;
- display:grid;
+ display: grid;
top: 0;
left: 0;
width: 100%;
position: absolute;
}
+
.collectionStackingView-masonrySingle {
- width:100%;
- height:100%;
+ width: 100%;
+ height: 100%;
position: absolute;
- display:flex;
+ display: flex;
flex-direction: column;
top: 0;
left: 0;
@@ -52,34 +57,50 @@
}
.collectionStackingView-columnDragger {
- width: 15;
- height: 15;
+ width: 15;
+ height: 15;
position: absolute;
margin-left: -5;
}
- .collectionStackingView-columnDoc{
+ .collectionStackingView-columnDoc {
display: inline-block;
}
- .collectionStackingView-columnDoc,
+ .collectionStackingView-columnDoc,
.collectionStackingView-masonryDoc {
margin-left: auto;
margin-right: auto;
}
-
+
.collectionStackingView-masonryDoc {
transform-origin: top left;
grid-column-end: span 1;
height: 100%;
}
- .collectionStackingView-sectionHeader {
- width: 90%;
+
+ .collectionStackingView-sectionHeader {
background: gray;
text-align: center;
- margin-left: 5%;
- margin-right: 5%;
- color: white;
+ margin-left: 10px;
+ margin-right: 10px;
margin-top: 10px;
+ color: $light-color;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ padding: 10px;
+
+ .editableView-input {
+ color: black;
+ }
+ }
+
+ .collectionStackingView-addDocumentButton,
+ .collectionStackingView-addGroupButton {
+ display: inline-block;
+ margin: 0 10px;
+ overflow: hidden;
+ width: 90%;
+ color: lightgrey;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 0e5f9a321..0ddd5528b 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -14,6 +14,7 @@ import { DragManager } from "../../util/DragManager";
import { DocumentType } from "../../documents/Documents";
import { Transform } from "../../util/Transform";
import { CursorProperty } from "csstype";
+import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
@observer
export class CollectionStackingView extends CollectionSubView(doc => doc) {
@@ -35,8 +36,13 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
let fields = new Map<object, Doc[]>();
sectionFilter && this.filteredChildren.map(d => {
let sectionValue = (d[sectionFilter] ? d[sectionFilter] : "-undefined-") as object;
- if (!fields.has(sectionValue)) fields.set(sectionValue, [d]);
- else fields.get(sectionValue)!.push(d);
+ let parsed = parseInt(sectionValue.toString());
+ let castedSectionValue: any = sectionValue;
+ if (!isNaN(parsed)) {
+ castedSectionValue = parsed;
+ }
+ if (!fields.has(castedSectionValue)) fields.set(castedSectionValue, [d]);
+ else fields.get(castedSectionValue)!.push(d);
});
return fields;
}
@@ -66,7 +72,9 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
getDisplayDoc(layoutDoc: Doc, d: Doc, dxf: () => Transform) {
let resolvedDataDoc = !this.props.Document.isTemplate && this.props.DataDoc !== this.props.Document ? this.props.DataDoc : undefined;
- let width = () => d.nativeWidth ? Math.min(layoutDoc[WidthSym](), this.columnWidth) : this.columnWidth;
+ let headings = Array.from(this.Sections.keys());
+ let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
+ let width = () => (d.nativeWidth ? Math.min(layoutDoc[WidthSym](), this.columnWidth) : this.columnWidth) / (uniqueHeadings.length + 1);
let height = () => this.getDocHeight(layoutDoc);
let finalDxf = () => dxf().scale(this.columnWidth / layoutDoc[WidthSym]());
return <CollectionSchemaPreview
@@ -96,49 +104,6 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
return (nw && nh) ? wid * aspect : d[HeightSym]();
}
- offsetTransform(doc: Doc, translateX: number, translateY: number) {
- let outerXf = Utils.GetScreenTransform(this._masonryGridRef!);
- let offset = this.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
- return this.props.ScreenToLocalTransform().translate(offset[0], offset[1]).scale(NumCast(doc.width, 1) / this.columnWidth);
- }
- getDocTransform(doc: Doc, dref: HTMLDivElement) {
- let { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
- return this.offsetTransform(doc, translateX, translateY);
- }
-
- getSingleDocTransform(doc: Doc, ind: number, width: number) {
- let localY = this.filteredChildren.reduce((height, d, i) =>
- height + (i < ind ? this.getDocHeight(Doc.expandTemplateLayout(d, this.props.DataDoc)) + this.gridGap : 0), this.yMargin);
- let translate = this.props.ScreenToLocalTransform().inverse().transformPoint((this.props.PanelWidth() - width) / 2, localY);
- return this.offsetTransform(doc, translate[0], translate[1]);
- }
-
- children(docs: Doc[]) {
- this._docXfs.length = 0;
- return docs.map((d, i) => {
- let layoutDoc = Doc.expandTemplateLayout(d, this.props.DataDoc);
- let width = () => d.nativeWidth ? Math.min(layoutDoc[WidthSym](), this.columnWidth) : this.columnWidth;
- let height = () => this.getDocHeight(layoutDoc);
- if (this.singleColumn) {
- //have to add the height of all previous single column sections or the doc decorations will be in the wrong place.
- let dxf = () => this.getSingleDocTransform(layoutDoc, i, width());
- let rowHgtPcnt = height();
- this._docXfs.push({ dxf: dxf, width: width, height: height });
- return <div className="collectionStackingView-columnDoc" key={d[Id]} style={{ width: width(), marginTop: i === 0 ? 0 : this.gridGap, height: `${rowHgtPcnt}` }} >
- {this.getDisplayDoc(layoutDoc, d, dxf)}
- </div>;
- } else {
- let dref = React.createRef<HTMLDivElement>();
- let dxf = () => this.getDocTransform(layoutDoc, dref.current!);
- let rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
- this._docXfs.push({ dxf: dxf, width: width, height: height });
- return <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={{ gridRowEnd: `span ${rowSpan}` }} >
- {this.getDisplayDoc(layoutDoc, d, dxf)}
- </div>;
- }
- });
- }
-
columnDividerDown = (e: React.PointerEvent) => {
e.stopPropagation();
e.preventDefault();
@@ -218,30 +183,37 @@ export class CollectionStackingView extends CollectionSubView(doc => doc) {
}
});
}
- section(heading: string, docList: Doc[]) {
- let cols = this.singleColumn ? 1 : Math.max(1, Math.min(this.filteredChildren.length,
+ section = (heading: string, docList: Doc[]) => {
+ let key = StrCast(this.props.Document.sectionFilter);
+ let types = docList.map(d => typeof d[key]);
+ let type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined = undefined;
+ if (types.map((i, idx) => types.indexOf(i) === idx).length === 1) {
+ type = types[0];
+ }
+ let parsed = parseInt(heading);
+ if (!isNaN(parsed)) {
+ heading = parsed.toString();
+ }
+ let cols = () => this.singleColumn ? 1 : Math.max(1, Math.min(this.filteredChildren.length,
Math.floor((this.props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap))));
- let templatecols = "";
- for (let i = 0; i < cols; i++) templatecols += `${this.columnWidth}px `;
- return <div key={heading}>
- {heading ? <div key={`${heading}`} className="collectionStackingView-sectionHeader">{heading}</div> : (null)}
- <div key={`${heading}-stack`} className={`collectionStackingView-masonry${this.singleColumn ? "Single" : "Grid"}`}
- style={{
- padding: this.singleColumn ? `${this.yMargin}px ${this.xMargin}px ${this.yMargin}px ${this.xMargin}px` : `${this.yMargin}px ${this.xMargin}px`,
- margin: "auto",
- width: this.singleColumn ? undefined : `${cols * (this.columnWidth + this.gridGap) + 2 * this.xMargin - this.gridGap}px`,
- height: 'max-content',
- position: "relative",
- gridGap: this.gridGap,
- gridTemplateColumns: this.singleColumn ? undefined : templatecols,
- gridAutoRows: this.singleColumn ? undefined : "0px"
- }}
- >
- {this.children(docList)}
- {this.singleColumn ? (null) : this.columnDragger}
- </div></div>;
+ return <CollectionStackingViewFieldColumn
+ cols={cols}
+ headings={() => Array.from(this.Sections.keys())}
+ heading={heading}
+ docList={docList}
+ parent={this}
+ type={type}
+ createDropTarget={this.createDropTarget} />;
+ }
+
+ @action
+ addGroup = () => {
+
}
+
render() {
+ let headings = Array.from(this.Sections.keys());
+ let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
return (
<div className="collectionStackingView"
ref={this.createRef} onDrop={this.onDrop.bind(this)} onWheel={(e: React.WheelEvent) => e.stopPropagation()} >
@@ -249,9 +221,14 @@ 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()).
+ {this.props.Document.sectionFilter ? Array.from(this.Sections.entries()).sort((a, b) => a[0].toString() > b[0].toString() ? 1 : -1).
map(section => this.section(section[0].toString(), section[1] as Doc[])) :
this.section("", this.filteredChildren)}
+ {this.props.Document.sectionFilter ?
+ <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
+ style={{ width: this.columnWidth / (uniqueHeadings.length + 1), marginTop: 10 }}>
+ <button style={{ width: "100%" }} onClick={this.addGroup}>+ Add a Group</button>
+ </div> : null}
</div>
);
}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
new file mode 100644
index 000000000..9f64a4e93
--- /dev/null
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -0,0 +1,202 @@
+import React = require("react");
+import { observer } from "mobx-react";
+import { number } from "prop-types";
+import { Doc, WidthSym } from "../../../new_fields/Doc";
+import { CollectionStackingView } from "./CollectionStackingView";
+import { Id } from "../../../new_fields/FieldSymbols";
+import { Utils } from "../../../Utils";
+import { NumCast, StrCast } from "../../../new_fields/Types";
+import { EditableView } from "../EditableView";
+import { action, observable } from "mobx";
+import { undoBatch } from "../../util/UndoManager";
+import { DragManager } from "../../util/DragManager";
+import { DocumentManager } from "../../util/DocumentManager";
+import { SelectionManager } from "../../util/SelectionManager";
+import "./CollectionStackingView.scss";
+import { Docs } from "../../documents/Documents";
+
+
+interface CSVFieldColumnProps {
+ cols: () => number;
+ headings: () => object[];
+ heading: string;
+ docList: Doc[];
+ parent: CollectionStackingView;
+ type: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" | undefined;
+ createDropTarget: (ele: HTMLDivElement) => void;
+}
+
+@observer
+export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldColumnProps> {
+ @observable private _background = "white";
+
+ private _dropRef: HTMLDivElement | null = null;
+ private dropDisposer?: DragManager.DragDropDisposer;
+
+ createColumnDropRef = (ele: HTMLDivElement | null) => {
+ this._dropRef = ele;
+ this.dropDisposer && this.dropDisposer();
+ if (ele) {
+ this.dropDisposer = DragManager.MakeDropTarget(ele, { handlers: { drop: this.columnDrop.bind(this) } });
+ }
+ }
+
+ @undoBatch
+ @action
+ columnDrop = (e: Event, de: DragManager.DropEvent) => {
+ if (de.data instanceof DragManager.DocumentDragData) {
+ let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ let castedValue = this.getValue(this.props.heading);
+ if (castedValue) {
+ de.data.droppedDocuments.forEach(d => d[key] = castedValue);
+ }
+ this.props.parent.drop(e, de);
+ e.stopPropagation();
+ }
+ }
+
+ children(docs: Doc[]) {
+ let style = this.props.parent;
+ this.props.parent._docXfs.length = 0;
+ return docs.map((d, i) => {
+ let layoutDoc = Doc.expandTemplateLayout(d, this.props.parent.props.DataDoc);
+ let headings = this.props.headings();
+ let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
+ let width = () => (d.nativeWidth ? Math.min(layoutDoc[WidthSym](), style.columnWidth) : style.columnWidth) / (uniqueHeadings.length + 1);
+ let height = () => this.props.parent.getDocHeight(layoutDoc);
+ if (style.singleColumn) {
+ let dxf;
+ let dref = React.createRef<HTMLDivElement>();
+ if (uniqueHeadings.length > 0) {
+ dxf = () => this.getDocTransform(layoutDoc, dref.current!);
+ this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height });
+ }
+ else {
+ //have to add the height of all previous single column sections or the doc decorations will be in the wrong place.
+ dxf = () => this.getSingleDocTransform(layoutDoc, i, width());
+ this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height });
+ }
+ let rowHgtPcnt = height();
+ return <div className="collectionStackingView-columnDoc" key={d[Id]} ref={dref} style={{ width: width(), marginTop: i === 0 ? 0 : style.gridGap, height: `${rowHgtPcnt}` }} >
+ {this.props.parent.getDisplayDoc(layoutDoc, d, dxf)}
+ </div>;
+ } else {
+ let dref = React.createRef<HTMLDivElement>();
+ let dxf = () => this.getDocTransform(layoutDoc, dref.current!);
+ let rowSpan = Math.ceil((height() + style.gridGap) / style.gridGap);
+ this.props.parent._docXfs.push({ dxf: dxf, width: width, height: height });
+ return <div className="collectionStackingView-masonryDoc" key={d[Id]} ref={dref} style={{ gridRowEnd: `span ${rowSpan}` }} >
+ {this.props.parent.getDisplayDoc(layoutDoc, d, dxf)}
+ </div>;
+ }
+ });
+ }
+
+ getSingleDocTransform(doc: Doc, ind: number, width: number) {
+ let localY = this.props.parent.filteredChildren.reduce((height, d, i) =>
+ height + (i < ind ? this.props.parent.getDocHeight(Doc.expandTemplateLayout(d, this.props.parent.props.DataDoc)) + this.props.parent.gridGap : 0), this.props.parent.yMargin);
+ let translate = this.props.parent.props.ScreenToLocalTransform().inverse().transformPoint((this.props.parent.props.PanelWidth() - width) / 2, localY);
+ return this.offsetTransform(doc, translate[0], translate[1]);
+ }
+
+ offsetTransform(doc: Doc, translateX: number, translateY: number) {
+ let outerXf = Utils.GetScreenTransform(this.props.parent._masonryGridRef!);
+ let offset = this.props.parent.props.ScreenToLocalTransform().transformDirection(outerXf.translateX - translateX, outerXf.translateY - translateY);
+ return this.props.parent.props.ScreenToLocalTransform().translate(offset[0], offset[1]).scale(NumCast(doc.width, 1) / this.props.parent.columnWidth);
+ }
+
+ getDocTransform(doc: Doc, dref: HTMLDivElement) {
+ let { scale, translateX, translateY } = Utils.GetScreenTransform(dref);
+ return this.offsetTransform(doc, translateX, translateY);
+ }
+
+ getValue = (value: string): any => {
+ let parsed = parseInt(value);
+ if (!isNaN(parsed)) {
+ return parsed;
+ }
+ if (value.toLowerCase().indexOf("true") > -1) {
+ return true;
+ }
+ if (value.toLowerCase().indexOf("false") > -1) {
+ return false;
+ }
+ return value;
+ }
+
+ headingChanged = (value: string, shiftDown?: boolean) => {
+ let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ let castedValue = this.getValue(value);
+ if (castedValue) {
+ this.props.docList.forEach(d => d[key] = castedValue);
+ return true;
+ }
+ return false;
+ }
+
+ @action
+ pointerEntered = () => {
+ if (SelectionManager.GetIsDragging()) {
+ this._background = "#b4b4b4";
+ }
+ }
+
+ @action
+ pointerLeave = () => {
+ this._background = "white";
+ }
+
+ @action
+ addDocument = () => {
+ let key = StrCast(this.props.parent.props.Document.sectionFilter);
+ let newDoc = Docs.Create.TextDocument({ height: 18, title: "new text document" });
+ newDoc[key] = this.getValue(this.props.heading);
+ this.props.parent.props.addDocument(newDoc);
+ }
+
+ render() {
+ let cols = this.props.cols();
+ let templatecols = "";
+ let headings = this.props.headings();
+ let heading = this.props.heading;
+ let style = this.props.parent;
+ let singleColumn = style.singleColumn;
+ let uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
+ let editableViewProps = {
+ GetValue: () => heading,
+ SetValue: this.headingChanged,
+ contents: heading,
+ }
+ let headingView = heading ?
+ <div key={heading} className="collectionStackingView-sectionHeader"
+ style={{ width: (style.columnWidth) / (uniqueHeadings.length + 1) }}>
+ <EditableView {...editableViewProps} />
+ </div> : (null);
+ for (let i = 0; i < cols; i++) templatecols += `${style.columnWidth}px `;
+ return (
+ <div key={heading} style={{ width: `${100 / (uniqueHeadings.length + 1)}%`, background: this._background }}
+ ref={this.createColumnDropRef} onPointerEnter={this.pointerEntered} onPointerLeave={this.pointerLeave}>
+ {headingView}
+ <div key={`${heading}-stack`} className={`collectionStackingView-masonry${singleColumn ? "Single" : "Grid"}`}
+ style={{
+ padding: singleColumn ? `${style.yMargin}px ${0}px ${style.yMargin}px ${0}px` : `${style.yMargin}px ${0}px`,
+ margin: "auto",
+ width: singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`,
+ height: 'max-content',
+ position: "relative",
+ gridGap: style.gridGap,
+ gridTemplateColumns: singleColumn ? undefined : templatecols,
+ gridAutoRows: singleColumn ? undefined : "0px"
+ }}
+ >
+ {this.children(this.props.docList)}
+ {singleColumn ? (null) : this.props.parent.columnDragger}
+ </div>
+ <div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton"
+ style={{ width: style.columnWidth / (uniqueHeadings.length + 1) }}>
+ <button style={{ width: "100%" }} onClick={this.addDocument}>+ New</button>
+ </div>
+ </div>
+ );
+ }
+} \ No newline at end of file
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index f4052c2c3..0d766cded 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -565,10 +565,7 @@ export class DocumentView extends DocComponent<DocumentViewProps, Document>(Docu
if (this.props.Document.detailedLayout && !this.props.Document.isTemplate) {
cm.addItem({ description: "Toggle detail", event: () => Doc.ToggleDetailLayout(this.props.Document), icon: "image" });
}
-<<<<<<< HEAD
-=======
cm.addItem({ description: "Add Repl", event: () => OverlayView.Instance.addWindow(<ScriptingRepl />, { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" }) });
->>>>>>> fc1dbb1327d10bd1832d33a87d18cff1e836ecfb
cm.addItem({ description: "Center View", event: () => this.props.focus(this.props.Document, false), icon: "crosshairs" });
cm.addItem({ description: "Zoom to Document", event: () => this.props.focus(this.props.Document, true), icon: "search" });
cm.addItem({ description: "Copy URL", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "link" });