diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/MainView.tsx | 6 | ||||
-rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionMenu.scss | 475 | ||||
-rw-r--r-- | src/client/views/collections/CollectionMenu.tsx | 443 | ||||
-rw-r--r-- | src/client/views/collections/CollectionView.tsx | 11 |
5 files changed, 888 insertions, 49 deletions
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 428fb835b..95b2dfcdb 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -392,7 +392,7 @@ export class MainView extends React.Component { doc.dockingConfig ? this.openWorkspace(doc) : CollectionDockingView.AddRightSplit(doc, libraryPath); } - sidebarScreenToLocal = () => new Transform(0, RichTextMenu.Instance.Pinned ? -35 : 0, 1); + sidebarScreenToLocal = () => new Transform(0, (RichTextMenu.Instance.Pinned ? -35 : 0) + (InkOptionsMenu.Instance.Pinned ? -35 : 0) + (CollectionMenu.Instance.Pinned ? -35 : 0), 1); mainContainerXf = () => this.sidebarScreenToLocal().translate(0, -this._buttonBarHeight); @computed get flyout() { @@ -469,11 +469,13 @@ export class MainView extends React.Component { @computed get mainContent() { const sidebar = this.userDoc?.["tabs-panelContainer"]; + const n = (RichTextMenu.Instance?.Pinned ? 1 : 0) + (InkOptionsMenu.Instance?.Pinned ? 1 : 0) + (CollectionMenu.Instance?.Pinned ? 1 : 0); + const height = `calc(100% - ${n * Number(ANTIMODEMENU_HEIGHT.replace("px", ""))}px)`; return !this.userDoc || !(sidebar instanceof Doc) ? (null) : ( <div className="mainView-mainContent" style={{ color: this.darkScheme ? "rgb(205,205,205)" : "black", //change to times 2 for both pinned - height: (RichTextMenu.Instance?.Pinned || InkOptionsMenu.Instance?.Pinned) ? (RichTextMenu.Instance?.Pinned && InkOptionsMenu.Instance?.Pinned) ? `calc(100% - 2*${ANTIMODEMENU_HEIGHT})` : `calc(100% - ${ANTIMODEMENU_HEIGHT})` : "100%", + height, width: (FormatShapePane.Instance?.Pinned) ? `calc(100% - 200px)` : "100%" }} > <div style={{ display: "contents", flexDirection: "row", position: "relative" }}> diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index f408e24a8..657296566 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -436,7 +436,7 @@ export class CollectionDockingView extends React.Component<SubCollectionViewProp const cur = this._containerRef.current; // bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed - this._goldenLayout && this._goldenLayout.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height); + this._goldenLayout?.updateSize(cur!.getBoundingClientRect().width, cur!.getBoundingClientRect().height); } @action diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss index 6280670d8..c1a3f9c09 100644 --- a/src/client/views/collections/CollectionMenu.scss +++ b/src/client/views/collections/CollectionMenu.scss @@ -1,12 +1,471 @@ -.antimodeMenu-button { - padding:0; - width:40px; +@import "../globalCssVariables"; + + +.collectionMenu-cont { + position:relative; + display:inline-flex; + width: 100%; + opacity: 0.9; + z-index: 9001; + transition: top .5s; + background: #121721; + color: white; + transform-origin: top left; + + .antimodeMenu-button { + padding:0; + width:40px; + display: flex; + svg { + margin:auto; + } + } + + .collectionViewChrome { + display: flex; + padding-bottom: 1px; + height: 32px; + border-bottom: .5px solid rgb(180, 180, 180); + overflow: visible; + z-index: 9001; + + .collectionViewBaseChrome { + display: flex; + + .collectionViewBaseChrome-viewPicker { + font-size: 75%; + //text-transform: uppercase; + //letter-spacing: 2px; + background: #121721; + color: white; + outline-color: black; + border: none; + //padding: 12px 10px 11px 10px; + } + + .collectionViewBaseChrome-viewPicker:active { + outline-color: black; + } + + .collectionViewBaseChrome-button { + font-size: 75%; + text-transform: uppercase; + letter-spacing: 2px; + background: rgb(238, 238, 238); + color: purple; + outline-color: black; + border: none; + padding: 12px 10px 11px 10px; + margin-left: 10px; + } + + .collectionViewBaseChrome-cmdPicker { + margin-left: 3px; + margin-right: 0px; + font-size: 75%; + background: #121721; + border: none; + color: white; + } + + .commandEntry-outerDiv { + pointer-events: all; + background-color: #121721; + display: flex; + flex-direction: row; + height: 30px; + + .commandEntry-drop { + color: white; + width: 30px; + margin-top: auto; + margin-bottom: auto; + } + } + + .collectionViewBaseChrome-collapse { + transition: all .5s, opacity 0.3s; + position: absolute; + width: 40px; + transform-origin: top left; + pointer-events: all; + // margin-top: 10px; + } + + @media only screen and (max-device-width: 480px) { + .collectionViewBaseChrome-collapse { + display: none; + } + } + + .collectionViewBaseChrome-template, + .collectionViewBaseChrome-viewModes { + display: grid; + background: rgb(238, 238, 238); + color: grey; + margin-top: auto; + margin-bottom: auto; + } + .collectionViewBaseChrome-viewSpecs { + margin-left: 5px; + display: grid; + + .collectionViewBaseChrome-filterIcon { + position: relative; + display: flex; + margin: auto; + background: #121721; + color: white; + width: 30px; + height: 30px; + align-items: center; + justify-content: center; + } + + .collectionViewBaseChrome-viewSpecsInput { + padding: 12px 10px 11px 10px; + border: 0px; + color: grey; + text-align: center; + letter-spacing: 2px; + outline-color: black; + font-size: 75%; + background: rgb(238, 238, 238); + height: 100%; + width: 75px; + } + + .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 1fr; + grid-gap: 10px; + margin: 10px; + } + } + } + } + + .collectionStackingViewChrome-cont, + .collectionTreeViewChrome-cont, + .collection3DCarouselViewChrome-cont { + display: flex; + justify-content: space-between; + } + + .collectionGridViewChrome-cont { + display: flex; + margin-left: 10; + + .collectionGridViewChrome-viewPicker { + font-size: 75%; + //text-transform: uppercase; + //letter-spacing: 2px; + background: #121721; + color: white; + outline-color: black; + border: none; + //padding: 12px 10px 11px 10px; + } + + .collectionGridViewChrome-viewPicker:active { + outline-color: black; + } + + .grid-control { + align-self: center; + display: flex; + flex-direction: row; + margin-right: 5px; + + .grid-icon { + margin-right: 5px; + align-self: center; + } + + .flexLabel { + margin-bottom: 0; + } + } + + .collectionGridViewChrome-entryBox { + width: 50%; + } + } + + + .collectionStackingViewChrome-sort, + .collectionTreeViewChrome-sort { + display: flex; + align-items: center; + justify-content: space-between; + + .collectionStackingViewChrome-sortIcon, + .collectionTreeViewChrome-sortIcon { + transition: transform .5s; + margin-left: 10px; + } + } + + button:hover { + transform: scale(1); + } + + + .collectionStackingViewChrome-pivotField-cont, + .collectionTreeViewChrome-pivotField-cont, + .collection3DCarouselViewChrome-scrollSpeed-cont { + justify-self: right; + display: flex; + font-size: 75%; + letter-spacing: 2px; + + .collectionStackingViewChrome-pivotField-label, + .collectionTreeViewChrome-pivotField-label, + .collection3DCarouselViewChrome-scrollSpeed-label { + vertical-align: center; + padding-left: 10px; + margin: auto; + } + + .collectionStackingViewChrome-pivotField, + .collectionTreeViewChrome-pivotField, + .collection3DCarouselViewChrome-scrollSpeed { + color: white; + width: 100%; + min-width: 100px; + display: flex; + align-items: center; + background: rgb(238, 238, 238); + + .editable-view-input, + input, + .editableView-container-editing-oneLine, + .editableView-container-editing { + margin: auto; + border: 0px; + color: grey !important; + text-align: center; + letter-spacing: 2px; + outline-color: black; + height: 100%; + } + + .react-autosuggest__container { + margin: 0; + color: grey; + padding: 0px; + } + } + } + + .collectionStackingViewChrome-pivotField:hover, + .collectionTreeViewChrome-pivotField:hover, + .collection3DCarouselViewChrome-scrollSpeed:hover { + cursor: text; + } + + } +} + +.collectionFreeFormMenu-cont { + width: 60px; display: flex; - svg { - margin:auto; + position: relative; + align-items: center; + + .fwdKeyframe, + .numKeyframe, + .backKeyframe { + cursor: pointer; + position: absolute; + width: 20; + height: 30; + bottom: 0; + background: #121721; + display: flex; + align-items: center; + color: white; + } + + .backKeyframe { + left: 0; + + svg { + display: block; + margin: auto; + } + } + + .numKeyframe { + left: 20; + display: flex; + flex-direction: column; + padding: 5px; + } + + .fwdKeyframe { + left: 40; + + svg { + display: block; + margin: auto; + } } } -.collectionViewChrome-cont { - position:relative; - display:inline-flex; + +.collectionSchemaViewChrome-cont { + display: flex; + font-size: 10.5px; + + .collectionSchemaViewChrome-toggle { + display: flex; + margin-left: 10px; + } + + .collectionSchemaViewChrome-label { + text-transform: uppercase; + letter-spacing: 2px; + margin-right: 5px; + display: flex; + flex-direction: column; + justify-content: center; + } + + .collectionSchemaViewChrome-toggler { + width: 100px; + height: 41px; + background-color: black; + position: relative; + } + + .collectionSchemaViewChrome-togglerButton { + width: 47px; + height: 35px; + background-color: $light-color-secondary; + // position: absolute; + transition: all 0.5s ease; + // top: 3px; + margin-top: 3px; + color: gray; + letter-spacing: 2px; + text-transform: uppercase; + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; + + &.on { + margin-left: 3px; + } + + &.off { + margin-left: 50px; + } + } +} + + +.commandEntry-outerDiv { + display: flex; + flex-direction: column; + height: 40px; +} + +.commandEntry-inputArea { + display: flex; + flex-direction: row; + width: 150px; + margin: auto auto auto auto; +} + +.react-autosuggest__container { + position: relative; + width: 100%; + margin-left: 5px; + margin-right: 5px; +} + +.react-autosuggest__input { + border: 1px solid #aaa; + border-radius: 4px; + width: 100%; +} + +.react-autosuggest__input--focused { + outline: none; +} + +.react-autosuggest__input--open { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.react-autosuggest__suggestions-container { + display: none; +} + +.react-autosuggest__suggestions-container--open { + display: block; + position: fixed; + overflow-y: auto; + max-height: 400px; + width: 180px; + border: 1px solid #aaa; + background-color: #fff; + font-family: Helvetica, sans-serif; + font-weight: 300; + font-size: 16px; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + z-index: 2; +} + +.react-autosuggest__suggestions-list { + margin: 0; + padding: 0; + list-style-type: none; +} + +.react-autosuggest__suggestion { + cursor: pointer; + padding: 10px 20px; +} + +.react-autosuggest__suggestion--highlighted { + background-color: #ddd; }
\ No newline at end of file diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index d79ffffa1..1644bc726 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -1,19 +1,20 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, observable, reaction, runInAction } from "mobx"; +import { action, computed, observable, reaction, runInAction, Lambda } from "mobx"; import { observer } from "mobx-react"; -import { Doc, DocListCast } from "../../../fields/Doc"; +import { Doc, DocListCast, Opt } from "../../../fields/Doc"; import { BoolCast, Cast, StrCast, NumCast } from "../../../fields/Types"; import AntimodeMenu from "../AntimodeMenu"; import "./CollectionMenu.scss"; import { undoBatch } from "../../util/UndoManager"; -import { CollectionViewType, CollectionView } from "./CollectionView"; +import { CollectionViewType, CollectionView, COLLECTION_BORDER_WIDTH } from "./CollectionView"; import { emptyFunction, setupMoveUpEvents, Utils } from "../../../Utils"; -import { CollectionGridViewChrome } from "./CollectionViewChromes"; import { DragManager } from "../../util/DragManager"; import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; import { List } from "../../../fields/List"; -import { SelectionManager } from "../../util/SelectionManager"; +import { EditableView } from "../EditableView"; +import { Id } from "../../../fields/FieldSymbols"; +import { listSpec } from "../../../fields/Schema"; @observer export default class CollectionMenu extends AntimodeMenu { @@ -33,19 +34,10 @@ export default class CollectionMenu extends AntimodeMenu { Doc.UserDoc()["menuCollections-pinned"] = this.Pinned = !this.Pinned; } - @computed get aButton() { - return <div className="btn-draw" key="draw"> - <button className="antimodeMenu-button" key="abutton" onClick={() => alert("clicked")} style={{ fontSize: "20" }}> - <FontAwesomeIcon icon="palette" size="lg" /> - </button> - </div>; - } - render() { return this.getElement([ - this.aButton, !this.SelectedCollection ? <></> : <CollectionViewBaseChrome CollectionView={this.SelectedCollection} type={StrCast(this.SelectedCollection.props.Document._viewType) as CollectionViewType} />, - <button className="antimodeMenu-button" key="pin menu" title="Pin menu" onClick={this.toggleMenuPin} style={{ backgroundColor: this.Pinned ? "#121212" : "" }}> + <button className="antimodeMenu-button" key="pin menu" title="Pin menu" onClick={this.toggleMenuPin} style={{ backgroundColor: "#121721" }}> <FontAwesomeIcon icon="thumbtack" size="lg" style={{ transitionProperty: "transform", transitionDuration: "0.1s", transform: `rotate(${this.Pinned ? 45 : 0}deg)` }} /> </button> ]); @@ -55,7 +47,6 @@ export default class CollectionMenu extends AntimodeMenu { interface CollectionViewChromeProps { CollectionView: CollectionView; type: CollectionViewType; - collapse?: (value: boolean) => any; } const stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation(); @@ -154,12 +145,12 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro @computed get subChrome() { switch (this.props.type) { case CollectionViewType.Freeform: return (<CollectionFreeFormViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />); - // case CollectionViewType.Stacking: return (<CollectionStackingViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />); - // case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />); - // case CollectionViewType.Tree: return (<CollectionTreeViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />); - // case CollectionViewType.Masonry: return (<CollectionStackingViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />); - // case CollectionViewType.Carousel3D: return (<Collection3DCarouselViewChrome key="collchrome" PanelWidth={this.props.PanelWidth} CollectionView={this.props.CollectionView} type={this.props.type} />); - // case CollectionViewType.Grid: return (<CollectionGridViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />); + case CollectionViewType.Stacking: return (<CollectionStackingViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />); + case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />); + case CollectionViewType.Tree: return (<CollectionTreeViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />); + case CollectionViewType.Masonry: return (<CollectionStackingViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />); + case CollectionViewType.Carousel3D: return (<Collection3DCarouselViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />); + case CollectionViewType.Grid: return (<CollectionGridViewChrome key="collchrome" CollectionView={this.props.CollectionView} type={this.props.type} />); default: return null; } } @@ -230,8 +221,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro } @computed get viewModes() { - const collapsed = this.props.CollectionView.props.Document._chromeStatus !== "enabled"; - return <div className="collectionViewBaseChrome-viewModes" style={{ display: collapsed ? "none" : undefined }}> + return <div className="collectionViewBaseChrome-viewModes" > <div className="commandEntry-outerDiv" title="drop document to apply or drag to create button" ref={this._viewRef} onPointerDown={this.dragViewDown}> <div className="commandEntry-drop"> <FontAwesomeIcon icon="bullseye" size="2x" /> @@ -258,7 +248,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewChro render() { const scale = Math.min(1, this.props.CollectionView.props.ScreenToLocalTransform()?.Scale); return ( - <div className="collectionViewChrome-cont" style={{ + <div className="collectionMenu-cont" style={{ top: 0, transform: `scale(${scale})`, width: "100%" @@ -293,7 +283,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView @undoBatch @action nextKeyframe = (): void => { - const currentFrame = NumCast(this.Document.currentFrame); + const currentFrame = Cast(this.Document.currentFrame, "number", null); if (currentFrame === undefined) { this.Document.currentFrame = 0; CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0); @@ -305,7 +295,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView @undoBatch @action prevKeyframe = (): void => { - const currentFrame = NumCast(this.Document.currentFrame); + const currentFrame = Cast(this.Document.currentFrame, "number", null); if (currentFrame === undefined) { this.Document.currentFrame = 0; CollectionFreeFormDocumentView.setupKeyframes(this.childDocs, 0); @@ -315,7 +305,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView } render() { return this.Document.isAnnotationOverlay ? (null) : - <div className="collectionFreeFormViewChrome-cont"> + <div className="collectionFreeFormMenu-cont"> <div key="back" title="back frame" className="backKeyframe" onClick={this.prevKeyframe}> <FontAwesomeIcon icon={"caret-left"} size={"lg"} /> </div> @@ -329,3 +319,400 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionView </div>; } } +@observer +export class CollectionStackingViewChrome extends React.Component<CollectionViewChromeProps> { + @observable private _currentKey: string = ""; + @observable private suggestions: string[] = []; + + @computed private get descending() { return StrCast(this.props.CollectionView.props.Document._columnsSort) === "descending"; } + @computed get pivotField() { return StrCast(this.props.CollectionView.props.Document._pivotField); } + + getKeySuggestions = async (value: string): Promise<string[]> => { + value = value.toLowerCase(); + const docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]); + 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 = []; + } + + @action + setValue = (value: string) => { + this.props.CollectionView.props.Document._pivotField = value; + return true; + } + + @action toggleSort = () => { + this.props.CollectionView.props.Document._columnsSort = + this.props.CollectionView.props.Document._columnsSort === "descending" ? "ascending" : + this.props.CollectionView.props.Document._columnsSort === "ascending" ? undefined : "descending"; + } + @action resetValue = () => { this._currentKey = this.pivotField; }; + + render() { + return ( + <div className="collectionStackingViewChrome-cont"> + <div className="collectionStackingViewChrome-pivotField-cont"> + <div className="collectionStackingViewChrome-pivotField-label"> + GROUP BY: + </div> + <div className="collectionStackingViewChrome-sortIcon" onClick={this.toggleSort} style={{ transform: `rotate(${this.descending ? "180" : "0"}deg)` }}> + <FontAwesomeIcon icon="caret-up" size="2x" color="white" /> + </div> + <div className="collectionStackingViewChrome-pivotField"> + <EditableView + GetValue={() => this.pivotField} + 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.pivotField ? this.pivotField : "N/A"} + /> + </div> + </div> + </div> + ); + } +} + + +@observer +export class CollectionSchemaViewChrome extends React.Component<CollectionViewChromeProps> { + // private _textwrapAllRows: boolean = Cast(this.props.CollectionView.props.Document.textwrappedSchemaRows, listSpec("string"), []).length > 0; + + @undoBatch + togglePreview = () => { + const dividerWidth = 4; + const borderWidth = Number(COLLECTION_BORDER_WIDTH); + const panelWidth = this.props.CollectionView.props.PanelWidth(); + const previewWidth = NumCast(this.props.CollectionView.props.Document.schemaPreviewWidth); + const tableWidth = panelWidth - 2 * borderWidth - dividerWidth - previewWidth; + this.props.CollectionView.props.Document.schemaPreviewWidth = previewWidth === 0 ? Math.min(tableWidth / 3, 200) : 0; + } + + @undoBatch + @action + toggleTextwrap = async () => { + const textwrappedRows = Cast(this.props.CollectionView.props.Document.textwrappedSchemaRows, listSpec("string"), []); + if (textwrappedRows.length) { + this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>([]); + } else { + const docs = DocListCast(this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey]); + const allRows = docs instanceof Doc ? [docs[Id]] : docs.map(doc => doc[Id]); + this.props.CollectionView.props.Document.textwrappedSchemaRows = new List<string>(allRows); + } + } + + + render() { + const previewWidth = NumCast(this.props.CollectionView.props.Document.schemaPreviewWidth); + const textWrapped = Cast(this.props.CollectionView.props.Document.textwrappedSchemaRows, listSpec("string"), []).length > 0; + + return ( + <div className="collectionSchemaViewChrome-cont"> + <div className="collectionSchemaViewChrome-toggle"> + <div className="collectionSchemaViewChrome-label">Show Preview: </div> + <div className="collectionSchemaViewChrome-toggler" onClick={this.togglePreview}> + <div className={"collectionSchemaViewChrome-togglerButton" + (previewWidth !== 0 ? " on" : " off")}> + {previewWidth !== 0 ? "on" : "off"} + </div> + </div> + </div> + </div > + ); + } +} + +@observer +export class CollectionTreeViewChrome extends React.Component<CollectionViewChromeProps> { + + get sortAscending() { + return this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey + "-sortAscending"]; + } + set sortAscending(value) { + this.props.CollectionView.props.Document[this.props.CollectionView.props.fieldKey + "-sortAscending"] = value; + } + @computed private get ascending() { + return Cast(this.sortAscending, "boolean", null); + } + + @action toggleSort = () => { + if (this.sortAscending) this.sortAscending = undefined; + else if (this.sortAscending === undefined) this.sortAscending = false; + else this.sortAscending = true; + } + + 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.ascending === undefined ? "90" : this.ascending ? "180" : "0"}deg)` }}> + <FontAwesomeIcon icon="caret-up" size="2x" color="white" /> + </div> + </button> + </div> + ); + } +} + +// Enter scroll speed for 3D Carousel +@observer +export class Collection3DCarouselViewChrome extends React.Component<CollectionViewChromeProps> { + @computed get scrollSpeed() { + return this.props.CollectionView.props.Document._autoScrollSpeed; + } + + @action + setValue = (value: string) => { + const numValue = Number(StrCast(value)); + if (numValue > 0) { + this.props.CollectionView.props.Document._autoScrollSpeed = numValue; + return true; + } + return false; + } + + render() { + return ( + <div className="collection3DCarouselViewChrome-cont"> + <div className="collection3DCarouselViewChrome-scrollSpeed-cont"> + <div className="collectionStackingViewChrome-scrollSpeed-label"> + AUTOSCROLL SPEED: + </div> + <div className="collection3DCarouselViewChrome-scrollSpeed"> + <EditableView + GetValue={() => StrCast(this.scrollSpeed)} + oneLine + SetValue={this.setValue} + contents={this.scrollSpeed ? this.scrollSpeed : 1000} /> + </div> + </div> + </div> + ); + } +} + +/** + * Chrome for grid view. + */ +@observer +export class CollectionGridViewChrome extends React.Component<CollectionViewChromeProps> { + + private clicked: boolean = false; + private entered: boolean = false; + private decrementLimitReached: boolean = false; + @observable private resize = false; + private resizeListenerDisposer: Opt<Lambda>; + + componentDidMount() { + + runInAction(() => this.resize = this.props.CollectionView.props.PanelWidth() < 700); + + // listener to reduce text on chrome resize (panel resize) + this.resizeListenerDisposer = computed(() => this.props.CollectionView.props.PanelWidth()).observe(({ newValue }) => { + runInAction(() => this.resize = newValue < 700); + }); + } + + componentWillUnmount() { + this.resizeListenerDisposer?.(); + } + + get numCols() { return NumCast(this.props.CollectionView.props.Document.gridNumCols, 10); } + + /** + * Sets the value of `numCols` on the grid's Document to the value entered. + */ + @undoBatch + onNumColsEnter = (e: React.KeyboardEvent<HTMLInputElement>) => { + if (e.key === "Enter" || e.key === "Tab") { + if (e.currentTarget.valueAsNumber > 0) { + this.props.CollectionView.props.Document.gridNumCols = e.currentTarget.valueAsNumber; + } + + } + } + + /** + * Sets the value of `rowHeight` on the grid's Document to the value entered. + */ + // @undoBatch + // onRowHeightEnter = (e: React.KeyboardEvent<HTMLInputElement>) => { + // if (e.key === "Enter" || e.key === "Tab") { + // if (e.currentTarget.valueAsNumber > 0 && this.props.CollectionView.props.Document.rowHeight as number !== e.currentTarget.valueAsNumber) { + // this.props.CollectionView.props.Document.rowHeight = e.currentTarget.valueAsNumber; + // } + // } + // } + + /** + * Sets whether the grid is flexible or not on the grid's Document. + */ + @undoBatch + toggleFlex = () => { + this.props.CollectionView.props.Document.gridFlex = !BoolCast(this.props.CollectionView.props.Document.gridFlex, true); + } + + /** + * Increments the value of numCols on button click + */ + onIncrementButtonClick = () => { + this.clicked = true; + this.entered && (this.props.CollectionView.props.Document.gridNumCols as number)--; + undoBatch(() => this.props.CollectionView.props.Document.gridNumCols = this.numCols + 1)(); + this.entered = false; + } + + /** + * Decrements the value of numCols on button click + */ + onDecrementButtonClick = () => { + this.clicked = true; + if (!this.decrementLimitReached) { + this.entered && (this.props.CollectionView.props.Document.gridNumCols as number)++; + undoBatch(() => this.props.CollectionView.props.Document.gridNumCols = this.numCols - 1)(); + } + this.entered = false; + } + + /** + * Increments the value of numCols on button hover + */ + incrementValue = () => { + this.entered = true; + if (!this.clicked && !this.decrementLimitReached) { + this.props.CollectionView.props.Document.gridNumCols = this.numCols + 1; + } + this.decrementLimitReached = false; + this.clicked = false; + } + + /** + * Decrements the value of numCols on button hover + */ + decrementValue = () => { + this.entered = true; + if (!this.clicked) { + if (this.numCols !== 1) { + this.props.CollectionView.props.Document.gridNumCols = this.numCols - 1; + } + else { + this.decrementLimitReached = true; + } + } + + this.clicked = false; + } + + /** + * Toggles the value of preventCollision + */ + toggleCollisions = () => { + this.props.CollectionView.props.Document.gridPreventCollision = !this.props.CollectionView.props.Document.gridPreventCollision; + } + + /** + * Changes the value of the compactType + */ + changeCompactType = (e: React.ChangeEvent<HTMLSelectElement>) => { + // need to change startCompaction so that this operation will be undoable. + this.props.CollectionView.props.Document.gridStartCompaction = e.target.selectedOptions[0].value; + } + + render() { + return ( + <div className="collectionGridViewChrome-cont" > + <span className="grid-control" style={{ width: this.resize ? "25%" : "30%" }}> + <span className="grid-icon"> + <FontAwesomeIcon icon="columns" size="1x" /> + </span> + <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.numCols.toString()} onKeyDown={this.onNumColsEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} /> + <input className="columnButton" onClick={this.onIncrementButtonClick} onMouseEnter={this.incrementValue} onMouseLeave={this.decrementValue} type="button" value="↑" /> + <input className="columnButton" style={{ marginRight: 5 }} onClick={this.onDecrementButtonClick} onMouseEnter={this.decrementValue} onMouseLeave={this.incrementValue} type="button" value="↓" /> + </span> + {/* <span className="grid-control"> + <span className="grid-icon"> + <FontAwesomeIcon icon="text-height" size="1x" /> + </span> + <input className="collectionGridViewChrome-entryBox" type="number" placeholder={this.props.CollectionView.props.Document.rowHeight as string} onKeyDown={this.onRowHeightEnter} onClick={(e: React.MouseEvent<HTMLInputElement, MouseEvent>) => { e.stopPropagation(); e.preventDefault(); e.currentTarget.focus(); }} /> + </span> */} + <span className="grid-control" style={{ width: this.resize ? "12%" : "20%" }}> + <input type="checkbox" style={{ marginRight: 5 }} onChange={this.toggleCollisions} checked={!this.props.CollectionView.props.Document.gridPreventCollision} /> + <label className="flexLabel">{this.resize ? "Coll" : "Collisions"}</label> + </span> + + <select className="collectionGridViewChrome-viewPicker" + style={{ marginRight: 5 }} + onPointerDown={stopPropagation} + onChange={this.changeCompactType} + value={StrCast(this.props.CollectionView.props.Document.gridStartCompaction, StrCast(this.props.CollectionView.props.Document.gridCompaction))}> + {["vertical", "horizontal", "none"].map(type => + <option className="collectionGridViewChrome-viewOption" + onPointerDown={stopPropagation} + value={type}> + {this.resize ? type[0].toUpperCase() + type.substring(1) : "Compact: " + type} + </option> + )} + </select> + + <span className="grid-control" style={{ width: this.resize ? "12%" : "20%" }}> + <input style={{ marginRight: 5 }} type="checkbox" onChange={this.toggleFlex} + checked={BoolCast(this.props.CollectionView.props.Document.gridFlex, true)} /> + <label className="flexLabel">{this.resize ? "Flex" : "Flexible"}</label> + </span> + + <button onClick={() => this.props.CollectionView.props.Document.gridResetLayout = true}> + {!this.resize ? "Reset" : + <FontAwesomeIcon icon="redo-alt" size="1x" />} + </button> + + </div> + ); + } +}
\ No newline at end of file diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index e18fe319e..6cd6a827f 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -47,7 +47,6 @@ import { SubCollectionViewProps } from './CollectionSubView'; import { CollectionTimeView } from './CollectionTimeView'; import { CollectionTreeView } from "./CollectionTreeView"; import './CollectionView.scss'; -import { CollectionViewBaseChrome } from './CollectionViewChromes'; import CollectionMenu from './CollectionMenu'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; @@ -235,16 +234,8 @@ export class CollectionView extends Touchable<FieldViewProps & CollectionViewCus } } - @action - private collapse = (value: boolean) => { - this.props.Document._chromeStatus = value ? "collapsed" : "enabled"; - } - private SubView = (type: CollectionViewType, renderProps: CollectionRenderProps) => { - // currently cant think of a reason for collection docking view to have a chrome. mind may change if we ever have nested docking views -syip - const chrome = this.props.Document._chromeStatus === "disabled" || this.props.Document._chromeStatus === "replaced" || type === CollectionViewType.Docking ? (null) : - <CollectionViewBaseChrome key="chrome" CollectionView={this} PanelWidth={this.bodyPanelWidth} type={type} collapse={this.collapse} />; - return <>{chrome} {this.SubViewHelper(type, renderProps)}</>; + return this.SubViewHelper(type, renderProps); } |