aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/documents/Documents.ts4
-rw-r--r--src/client/views/collections/CollectionSchemaCells.tsx236
-rw-r--r--src/client/views/collections/CollectionSchemaHeaders.tsx23
-rw-r--r--src/client/views/collections/CollectionSchemaView.scss40
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx6
5 files changed, 298 insertions, 11 deletions
diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts
index d1f6c9cea..204d2afae 100644
--- a/src/client/documents/Documents.ts
+++ b/src/client/documents/Documents.ts
@@ -21,7 +21,7 @@ import { YoutubeBox } from "../apis/youtube/YoutubeBox";
import { CollectionDockingView } from "../views/collections/CollectionDockingView";
import { LinkManager } from "../util/LinkManager";
import { DocumentManager } from "../util/DocumentManager";
-import DirectoryImportBox from "../util/Import & Export/DirectoryImportBox";
+import { DirectoryImportBox } from "../util/Import & Export/DirectoryImportBox";
import { Scripting } from "../util/Scripting";
import { LabelBox } from "../views/nodes/LabelBox";
import { SliderBox } from "../views/nodes/SliderBox";
@@ -385,7 +385,7 @@ export namespace Docs {
// synthesize the default options, the type and title from computed values and
// whatever options pertain to this specific prototype
const options = { title, type, baseProto: true, ...defaultOptions, ...(template.options || {}) };
- options.layout = layout.view.LayoutString(layout.dataField);
+ options.layout = layout.view?.LayoutString(layout.dataField);
const doc = Doc.assign(new Doc(prototypeId, true), { layoutKey: "layout", ...options });
doc.layout_keyValue = KeyValueBox.LayoutString("");
return doc;
diff --git a/src/client/views/collections/CollectionSchemaCells.tsx b/src/client/views/collections/CollectionSchemaCells.tsx
index baf9d4156..b44c830e6 100644
--- a/src/client/views/collections/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/CollectionSchemaCells.tsx
@@ -3,7 +3,7 @@ import { action, observable, trace } from "mobx";
import { observer } from "mobx-react";
import { CellInfo } from "react-table";
import "react-table/react-table.css";
-import { emptyFunction, returnFalse, returnZero, returnOne } from "../../../Utils";
+import { emptyFunction, returnFalse, returnZero, returnOne, Utils } from "../../../Utils";
import { Doc, DocListCast, Field, Opt } from "../../../fields/Doc";
import { Id } from "../../../fields/FieldSymbols";
import { KeyCodes } from "../../util/KeyCodes";
@@ -15,7 +15,7 @@ import '../DocumentDecorations.scss';
import { EditableView } from "../EditableView";
import { FieldView, FieldViewProps } from "../nodes/FieldView";
import "./CollectionSchemaView.scss";
-import { CollectionView } from "./CollectionView";
+import { CollectionView, Flyout } from "./CollectionView";
import { NumCast, StrCast, BoolCast, FieldValue, Cast } from "../../../fields/Types";
import { Docs } from "../../documents/Documents";
import { library } from '@fortawesome/fontawesome-svg-core';
@@ -24,6 +24,15 @@ import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { undoBatch } from "../../util/UndoManager";
import { SnappingManager } from "../../util/SnappingManager";
import { ComputedField } from "../../../fields/ScriptField";
+import { ImageField } from "../../../fields/URLField";
+import { KeysDropdown } from "./CollectionSchemaHeaders";
+import { listSpec } from "../../../fields/Schema";
+import { ObjectField } from "../../../fields/ObjectField";
+import { List } from "../../../fields/List";
+import { LinkBox } from "../nodes/LinkBox";
+import { OverlayView } from "../OverlayView";
+import { DocumentIconContainer } from "../nodes/DocumentIcon";
+const path = require('path');
library.add(faExpand);
@@ -205,6 +214,18 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
const doc = FieldValue(Cast(field, Doc));
contents = typeof field === "object" ? doc ? StrCast(doc.title) === "" ? "--" : StrCast(doc.title) : `--${typeof field}--` : `--${typeof field}--`;
}
+ if (type === "image") {
+ // fix this
+
+ const image = FieldValue(Cast(field, ImageField));
+ const doc = FieldValue(Cast(field, Doc));
+ contents = typeof field === "object" ? doc ? StrCast(doc.title) === "" ? "--" : StrCast(doc.title) : `--${typeof field}--` : `--${typeof field}--`;
+ }
+ if (type === "list") {
+ // fix this
+ contents = typeof field === "object" ? doc ? StrCast(field) === "" ? "--" : StrCast(field) : `--${typeof field}--` : `--${typeof field}--`;
+ }
+
let className = "collectionSchemaView-cellWrapper";
if (this._isEditing) className += " editing";
@@ -305,6 +326,217 @@ export class CollectionSchemaDocCell extends CollectionSchemaCell {
}
@observer
+export class CollectionSchemaImageCell extends CollectionSchemaCell {
+ // render() {
+ // return this.renderCellWithType("image");
+ // }
+
+ choosePath(url: URL, dataDoc: any) {
+ const lower = url.href.toLowerCase();
+ if (url.protocol === "data") {
+ return url.href;
+ } else if (url.href.indexOf(window.location.origin) === -1) {
+ return Utils.CorsProxy(url.href);
+ } else if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) {
+ return url.href;//Why is this here
+ }
+ const ext = path.extname(url.href);
+ const _curSuffix = "_o";
+ return url.href.replace(ext, _curSuffix + ext);
+ }
+
+ render() {
+ const props: FieldViewProps = {
+ Document: this.props.rowProps.original,
+ DataDoc: this.props.rowProps.original,
+ LibraryPath: [],
+ dropAction: "alias",
+ bringToFront: emptyFunction,
+ rootSelected: returnFalse,
+ fieldKey: this.props.rowProps.column.id as string,
+ ContainingCollectionView: this.props.CollectionView,
+ ContainingCollectionDoc: this.props.CollectionView && this.props.CollectionView.props.Document,
+ isSelected: returnFalse,
+ select: emptyFunction,
+ renderDepth: this.props.renderDepth + 1,
+ ScreenToLocalTransform: Transform.Identity,
+ focus: emptyFunction,
+ active: returnFalse,
+ whenActiveChanged: emptyFunction,
+ PanelHeight: returnZero,
+ PanelWidth: returnZero,
+ NativeHeight: returnZero,
+ NativeWidth: returnZero,
+ addDocTab: this.props.addDocTab,
+ pinToPres: this.props.pinToPres,
+ ContentScaling: returnOne
+ };
+
+ let image = true;
+ let url = [];
+ if (props.DataDoc) {
+ const field = Cast(props.DataDoc[props.fieldKey], ImageField, null); // retrieve the primary image URL that is being rendered from the data doc
+ const alts = DocListCast(props.DataDoc[props.fieldKey + "-alternates"]); // retrieve alternate documents that may be rendered as alternate images
+ const altpaths = alts.map(doc => Cast(doc[Doc.LayoutFieldKey(doc)], ImageField, null)?.url).filter(url => url).map(url => this.choosePath(url, props.DataDoc)); // access the primary layout data of the alternate documents
+ const paths = field ? [this.choosePath(field.url, props.DataDoc), ...altpaths] : altpaths;
+ if (paths.length) {
+ url = paths;
+ } else {
+ url = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
+ image = false;
+ }
+ //url = paths.length ? paths : [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
+ } else {
+ url = [Utils.CorsProxy("http://www.cs.brown.edu/~bcz/noImage.png")];
+ image = false;
+ }
+
+ const heightToWidth = NumCast(props.DataDoc?._nativeHeight) / NumCast(props.DataDoc?._nativeWidth);
+ const height = this.props.rowProps.width * heightToWidth;
+
+ if (props.fieldKey === "data") {
+ if (url !== []) {
+ const reference = React.createRef<HTMLDivElement>();
+ return (
+ <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
+ <div className="collectionSchemaView-cellContents" key={this._document[Id]} ref={reference}>
+ <img src={url[0]} width={image ? this.props.rowProps.width : "30px"}
+ height={image ? height : "30px"} />
+ </div >
+ </div>
+ );
+
+ } else {
+ return this.renderCellWithType("image");
+ }
+ } else {
+ return this.renderCellWithType("image");
+ }
+ }
+}
+
+@observer
+export class CollectionSchemaListCell extends CollectionSchemaCell {
+
+ _overlayDisposer?: () => void;
+
+ private prop: FieldViewProps = {
+ Document: this.props.rowProps.original,
+ DataDoc: this.props.rowProps.original,
+ LibraryPath: [],
+ dropAction: "alias",
+ bringToFront: emptyFunction,
+ rootSelected: returnFalse,
+ fieldKey: this.props.rowProps.column.id as string,
+ ContainingCollectionView: this.props.CollectionView,
+ ContainingCollectionDoc: this.props.CollectionView && this.props.CollectionView.props.Document,
+ isSelected: returnFalse,
+ select: emptyFunction,
+ renderDepth: this.props.renderDepth + 1,
+ ScreenToLocalTransform: Transform.Identity,
+ focus: emptyFunction,
+ active: returnFalse,
+ whenActiveChanged: emptyFunction,
+ PanelHeight: returnZero,
+ PanelWidth: returnZero,
+ NativeHeight: returnZero,
+ NativeWidth: returnZero,
+ addDocTab: this.props.addDocTab,
+ pinToPres: this.props.pinToPres,
+ ContentScaling: returnOne
+ };
+ @observable private _field = this.prop.Document[this.prop.fieldKey];
+ @observable private _optionsList = this._field as List<any>;
+ @observable private _opened = false;
+ @observable private _text = "select an item";
+ @observable private _selectedNum = 0;
+
+ @action
+ toggleOpened(open: boolean) {
+ console.log("open: " + open);
+ this._opened = open;
+ }
+
+ @action
+ onChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
+ this._text = e.target.value;
+
+ // change if its a document
+ this._optionsList[this._selectedNum] = this._text;
+ }
+
+ @action
+ onSelected = (element: string, index: number) => {
+ this._text = element;
+ this._selectedNum = index;
+ }
+
+ onFocus = () => {
+ this._overlayDisposer?.();
+ this._overlayDisposer = OverlayView.Instance.addElement(<DocumentIconContainer />, { x: 0, y: 0 });
+ }
+
+ render() {
+
+ let link = false;
+ let doc = false;
+ const reference = React.createRef<HTMLDivElement>();
+
+ if (typeof this._field === "object" && this._optionsList[1]) {
+
+ const options = this._optionsList.map((element, index) => {
+ if (element instanceof Doc) {
+ doc = true;
+ if (this.prop.fieldKey.toLowerCase() === "links") {
+ link = true;
+ }
+ const title = element.title;
+ return <div
+ className="collectionSchemaView-dropdownOption"
+ onPointerDown={(e) => { this.onSelected(StrCast(element.title), index); }}
+ style={{ padding: "6px" }}>
+ {title}
+ </div>;
+ } else {
+ return <div>{element}</div>;
+ }
+ });
+
+
+ const plainText = <div>{this._text}</div>;
+ const textarea = <textarea onChange={this.onChange} value={this._text}
+ onFocus={doc ? this.onFocus : undefined}
+ onBlur={doc ? e => this._overlayDisposer?.() : undefined}
+ style={{ resize: "none" }}
+ placeholder={"select an item"}></textarea>;
+ const dropdown = <div>
+ {options}
+ </div>;
+
+ return (
+ <div className="collectionSchemaView-cellWrapper" ref={this._focusRef} tabIndex={-1} onPointerDown={this.onPointerDown}>
+ <div className="collectionSchemaView-cellContents" key={this._document[Id]} ref={reference}>
+ <div className="collectionSchemaView-dropDownWrapper">
+ <button type="button" className="collectionSchemaView-dropdownButton" onClick={(e) => { this.toggleOpened(!this._opened); }}
+ style={{ right: "length", position: "relative" }}>
+ ☰
+ </button>
+ <div className="collectionSchemaView-dropdownText"> {link ? plainText : textarea} </div>
+ </div>
+
+ {this._opened ? dropdown : null}
+ </div >
+ </div>
+ );
+ } else {
+ console.log("not an array!");
+
+ return this.renderCellWithType("list");
+ }
+ }
+}
+
+@observer
export class CollectionSchemaCheckboxCell extends CollectionSchemaCell {
@observable private _isChecked: boolean = typeof this.props.rowProps.original[this.props.rowProps.column.id as string] === "boolean" ? BoolCast(this.props.rowProps.original[this.props.rowProps.column.id as string]) : false;
diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx
index dae0600b1..0d6cf4474 100644
--- a/src/client/views/collections/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/CollectionSchemaHeaders.tsx
@@ -2,7 +2,7 @@ import React = require("react");
import { action, observable } from "mobx";
import { observer } from "mobx-react";
import "./CollectionSchemaView.scss";
-import { faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faSortAmountDown, faSortAmountUp, faTimes } from '@fortawesome/free-solid-svg-icons';
+import { faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faSortAmountDown, faSortAmountUp, faTimes, faImage, faListUl } from '@fortawesome/free-solid-svg-icons';
import { library, IconProp } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { ColumnType } from "./CollectionSchemaView";
@@ -13,7 +13,7 @@ const higflyout = require("@hig/flyout");
export const { anchorPoints } = higflyout;
export const Flyout = higflyout.default;
-library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile as any, faSortAmountDown, faSortAmountUp, faTimes);
+library.add(faPlus, faFont, faHashtag, faAlignJustify, faCheckSquare, faToggleOn, faFile as any, faSortAmountDown, faSortAmountUp, faTimes, faImage, faListUl);
export interface HeaderProps {
keyValue: SchemaHeaderField;
@@ -33,7 +33,8 @@ export interface HeaderProps {
export class CollectionSchemaHeader extends React.Component<HeaderProps> {
render() {
const icon: IconProp = this.props.keyType === ColumnType.Number ? "hashtag" : this.props.keyType === ColumnType.String ? "font" :
- this.props.keyType === ColumnType.Boolean ? "check-square" : this.props.keyType === ColumnType.Doc ? "file" : "align-justify";
+ this.props.keyType === ColumnType.Boolean ? "check-square" : this.props.keyType === ColumnType.Doc ? "file" :
+ this.props.keyType === ColumnType.Image ? "image" : this.props.keyType === ColumnType.List ? "list-ul" : "align-justify";
return (
<div className="collectionSchemaView-header" style={{ background: this.props.keyValue.color }}>
<CollectionSchemaColumnMenu
@@ -160,10 +161,18 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
<FontAwesomeIcon icon={"check-square"} size="sm" />
Checkbox
</div>
+ <div className={"columnMenu-option" + (type === ColumnType.List ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.List)}>
+ <FontAwesomeIcon icon={"list-ul"} size="sm" />
+ List
+ </div>
<div className={"columnMenu-option" + (type === ColumnType.Doc ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Doc)}>
<FontAwesomeIcon icon={"file"} size="sm" />
Document
</div>
+ <div className={"columnMenu-option" + (type === ColumnType.Image ? " active" : "")} onClick={() => this.changeColumnType(ColumnType.Image)}>
+ <FontAwesomeIcon icon={"image"} size="sm" />
+ Image
+ </div>
</div>
</div >
);
@@ -258,7 +267,7 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
}
-interface KeysDropdownProps {
+export interface KeysDropdownProps {
keyValue: string;
possibleKeys: string[];
existingKeys: string[];
@@ -268,7 +277,7 @@ interface KeysDropdownProps {
setIsEditing: (isEditing: boolean) => void;
}
@observer
-class KeysDropdown extends React.Component<KeysDropdownProps> {
+export class KeysDropdown extends React.Component<KeysDropdownProps> {
@observable private _key: string = this.props.keyValue;
@observable private _searchTerm: string = this.props.keyValue;
@observable private _isOpen: boolean = false;
@@ -331,7 +340,9 @@ class KeysDropdown extends React.Component<KeysDropdownProps> {
renderOptions = (): JSX.Element[] | JSX.Element => {
if (!this._isOpen) return <></>;
- const keyOptions = this._searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
+ const searchTerm = this._searchTerm.trim() === "New field" ? "" : this._searchTerm;
+
+ const keyOptions = searchTerm === "" ? this.props.possibleKeys : this.props.possibleKeys.filter(key => key.toUpperCase().indexOf(this._searchTerm.toUpperCase()) > -1);
const exactFound = keyOptions.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1 ||
this.props.existingKeys.findIndex(key => key.toUpperCase() === this._searchTerm.toUpperCase()) > -1;
diff --git a/src/client/views/collections/CollectionSchemaView.scss b/src/client/views/collections/CollectionSchemaView.scss
index a24140b48..5a4e1aa58 100644
--- a/src/client/views/collections/CollectionSchemaView.scss
+++ b/src/client/views/collections/CollectionSchemaView.scss
@@ -412,6 +412,45 @@ button.add-column {
&:hover .collectionSchemaView-cellContents-docExpander {
display: block;
}
+
+ .collectionSchemaView-dropdownWrapper {
+
+ border: grey;
+ border-style: solid;
+ border-width: 1px;
+ height: 100%;
+
+ .collectionSchemaView-dropdownButton {
+
+ //display: inline-block;
+ float: left;
+ height: 100%;
+
+
+ }
+
+ .collectionSchemaView-dropdownText {
+ display: inline-block;
+ //float: right;
+ height: 100%;
+ display: "flex";
+ font-size: 13;
+ justify-content: "center";
+ align-items: "center";
+ }
+
+ }
+
+ .collectionSchemaView-dropdownContainer {
+ position: absolute;
+ border: 1px solid rgba(0, 0, 0, 0.04);
+ box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14);
+
+ .collectionSchemaView-dropdownOption:hover {
+ background-color: rgba(0, 0, 0, 0.14);
+ cursor: pointer;
+ }
+ }
}
.collectionSchemaView-cellContents-docExpander {
@@ -422,6 +461,7 @@ button.add-column {
top: 0;
right: 0;
background-color: lightgray;
+
}
.doc-drag-over {
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 6dbee217a..610e2336e 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -20,7 +20,7 @@ import { undoBatch } from "../../util/UndoManager";
import { COLLECTION_BORDER_WIDTH } from '../../views/globalCssVariables.scss';
import { ContextMenu } from "../ContextMenu";
import '../DocumentDecorations.scss';
-import { CellProps, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDocCell, CollectionSchemaNumberCell, CollectionSchemaStringCell } from "./CollectionSchemaCells";
+import { CellProps, CollectionSchemaCell, CollectionSchemaCheckboxCell, CollectionSchemaDocCell, CollectionSchemaNumberCell, CollectionSchemaStringCell, CollectionSchemaImageCell, CollectionSchemaListCell } from "./CollectionSchemaCells";
import { CollectionSchemaAddColumnHeader, CollectionSchemaHeader } from "./CollectionSchemaHeaders";
import { MovableColumn, MovableRow } from "./CollectionSchemaMovableTableHOC";
import "./CollectionSchemaView.scss";
@@ -40,6 +40,8 @@ export enum ColumnType {
String,
Boolean,
Doc,
+ Image,
+ List
}
// this map should be used for keys that should have a const type of value
const columnTypes: Map<string, ColumnType> = new Map([
@@ -353,6 +355,8 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
if (colType === ColumnType.String) return <CollectionSchemaStringCell {...props} />;
if (colType === ColumnType.Boolean) return <CollectionSchemaCheckboxCell {...props} />;
if (colType === ColumnType.Doc) return <CollectionSchemaDocCell {...props} />;
+ if (colType === ColumnType.Image) return <CollectionSchemaImageCell {...props} />;
+ if (colType === ColumnType.List) return <CollectionSchemaListCell {...props} />;
return <CollectionSchemaCell {...props} />;
},
minWidth: 200,