aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorAubrey Li <Aubrey-Li>2021-09-15 14:41:24 -0400
committerAubrey Li <Aubrey-Li>2021-09-15 14:41:24 -0400
commiteb63330e172935343767d0dcc7ffad9bfa1a75c4 (patch)
tree031bf155df50200f9652e881aec18002bc9e399e /src/client/views/collections
parentb7a88c6292c2e7bfffc3cdc4f7c7037922b3de25 (diff)
parent8386ad690c10d5c76bbd1b4f85314514b7f11b55 (diff)
merge master into trails-aubrey
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx4
-rw-r--r--src/client/views/collections/CollectionLinearView.tsx193
-rw-r--r--src/client/views/collections/CollectionMenu.scss1255
-rw-r--r--src/client/views/collections/CollectionMenu.tsx175
-rw-r--r--src/client/views/collections/CollectionSchemaView.tsx4
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.scss142
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx773
-rw-r--r--src/client/views/collections/CollectionStackingView.scss24
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx121
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx65
-rw-r--r--src/client/views/collections/CollectionTreeView.scss8
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx139
-rw-r--r--src/client/views/collections/CollectionView.tsx23
-rw-r--r--src/client/views/collections/TabDocView.scss30
-rw-r--r--src/client/views/collections/TabDocView.tsx37
-rw-r--r--src/client/views/collections/TreeView.tsx40
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx32
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx18
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx85
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx19
-rw-r--r--src/client/views/collections/collectionFreeForm/index.ts7
-rw-r--r--src/client/views/collections/collectionGrid/index.ts2
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.scss (renamed from src/client/views/collections/CollectionLinearView.scss)63
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx226
-rw-r--r--src/client/views/collections/collectionLinear/index.ts1
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx4
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx10
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx6
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss120
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx2
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTable.tsx6
32 files changed, 2266 insertions, 1370 deletions
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index cae08e1f4..5325d5827 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -428,7 +428,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
const emptyPane = CurrentUserUtils.EmptyPane;
emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
const docToAdd = Docs.Create.FreeformDocument([], {
- _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`,
+ _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`,
});
this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
CollectionDockingView.AddSplit(docToAdd, "", stack);
@@ -453,7 +453,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) {
const emptyPane = CurrentUserUtils.EmptyPane;
emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1;
const docToAdd = Docs.Create.FreeformDocument([], {
- _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
+ _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`
});
this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd);
CollectionDockingView.AddSplit(docToAdd, "", stack);
diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx
deleted file mode 100644
index 52c836556..000000000
--- a/src/client/views/collections/CollectionLinearView.tsx
+++ /dev/null
@@ -1,193 +0,0 @@
-import { Tooltip } from '@material-ui/core';
-import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { Doc, HeightSym, WidthSym } from '../../../fields/Doc';
-import { documentSchema } from '../../../fields/documentSchemas';
-import { Id } from '../../../fields/FieldSymbols';
-import { makeInterface } from '../../../fields/Schema';
-import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, Utils } from '../../../Utils';
-import { DragManager } from '../../util/DragManager';
-import { Transform } from '../../util/Transform';
-import { DocumentLinksButton } from '../nodes/DocumentLinksButton';
-import { DocumentView } from '../nodes/DocumentView';
-import { LinkDescriptionPopup } from '../nodes/LinkDescriptionPopup';
-import { StyleProp } from '../StyleProvider';
-import "./CollectionLinearView.scss";
-import { CollectionSubView } from './CollectionSubView';
-import { CollectionViewType } from './CollectionView';
-
-
-type LinearDocument = makeInterface<[typeof documentSchema,]>;
-const LinearDocument = makeInterface(documentSchema);
-
-@observer
-export class CollectionLinearView extends CollectionSubView(LinearDocument) {
- @observable public addMenuToggle = React.createRef<HTMLInputElement>();
- @observable private _selectedIndex = -1;
- private _dropDisposer?: DragManager.DragDropDisposer;
- private _widthDisposer?: IReactionDisposer;
- private _selectedDisposer?: IReactionDisposer;
-
- componentWillUnmount() {
- this._dropDisposer?.();
- this._widthDisposer?.();
- this._selectedDisposer?.();
- this.childLayoutPairs.map((pair, ind) => ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log));
- }
-
- componentDidMount() {
- this._widthDisposer = reaction(() => 5 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * (this.rootDoc[HeightSym]()) : 10),
- width => this.childDocs.length && (this.layoutDoc._width = width),
- { fireImmediately: true }
- );
-
- this._selectedDisposer = reaction(
- () => NumCast(this.layoutDoc.selectedIndex),
- (i) => runInAction(() => {
- this._selectedIndex = i;
- let selected: any = undefined;
- this.childLayoutPairs.map(async (pair, ind) => {
- const isSelected = this._selectedIndex === ind;
- if (isSelected) {
- selected = pair;
- }
- else {
- ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
- }
- });
- if (selected && selected.layout) {
- ScriptCast(selected.layout.proto?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
- }
- }),
- { fireImmediately: true }
- );
- }
- protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
- this._dropDisposer && this._dropDisposer();
- if (ele) {
- this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
- }
- }
-
- dimension = () => NumCast(this.rootDoc._height); // 2 * the padding
- getTransform = (ele: React.RefObject<HTMLDivElement>) => () => {
- if (!ele.current) return Transform.Identity();
- const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current);
- return new Transform(-translateX, -translateY, 1);
- }
-
- @action
- exitLongLinks = () => {
- if (DocumentLinksButton.StartLink) {
- if (DocumentLinksButton.StartLink.Document) {
- action((e: React.PointerEvent<HTMLDivElement>) => {
- Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc);
- });
- }
- }
- DocumentLinksButton.StartLink = undefined;
- DocumentLinksButton.StartLinkView = undefined;
- }
-
- @action
- changeDescriptionSetting = () => {
- if (LinkDescriptionPopup.showDescriptions) {
- if (LinkDescriptionPopup.showDescriptions === "ON") {
- LinkDescriptionPopup.showDescriptions = "OFF";
- LinkDescriptionPopup.descriptionPopup = false;
- } else {
- LinkDescriptionPopup.showDescriptions = "ON";
- }
- } else {
- LinkDescriptionPopup.showDescriptions = "OFF";
- LinkDescriptionPopup.descriptionPopup = false;
- }
- }
-
- render() {
- const guid = Utils.GenerateGuid();
- const flexDir: any = StrCast(this.Document.flexDirection);
- const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
- const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
-
- const menuOpener = <label htmlFor={`${guid}`} style={{ pointerEvents: "all", cursor: "pointer", background: backgroundColor === color ? "black" : backgroundColor, }}
- onPointerDown={e => e.stopPropagation()} >
- <p>{BoolCast(this.layoutDoc.linearViewIsExpanded) ? "–" : "+"}</p>
- </label>;
-
- return <div className="collectionLinearView-outer">
- <div className="collectionLinearView" ref={this.createDashEventsTarget} >
- <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.layoutDoc.linearViewIsExpanded) ? "Close menu" : "Open menu"}</div></>} placement="top">
- {menuOpener}
- </Tooltip>
- <input id={`${guid}`} type="checkbox" checked={BoolCast(this.layoutDoc.linearViewIsExpanded)} ref={this.addMenuToggle}
- onChange={action(() => this.layoutDoc.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
-
- <div className="collectionLinearView-content" style={{ height: this.dimension(), flexDirection: flexDir }}>
- {this.childLayoutPairs.map((pair, ind) => {
- const nested = pair.layout._viewType === CollectionViewType.Linear;
- const dref = React.createRef<HTMLDivElement>();
- const scalable = pair.layout.onClick || pair.layout.onDragStart;
- return <div className={`collectionLinearView-docBtn` + (scalable ? "-scalable" : "")} key={pair.layout[Id]} ref={dref}
- style={{
- pointerEvents: "all",
- minWidth: 30,
- width: nested ? pair.layout[WidthSym]() : this.dimension(),
- height: nested && pair.layout.linearViewIsExpanded ? pair.layout[HeightSym]() : this.dimension(),
- }} >
- <DocumentView
- Document={pair.layout}
- DataDoc={pair.data}
- isContentActive={returnFalse}
- isDocumentActive={returnTrue}
- addDocument={this.props.addDocument}
- moveDocument={this.props.moveDocument}
- addDocTab={this.props.addDocTab}
- pinToPres={emptyFunction}
- rootSelected={this.props.isSelected}
- removeDocument={this.props.removeDocument}
- ScreenToLocalTransform={this.getTransform(dref)}
- PanelWidth={nested ? pair.layout[WidthSym] : this.dimension}
- PanelHeight={nested ? pair.layout[HeightSym] : this.dimension}
- renderDepth={this.props.renderDepth + 1}
- focus={emptyFunction}
- styleProvider={this.props.styleProvider}
- layerProvider={this.props.layerProvider}
- docViewPath={returnEmptyDoclist}
- whenChildContentsActiveChanged={emptyFunction}
- bringToFront={emptyFunction}
- docFilters={this.props.docFilters}
- docRangeFilters={this.props.docRangeFilters}
- searchFilterDocs={this.props.searchFilterDocs}
- ContainingCollectionView={undefined}
- ContainingCollectionDoc={undefined} />
- </div>;
- })}
- </div>
- {DocumentLinksButton.StartLink ? <span className="bottomPopup-background" style={{
- pointerEvents: "all"
- }}
- onPointerDown={e => e.stopPropagation()} >
- <span className="bottomPopup-text" >
- Creating link from: <b>{DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'}</b>
- </span>
-
- <Tooltip title={<><div className="dash-tooltip">{"Toggle description pop-up"} </div></>} placement="top">
- <span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}>
- Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"}
- </span>
- </Tooltip>
-
- <Tooltip title={<><div className="dash-tooltip">Exit linking mode</div></>} placement="top">
- <span className="bottomPopup-exit" onClick={this.exitLongLinks}>
- Stop
- </span>
- </Tooltip>
-
- </span> : null}
- </div>
- </div>;
- }
-} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss
index f04b19ef7..c35f088a6 100644
--- a/src/client/views/collections/CollectionMenu.scss
+++ b/src/client/views/collections/CollectionMenu.scss
@@ -1,628 +1,659 @@
@import "../global/globalCssVariables";
-.collectionMenu-cont {
- position: relative;
- display: inline-flex;
- width: 100%;
- opacity: 0.9;
- z-index: 901;
- transition: top .5s;
- background: $dark-gray;
- color: $white;
- transform-origin: top left;
- top: 0;
- width: 100%;
-
- .recordButtonOutline {
- border-radius: 100%;
- width: 18px;
- height: 18px;
- border: solid 1px $white;
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- .recordButtonInner {
- border-radius: 100%;
- width: 70%;
- height: 70%;
- background: $white;
- }
-
- .collectionMenu {
- display: flex;
- height: 100%;
- overflow: visible;
- z-index: 901;
- border: unset;
-
- .collectionMenu-divider {
- height: 100%;
- margin-left: 3px;
- margin-right: 3px;
- width: 2px;
- background-color: $medium-gray;
- }
-
- .collectionViewBaseChrome {
- display: flex;
- align-items: center;
-
- .collectionViewBaseChrome-viewPicker {
- font-size: $small-text;
- outline-color: $black;
- color: $white;
- border: none;
- background: $dark-gray;
- }
-
- .collectionViewBaseChrome-viewPicker:focus {
- outline: none;
- border: none;
- }
-
- .collectionViewBaseChrome-viewPicker:active {
- outline-color: $black;
- }
-
- .collectionViewBaseChrome-button {
- font-size: $small-text;
- text-transform: uppercase;
- letter-spacing: 2px;
- background: $white;
- color: $pink;
- outline-color: $black;
- border: none;
- padding: 12px 10px 11px 10px;
- margin-left: 10px;
- }
-
- .collectionViewBaseChrome-cmdPicker {
- margin-left: 3px;
- margin-right: 0px;
- font-size: $small-text;
- text-transform: capitalize;
- color: $white;
- border: none;
- background: $dark-gray;
- }
-
- .collectionViewBaseChrome-cmdPicker:focus {
- border: none;
- outline: none;
- }
-
- .commandEntry-outerDiv {
- pointer-events: all;
- background-color: transparent;
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: center;
- height: 100%;
- overflow: hidden;
-
- .commandEntry-drop {
- color: $white;
- width: 30px;
- margin-top: auto;
- margin-bottom: auto;
- }
- }
-
- .commandEntry-outerDiv:hover{
- background-color: $drop-shadow;
-
- .collectionViewBaseChrome-viewPicker,
- .collectionViewBaseChrome-cmdPicker{
- background: $dark-gray;
- }
- }
-
- .collectionViewBaseChrome-collapse {
- transition: all .5s, opacity 0.3s;
- position: absolute;
- width: 30px;
- 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 {
- align-items: center;
- height: 100%;
- display: flex;
- background: transparent;
- color: $medium-gray;
- justify-content: center;
- }
-
- .collectionViewBaseChrome-viewSpecs {
- margin-left: 5px;
- display: grid;
- border: none;
- border-right: solid $medium-gray 1px;
-
- .collectionViewBaseChrome-filterIcon {
- position: relative;
- display: flex;
- margin: auto;
- background: $dark-gray;
- color: $white;
- width: 30px;
- height: 30px;
- align-items: center;
- justify-content: center;
- border: none;
- border-right: solid $medium-gray 1px;
- }
-
- .collectionViewBaseChrome-viewSpecsInput {
- padding: 12px 10px 11px 10px;
- border: 0px;
- color: $medium-gray;
- text-align: center;
- letter-spacing: 2px;
- outline-color: $black;
- font-size: $small-text;
- background: $white;
- 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: $white;
- box-shadow: $medium-gray 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: $small-text;
- letter-spacing: 2px;
- color: $medium-gray;
- 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: $small-text;
- //text-transform: uppercase;
- //letter-spacing: 2px;
- background: $dark-gray;
- color: $white;
- outline-color: $black;
- color: $white;
- border: none;
- border-right: solid $medium-gray 1px;
- }
-
- .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%;
- color: $black;
- }
-
- .collectionGridViewChrome-columnButton {
- color: $black;
- }
- }
- }
-
- .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;
- align-items: center;
- display: flex;
- grid-auto-columns: auto;
- font-size: $small-text;
- letter-spacing: 2px;
-
- .collectionStackingViewChrome-pivotField-label,
- .collectionTreeViewChrome-pivotField-label,
- .collection3DCarouselViewChrome-scrollSpeed-label {
- grid-column: 1;
- margin-right: 7px;
- user-select: none;
- font-family: $sans-serif;
- letter-spacing: normal;
- }
-
- .collectionStackingViewChrome-sortIcon {
- transition: transform .5s;
- grid-column: 3;
- text-align: center;
- display: flex;
- justify-content: center;
- align-items: center;
- cursor: pointer;
- width: 25px;
- height: 25px;
- border-radius: 100%;
- }
-
- .collectionStackingViewChrome-sortIcon:hover {
- background-color: $drop-shadow;
- }
-
- .collectionStackingViewChrome-pivotField,
- .collectionTreeViewChrome-pivotField,
- .collection3DCarouselViewChrome-scrollSpeed {
- color: $white;
- grid-column: 2;
- grid-row: 1;
- width: 90%;
- min-width: 100px;
- display: flex;
- height: 80%;
- border-radius: 7px;
- align-items: center;
- background: $white;
-
- .editable-view-input,
- input,
- .editableView-container-editing-oneLine,
- .editableView-container-editing {
- margin: auto;
- border: 0px;
- color: $light-gray !important;
- text-align: center;
- letter-spacing: 2px;
- outline-color: $black;
- height: 100%;
- }
-
- .react-autosuggest__container {
- margin: 0;
- color: $medium-gray;
- padding: 0px;
- }
- }
- }
-
- .collectionStackingViewChrome-pivotField:hover,
- .collectionTreeViewChrome-pivotField:hover,
- .collection3DCarouselViewChrome-scrollSpeed:hover {
- cursor: text;
- }
-
- }
-}
-
-.collectionMenu-webUrlButtons {
- margin-left: 44;
- background: lightGray;
+.collectionMenu-container {
display: flex;
-}
-
-.webBox-urlEditor {
- position: relative;
- opacity: 0.9;
- z-index: 901;
- transition: top .5s;
-
- .urlEditor {
- display: grid;
- grid-template-columns: 1fr auto;
- padding-bottom: 10px;
- overflow: hidden;
- margin-top: 5px;
- height: 35px;
-
- .editorBase {
- display: flex;
-
- .editor-collapse {
- transition: all .5s, opacity 0.3s;
- position: absolute;
- width: 40px;
- transform-origin: top left;
- }
-
- .switchToText {
- color: $medium-gray;
- }
-
- .switchToText:hover {
- color: $dark-gray;
- }
- }
-
- button:hover {
- transform: scale(1);
- }
- }
-}
-
-.collectionMenu-urlInput {
- padding: 12px 10px 11px 10px;
- border: 0px;
- color: $black;
- font-size: $small-text;
- letter-spacing: 2px;
- outline-color: $black;
- background: $white;
- width: 100%;
- min-width: 350px;
- margin-right: 10px;
- height: 100%;
-}
-
-.collectionFreeFormMenu-cont {
- display: inline-flex;
position: relative;
+ align-content: center;
+ justify-content: space-between;
+ background-color: $dark-gray;
+ height: 35px;
+ border-bottom: $standard-border;
+ padding-right: 5px;
align-items: center;
- height: 100%;
-
- .color-previewI {
- width: 60%;
- top: 80%;
- position: absolute;
- height: 4px;
- }
-
- .color-previewII {
- width: 80%;
- height: 80%;
- margin-left: 10%;
- position: absolute;
- bottom: 5;
- }
-
- .btn-group {
- display: grid;
- grid-template-columns: auto auto auto auto;
- margin: auto;
- /* Make the buttons appear below each other */
- }
- .btn-draw {
- display: inline-flex;
- margin: auto;
- /* Make the buttons appear below each other */
- }
-
- .fwdKeyframe,
- .numKeyframe,
- .backKeyframe {
+ .collectionMenu-hardCodedButton {
cursor: pointer;
- position: relative;
- width: 20;
- height: 30;
- bottom: 0;
- background: $dark-gray;
- display: inline-flex;
- align-items: center;
color: $white;
- }
-
- .backKeyframe {
- svg {
- display: block;
- margin: auto;
- }
- }
-
-
- .numKeyframe {
- flex-direction: column;
- padding-top: 5px;
- }
-
- .fwdKeyframe {
- svg {
- display: block;
- margin: auto;
- }
-
- border-right: solid $medium-gray 1px;
- }
-}
-
-.collectionSchemaViewChrome-cont {
- display: flex;
- font-size: $small-text;
-
- .collectionSchemaViewChrome-toggle {
- display: flex;
- margin-left: 10px;
- }
-
- .collectionSchemaViewChrome-label {
- text-transform: uppercase;
- letter-spacing: 2px;
- margin-right: 5px;
+ width: 25px;
+ height: 25px;
+ padding: 5;
+ text-align: center;
display: flex;
- flex-direction: column;
justify-content: center;
- }
-
- .collectionSchemaViewChrome-toggler {
- width: 100px;
- height: 35px;
- background-color: $black;
+ align-items: center;
position: relative;
- }
-
- .collectionSchemaViewChrome-togglerButton {
- width: 47px;
- height: 30px;
- background-color: $light-gray;
- // position: absolute;
- transition: all 0.5s ease;
- // top: 3px;
- margin-top: 3px;
- color: $medium-gray;
- letter-spacing: 2px;
- text-transform: uppercase;
- display: flex;
- flex-direction: column;
- justify-content: center;
- text-align: center;
+ transition: 0.2s;
+ border-radius: 3px;
- &.on {
- margin-left: 3px;
- }
-
- &.off {
- margin-left: 50px;
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.2);
}
}
}
-
-.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 $light-gray;
- 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 $light-gray;
- background-color: $white;
- font-family: $sans-serif;
- font-weight: 300;
- font-size: $large-header;
- 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: $light-gray;
-} \ No newline at end of file
+// .collectionMenu-cont {
+// position: relative;
+// display: inline-flex;
+// width: 100%;
+// opacity: 0.9;
+// z-index: 901;
+// transition: top .5s;
+// background: $dark-gray;
+// color: $white;
+// transform-origin: top left;
+// top: 0;
+// width: 100%;
+
+// .recordButtonOutline {
+// border-radius: 100%;
+// width: 18px;
+// height: 18px;
+// border: solid 1px $white;
+// display: flex;
+// align-items: center;
+// justify-content: center;
+// }
+
+// .recordButtonInner {
+// border-radius: 100%;
+// width: 70%;
+// height: 70%;
+// background: $white;
+// }
+
+// .collectionMenu {
+// display: flex;
+// height: 100%;
+// overflow: visible;
+// z-index: 901;
+// border: unset;
+
+// .collectionMenu-divider {
+// height: 100%;
+// margin-left: 3px;
+// margin-right: 3px;
+// width: 2px;
+// background-color: $medium-gray;
+// }
+
+// .collectionViewBaseChrome {
+// display: flex;
+// align-items: center;
+
+// .collectionViewBaseChrome-viewPicker {
+// font-size: $small-text;
+// outline-color: $black;
+// color: $white;
+// border: none;
+// background: $dark-gray;
+// }
+
+// .collectionViewBaseChrome-viewPicker:focus {
+// outline: none;
+// border: none;
+// }
+
+// .collectionViewBaseChrome-viewPicker:active {
+// outline-color: $black;
+// }
+
+// .collectionViewBaseChrome-button {
+// font-size: $small-text;
+// text-transform: uppercase;
+// letter-spacing: 2px;
+// background: $white;
+// color: $pink;
+// outline-color: $black;
+// border: none;
+// padding: 12px 10px 11px 10px;
+// margin-left: 10px;
+// }
+
+// .collectionViewBaseChrome-cmdPicker {
+// margin-left: 3px;
+// margin-right: 0px;
+// font-size: $small-text;
+// text-transform: capitalize;
+// color: $white;
+// border: none;
+// background: $dark-gray;
+// }
+
+// .collectionViewBaseChrome-cmdPicker:focus {
+// border: none;
+// outline: none;
+// }
+
+// .commandEntry-outerDiv {
+// pointer-events: all;
+// background-color: transparent;
+// display: flex;
+// flex-direction: row;
+// align-items: center;
+// justify-content: center;
+// height: 100%;
+// overflow: hidden;
+
+// .commandEntry-drop {
+// color: $white;
+// width: 30px;
+// margin-top: auto;
+// margin-bottom: auto;
+// }
+// }
+
+// .commandEntry-outerDiv:hover{
+// background-color: $drop-shadow;
+
+// .collectionViewBaseChrome-viewPicker,
+// .collectionViewBaseChrome-cmdPicker{
+// background: $dark-gray;
+// }
+// }
+
+// .collectionViewBaseChrome-collapse {
+// transition: all .5s, opacity 0.3s;
+// position: absolute;
+// width: 30px;
+// 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 {
+// align-items: center;
+// height: 100%;
+// display: flex;
+// background: transparent;
+// color: $medium-gray;
+// justify-content: center;
+// }
+
+// .collectionViewBaseChrome-viewSpecs {
+// margin-left: 5px;
+// display: grid;
+// border: none;
+// border-right: solid $medium-gray 1px;
+
+// .collectionViewBaseChrome-filterIcon {
+// position: relative;
+// display: flex;
+// margin: auto;
+// background: $dark-gray;
+// color: $white;
+// width: 30px;
+// height: 30px;
+// align-items: center;
+// justify-content: center;
+// border: none;
+// border-right: solid $medium-gray 1px;
+// }
+
+// .collectionViewBaseChrome-viewSpecsInput {
+// padding: 12px 10px 11px 10px;
+// border: 0px;
+// color: $medium-gray;
+// text-align: center;
+// letter-spacing: 2px;
+// outline-color: $black;
+// font-size: $small-text;
+// background: $white;
+// 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: $white;
+// box-shadow: $medium-gray 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: $small-text;
+// letter-spacing: 2px;
+// color: $medium-gray;
+// 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: $small-text;
+// //text-transform: uppercase;
+// //letter-spacing: 2px;
+// background: $dark-gray;
+// color: $white;
+// outline-color: $black;
+// color: $white;
+// border: none;
+// border-right: solid $medium-gray 1px;
+// }
+
+// .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%;
+// color: $black;
+// }
+
+// .collectionGridViewChrome-columnButton {
+// color: $black;
+// }
+// }
+// }
+
+// .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;
+// align-items: center;
+// display: flex;
+// grid-auto-columns: auto;
+// font-size: $small-text;
+// letter-spacing: 2px;
+
+// .collectionStackingViewChrome-pivotField-label,
+// .collectionTreeViewChrome-pivotField-label,
+// .collection3DCarouselViewChrome-scrollSpeed-label {
+// grid-column: 1;
+// margin-right: 7px;
+// user-select: none;
+// font-family: $sans-serif;
+// letter-spacing: normal;
+// }
+
+// .collectionStackingViewChrome-sortIcon {
+// transition: transform .5s;
+// grid-column: 3;
+// text-align: center;
+// display: flex;
+// justify-content: center;
+// align-items: center;
+// cursor: pointer;
+// width: 25px;
+// height: 25px;
+// border-radius: 100%;
+// }
+
+// .collectionStackingViewChrome-sortIcon:hover {
+// background-color: $drop-shadow;
+// }
+
+// .collectionStackingViewChrome-pivotField,
+// .collectionTreeViewChrome-pivotField,
+// .collection3DCarouselViewChrome-scrollSpeed {
+// color: $white;
+// grid-column: 2;
+// grid-row: 1;
+// width: 90%;
+// min-width: 100px;
+// display: flex;
+// height: 80%;
+// border-radius: 7px;
+// align-items: center;
+// background: $white;
+
+// .editable-view-input,
+// input,
+// .editableView-container-editing-oneLine,
+// .editableView-container-editing {
+// margin: auto;
+// border: 0px;
+// color: $light-gray !important;
+// text-align: center;
+// letter-spacing: 2px;
+// outline-color: $black;
+// height: 100%;
+// }
+
+// .react-autosuggest__container {
+// margin: 0;
+// color: $medium-gray;
+// padding: 0px;
+// }
+// }
+// }
+
+// .collectionStackingViewChrome-pivotField:hover,
+// .collectionTreeViewChrome-pivotField:hover,
+// .collection3DCarouselViewChrome-scrollSpeed:hover {
+// cursor: text;
+// }
+
+// }
+// }
+
+// .collectionMenu-webUrlButtons {
+// margin-left: 44;
+// background: lightGray;
+// display: flex;
+// }
+
+// .webBox-urlEditor {
+// position: relative;
+// opacity: 0.9;
+// z-index: 901;
+// transition: top .5s;
+
+// .urlEditor {
+// display: grid;
+// grid-template-columns: 1fr auto;
+// padding-bottom: 10px;
+// overflow: hidden;
+// margin-top: 5px;
+// height: 35px;
+
+// .editorBase {
+// display: flex;
+
+// .editor-collapse {
+// transition: all .5s, opacity 0.3s;
+// position: absolute;
+// width: 40px;
+// transform-origin: top left;
+// }
+
+// .switchToText {
+// color: $medium-gray;
+// }
+
+// .switchToText:hover {
+// color: $dark-gray;
+// }
+// }
+
+// button:hover {
+// transform: scale(1);
+// }
+// }
+// }
+
+// .collectionMenu-urlInput {
+// padding: 12px 10px 11px 10px;
+// border: 0px;
+// color: $black;
+// font-size: $small-text;
+// letter-spacing: 2px;
+// outline-color: $black;
+// background: $white;
+// width: 100%;
+// min-width: 350px;
+// margin-right: 10px;
+// height: 100%;
+// }
+
+// .collectionFreeFormMenu-cont {
+// display: inline-flex;
+// position: relative;
+// align-items: center;
+// height: 100%;
+
+// .color-previewI {
+// width: 60%;
+// top: 80%;
+// position: absolute;
+// height: 4px;
+// }
+
+// .color-previewII {
+// width: 80%;
+// height: 80%;
+// margin-left: 10%;
+// position: absolute;
+// bottom: 5;
+// }
+
+// .btn-group {
+// display: grid;
+// grid-template-columns: auto auto auto auto;
+// margin: auto;
+// /* Make the buttons appear below each other */
+// }
+
+// .btn-draw {
+// display: inline-flex;
+// margin: auto;
+// /* Make the buttons appear below each other */
+// }
+
+// .fwdKeyframe,
+// .numKeyframe,
+// .backKeyframe {
+// cursor: pointer;
+// position: relative;
+// width: 20;
+// height: 30;
+// bottom: 0;
+// background: $dark-gray;
+// display: inline-flex;
+// align-items: center;
+// color: $white;
+// }
+
+// .backKeyframe {
+// svg {
+// display: block;
+// margin: auto;
+// }
+// }
+
+
+// .numKeyframe {
+// flex-direction: column;
+// padding-top: 5px;
+// }
+
+// .fwdKeyframe {
+// svg {
+// display: block;
+// margin: auto;
+// }
+
+// border-right: solid $medium-gray 1px;
+// }
+// }
+
+// .collectionSchemaViewChrome-cont {
+// display: flex;
+// font-size: $small-text;
+
+// .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: 35px;
+// background-color: $black;
+// position: relative;
+// }
+
+// .collectionSchemaViewChrome-togglerButton {
+// width: 47px;
+// height: 30px;
+// background-color: $light-gray;
+// // position: absolute;
+// transition: all 0.5s ease;
+// // top: 3px;
+// margin-top: 3px;
+// color: $medium-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 $light-gray;
+// 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 $light-gray;
+// background-color: $white;
+// font-family: $sans-serif;
+// font-weight: 300;
+// font-size: $large-header;
+// 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: $light-gray;
+// } \ No newline at end of file
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index a5f54c342..131f5ba46 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -2,7 +2,7 @@ import React = require("react");
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon, FontAwesomeIconProps } from "@fortawesome/react-fontawesome";
import { Tooltip } from "@material-ui/core";
-import { action, computed, Lambda, observable, reaction, runInAction } from "mobx";
+import { action, computed, Lambda, observable, reaction, runInAction, trace } from "mobx";
import { observer } from "mobx-react";
import { ColorState } from "react-color";
import { Doc, DocListCast, Opt } from "../../../fields/Doc";
@@ -15,37 +15,47 @@ import { RichTextField } from "../../../fields/RichTextField";
import { listSpec } from "../../../fields/Schema";
import { ScriptField } from "../../../fields/ScriptField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { emptyFunction, setupMoveUpEvents, Utils } from "../../../Utils";
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
+import { Docs } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
import { CurrentUserUtils } from "../../util/CurrentUserUtils";
import { DragManager } from "../../util/DragManager";
import { Scripting } from "../../util/Scripting";
import { SelectionManager } from "../../util/SelectionManager";
+import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu";
import { EditableView } from "../EditableView";
import { GestureOverlay } from "../GestureOverlay";
-import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, ActiveArrowStart, ActiveArrowEnd } from "../InkingStroke";
+import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke";
+import { LightboxView } from "../LightboxView";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
import { DocumentView } from "../nodes/DocumentView";
+import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
import { RichTextMenu } from "../nodes/formattedText/RichTextMenu";
import { PresBox } from "../nodes/trails/PresBox";
+import { DefaultStyleProvider } from "../StyleProvider";
+import { CollectionDockingView } from "./CollectionDockingView";
+import { CollectionLinearView } from "./collectionLinear";
import "./CollectionMenu.scss";
import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView";
import { TabDocView } from "./TabDocView";
-import { LightboxView } from "../LightboxView";
-import { Docs } from "../../documents/Documents";
-import { DocumentManager } from "../../util/DocumentManager";
-import { CollectionDockingView } from "./CollectionDockingView";
-import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox";
+import { Colors } from "../global/globalEnums";
+
+interface CollectionMenuProps {
+ panelHeight: () => number;
+ panelWidth: () => number;
+}
@observer
-export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
+export class CollectionMenu extends AntimodeMenu<CollectionMenuProps>{
@observable static Instance: CollectionMenu;
@observable SelectedCollection: DocumentView | undefined;
@observable FieldKey: string;
+ private _docBtnRef = React.createRef<HTMLDivElement>();
+
constructor(props: any) {
super(props);
this.FieldKey = "";
@@ -57,7 +67,7 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
componentDidMount() {
reaction(() => SelectionManager.Views().length && SelectionManager.Views()[0],
- (doc) => doc && this.SetSelection(doc));
+ view => view && this.SetSelection(view));
}
@action
@@ -82,34 +92,92 @@ export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> {
}
}
+ buttonBarXf = () => {
+ if (!this._docBtnRef.current) return Transform.Identity();
+ const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current);
+ return new Transform(-translateX, -translateY, 1 / scale);
+ }
+
+ @computed get contMenuButtons() {
+ const selDoc = Doc.UserDoc().contextMenuBtns;
+ return !(selDoc instanceof Doc) ? (null) : <div className="collectionMenu-contMenuButtons" ref={this._docBtnRef} style={{ height: this.props.panelHeight() }} >
+ <CollectionLinearView
+ Document={selDoc}
+ DataDoc={undefined}
+ fieldKey={"data"}
+ dropAction={"alias"}
+ setHeight={returnFalse}
+ styleProvider={DefaultStyleProvider}
+ layerProvider={undefined}
+ rootSelected={returnTrue}
+ bringToFront={emptyFunction}
+ select={emptyFunction}
+ isContentActive={returnTrue}
+ isAnyChildContentActive={returnFalse}
+ isSelected={returnFalse}
+ docViewPath={returnEmptyDoclist}
+ moveDocument={returnFalse}
+ CollectionView={undefined}
+ addDocument={returnFalse}
+ addDocTab={returnFalse}
+ pinToPres={emptyFunction}
+ removeDocument={returnFalse}
+ ScreenToLocalTransform={this.buttonBarXf}
+ PanelWidth={this.props.panelWidth}
+ PanelHeight={this.props.panelHeight}
+ renderDepth={0}
+ focus={emptyFunction}
+ whenChildContentsActiveChanged={emptyFunction}
+ docFilters={returnEmptyFilter}
+ docRangeFilters={returnEmptyFilter}
+ searchFilterDocs={returnEmptyDoclist}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined} />
+ </div>;
+ }
+
render() {
- const button = <Tooltip title={<div className="dash-tooltip">Pin Menu</div>} key="pin menu" placement="bottom">
- <button className="antimodeMenu-button" 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>
- </Tooltip>;
const propIcon = CurrentUserUtils.propertiesWidth > 0 ? "angle-double-right" : "angle-double-left";
const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Properties Panel" : "Open Properties Panel";
const prop = <Tooltip title={<div className="dash-tooltip">{propTitle}</div>} key="properties" placement="bottom">
- <button className="antimodeMenu-button" key="properties" style={{ backgroundColor: "#424242" }}
+ <div className="collectionMenu-hardCodedButton"
+ style={{ backgroundColor: CurrentUserUtils.propertiesWidth > 0 ? Colors.MEDIUM_BLUE : undefined }}
+ key="properties"
onPointerDown={this.toggleProperties}>
<FontAwesomeIcon icon={propIcon} size="lg" />
- </button>
+ </div>
</Tooltip>;
- return this.getElement(!this.SelectedCollection ? [/*button*/] :
- [<CollectionViewBaseChrome key="chrome"
- docView={this.SelectedCollection}
- fieldKey={this.SelectedCollection.LayoutFieldKey}
- type={StrCast(this.SelectedCollection?.props.Document._viewType, CollectionViewType.Invalid) as CollectionViewType} />,
- prop,
- /*button*/]);
+ // NEW BUTTONS
+ //dash col linear view buttons
+ const contMenuButtons =
+ <div className="collectionMenu-container">
+ {this.contMenuButtons}
+ {prop}
+ </div>;
+
+ return contMenuButtons;
+
+ // const button = <Tooltip title={<div className="dash-tooltip">Pin Menu</div>} key="pin menu" placement="bottom">
+ // <button className="antimodeMenu-button" 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>
+ // </Tooltip>;
+
+ // OLD BUTTONS
+ // return this.getElement(!this.SelectedCollection ? [/*button*/] :
+ // [<CollectionViewBaseChrome key="chrome"
+ // docView={this.SelectedCollection}
+ // fieldKey={this.SelectedCollection.LayoutFieldKey}
+ // type={StrCast(this.SelectedCollection?.props.Document._viewType, CollectionViewType.Invalid) as CollectionViewType} />,
+ // prop,
+ // /*button*/]);
}
}
-interface CollectionMenuProps {
+interface CollectionViewMenuProps {
type: CollectionViewType;
fieldKey: string;
docView: DocumentView;
@@ -118,7 +186,7 @@ interface CollectionMenuProps {
const stopPropagation = (e: React.SyntheticEvent) => e.stopPropagation();
@observer
-export class CollectionViewBaseChrome extends React.Component<CollectionMenuProps> {
+export class CollectionViewBaseChrome extends React.Component<CollectionViewMenuProps> {
//(!)?\(\(\(doc.(\w+) && \(doc.\w+ as \w+\).includes\(\"(\w+)\"\)
get document() { return this.props.docView?.props.Document; }
@@ -375,10 +443,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
</div>);
}
- @computed get selectedDocumentView() {
- return SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined;
- }
- @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
+ @computed get selectedDocumentView() { return SelectionManager.Views().lastElement(); }
+ @computed get selectedDoc() { return SelectionManager.Docs().lastElement(); }
@computed get notACollection() {
if (this.selectedDoc) {
const layoutField = Doc.LayoutField(this.selectedDoc);
@@ -486,8 +552,8 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
@undoBatch
onAliasButtonMoved = (e: PointerEvent) => {
const contentDiv = this.selectedDocumentView?.ContentDiv;
- if (contentDiv) {
- const dragData = new DragManager.DocumentDragData([this.selectedDocumentView!.props.Document]);
+ if (contentDiv && this.selectedDoc) {
+ const dragData = new DragManager.DocumentDragData([this.selectedDoc]);
const offset = [e.clientX - contentDiv.getBoundingClientRect().x, e.clientY - contentDiv.getBoundingClientRect().y];
dragData.defaultDropAction = "alias";
dragData.canEmbed = true;
@@ -564,14 +630,14 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp
}
@observer
-export class CollectionDockingChrome extends React.Component<CollectionMenuProps> {
+export class CollectionDockingChrome extends React.Component<CollectionViewMenuProps> {
render() {
return (null);
}
}
@observer
-export class CollectionFreeFormViewChrome extends React.Component<CollectionMenuProps & { isOverlay: boolean, isDoc?: boolean }> {
+export class CollectionFreeFormViewChrome extends React.Component<CollectionViewMenuProps & { isOverlay: boolean, isDoc?: boolean }> {
public static Instance: CollectionFreeFormViewChrome;
constructor(props: any) {
super(props);
@@ -582,11 +648,9 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
return this.document[this.props.docView.LayoutFieldKey + (this.props.isOverlay ? "-annotations" : "")];
}
@computed get childDocs() { return DocListCast(this.dataField); }
- @computed get selectedDocumentView() { return SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; }
- @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; }
- @computed get isText() {
- return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false;
- }
+ @computed get selectedDocumentView() { return SelectionManager.Views().lastElement(); }
+ @computed get selectedDoc() { return SelectionManager.Docs().lastElement(); }
+ @computed get isText() { return this.selectedDoc?.type === DocumentType.RTF || (RichTextMenu.Instance?.view as any) ? true : false; }
@undoBatch
@action
@@ -615,12 +679,12 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
private _palette = ["#D0021B", "#F5A623", "#F8E71C", "#8B572A", "#7ED321", "#417505", "#9013FE", "#4A90E2", "#50E3C2", "#B8E986", "#000000", "#4A4A4A", "#9B9B9B", "#FFFFFF", ""];
private _width = ["1", "5", "10", "100"];
private _dotsize = [10, 20, 30, 40];
- private _draw = ["∿", "⎯", "→", "↔︎", "ロ", "O"];
- private _head = ["", "", "", "arrow", "", ""];
- private _end = ["", "", "arrow", "arrow", "", ""];
- private _shapePrims = ["", "line", "line", "line", "rectangle", "circle"];
- private _title = ["pen", "line", "line with arrow", "line with double arrows", "square", "circle",];
- private _faName = ["pen-fancy", "minus", "long-arrow-alt-right", "arrows-alt-h", "square", "circle"];
+ private _draw = ["∿", "=", "⎯", "→", "↔︎", "ロ", "O"];
+ private _head = ["", "", "", "", "arrow", "", ""];
+ private _end = ["", "", "", "arrow", "arrow", "", ""];
+ private _shapePrims = ["", "", "line", "line", "line", "rectangle", "circle"];
+ private _title = ["pen", "highlighter", "line", "line with arrow", "line with double arrows", "square", "circle"];
+ private _faName = ["pen-fancy", "highlighter", "minus", "long-arrow-alt-right", "arrows-alt-h", "square", "circle"];
@observable _selectedPrimitive = this._shapePrims.length;
@observable _keepPrimitiveMode = false; // for whether primitive selection enters a one-shot or persistent mode
@observable _colorBtn = false;
@@ -630,6 +694,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
@action clearKeepPrimitiveMode() { this._selectedPrimitive = this._shapePrims.length; }
@action primCreated() {
if (!this._keepPrimitiveMode) { //get out of ink mode after each stroke=
+ if (CurrentUserUtils.SelectedTool === InkTool.Highlighter && GestureOverlay.Instance.SavedColor) SetActiveInkColor(GestureOverlay.Instance.SavedColor);
CurrentUserUtils.SelectedTool = InkTool.None;
this._selectedPrimitive = this._shapePrims.length;
SetActiveArrowStart("none");
@@ -670,7 +735,13 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
this._keepPrimitiveMode = keep;
if (this._selectedPrimitive !== i) {
this._selectedPrimitive = i;
- CurrentUserUtils.SelectedTool = InkTool.Pen;
+ if (this._title[i] === "highlighter") {
+ CurrentUserUtils.SelectedTool = InkTool.Highlighter;
+ GestureOverlay.Instance.SavedColor = ActiveInkColor();
+ SetActiveInkColor("rgba(245, 230, 95, 0.75)");
+ } else {
+ CurrentUserUtils.SelectedTool = InkTool.Pen;
+ }
SetActiveArrowStart(this._head[i]);
SetActiveArrowEnd(this._end[i]);
SetActiveBezierApprox("300");
@@ -760,7 +831,6 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
</div>;
}
- @observable viewType = this.selectedDoc?._viewType;
render() {
return !this.props.docView.layoutDoc ? (null) :
@@ -798,7 +868,7 @@ export class CollectionFreeFormViewChrome extends React.Component<CollectionMenu
}
}
@observer
-export class CollectionStackingViewChrome extends React.Component<CollectionMenuProps> {
+export class CollectionStackingViewChrome extends React.Component<CollectionViewMenuProps> {
@observable private _currentKey: string = "";
@observable private suggestions: string[] = [];
@@ -919,7 +989,7 @@ export class CollectionStackingViewChrome extends React.Component<CollectionMenu
@observer
-export class CollectionSchemaViewChrome extends React.Component<CollectionMenuProps> {
+export class CollectionSchemaViewChrome extends React.Component<CollectionViewMenuProps> {
// private _textwrapAllRows: boolean = Cast(this.document.textwrappedSchemaRows, listSpec("string"), []).length > 0;
get document() { return this.props.docView.props.Document; }
@@ -967,7 +1037,7 @@ export class CollectionSchemaViewChrome extends React.Component<CollectionMenuPr
}
@observer
-export class CollectionTreeViewChrome extends React.Component<CollectionMenuProps> {
+export class CollectionTreeViewChrome extends React.Component<CollectionViewMenuProps> {
get document() { return this.props.docView.props.Document; }
get sortAscending() {
@@ -1004,7 +1074,7 @@ export class CollectionTreeViewChrome extends React.Component<CollectionMenuProp
// Enter scroll speed for 3D Carousel
@observer
-export class Collection3DCarouselViewChrome extends React.Component<CollectionMenuProps> {
+export class Collection3DCarouselViewChrome extends React.Component<CollectionViewMenuProps> {
get document() { return this.props.docView.props.Document; }
@computed get scrollSpeed() {
return this.document._autoScrollSpeed;
@@ -1045,7 +1115,7 @@ export class Collection3DCarouselViewChrome extends React.Component<CollectionMe
* Chrome for grid view.
*/
@observer
-export class CollectionGridViewChrome extends React.Component<CollectionMenuProps> {
+export class CollectionGridViewChrome extends React.Component<CollectionViewMenuProps> {
private clicked: boolean = false;
private entered: boolean = false;
@@ -1226,3 +1296,4 @@ Scripting.addGlobal(function gotoFrame(doc: any, newFrame: any) {
CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0);
doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame);
});
+
diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx
index 1efea96be..6a22acae8 100644
--- a/src/client/views/collections/CollectionSchemaView.tsx
+++ b/src/client/views/collections/CollectionSchemaView.tsx
@@ -18,6 +18,7 @@ import { SnappingManager } from "../../util/SnappingManager";
import { Transform } from "../../util/Transform";
import { undoBatch } from "../../util/UndoManager";
import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../views/global/globalCssVariables.scss';
+import { SchemaTable } from "../collections/collectionSchema/SchemaTable";
import { ContextMenu } from "../ContextMenu";
import { ContextMenuProps } from "../ContextMenuItem";
import '../DocumentDecorations.scss';
@@ -25,7 +26,6 @@ import { DocumentView } from "../nodes/DocumentView";
import { DefaultStyleProvider } from "../StyleProvider";
import "./CollectionSchemaView.scss";
import { CollectionSubView } from "./CollectionSubView";
-import { SchemaTable } from "../collections/collectionSchema/SchemaTable";
// bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657
export enum ColumnType {
@@ -338,7 +338,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
{this.renderColors(this._col)}
<div className="collectionSchema-headerMenu-group">
<button onClick={() => { this.deleteColumn(this._col.heading); }}
- >Delete Column</button>
+ >Hide Column</button>
</div>
</div>;
}
diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss
index e456c0664..59c21210a 100644
--- a/src/client/views/collections/CollectionStackedTimeline.scss
+++ b/src/client/views/collections/CollectionStackedTimeline.scss
@@ -1,70 +1,94 @@
+@import "../global/globalCssVariables.scss";
+
.collectionStackedTimeline {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ z-index: 1000;
+ overflow: hidden;
+ top: 0px;
+
+ .collectionStackedTimeline-trim-shade {
position: absolute;
- width: 100%;
height: 100%;
- border: gray solid 1px;
- border-radius: 3px;
- z-index: 1000;
- overflow: hidden;
- top: 0px;
+ background-color: $dark-gray;
+ opacity: 0.3;
+ }
- .collectionStackedTimeline-selector {
- position: absolute;
- width: 10px;
- top: 2.5%;
- height: 95%;
- background: lightblue;
- border-radius: 5px;
- opacity: 0.3;
- z-index: 500;
- border-style: solid;
- border-color: darkblue;
- border-width: 1px;
- }
+ .collectionStackedTimeline-trim-controls {
+ height: 100%;
+ position: absolute;
+ box-sizing: border-box;
+ border: 2px solid $medium-blue;
+ display: flex;
+ justify-content: space-between;
+ max-width: 100%;
- .collectionStackedTimeline-current {
- width: 1px;
- height: 100%;
- background-color: red;
- position: absolute;
- top: 0px;
- pointer-events: none;
+ .collectionStackedTimeline-trim-handle {
+ background-color: $medium-blue;
+ height: 100%;
+ width: 5px;
+ cursor: ew-resize;
}
+ }
- .collectionStackedTimeline-marker-timeline {
- position: absolute;
- top: 2.5%;
- height: 95%;
- border-radius: 4px;
- &:hover {
- opacity: 1;
- }
+ .collectionStackedTimeline-selector {
+ position: absolute;
+ width: 10px;
+ top: 2.5%;
+ height: 95%;
+ background: $light-blue;
+ border-radius: 3px;
+ opacity: 0.3;
+ z-index: 500;
+ border-style: solid;
+ border-color: $medium-blue;
+ border-width: 1px;
+ }
- .collectionStackedTimeline-left-resizer,
- .collectionStackedTimeline-resizer {
- background: dimgrey;
- position: absolute;
- top: 0;
- height: 100%;
- width: 10px;
- pointer-events: all;
- cursor: ew-resize;
- z-index: 100;
- }
- .collectionStackedTimeline-resizer {
- right: 0;
- }
- .collectionStackedTimeline-left-resizer {
- left: 0;
- }
+ .collectionStackedTimeline-current {
+ width: 1px;
+ height: 100%;
+ background-color: $pink;
+ position: absolute;
+ top: 0px;
+ pointer-events: none;
+ }
+
+ .collectionStackedTimeline-marker-timeline {
+ position: absolute;
+ top: 2.5%;
+ height: 95%;
+ border-radius: 4px;
+ &:hover {
+ opacity: 1;
}
- .collectionStackedTimeline-waveform {
- position: absolute;
- width: 100%;
- height: 100%;
- top: 0;
- left: 0;
- pointer-events: none;
+ .collectionStackedTimeline-left-resizer,
+ .collectionStackedTimeline-resizer {
+ background: $medium-gray;
+ position: absolute;
+ top: 0;
+ height: 100%;
+ width: 10px;
+ pointer-events: all;
+ cursor: ew-resize;
+ z-index: 100;
+ }
+ .collectionStackedTimeline-resizer {
+ right: 0;
}
-} \ No newline at end of file
+ .collectionStackedTimeline-left-resizer {
+ left: 0;
+ }
+ }
+
+ .collectionStackedTimeline-waveform {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ pointer-events: none;
+ }
+}
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index a2c95df6e..65022fdfd 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -1,5 +1,12 @@
import React = require("react");
-import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
+import {
+ action,
+ computed,
+ IReactionDisposer,
+ observable,
+ reaction,
+ runInAction,
+} from "mobx";
import { observer } from "mobx-react";
import { computedFn } from "mobx-utils";
import { Doc, DocListCast } from "../../../fields/Doc";
@@ -8,7 +15,16 @@ import { List } from "../../../fields/List";
import { listSpec, makeInterface } from "../../../fields/Schema";
import { ComputedField, ScriptField } from "../../../fields/ScriptField";
import { Cast, NumCast } from "../../../fields/Types";
-import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, StopEvent, returnTrue } from "../../../Utils";
+import {
+ emptyFunction,
+ formatTime,
+ OmitKeys,
+ returnFalse,
+ returnOne,
+ setupMoveUpEvents,
+ StopEvent,
+ returnTrue,
+} from "../../../Utils";
import { Docs } from "../../documents/Documents";
import { LinkManager } from "../../util/LinkManager";
import { Scripting } from "../../util/Scripting";
@@ -18,9 +34,18 @@ import { undoBatch } from "../../util/UndoManager";
import { AudioWaveform } from "../AudioWaveform";
import { CollectionSubView } from "../collections/CollectionSubView";
import { LightboxView } from "../LightboxView";
-import { DocAfterFocusFunc, DocFocusFunc, DocumentView, DocumentViewProps } from "../nodes/DocumentView";
+import {
+ DocAfterFocusFunc,
+ DocFocusFunc,
+ DocumentView,
+ DocumentViewProps,
+} from "../nodes/DocumentView";
import { LabelBox } from "../nodes/LabelBox";
import "./CollectionStackedTimeline.scss";
+import { Colors } from "../global/globalEnums";
+import { DocumentManager } from "../../util/DocumentManager";
+import { SnappingManager } from "../../util/SnappingManager";
+import { DragManager } from "../../util/DragManager";
type PanZoomDocument = makeInterface<[]>;
const PanZoomDocument = makeInterface();
@@ -36,11 +61,21 @@ export type CollectionStackedTimelineProps = {
endTag: string;
mediaPath: string;
dictationKey: string;
+ trimming: boolean;
+ trimStart: number;
+ trimEnd: number;
+ trimDuration: number;
+ setStartTrim: (newStart: number) => void;
+ setEndTrim: (newEnd: number) => void;
};
@observer
-export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument, CollectionStackedTimelineProps>(PanZoomDocument) {
- @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined;
+export class CollectionStackedTimeline extends CollectionSubView<
+ PanZoomDocument,
+ CollectionStackedTimelineProps
+>(PanZoomDocument) {
+ @observable static SelectingRegion: CollectionStackedTimeline | undefined =
+ undefined;
static RangeScript: ScriptField;
static LabelScript: ScriptField;
static RangePlayScript: ScriptField;
@@ -50,48 +85,111 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument
private _markerStart: number = 0;
@observable _markerEnd: number = 0;
- get duration() { return this.props.duration; }
- @computed get currentTime() { return NumCast(this.layoutDoc._currentTimecode); }
+ get minLength() {
+ const rect = this._timeline?.getBoundingClientRect();
+ if (rect) {
+ return 0.05 * this.duration;
+ }
+ return 0;
+ }
+
+ get trimStart() {
+ return this.props.trimStart;
+ }
+
+ get trimEnd() {
+ return this.props.trimEnd;
+ }
+
+ get duration() {
+ return this.props.duration;
+ }
+
+ @computed get currentTime() {
+ return NumCast(this.layoutDoc._currentTimecode);
+ }
@computed get selectionContainer() {
- return CollectionStackedTimeline.SelectingRegion !== this ? (null) : <div className="collectionStackedTimeline-selector" style={{
- left: `${Math.min(NumCast(this._markerStart), NumCast(this._markerEnd)) / this.duration * 100}%`,
- width: `${Math.abs(this._markerStart - this._markerEnd) / this.duration * 100}%`
- }} />;
+ return CollectionStackedTimeline.SelectingRegion !== this ? null : (
+ <div
+ className="collectionStackedTimeline-selector"
+ style={{
+ left: `${((Math.min(this._markerStart, this._markerEnd) - this.trimStart) / this.props.trimDuration) * 100}%`,
+ width: `${(Math.abs(this._markerStart - this._markerEnd) / this.props.trimDuration) * 100}%`,
+ }}
+ />
+ );
}
constructor(props: any) {
super(props);
// onClick play scripts
- CollectionStackedTimeline.RangeScript = CollectionStackedTimeline.RangeScript || ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, { self: Doc.name, scriptContext: "any", clientX: "number" })!;
- CollectionStackedTimeline.RangePlayScript = CollectionStackedTimeline.RangePlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(this, clientX)`, { self: Doc.name, scriptContext: "any", clientX: "number" })!;
+ CollectionStackedTimeline.RangeScript =
+ CollectionStackedTimeline.RangeScript ||
+ ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, {
+ self: Doc.name,
+ scriptContext: "any",
+ clientX: "number",
+ })!;
+ CollectionStackedTimeline.RangePlayScript =
+ CollectionStackedTimeline.RangePlayScript ||
+ ScriptField.MakeFunction(`scriptContext.playOnClick(this, clientX)`, {
+ self: Doc.name,
+ scriptContext: "any",
+ clientX: "number",
+ })!;
}
- componentDidMount() { document.addEventListener("keydown", this.keyEvents, true); }
+ componentDidMount() {
+ document.addEventListener("keydown", this.keyEvents, true);
+ }
componentWillUnmount() {
document.removeEventListener("keydown", this.keyEvents, true);
- if (CollectionStackedTimeline.SelectingRegion === this) runInAction(() => CollectionStackedTimeline.SelectingRegion = undefined);
+ if (CollectionStackedTimeline.SelectingRegion === this) {
+ runInAction(
+ () => (CollectionStackedTimeline.SelectingRegion = undefined)
+ );
+ }
}
- anchorStart = (anchor: Doc) => NumCast(anchor._timecodeToShow, NumCast(anchor[this.props.startTag]));
+ anchorStart = (anchor: Doc) =>
+ NumCast(anchor._timecodeToShow, NumCast(anchor[this.props.startTag]))
anchorEnd = (anchor: Doc, val: any = null) => {
const endVal = NumCast(anchor[this.props.endTag], val);
- return NumCast(anchor._timecodeToHide, endVal === undefined ? null : endVal);
+ return NumCast(
+ anchor._timecodeToHide,
+ endVal === undefined ? null : endVal
+ );
}
- toTimeline = (screen_delta: number, width: number) => Math.max(0, Math.min(this.duration, screen_delta / width * this.duration));
+ toTimeline = (screen_delta: number, width: number) => {
+ return Math.max(
+ this.trimStart,
+ Math.min(this.trimEnd, (screen_delta / width) * this.props.trimDuration + this.trimStart));
+ }
+
rangeClickScript = () => CollectionStackedTimeline.RangeScript;
rangePlayScript = () => CollectionStackedTimeline.RangePlayScript;
// for creating key anchors with key events
@action
keyEvents = (e: KeyboardEvent) => {
- if (!(e.target instanceof HTMLInputElement) && this.props.isSelected(true)) {
+ if (
+ !(e.target instanceof HTMLInputElement) &&
+ this.props.isSelected(true)
+ ) {
switch (e.key) {
case " ":
if (!CollectionStackedTimeline.SelectingRegion) {
this._markerStart = this._markerEnd = this.currentTime;
CollectionStackedTimeline.SelectingRegion = this;
} else {
- CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime);
+ CollectionStackedTimeline.createAnchor(
+ this.rootDoc,
+ this.dataDoc,
+ this.props.fieldKey,
+ this.props.startTag,
+ this.props.endTag,
+ this.currentTime
+ );
CollectionStackedTimeline.SelectingRegion = undefined;
}
}
@@ -101,7 +199,10 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument
getLinkData(l: Doc) {
let la1 = l.anchor1 as Doc;
let la2 = l.anchor2 as Doc;
- const linkTime = NumCast(la2[this.props.startTag], NumCast(la1[this.props.startTag]));
+ const linkTime = NumCast(
+ la2[this.props.startTag],
+ NumCast(la1[this.props.startTag])
+ );
if (Doc.AreProtosEqual(la1, this.dataDoc)) {
la1 = l.anchor2 as Doc;
la2 = l.anchor1 as Doc;
@@ -118,10 +219,18 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument
const wasPlaying = this.props.playing();
if (wasPlaying) this.props.Pause();
const wasSelecting = CollectionStackedTimeline.SelectingRegion === this;
- setupMoveUpEvents(this, e,
- action(e => {
- if (!wasSelecting && CollectionStackedTimeline.SelectingRegion !== this) {
- this._markerStart = this._markerEnd = this.toTimeline(clientX - rect.x, rect.width);
+ setupMoveUpEvents(
+ this,
+ e,
+ action((e) => {
+ if (
+ !wasSelecting &&
+ CollectionStackedTimeline.SelectingRegion !== this
+ ) {
+ this._markerStart = this._markerEnd = this.toTimeline(
+ clientX - rect.x,
+ rect.width
+ );
CollectionStackedTimeline.SelectingRegion = this;
}
this._markerEnd = this.toTimeline(e.clientX - rect.x, rect.width);
@@ -134,32 +243,147 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument
this._markerStart = this._markerEnd;
this._markerEnd = tmp;
}
- if (!isClick && CollectionStackedTimeline.SelectingRegion === this && (Math.abs(movement[0]) > 15)) {
- CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag,
- this._markerStart, this._markerEnd);
+ if (
+ !isClick &&
+ CollectionStackedTimeline.SelectingRegion === this &&
+ Math.abs(movement[0]) > 15 &&
+ !this.props.trimming
+ ) {
+ const anchor = CollectionStackedTimeline.createAnchor(
+ this.rootDoc,
+ this.dataDoc,
+ this.props.fieldKey,
+ this.props.startTag,
+ this.props.endTag,
+ this._markerStart,
+ this._markerEnd
+ );
+ setTimeout(() => DocumentManager.Instance.getDocumentView(anchor)?.select(false));
}
- (!isClick || !wasSelecting) && (CollectionStackedTimeline.SelectingRegion = undefined);
+ (!isClick || !wasSelecting) &&
+ (CollectionStackedTimeline.SelectingRegion = undefined);
}),
(e, doubleTap) => {
this.props.select(false);
- e.shiftKey && CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime);
+ e.shiftKey &&
+ CollectionStackedTimeline.createAnchor(
+ this.rootDoc,
+ this.dataDoc,
+ this.props.fieldKey,
+ this.props.startTag,
+ this.props.endTag,
+ this.currentTime
+ );
!wasPlaying && doubleTap && this.props.Play();
},
- this.props.isSelected(true) || this.props.isContentActive(), undefined,
- () => !wasPlaying && this.props.setTime((clientX - rect.x) / rect.width * this.duration));
+ this.props.isSelected(true) || this.props.isContentActive(),
+ undefined,
+ () => {
+ !wasPlaying &&
+ (this.props.trimming && this.duration ?
+ this.props.setTime(((clientX - rect.x) / rect.width) * this.duration)
+ :
+ this.props.setTime(((clientX - rect.x) / rect.width) * this.props.trimDuration + this.trimStart)
+ );
+ }
+ );
}
+
+ }
+
+ @action
+ trimLeft = (e: React.PointerEvent): void => {
+ const rect = this._timeline?.getBoundingClientRect();
+ const clientX = e.movementX;
+ setupMoveUpEvents(
+ this,
+ e,
+ action((e, [], []) => {
+ if (rect && this.props.isContentActive()) {
+ this.props.setStartTrim(Math.min(
+ Math.max(
+ this.trimStart + (e.movementX / rect.width) * this.duration,
+ 0
+ ),
+ this.trimEnd - this.minLength
+ ));
+ }
+ return false;
+ }),
+ emptyFunction,
+ action((e, doubleTap) => {
+ if (doubleTap) {
+ this.props.setStartTrim(0);
+ }
+ })
+ );
+ }
+
+ @action
+ trimRight = (e: React.PointerEvent): void => {
+ const rect = this._timeline?.getBoundingClientRect();
+ const clientX = e.movementX;
+ setupMoveUpEvents(
+ this,
+ e,
+ action((e, [], []) => {
+ if (rect && this.props.isContentActive()) {
+ this.props.setEndTrim(Math.max(
+ Math.min(
+ this.trimEnd + (e.movementX / rect.width) * this.duration,
+ this.duration
+ ),
+ this.trimStart + this.minLength
+ ));
+ }
+ return false;
+ }),
+ emptyFunction,
+ action((e, doubleTap) => {
+ if (doubleTap) {
+ this.props.setEndTrim(this.duration);
+ }
+ })
+ );
+ }
+
+ @action
+ internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number) {
+ if (!de.embedKey && this.props.layerProvider?.(this.props.Document) !== false && this.props.Document._isGroup) return false;
+ if (!super.onInternalDrop(e, de)) return false;
+
+
+ // determine x coordinate of drop and assign it to the documents being dragged --- see internalDocDrop of collectionFreeFormView.tsx for how it's done when dropping onto a 2D freeform view
+
+ return true;
+ }
+
+ onInternalDrop = (e: Event, de: DragManager.DropEvent) => {
+ if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData, 0);
+ return false;
}
@undoBatch
@action
- static createAnchor(rootDoc: Doc, dataDoc: Doc, fieldKey: string, startTag: string, endTag: string, anchorStartTime?: number, anchorEndTime?: number) {
+ static createAnchor(
+ rootDoc: Doc,
+ dataDoc: Doc,
+ fieldKey: string,
+ startTag: string,
+ endTag: string,
+ anchorStartTime?: number,
+ anchorEndTime?: number
+ ) {
if (anchorStartTime === undefined) return rootDoc;
const anchor = Docs.Create.LabelDocument({
- title: ComputedField.MakeFunction(`"#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"])`) as any,
+ title: ComputedField.MakeFunction(
+ `"#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"])`
+ ) as any,
+ _stayInCollection: true,
useLinkSmallAnchor: true,
hideLinkButton: true,
annotationOn: rootDoc,
- _timelineLabel: true
+ _timelineLabel: true,
});
Doc.GetProto(anchor)[startTag] = anchorStartTime;
Doc.GetProto(anchor)[endTag] = anchorEndTime;
@@ -179,7 +403,10 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument
if (this.props.playing()) this.props.Pause();
else this.props.playFrom(seekTimeInSeconds, endTime);
} else {
- if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) && endTime > NumCast(this.layoutDoc._currentTimecode)) {
+ if (
+ seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) &&
+ endTime > NumCast(this.layoutDoc._currentTimecode)
+ ) {
if (!this.layoutDoc.autoPlayAnchors && this.props.playing()) {
this.props.Pause();
} else {
@@ -194,39 +421,60 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument
@action
clickAnchor = (anchorDoc: Doc, clientX: number) => {
- if (anchorDoc.isLinkButton) LinkManager.FollowLink(undefined, anchorDoc, this.props, false);
+ if (anchorDoc.isLinkButton) {
+ LinkManager.FollowLink(undefined, anchorDoc, this.props, false);
+ }
const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25;
const endTime = this.anchorEnd(anchorDoc);
- if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4) {
+ if (
+ seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 &&
+ endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4
+ ) {
if (this.props.playing()) this.props.Pause();
else if (this.layoutDoc.autoPlayAnchors) this.props.Play();
else if (!this.layoutDoc.autoPlayAnchors) {
const rect = this._timeline?.getBoundingClientRect();
- rect && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
+ rect &&
+ this.props.setTime(this.toTimeline(clientX - rect.x, rect.width));
}
} else {
- if (this.layoutDoc.autoPlayAnchors) this.props.playFrom(seekTimeInSeconds, endTime);
- else this.props.setTime(seekTimeInSeconds);
+ if (this.layoutDoc.autoPlayAnchors) {
+ this.props.playFrom(seekTimeInSeconds, endTime);
+ }
+ else {
+ this.props.setTime(seekTimeInSeconds);
+ }
}
return { select: true };
}
-
// makes sure no anchors overlaps each other by setting the correct position and width
- getLevel = (m: Doc, placed: { anchorStartTime: number, anchorEndTime: number, level: number }[]) => {
+ getLevel = (
+ m: Doc,
+ placed: { anchorStartTime: number; anchorEndTime: number; level: number }[]
+ ) => {
const timelineContentWidth = this.props.PanelWidth();
const x1 = this.anchorStart(m);
- const x2 = this.anchorEnd(m, x1 + 10 / timelineContentWidth * this.duration);
+ const x2 = this.anchorEnd(
+ m,
+ x1 + (10 / timelineContentWidth) * this.duration
+ );
let max = 0;
- const overlappedLevels = new Set(placed.map(p => {
- const y1 = p.anchorStartTime;
- const y2 = p.anchorEndTime;
- if ((x1 >= y1 && x1 <= y2) || (x2 >= y1 && x2 <= y2) ||
- (y1 >= x1 && y1 <= x2) || (y2 >= x1 && y2 <= x2)) {
- max = Math.max(max, p.level);
- return p.level;
- }
- }));
+ const overlappedLevels = new Set(
+ placed.map((p) => {
+ const y1 = p.anchorStartTime;
+ const y2 = p.anchorEndTime;
+ if (
+ (x1 >= y1 && x1 <= y2) ||
+ (x2 >= y1 && x2 <= y2) ||
+ (y1 >= x1 && y1 <= x2) ||
+ (y2 >= x1 && y2 <= x2)
+ ) {
+ max = Math.max(max, p.level);
+ return p.level;
+ }
+ })
+ );
let level = max + 1;
for (let j = max; j >= 0; j--) !overlappedLevels.has(j) && (level = j);
@@ -235,82 +483,185 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument
}
dictationHeightPercent = 50;
- dictationHeight = () => this.props.PanelHeight() * (100 - this.dictationHeightPercent) / 100;
- timelineContentHeight = () => this.props.PanelHeight() * this.dictationHeightPercent / 100;
- dictationScreenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.timelineContentHeight());
+ dictationHeight = () =>
+ (this.props.PanelHeight() * (100 - this.dictationHeightPercent)) / 100
+ timelineContentHeight = () =>
+ (this.props.PanelHeight() * this.dictationHeightPercent) / 100
+ dictationScreenToLocalTransform = () =>
+ this.props
+ .ScreenToLocalTransform()
+ .translate(0, -this.timelineContentHeight())
@computed get renderDictation() {
const dictation = Cast(this.dataDoc[this.props.dictationKey], Doc, null);
- return !dictation ? (null) : <div style={{ position: "absolute", height: "100%", top: this.timelineContentHeight(), background: "tan" }}>
- <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "setContentView"]).omit}
- Document={dictation}
- PanelHeight={this.dictationHeight}
- isAnnotationOverlay={true}
- isDocumentActive={returnFalse}
- select={emptyFunction}
- scaling={returnOne}
- xMargin={25}
- yMargin={10}
- ScreenToLocalTransform={this.dictationScreenToLocalTransform}
- whenChildContentsActiveChanged={emptyFunction}
- removeDocument={returnFalse}
- moveDocument={returnFalse}
- addDocument={returnFalse}
- CollectionView={undefined}
- renderDepth={this.props.renderDepth + 1}>
- </DocumentView>
- </div>;
+ return !dictation ? null : (
+ <div
+ style={{
+ position: "absolute",
+ height: "100%",
+ top: this.timelineContentHeight(),
+ background: Colors.LIGHT_BLUE,
+ }}
+ >
+ <DocumentView
+ {...OmitKeys(this.props, [
+ "NativeWidth",
+ "NativeHeight",
+ "setContentView",
+ ]).omit}
+ Document={dictation}
+ PanelHeight={this.dictationHeight}
+ isAnnotationOverlay={true}
+ isDocumentActive={returnFalse}
+ select={emptyFunction}
+ scaling={returnOne}
+ xMargin={25}
+ yMargin={10}
+ ScreenToLocalTransform={this.dictationScreenToLocalTransform}
+ whenChildContentsActiveChanged={emptyFunction}
+ removeDocument={returnFalse}
+ moveDocument={returnFalse}
+ addDocument={returnFalse}
+ CollectionView={undefined}
+ renderDepth={this.props.renderDepth + 1}
+ ></DocumentView>
+ </div>
+ );
}
@computed get renderAudioWaveform() {
- return !this.props.mediaPath ? (null) :
- <div className="collectionStackedTimeline-waveform" >
+ return !this.props.mediaPath ? null : (
+ <div className="collectionStackedTimeline-waveform">
<AudioWaveform
duration={this.duration}
mediaPath={this.props.mediaPath}
- dataDoc={this.dataDoc}
- PanelHeight={this.timelineContentHeight} />
- </div>;
+ layoutDoc={this.layoutDoc}
+ PanelHeight={this.timelineContentHeight}
+ trimming={this.props.trimming}
+ />
+ </div>
+ );
}
+
currentTimecode = () => this.currentTime;
render() {
const timelineContentWidth = this.props.PanelWidth();
- const overlaps: { anchorStartTime: number, anchorEndTime: number, level: number }[] = [];
- const drawAnchors = this.childDocs.map(anchor => ({ level: this.getLevel(anchor, overlaps), anchor }));
+ const overlaps: {
+ anchorStartTime: number;
+ anchorEndTime: number;
+ level: number;
+ }[] = [];
+ const drawAnchors = this.childDocs.map((anchor) => ({
+ level: this.getLevel(anchor, overlaps),
+ anchor,
+ }));
const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2;
- const isActive = this.props.isContentActive() || this.props.isSelected(false);
- return <div className="collectionStackedTimeline" ref={(timeline: HTMLDivElement | null) => this._timeline = timeline}
- onClick={e => isActive && StopEvent(e)} onPointerDown={e => isActive && this.onPointerDownTimeline(e)}>
- {drawAnchors.map(d => {
- const start = this.anchorStart(d.anchor);
- const end = this.anchorEnd(d.anchor, start + 10 / timelineContentWidth * this.duration);
- const left = start / this.duration * timelineContentWidth;
- const top = d.level / maxLevel * this.timelineContentHeight();
- const timespan = end - start;
- return this.props.Document.hideAnchors ? (null) :
- <div className={"collectionStackedTimeline-marker-timeline"} key={d.anchor[Id]}
- style={{ left, top, width: `${timespan / this.duration * timelineContentWidth}px`, height: `${this.timelineContentHeight() / maxLevel}px` }}
- onClick={e => { this.props.playFrom(start, this.anchorEnd(d.anchor)); e.stopPropagation(); }} >
- <StackedTimelineAnchor {...this.props}
- mark={d.anchor}
- rangeClickScript={this.rangeClickScript}
- rangePlayScript={this.rangePlayScript}
- left={left}
- top={top}
- width={timelineContentWidth * timespan / this.duration}
- height={this.timelineContentHeight() / maxLevel}
- toTimeline={this.toTimeline}
- layoutDoc={this.layoutDoc}
- currentTimecode={this.currentTimecode}
- _timeline={this._timeline}
- stackedTimeline={this}
- />
- </div>;
- })}
- {this.selectionContainer}
- {this.renderAudioWaveform}
- {this.renderDictation}
-
- <div className="collectionStackedTimeline-current" style={{ left: `${this.currentTime / this.duration * 100}%` }} />
- </div>;
+ const isActive =
+ this.props.isContentActive() || this.props.isSelected(false);
+ return (<div ref={this.createDashEventsTarget} style={{ pointerEvents: SnappingManager.GetIsDragging() ? "all" : undefined }}>
+ <div
+ className="collectionStackedTimeline"
+ ref={(timeline: HTMLDivElement | null) => (this._timeline = timeline)}
+ onClick={(e) => isActive && StopEvent(e)}
+ onPointerDown={(e) => isActive && this.onPointerDownTimeline(e)}
+ >
+ {drawAnchors.map((d) => {
+
+ const start = this.anchorStart(d.anchor);
+ const end = this.anchorEnd(
+ d.anchor,
+ start + (10 / timelineContentWidth) * this.duration
+ );
+ const left = this.props.trimming ?
+ (start / this.duration) * timelineContentWidth
+ : (start - this.trimStart) / this.props.trimDuration * timelineContentWidth;
+ const top = (d.level / maxLevel) * this.timelineContentHeight();
+ const timespan = end - start;
+ const width = (timespan / this.props.trimDuration) * timelineContentWidth;
+ const height = this.timelineContentHeight() / maxLevel;
+ return this.props.Document.hideAnchors ? null : (
+ <div
+ className={"collectionStackedTimeline-marker-timeline"}
+ key={d.anchor[Id]}
+ style={{
+ left,
+ top,
+ width: `${width}px`,
+ height: `${height}px`,
+ }}
+ onClick={(e) => {
+ this.props.playFrom(start, this.anchorEnd(d.anchor));
+ e.stopPropagation();
+ }}
+ >
+ <StackedTimelineAnchor
+ {...this.props}
+ mark={d.anchor}
+ rangeClickScript={this.rangeClickScript}
+ rangePlayScript={this.rangePlayScript}
+ left={left}
+ top={top}
+ width={width}
+ height={height}
+ toTimeline={this.toTimeline}
+ layoutDoc={this.layoutDoc}
+ currentTimecode={this.currentTimecode}
+ _timeline={this._timeline}
+ stackedTimeline={this}
+ trimStart={this.trimStart}
+ trimEnd={this.trimEnd}
+ />
+ </div>
+ );
+ })}
+ {!this.props.trimming && this.selectionContainer}
+ {this.renderAudioWaveform}
+ {this.renderDictation}
+
+ <div
+ className="collectionStackedTimeline-current"
+ style={{
+ left: this.props.trimming
+ ? `${(this.currentTime / this.duration) * 100}%`
+ : `${(this.currentTime - this.trimStart) / (this.trimEnd - this.trimStart) * 100}%`,
+ }}
+ />
+
+ {this.props.trimming && (
+ <>
+ <div
+ className="collectionStackedTimeline-trim-shade"
+ style={{ width: `${(this.trimStart / this.duration) * 100}%` }}
+ ></div>
+
+ <div
+ className="collectionStackedTimeline-trim-controls"
+ style={{
+ left: `${(this.trimStart / this.duration) * 100}%`,
+ width: `${((this.trimEnd - this.trimStart) / this.duration) * 100
+ }%`,
+ }}
+ >
+ <div
+ className="collectionStackedTimeline-trim-handle"
+ onPointerDown={this.trimLeft}
+ ></div>
+ <div
+ className="collectionStackedTimeline-trim-handle"
+ onPointerDown={this.trimRight}
+ ></div>
+ </div>
+
+ <div
+ className="collectionStackedTimeline-trim-shade"
+ style={{
+ left: `${(this.trimEnd / this.duration) * 100}%`,
+ width: `${((this.duration - this.trimEnd) / this.duration) * 100
+ }%`,
+ }}
+ ></div>
+ </>
+ )}
+ </div>
+ </div>);
}
}
@@ -335,6 +686,8 @@ interface StackedTimelineAnchorProps {
currentTimecode: () => number;
isSelected: (outsideReaction?: boolean) => boolean;
stackedTimeline: CollectionStackedTimeline;
+ trimStart: number;
+ trimEnd: number;
}
@observer
class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> {
@@ -345,22 +698,41 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
this._lastTimecode = this.props.currentTimecode();
}
componentDidMount() {
- this._disposer = reaction(() => this.props.currentTimecode(),
+ this._disposer = reaction(
+ () => this.props.currentTimecode(),
(time) => {
- const dictationDoc = Cast(this.props.layoutDoc["data-dictation"], Doc, null);
- const isDictation = dictationDoc && DocListCast(this.props.mark.links).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc);
- if (!LightboxView.LightboxDoc
+ const dictationDoc = Cast(
+ this.props.layoutDoc["data-dictation"],
+ Doc,
+ null
+ );
+ const isDictation =
+ dictationDoc &&
+ DocListCast(this.props.mark.links).some(
+ (link) =>
+ Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc
+ );
+ if (
+ !LightboxView.LightboxDoc &&
// bcz: when should links be followed? we don't want to move away from the video to follow a link but we can open it in a sidebar/etc. But we don't know that upfront.
// for now, we won't follow any links when the lightbox is oepn to avoid "losing" the video.
/*(isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this.props.layoutDoc))*/
- && DocListCast(this.props.mark.links).length &&
+ DocListCast(this.props.mark.links).length &&
time > NumCast(this.props.mark[this.props.startTag]) &&
time < NumCast(this.props.mark[this.props.endTag]) &&
- this._lastTimecode < NumCast(this.props.mark[this.props.startTag])) {
- LinkManager.FollowLink(undefined, this.props.mark, this.props as any as DocumentViewProps, false, true);
+ this._lastTimecode < NumCast(this.props.mark[this.props.startTag])
+ ) {
+ LinkManager.FollowLink(
+ undefined,
+ this.props.mark,
+ this.props as any as DocumentViewProps,
+ false,
+ true
+ );
}
this._lastTimecode = time;
- });
+ }
+ );
}
componentWillUnmount() {
this._disposer?.();
@@ -373,57 +745,136 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
return this.props.toTimeline(e.clientX - rect.x, rect.width);
};
const changeAnchor = (anchor: Doc, left: boolean, time: number) => {
- const timelineOnly = Cast(anchor[this.props.startTag], "number", null) !== undefined;
- if (timelineOnly) Doc.SetInPlace(anchor, left ? this.props.startTag : this.props.endTag, time, true);
- else left ? anchor._timecodeToShow = time : anchor._timecodeToHide = time;
+ const timelineOnly =
+ Cast(anchor[this.props.startTag], "number", null) !== undefined;
+ if (timelineOnly) {
+ Doc.SetInPlace(
+ anchor,
+ left ? this.props.startTag : this.props.endTag,
+ time,
+ true
+ );
+ }
+ else {
+ left
+ ? (anchor._timecodeToShow = time)
+ : (anchor._timecodeToHide = time);
+ }
return false;
};
- setupMoveUpEvents(this, e,
+ setupMoveUpEvents(
+ this,
+ e,
(e) => changeAnchor(anchor, left, newTime(e)),
(e) => {
this.props.setTime(newTime(e));
this.props._timeline?.releasePointerCapture(e.pointerId);
},
- emptyFunction);
+ emptyFunction
+ );
}
- renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number) {
+
+ @action
+ computeTitle = () => {
+ const start = Math.max(NumCast(this.props.mark[this.props.startTag]), this.props.trimStart) - this.props.trimStart;
+ const end = Math.min(NumCast(this.props.mark[this.props.endTag]), this.props.trimEnd) - this.props.trimStart;
+ return `#${formatTime(start)}-${formatTime(end)}`;
+ }
+
+ renderInner = computedFn(function (
+ this: StackedTimelineAnchor,
+ mark: Doc,
+ script: undefined | (() => ScriptField),
+ doublescript: undefined | (() => ScriptField),
+ x: number,
+ y: number,
+ width: number,
+ height: number
+ ) {
const anchor = observable({ view: undefined as any });
- const focusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, docTransform?: Transform) => {
+ const focusFunc = (
+ doc: Doc,
+ willZoom?: boolean,
+ scale?: number,
+ afterFocus?: DocAfterFocusFunc,
+ docTransform?: Transform
+ ) => {
this.props.playLink(mark);
this.props.focus(doc, { willZoom, scale, afterFocus, docTransform });
};
return {
- anchor, view: <DocumentView key="view" {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
- ref={action((r: DocumentView | null) => anchor.view = r)}
- Document={mark}
- DataDoc={undefined}
- renderDepth={this.props.renderDepth + 1}
- LayoutTemplate={undefined}
- LayoutTemplateString={LabelBox.LayoutString("data")}
- isDocumentActive={returnFalse}
- PanelWidth={() => width}
- PanelHeight={() => height}
- ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-x, -y)}
- focus={focusFunc}
- rootSelected={returnFalse}
- onClick={script}
- onDoubleClick={this.props.layoutDoc.autoPlayAnchors ? undefined : doublescript}
- ignoreAutoHeight={false}
- hideResizeHandles={true}
- bringToFront={emptyFunction}
- scriptContext={this.props.stackedTimeline} />
+ anchor,
+ view: (
+ <DocumentView
+ key="view"
+ {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit}
+ ref={action((r: DocumentView | null) => (anchor.view = r))}
+ Document={mark}
+ DataDoc={undefined}
+ renderDepth={this.props.renderDepth + 1}
+ LayoutTemplate={undefined}
+ LayoutTemplateString={LabelBox.LayoutStringWithTitle(LabelBox, "data", this.computeTitle())}
+ isDocumentActive={returnFalse}
+ PanelWidth={() => width}
+ PanelHeight={() => height}
+ ScreenToLocalTransform={() =>
+ this.props.ScreenToLocalTransform().translate(-x, -y)
+ }
+ focus={focusFunc}
+ rootSelected={returnFalse}
+ onClick={script}
+ onDoubleClick={
+ this.props.layoutDoc.autoPlayAnchors ? undefined : doublescript
+ }
+ ignoreAutoHeight={false}
+ hideResizeHandles={true}
+ bringToFront={emptyFunction}
+ scriptContext={this.props.stackedTimeline}
+ />
+ ),
};
});
+
render() {
- const inner = this.renderInner(this.props.mark, this.props.rangeClickScript, this.props.rangePlayScript, this.props.left, this.props.top, this.props.width, this.props.height);
- return <>
- {inner.view}
- {!inner.anchor.view || !SelectionManager.IsSelected(inner.anchor.view) ? (null) :
- <>
- <div key="left" className="collectionStackedTimeline-left-resizer" onPointerDown={e => this.onAnchorDown(e, this.props.mark, true)} />
- <div key="right" className="collectionStackedTimeline-resizer" onPointerDown={e => this.onAnchorDown(e, this.props.mark, false)} />
- </>}
- </>;
+ const inner = this.renderInner(
+ this.props.mark,
+ this.props.rangeClickScript,
+ this.props.rangePlayScript,
+ this.props.left,
+ this.props.top,
+ this.props.width,
+ this.props.height
+ );
+ return (
+ <>
+ {inner.view}
+ {!inner.anchor.view ||
+ !SelectionManager.IsSelected(inner.anchor.view) ? null : (
+ <>
+ <div
+ key="left"
+ className="collectionStackedTimeline-left-resizer"
+ onPointerDown={(e) => this.onAnchorDown(e, this.props.mark, true)}
+ />
+ <div
+ key="right"
+ className="collectionStackedTimeline-resizer"
+ onPointerDown={(e) =>
+ this.onAnchorDown(e, this.props.mark, false)
+ }
+ />
+ </>
+ )}
+ </>
+ );
}
}
-Scripting.addGlobal(function formatToTime(time: number): any { return formatTime(time); }); \ No newline at end of file
+Scripting.addGlobal(function formatToTime(time: number): any {
+ return formatTime(time);
+});
+Scripting.addGlobal(function min(num1: number, num2: number): number {
+ return Math.min(num1, num2);
+});
+Scripting.addGlobal(function max(num1: number, num2: number): number {
+ return Math.max(num1, num2);
+}); \ No newline at end of file
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 4b123c8b6..2f002736d 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -14,6 +14,30 @@
width: 100%;
}
+// TODO:glr Turn this into a seperate class
+.documentButtonMenu {
+ position: relative;
+ height: fit-content;
+ border-bottom: $standard-border;
+ display: flex;
+ justify-content: center;
+ flex-direction: column;
+ align-items: center;
+ align-content: center;
+ padding: 5px 0 5px 0;
+
+ .documentExplanation {
+ width: 90%;
+ margin: 5px;
+ font-size: 11px;
+ background-color: $light-blue;
+ color: $medium-blue;
+ padding: 10px;
+ border-radius: 10px;
+ border: solid 2px $medium-blue;
+ }
+}
+
.collectionStackingView,
.collectionMasonryView {
height: 100%;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index b9bc83d49..648ff5087 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -3,7 +3,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { CursorProperty } from "csstype";
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
-import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc";
+import { DataSym, Doc, HeightSym, Opt, WidthSym, DocListCast } from "../../../fields/Doc";
import { collectionSchema, documentSchema } from "../../../fields/documentSchemas";
import { Id } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
@@ -11,7 +11,7 @@ import { listSpec, makeInterface } from "../../../fields/Schema";
import { SchemaHeaderField } from "../../../fields/SchemaHeaderField";
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from "../../../fields/Types";
import { TraceMobx } from "../../../fields/util";
-import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils } from "../../../Utils";
+import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, smoothScroll, Utils, returnTrue, returnEmptyDoclist, returnEmptyFilter } from "../../../Utils";
import { DocUtils, Docs } from "../../documents/Documents";
import { DragManager, dropActionType } from "../../util/DragManager";
import { SnappingManager } from "../../util/SnappingManager";
@@ -23,12 +23,14 @@ import { EditableView } from "../EditableView";
import { LightboxView } from "../LightboxView";
import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView";
import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from "../nodes/DocumentView";
-import { StyleProp } from "../StyleProvider";
+import { StyleProp, DefaultStyleProvider } from "../StyleProvider";
import { CollectionMasonryViewFieldRow } from "./CollectionMasonryViewFieldRow";
import "./CollectionStackingView.scss";
import { CollectionStackingViewFieldColumn } from "./CollectionStackingViewFieldColumn";
import { CollectionSubView } from "./CollectionSubView";
import { CollectionViewType } from "./CollectionView";
+import { FontIconBox } from "../nodes/button/FontIconBox";
+import { CurrentUserUtils } from "../../util/CurrentUserUtils";
const _global = (window /* browser */ || global /* node */) as any;
type StackingDocument = makeInterface<[typeof collectionSchema, typeof documentSchema]>;
@@ -144,7 +146,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
() => this.layoutDoc._columnHeaders = new List()
);
this._autoHeightDisposer = reaction(() => this.layoutDoc._autoHeight,
- () => this.props.setHeight(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
+ autoHeight => autoHeight && this.props.setHeight(Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER),
this.headerMargin + (this.isStackingView ?
Math.max(...this.refList.map(r => Number(getComputedStyle(r).height.replace("px", "")))) :
this.refList.reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0)))));
@@ -234,6 +236,7 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
dontCenter={this.props.childIgnoreNativeSize ? "xy" : undefined}
dontRegisterView={dataDoc ? true : BoolCast(this.layoutDoc.childDontRegisterViews, this.props.dontRegisterView)}
rootSelected={this.rootSelected}
+ showTitle={this.props.childShowTitle}
dropAction={StrCast(this.layoutDoc.childDropAction) as dropActionType}
onClick={this.onChildClickHandler}
onDoubleClick={this.onChildDoubleClickHandler}
@@ -514,6 +517,47 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
return sections.map((section, i) => this.isStackingView ? this.sectionStacking(section[0], section[1]) : this.sectionMasonry(section[0], section[1], i === 0));
}
+ @computed get buttonMenu() {
+ const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
+ // TODO:glr Allow support for multiple buttons
+ if (menuDoc) {
+ const width: number = NumCast(menuDoc._width, 30);
+ const height: number = NumCast(menuDoc._height, 30);
+ console.log(menuDoc.title, width, height);
+ return (<div className="buttonMenu-docBtn"
+ style={{ width: width, height: height }}>
+ <DocumentView
+ Document={menuDoc}
+ DataDoc={menuDoc}
+ isContentActive={this.props.isContentActive}
+ isDocumentActive={returnTrue}
+ addDocument={this.props.addDocument}
+ moveDocument={this.props.moveDocument}
+ addDocTab={this.props.addDocTab}
+ pinToPres={emptyFunction}
+ rootSelected={this.props.isSelected}
+ removeDocument={this.props.removeDocument}
+ ScreenToLocalTransform={Transform.Identity}
+ PanelWidth={() => 35}
+ PanelHeight={() => 35}
+ renderDepth={this.props.renderDepth}
+ focus={emptyFunction}
+ styleProvider={this.props.styleProvider}
+ layerProvider={this.props.layerProvider}
+ docViewPath={returnEmptyDoclist}
+ whenChildContentsActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ docFilters={this.props.docFilters}
+ docRangeFilters={this.props.docRangeFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ />
+ </div>
+ );
+ }
+ }
+
@computed get nativeWidth() { return this.props.NativeWidth?.() ?? Doc.NativeWidth(this.layoutDoc); }
@computed get nativeHeight() { return this.props.NativeHeight?.() ?? Doc.NativeHeight(this.layoutDoc); }
@@ -529,34 +573,49 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument,
SetValue: this.addGroup,
contents: "+ ADD A GROUP"
};
+ const buttonMenu = this.rootDoc.buttonMenu;
+ const noviceExplainer = this.rootDoc.explainer;
return (
- <div className="collectionStackingMasonry-cont" >
- <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
- ref={this.createRef}
- style={{
- overflowY: this.props.isContentActive() ? "auto" : "hidden",
- background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
- pointerEvents: this.backgroundEvents ? "all" : undefined
- }}
- onScroll={action(e => this._scroll = e.currentTarget.scrollTop)}
- onDrop={this.onExternalDrop.bind(this)}
- onContextMenu={this.onContextMenu}
- onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} >
- {this.renderedSections}
- {!this.showAddAGroup ? (null) :
- <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
- style={{ width: !this.isStackingView ? "100%" : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
- <EditableView {...editableViewProps} />
- </div>}
- {/* {this.chromeHidden || !this.props.isSelected() ? (null) :
- <Switch
- onChange={this.onToggle}
- onClick={this.onToggle}
- defaultChecked={true}
- checkedChildren="edit"
- unCheckedChildren="view"
- />} */}
- </div> </div>
+ <>
+ {buttonMenu || noviceExplainer ? <div className="documentButtonMenu">
+ {buttonMenu ? this.buttonMenu : null}
+ {Doc.UserDoc().noviceMode && noviceExplainer ?
+ <div className="documentExplanation">
+ {noviceExplainer}
+ </div>
+ : null
+ }
+ </div> : null}
+ <div className="collectionStackingMasonry-cont" >
+ <div className={this.isStackingView ? "collectionStackingView" : "collectionMasonryView"}
+ ref={this.createRef}
+ style={{
+ overflowY: this.props.isContentActive() ? "auto" : "hidden",
+ background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
+ pointerEvents: this.backgroundEvents ? "all" : undefined
+ }}
+ onScroll={action(e => this._scroll = e.currentTarget.scrollTop)}
+ onDrop={this.onExternalDrop.bind(this)}
+ onContextMenu={this.onContextMenu}
+ onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} >
+ {this.renderedSections}
+ {!this.showAddAGroup ? (null) :
+ <div key={`${this.props.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton"
+ style={{ width: !this.isStackingView ? "100%" : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
+ <EditableView {...editableViewProps} />
+ </div>}
+ {/* {this.chromeHidden || !this.props.isSelected() ? (null) :
+ <Switch
+ onChange={this.onToggle}
+ onClick={this.onToggle}
+ defaultChecked={true}
+ checkedChildren="edit"
+ unCheckedChildren="view"
+ />} */}
+ </div>
+ </div>
+ </>
+
);
}
}
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 47733994b..58289a161 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -111,7 +111,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
@action pointerEntered = () => SnappingManager.GetIsDragging() && (this._background = "#b4b4b4");
@action pointerLeave = () => this._background = "inherit";
- textCallback = (char: string) => this.addNewTextDoc("", false, true);
+ textCallback = (char: string) => this.addNewTextDoc("-typed text-", false, true);
@action
addNewTextDoc = (value: string, shiftDown?: boolean, forceEmptyNote?: boolean) => {
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 2f07c0241..cb8b55cb2 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -1,6 +1,6 @@
import { action, computed, IReactionDisposer, reaction, observable, runInAction } from "mobx";
import CursorField from "../../../fields/CursorField";
-import { Doc, Opt, Field, DocListCast, AclPrivate } from "../../../fields/Doc";
+import { Doc, Opt, Field, DocListCast, AclPrivate, StrListCast } from "../../../fields/Doc";
import { Id, ToString } from "../../../fields/FieldSymbols";
import { List } from "../../../fields/List";
import { listSpec } from "../../../fields/Schema";
@@ -23,6 +23,7 @@ import ReactLoading from 'react-loading';
export interface SubCollectionViewProps extends CollectionViewProps {
CollectionView: Opt<CollectionView>;
SetSubView?: (subView: any) => void;
+ isAnyChildContentActive: () => boolean;
}
export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: X) {
@@ -88,12 +89,13 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
get childDocList() {
return Cast(this.dataField, listSpec(Doc));
}
- collectionFilters = () => this._focusFilters ?? Cast(this.props.Document._docFilters, listSpec("string"), []);
+ collectionFilters = () => this._focusFilters ?? StrListCast(this.props.Document._docFilters);
collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec("string"), []);
- childDocFilters = () => [...this.props.docFilters(), ...this.collectionFilters()];
+ childDocFilters = () => [...(this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()];
+ unrecursiveDocFilters = () => [...(this.props.docFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])];
childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()];
IsFiltered = () => this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" :
- this.props.docFilters().length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined
+ this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined
searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs);
@computed.struct get childDocs() {
TraceMobx();
@@ -113,10 +115,10 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const viewSpecScript = Cast(this.props.Document.viewSpecScript, ScriptField);
const childDocs = viewSpecScript ? docs.filter(d => viewSpecScript.script.run({ doc: d }, console.log).result) : docs;
- const docFilters = this.childDocFilters();
+ const childDocFilters = this.childDocFilters();
const docRangeFilters = this.childDocRangeFilters();
const searchDocs = this.searchFilterDocs();
- if (this.props.Document.dontRegisterView || (!docFilters.length && !docRangeFilters.length && !searchDocs.length)) {
+ if (this.props.Document.dontRegisterView || (!childDocFilters.length && !this.unrecursiveDocFilters().length && !docRangeFilters.length && !searchDocs.length)) {
return childDocs.filter(cd => !cd.cookies); // remove any documents that require a cookie if there are no filters to provide one
}
@@ -127,24 +129,27 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const docsforFilter: Doc[] = [];
childDocs.forEach((d) => {
// if (DocUtils.Excluded(d, docFilters)) return;
- let notFiltered = d.z || Doc.IsSystem(d) || ((!searchDocs.length || searchDocs.includes(d)) && (DocUtils.FilterDocs([d], docFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0));
- const fieldKey = Doc.LayoutFieldKey(d);
- const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
- const data = d[annos ? fieldKey + "-annotations" : fieldKey];
- if (data !== undefined) {
- let subDocs = DocListCast(data);
- if (subDocs.length > 0) {
- let newarray: Doc[] = [];
- notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, docFilters, docRangeFilters, viewSpecScript, d).length);
- while (subDocs.length > 0 && !notFiltered) {
- newarray = [];
- subDocs.forEach((t) => {
- const fieldKey = Doc.LayoutFieldKey(t);
- const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView");
- notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!docFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], docFilters, docRangeFilters, viewSpecScript, d).length));
- DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc));
- });
- subDocs = newarray;
+ let notFiltered = d.z || Doc.IsSystem(d) || (DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), docRangeFilters, viewSpecScript, this.props.Document).length > 0);
+ if (notFiltered) {
+ notFiltered = ((!searchDocs.length || searchDocs.includes(d)) && (DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0));
+ const fieldKey = Doc.LayoutFieldKey(d);
+ const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView");
+ const data = d[annos ? fieldKey + "-annotations" : fieldKey];
+ if (data !== undefined) {
+ let subDocs = DocListCast(data);
+ if (subDocs.length > 0) {
+ let newarray: Doc[] = [];
+ notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, childDocFilters, docRangeFilters, viewSpecScript, d).length);
+ while (subDocs.length > 0 && !notFiltered) {
+ newarray = [];
+ subDocs.forEach((t) => {
+ const fieldKey = Doc.LayoutFieldKey(t);
+ const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView");
+ notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!childDocFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], childDocFilters, docRangeFilters, viewSpecScript, d).length));
+ DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc));
+ });
+ subDocs = newarray;
+ }
}
}
}
@@ -211,7 +216,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
const targetDocments = DocListCast(this.dataDoc[this.props.fieldKey]);
const someMoved = !docDragData.userDropAction && docDragData.draggedDocuments.some(drag => targetDocments.includes(drag));
if (someMoved) docDragData.droppedDocuments = docDragData.droppedDocuments.map((drop, i) => targetDocments.includes(docDragData.draggedDocuments[i]) ? docDragData.draggedDocuments[i] : drop);
- if ((!dropAction || dropAction === "move" || someMoved) && docDragData.moveDocument) {
+ if ((!dropAction || dropAction === "same" || dropAction === "move" || someMoved) && docDragData.moveDocument) {
const movedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] === d);
const addedDocs = docDragData.droppedDocuments.filter((d, i) => docDragData.draggedDocuments[i] !== d);
if (movedDocs.length) {
@@ -316,12 +321,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?:
}
});
} else {
- let srcUrl: string | undefined;
- let srcWeb: Doc | undefined;
- if (SelectionManager.Views().length) {
- srcWeb = SelectionManager.Views()[0].props.Document;
- srcUrl = (srcWeb.data as WebField).url?.href?.match(/http[s]?:\/\/[^/]*/)?.[0];
- }
+ const srcWeb = SelectionManager.Docs().lastElement();
+ const srcUrl = (srcWeb?.data as WebField).url?.href?.match(/http[s]?:\/\/[^/]*/)?.[0];
const reg = new RegExp(Utils.prepend(""), "g");
const modHtml = srcUrl ? html.replace(reg, srcUrl) : html;
const htmlDoc = Docs.Create.HtmlDocument(modHtml, { ...options, title: "-web page-", _width: 300, _height: 300 });
@@ -486,7 +487,5 @@ import { FormattedTextBox, GoogleRef } from "../nodes/formattedText/FormattedTex
import { CollectionView, CollectionViewType, CollectionViewProps } from "./CollectionView";
import { SelectionManager } from "../../util/SelectionManager";
import { OverlayView } from "../OverlayView";
-import { Hypothesis } from "../../util/HypothesisUtils";
import { GetEffectiveAcl, TraceMobx } from "../../../fields/util";
-import { FilterBox } from "../nodes/FilterBox";
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index ec461ab94..d370d21ab 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -1,5 +1,8 @@
@import "../global/globalCssVariables";
+.collectionTreeView-container {
+ transform-origin: top left;
+}
.collectionTreeView-dropTarget {
border-width: $COLLECTION_BORDER_WIDTH;
border-color: transparent;
@@ -35,6 +38,11 @@
width: max-content;
}
+ .no-indent-outline {
+ padding-left: 0;
+ width: 100%;
+ }
+
.editableView-container {
font-weight: bold;
}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index a78034dca..3852987b9 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -1,15 +1,16 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, reaction, IReactionDisposer, observable } from "mobx";
+import { action, computed, IReactionDisposer, observable, reaction } from "mobx";
import { observer } from "mobx-react";
-import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym, StrListCast } from '../../../fields/Doc';
+import { DataSym, Doc, DocListCast, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
-import { List } from '../../../fields/List';
+import { InkTool } from '../../../fields/InkField';
import { Document, listSpec } from '../../../fields/Schema';
import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../../Utils';
+import { returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, emptyFunction } from '../../../Utils';
import { DocUtils } from '../../documents/Documents';
+import { CurrentUserUtils } from '../../util/CurrentUserUtils';
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager, dropActionType } from "../../util/DragManager";
import { SelectionManager } from '../../util/SelectionManager';
@@ -25,9 +26,7 @@ import { CollectionSubView } from "./CollectionSubView";
import "./CollectionTreeView.scss";
import { TreeView } from "./TreeView";
import React = require("react");
-import { InkTool } from '../../../fields/InkField';
-import { CurrentUserUtils } from '../../util/CurrentUserUtils';
-import { CollectionView, CollectionViewType } from './CollectionView';
+import { Transform } from '../../util/Transform';
const _global = (window /* browser */ || global /* node */) as any;
export type collectionTreeViewProps = {
@@ -63,7 +62,9 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
this.props.isSelected(outsideReaction) || this._isAnyChildContentActive ||
this.props.rootSelected(outsideReaction)) ? true : false)
+ isDisposing = false;
componentWillUnmount() {
+ this.isDisposing = true;
super.componentWillUnmount();
this.treedropDisposer?.();
Object.values(this._disposers).forEach(disposer => disposer?.());
@@ -78,15 +79,16 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
refList: Set<any> = new Set();
observer: any;
computeHeight = () => {
- const hgt = this.paddingTop() + 26/* bcz: ugh: title bar height hack ... get ref and compute instead */ +
- Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0);
- this.props.setHeight(hgt);
+ if (this.isDisposing) return;
+ const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), this.paddingTop() + this.paddingBot());
+ this.layoutDoc._autoHeightMargins = bodyHeight;
+ this.props.setHeight(this.documentTitleHeight() + bodyHeight);
}
unobserveHeight = (ref: any) => {
this.refList.delete(ref);
this.rootDoc.autoHeight && this.computeHeight();
}
- observerHeight = (ref: any) => {
+ observeHeight = (ref: any) => {
if (ref) {
this.refList.add(ref);
this.observer = new _global.ResizeObserver(action((entries: any) => {
@@ -106,7 +108,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
const dragData = de.complete.docDragData;
if (dragData) {
- const isInTree = () => dragData.draggedDocuments.some(d => d.context === this.doc && this.childDocs.includes(d));
+ const isInTree = () => Doc.AreProtosEqual(dragData.treeViewDoc, this.props.Document) || dragData.draggedDocuments.some(d => d.context === this.doc && this.childDocs.includes(d));
dragData.dropAction = targetAction && !isInTree() ? targetAction : this.doc === dragData?.treeViewDoc ? "same" : dragData.dropAction;
}
}
@@ -201,6 +203,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
NativeWidth={this.documentTitleWidth}
NativeHeight={this.documentTitleHeight}
focus={this.props.focus}
+ treeViewDoc={this.props.Document}
ScreenToLocalTransform={this.titleTransform}
docFilters={returnEmptyFilter}
docRangeFilters={returnEmptyFilter}
@@ -219,8 +222,9 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
}
childContextMenuItems = () => {
const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
- const filterScripts = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
- return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: filterScripts[i], label }));
+ const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
+ const icons = StrListCast(this.doc.childContextMenuIcons);
+ return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
}
@computed get treeViewElements() {
TraceMobx();
@@ -253,50 +257,111 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll
true,
this.whenChildContentsActiveChanged,
this.props.dontRegisterView || Cast(this.props.Document.childDontRegisterViews, "boolean", null),
- this.observerHeight,
+ this.observeHeight,
this.unobserveHeight,
this.childContextMenuItems()
);
}
@computed get titleBar() {
const hideTitle = this.props.treeViewHideTitle || this.doc.treeViewHideTitle;
- return hideTitle ? (null) : (this.doc.treeViewType === "outline" ? this.documentTitle : this.editableTitle)(this.treeChildren);
+ return hideTitle ? (null) : (this.outlineMode ? this.documentTitle : this.editableTitle)(this.treeChildren);
}
- @computed get renderClearButton() {
- return !this.doc.treeViewShowClearButton ? (null) : <div key="toolbar">
- <button className="toolbar-button round-button" title="Empty" onClick={undoBatch(action(() => Doc.GetProto(this.doc)[this.props.fieldKey] = undefined))}>
- <FontAwesomeIcon icon={"trash"} size="sm" />
- </button>
- </div >;
+ return35 = () => 35;
+ @computed get buttonMenu() {
+ const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null);
+ // To create a multibutton menu add a CollectionLinearView
+ if (menuDoc) {
+
+ const width: number = NumCast(menuDoc._width, 30);
+ const height: number = NumCast(menuDoc._height, 30);
+ console.log(menuDoc.title, width, height);
+ return (<div className="buttonMenu-docBtn"
+ style={{ width: width, height: height }}>
+ <DocumentView
+ Document={menuDoc}
+ DataDoc={menuDoc}
+ isContentActive={this.props.isContentActive}
+ isDocumentActive={returnTrue}
+ addDocument={this.props.addDocument}
+ moveDocument={this.props.moveDocument}
+ addDocTab={this.props.addDocTab}
+ pinToPres={emptyFunction}
+ rootSelected={this.props.isSelected}
+ removeDocument={this.props.removeDocument}
+ ScreenToLocalTransform={Transform.Identity}
+ PanelWidth={this.return35}
+ PanelHeight={this.return35}
+ renderDepth={this.props.renderDepth + 1}
+ focus={emptyFunction}
+ styleProvider={this.props.styleProvider}
+ layerProvider={this.props.layerProvider}
+ docViewPath={returnEmptyDoclist}
+ whenChildContentsActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ docFilters={this.props.docFilters}
+ docRangeFilters={this.props.docRangeFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined}
+ />
+ </div>);
+ }
}
+ @observable _explainerHeight: number = 0;
+
+ @computed get nativeWidth() { return Doc.NativeWidth(this.Document, undefined, true); }
+ @computed get nativeHeight() { return Doc.NativeHeight(this.Document, undefined, true); }
+
+ @computed get contentScaling() {
+ const nw = this.nativeWidth;
+ const nh = this.nativeHeight;
+ const hscale = nh ? this.props.PanelHeight() / nh : 1;
+ const wscale = nw ? this.props.PanelWidth() / nw : 1;
+ return wscale < hscale ? wscale : hscale;
+ }
paddingX = () => NumCast(this.doc._xPadding, 15);
paddingTop = () => NumCast(this.doc._yPadding, 20);
+ paddingBot = () => NumCast(this.doc._yPadding, 20);
documentTitleWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.panelWidth());
- documentTitleHeight = () => Math.min(this.layoutDoc?.[HeightSym](), (StrCast(this.layoutDoc?._fontSize) ? Number(StrCast(this.layoutDoc?._fontSize, "32px").replace("px", "")) : NumCast(this.layoutDoc?._fontSize)) * 2);
+ documentTitleHeight = () => (this.layoutDoc?.[HeightSym]() || 0) - NumCast(this.layoutDoc.autoHeightMargins);
titleTransform = () => this.props.ScreenToLocalTransform().translate(-NumCast(this.doc._xPadding, 10), -NumCast(this.doc._yPadding, 20));
truncateTitleWidth = () => this.treeViewtruncateTitleWidth;
onChildClick = () => this.props.onChildClick?.() || ScriptCast(this.doc.onChildClick);
- panelWidth = () => this.props.PanelWidth() - 2 * this.paddingX();
+ panelWidth = () => (this.props.PanelWidth() - 2 * this.paddingX()) * (this.props.scaling?.() || 1);
render() {
TraceMobx();
const background = () => this.props.styleProvider?.(this.doc, this.props, StyleProp.BackgroundColor);
const pointerEvents = () => !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined;
+ const buttonMenu = this.rootDoc.buttonMenu;
+ const noviceExplainer = this.rootDoc.explainer;
return !(this.doc instanceof Doc) || !this.treeChildren ? (null) :
- <div className="collectionTreeView-container" onContextMenu={this.onContextMenu}>
- <div className="collectionTreeView-dropTarget"
- style={{ background: background(), paddingLeft: `${this.paddingX()}px`, paddingRight: `${this.paddingX()}px`, paddingTop: `${this.paddingTop()}px`, pointerEvents: pointerEvents() }}
- onWheel={e => e.stopPropagation()}
- onDrop={this.onTreeDrop}
- ref={this.createTreeDropTarget}>
- {this.titleBar}
- {this.renderClearButton}
- <ul className="no-indent">
- {this.treeViewElements}
- </ul>
- </div >
- </div>;
+ <>
+ {this.titleBar}
+ <div className="collectionTreeView-container"
+ style={this.outlineMode ? { transform: `scale(${this.contentScaling})`, width: `calc(${100 / this.contentScaling}%)` } : {}}
+ onContextMenu={this.onContextMenu}>
+ {buttonMenu || noviceExplainer ? <div className="documentButtonMenu" ref={action((r: HTMLDivElement) => r && (this._explainerHeight = r.getBoundingClientRect().height))}>
+ {buttonMenu ? this.buttonMenu : null}
+ {Doc.UserDoc().noviceMode && noviceExplainer ?
+ <div className="documentExplanation">
+ {noviceExplainer}
+ </div>
+ : null
+ }
+ </div> : null}
+ <div className="collectionTreeView-dropTarget"
+ style={{ background: background(), height: `calc(100% - ${this._explainerHeight}px)`, paddingLeft: `${this.paddingX()}px`, paddingRight: `${this.paddingX()}px`, paddingBottom: `${this.paddingBot()}px`, paddingTop: `${this.paddingTop()}px`, pointerEvents: pointerEvents() }}
+ onWheel={e => e.stopPropagation()}
+ onDrop={this.onTreeDrop}
+ ref={this.createTreeDropTarget}>
+ <ul className={`no-indent${this.outlineMode ? "-outline" : ""}`} >
+ {this.treeViewElements}
+ </ul>
+ </div >
+ </div>
+ </>;
}
} \ No newline at end of file
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 2485c0f6a..0dd1e6e36 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -24,7 +24,7 @@ import { CollectionCarouselView } from './CollectionCarouselView';
import { CollectionDockingView } from "./CollectionDockingView";
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import { CollectionGridView } from './collectionGrid/CollectionGridView';
-import { CollectionLinearView } from './CollectionLinearView';
+import { CollectionLinearView } from './collectionLinear';
import CollectionMapView from './MapView/CollectionMapView';
import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
@@ -36,6 +36,8 @@ import { CollectionTimeView } from './CollectionTimeView';
import { CollectionTreeView } from "./CollectionTreeView";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import './CollectionView.scss';
+import { returnEmptyString } from '../../../Utils';
+import { InkTool } from '../../../fields/InkField';
export const COLLECTION_BORDER_WIDTH = 2;
const path = require('path');
@@ -62,13 +64,14 @@ export enum CollectionViewType {
export interface CollectionViewProps extends FieldViewProps {
isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
layoutEngine?: () => string;
- setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
+ setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
// property overrides for child documents
children?: never | (() => JSX.Element[]) | React.ReactNode;
childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox)
childDocumentsActive?: () => boolean;// whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode)
childFitWidth?: () => boolean;
+ childShowTitle?: () => string;
childOpacity?: () => number;
childContextMenuItems?: () => { script: ScriptField, label: string }[];
childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children
@@ -84,7 +87,7 @@ export interface CollectionViewProps extends FieldViewProps {
type CollectionDocument = makeInterface<[typeof documentSchema]>;
const CollectionDocument = makeInterface(documentSchema);
@observer
-export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps, CollectionDocument>(CollectionDocument, "") {
+export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & CollectionViewProps, CollectionDocument>(CollectionDocument) {
public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); }
@observable private static _safeMode = false;
@@ -92,6 +95,11 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer;
+ constructor(props: any) {
+ super(props);
+ runInAction(() => this._annotationKeySuffix = returnEmptyString);
+ }
+
get collectionViewType(): CollectionViewType | undefined {
const viewField = StrCast(this.layoutDoc._viewType);
if (CollectionView._safeMode) {
@@ -113,7 +121,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
// const imageProtos = children.filter(doc => Cast(doc.data, ImageField)).map(Doc.GetProto);
// const allTagged = imageProtos.length > 0 && imageProtos.every(image => image.googlePhotosTags);
// return !allTagged ? (null) : <img id={"google-tags"} src={"/assets/google_tags.png"} />;
- this.isContentActive();
+ //this.isContentActive();
}
screenToLocalTransform = () => this.props.renderDepth ? this.props.ScreenToLocalTransform() : this.props.ScreenToLocalTransform().scale(this.props.PanelWidth() / this.bodyPanelWidth());
@@ -158,7 +166,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
!Doc.UserDoc().noviceMode && subItems.push({ description: "Map", event: () => func(CollectionViewType.Map), icon: "globe-americas" });
subItems.push({ description: "Grid", event: () => func(CollectionViewType.Grid), icon: "th-list" });
- if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.annotationOn) {
+ if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) {
const existingVm = ContextMenu.Instance.findByDescription(category);
const catItems = existingVm && "subitems" in existingVm ? existingVm.subitems : [];
catItems.push({ description: "Add a Perspective...", addDivider: true, noexpand: true, subitems: subItems, icon: "eye" });
@@ -247,6 +255,9 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
@observable _subView: any = undefined;
+ isContentActive = (outsideReaction?: boolean) => {
+ return this.props.isContentActive() ? true : false;
+ }
render() {
TraceMobx();
const props: SubCollectionViewProps = {
@@ -256,6 +267,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<ViewBoxAnnotatab
moveDocument: this.moveDocument,
removeDocument: this.removeDocument,
isContentActive: this.isContentActive,
+ isAnyChildContentActive: this.isAnyChildContentActive,
+ whenChildContentsActiveChanged: this.whenChildContentsActiveChanged,
PanelWidth: this.bodyPanelWidth,
PanelHeight: this.props.PanelHeight,
ScreenToLocalTransform: this.screenToLocalTransform,
diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss
index dddccd18c..0d045bada 100644
--- a/src/client/views/collections/TabDocView.scss
+++ b/src/client/views/collections/TabDocView.scss
@@ -1,3 +1,6 @@
+@import "../global/globalCssVariables.scss";
+
+
input.lm_title:focus,
input.lm_title {
max-width: unset !important;
@@ -57,12 +60,11 @@ input.lm_title {
}
}
-.miniMap-hidden,
.miniMap {
position: absolute;
overflow: hidden;
- right: 10;
- bottom: 10;
+ right: 15;
+ bottom: 15;
border: solid 1px;
width: 100%;
height: 100%;
@@ -81,17 +83,21 @@ input.lm_title {
}
.miniMap-hidden {
+ cursor: pointer;
position: absolute;
- bottom: 0;
- right: 0;
- width: 45px;
- height: 45px;
- transform: translate(20px, 20px) rotate(45deg);
- border-radius: 30px;
+ bottom: 5;
+ display: flex;
+ right: 5;
+ width: 25px;
+ height: 25px;
+ border-radius: 3px;
padding: 2px;
+ justify-content: center;
+ align-items: center;
+ align-content: center;
+ background-color: $light-gray;
- > svg {
- margin-top: 3px;
- transform: translate(0px, 7px);
+ &:hover {
+ box-shadow: none;
}
}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index e6e71a3c4..73c065482 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -11,7 +11,6 @@ import { DataSym, Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym }
import { Id } from '../../../fields/FieldSymbols';
import { FieldId } from "../../../fields/RefField";
import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types";
-import { TraceMobx } from '../../../fields/util';
import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils";
import { DocServer } from "../../DocServer";
import { DocUtils } from '../../documents/Documents';
@@ -34,6 +33,7 @@ import { CollectionView, CollectionViewType } from './CollectionView';
import "./TabDocView.scss";
import React = require("react");
import Color = require('color');
+import { Colors, Shadows } from '../global/globalEnums';
const _global = (window /* browser */ || global /* node */) as any;
interface TabDocViewProps {
@@ -124,6 +124,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
tab.element[0].prepend(iconWrap);
tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
({ layer, color }) => {
+ // console.log("TabDocView: " + this.tabColor);
+ // console.log("lightOrDark: " + lightOrDark(this.tabColor));
const textColor = lightOrDark(this.tabColor); //not working with StyleProp.Color
titleEle.style.color = textColor;
titleEle.style.backgroundColor = "transparent";
@@ -132,12 +134,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
moreInfoDrag.style.backgroundColor = textColor;
tab.element[0].style.background = !layer ? color : "dimgrey";
}, { fireImmediately: true });
- // TODO:glr fix
- // tab.element[0].style.borderTopRightRadius = "8px";
- // tab.element[0].children[1].appendChild(toggle);
- // tab._disposers.layerDisposer = reaction(() =>
- // ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }),
- // ({ layer, color }) => toggle.style.background = !layer ? color : "dimgrey", { fireImmediately: true });
}
// shifts the focus to this tab when another tab is dragged over it
tab.element[0].onmouseenter = (e: MouseEvent) => {
@@ -313,6 +309,14 @@ export class TabDocView extends React.Component<TabDocViewProps> {
return CollectionDockingView.AddSplit(doc, locationParams, this.stack);
}
}
+ remDocTab = (doc: Doc | Doc[]) => {
+ if (doc === this._document) {
+ SelectionManager.DeselectAll();
+ CollectionDockingView.CloseSplit(this._document);
+ return true;
+ }
+ return false;
+ }
getCurrentFrame = () => {
return NumCast(Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null)._currentFrame);
@@ -349,7 +353,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
@computed get layerProvider() { return this._document && DefaultLayerProvider(this._document); }
@computed get docView() {
- TraceMobx();
return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) :
<><DocumentView key={this._document[Id]} ref={action((r: DocumentView) => this._view = r)}
renderDepth={0}
@@ -366,7 +369,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters}
searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs}
addDocument={undefined}
- removeDocument={undefined}
+ removeDocument={this.remDocTab}
addDocTab={this.addDocTab}
ScreenToLocalTransform={this.ScreenToLocalTransform}
dontCenter={"y"}
@@ -384,8 +387,16 @@ export class TabDocView extends React.Component<TabDocViewProps> {
background={this.miniMapColor}
document={this._document}
tabView={this.tabView} />
- <Tooltip style={{ display: this.disableMinimap() ? "none" : undefined }} key="ttip" title={<div className="dash-tooltip">{"toggle minimap"}</div>}>
- <div className="miniMap-hidden" onPointerDown={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} >
+ <Tooltip key="ttip" title={<div className="dash-tooltip">{this._document.hideMinimap ? "Open minimap" : "Close minimap"}</div>}>
+ <div className="miniMap-hidden"
+ style={{
+ display: this.disableMinimap() || this._document._viewType !== "freeform" ? "none" : undefined,
+ color: this._document.hideMinimap ? Colors.BLACK : Colors.WHITE,
+ backgroundColor: this._document.hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE,
+ boxShadow: this._document.hideMinimap ? Shadows.STANDARD_SHADOW : undefined
+ }}
+ onPointerDown={e => e.stopPropagation()}
+ onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} >
<FontAwesomeIcon icon={"globe-asia"} size="lg" />
</div>
</Tooltip>
@@ -393,7 +404,6 @@ export class TabDocView extends React.Component<TabDocViewProps> {
}
render() {
- this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab);
return (
<div className="collectionDockingView-content" style={{
fontFamily: Doc.UserDoc().renderStyle === "comic" ? "Comic Sans MS" : undefined,
@@ -475,7 +485,8 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
childLayoutTemplate={this.childLayoutTemplate} // bcz: Ugh .. should probably be rendering a CollectionView or the minimap should be part of the collectionFreeFormView to avoid having to set stuff like this.
noOverlay={true} // don't render overlay Docs since they won't scale
setHeight={returnFalse}
- isContentActive={returnTrue}
+ isContentActive={returnFalse}
+ isAnyChildContentActive={returnFalse}
select={emptyFunction}
dropAction={undefined}
isSelected={returnFalse}
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 3ee9dbf59..97de097e0 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -9,7 +9,7 @@ import { listSpec } from '../../../fields/Schema';
import { ComputedField, ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { TraceMobx } from '../../../fields/util';
-import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, simulateMouseClick, Utils } from '../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, simulateMouseClick, Utils, returnOne } from '../../../Utils';
import { Docs, DocUtils } from '../../documents/Documents';
import { DocumentType } from "../../documents/DocumentTypes";
import { CurrentUserUtils } from '../../util/CurrentUserUtils';
@@ -54,7 +54,7 @@ export interface TreeViewProps {
indentDocument?: (editTitle: boolean) => void;
outdentDocument?: (editTitle: boolean) => void;
ScreenToLocalTransform: () => Transform;
- contextMenuItems: { script: ScriptField, filter: ScriptField, label: string }[];
+ contextMenuItems: { script: ScriptField, filter: ScriptField, icon: string, label: string }[];
dontRegisterView?: boolean;
styleProvider?: StyleProviderFunc | undefined;
treeViewHideHeaderFields: () => boolean;
@@ -154,8 +154,8 @@ export class TreeView extends React.Component<TreeViewProps> {
} else {
// choose an appropriate alias or make one. --- choose the first alias that (1) user owns, (2) has no context field ... otherwise make a new alias
// this.props.addDocTab(CurrentUserUtils.ActiveDashboard.isShared ? Doc.MakeAlias(this.props.document) : this.props.document, "add:right");
- // choose an appropriate alias or make one -- -- choose the first alias that (1) the user owns, (2) has no context field - if I own it and someone else does not have it open,, otherwise create an alias
- this.props.addDocTab(this.props.document, "add:right");
+ const bestAlias = DocListCast(this.props.document.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail);
+ this.props.addDocTab(bestAlias ?? Doc.MakeAlias(this.props.document), "add:right");
}
}
@@ -240,10 +240,13 @@ export class TreeView extends React.Component<TreeViewProps> {
}
makeFolder = () => {
- const folder = Docs.Create.TreeDocument([], { title: "-folder-", _stayInCollection: true, isFolder: true });
+ const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true });
TreeView._editTitleOnLoad = { id: folder[Id], parent: this.props.parentTreeView };
return this.props.addDocument(folder);
}
+ deleteFolder = () => {
+ return this.props.removeDoc?.(this.doc);
+ }
preTreeDrop = (e: Event, de: DragManager.DropEvent, targetAction: dropActionType) => {
const dragData = de.complete.docDragData;
@@ -296,7 +299,7 @@ export class TreeView extends React.Component<TreeViewProps> {
const aspect = Doc.NativeAspect(layoutDoc);
if (layoutDoc._fitWidth) return Math.min(this.props.panelWidth() - treeBulletWidth(), layoutDoc[WidthSym]());
if (aspect) return Math.min(layoutDoc[WidthSym](), Math.min(this.MAX_EMBED_HEIGHT * aspect, this.props.panelWidth() - treeBulletWidth()));
- return Math.min(this.props.panelWidth() - treeBulletWidth(), Doc.NativeWidth(layoutDoc) ? layoutDoc[WidthSym]() : this.layoutDoc[WidthSym]());
+ return Math.min((this.props.panelWidth() - treeBulletWidth()) / (this.props.treeView.props.scaling?.() || 1), Doc.NativeWidth(layoutDoc) ? layoutDoc[WidthSym]() : this.layoutDoc[WidthSym]());
}
docHeight = () => {
const layoutDoc = this.layoutDoc;
@@ -365,7 +368,7 @@ export class TreeView extends React.Component<TreeViewProps> {
return rows;
}
- rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), this.props.panelWidth() - treeBulletWidth());
+ rtfWidth = () => Math.min(this.layoutDoc?.[WidthSym](), (this.props.panelWidth() - treeBulletWidth())) / (this.props.treeView.props.scaling?.() || 1);
rtfHeight = () => this.rtfWidth() <= this.layoutDoc?.[WidthSym]() ? Math.min(this.layoutDoc?.[HeightSym](), this.MAX_EMBED_HEIGHT) : this.MAX_EMBED_HEIGHT;
rtfOutlineHeight = () => Math.max(this.layoutDoc?.[HeightSym](), treeBulletWidth());
expandPanelHeight = () => {
@@ -512,10 +515,12 @@ export class TreeView extends React.Component<TreeViewProps> {
DocumentViewInternal.SelectAfterContextMenu = true;
}
contextMenuItems = () => {
- const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, label: "New Folder" };
- const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" };
- const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" };
- return [...this.props.contextMenuItems.filter(mi => !mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result), ... (this.doc.isFolder ? [makeFolder] :
+ const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, icon: "folder-plus", label: "New Folder" };
+ const deleteFolder = { script: ScriptField.MakeFunction(`scriptContext.deleteFolder()`, { scriptContext: "any" })!, icon: "folder-plus", label: "Delete Folder" };
+ const folderOp = this.childDocs?.length ? makeFolder : deleteFolder;
+ const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, icon: "copy", label: "Open Alias" };
+ const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: "eye", label: "Focus or Open" };
+ return [...this.props.contextMenuItems.filter(mi => !mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result), ... (this.doc.isFolder ? [folderOp] :
Doc.IsSystem(this.doc) ? [] :
this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ?
[openAlias, makeFolder] :
@@ -525,7 +530,8 @@ export class TreeView extends React.Component<TreeViewProps> {
childContextMenuItems = () => {
const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []);
const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []);
- return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], label }));
+ const icons = StrListCast(this.doc.childContextMenuIcons);
+ return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], icon: icons[i], label }));
}
onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick));
@@ -544,7 +550,7 @@ export class TreeView extends React.Component<TreeViewProps> {
switch (property.split(":")[0]) {
case StyleProp.Opacity: return this.props.treeView.outlineMode ? undefined : 1;
case StyleProp.BackgroundColor: return this.selected ? "#7089bb" : StrCast(doc._backgroundColor, StrCast(doc.backgroundColor));
- case StyleProp.DocContents: return testDocProps(props) && !props?.treeViewDoc ? (null) :
+ case StyleProp.DocContents: return this.props.treeView.outlineMode ? (null) :
<div className="treeView-label" style={{ // just render a title for a tree view label (identified by treeViewDoc being set in 'props')
maxWidth: props?.PanelWidth() || undefined,
background: props?.styleProvider?.(doc, props, StyleProp.BackgroundColor),
@@ -646,6 +652,7 @@ export class TreeView extends React.Component<TreeViewProps> {
searchFilterDocs={returnEmptyDoclist}
ContainingCollectionView={undefined}
ContainingCollectionDoc={this.props.treeView.props.Document}
+ ContentScaling={returnOne}
/>;
const buttons = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Decorations + (Doc.IsSystem(this.props.containerCollection) ? ":afterHeader" : ""));
@@ -702,10 +709,12 @@ export class TreeView extends React.Component<TreeViewProps> {
hideDecorationTitle={this.props.treeView.outlineMode}
hideResizeHandles={this.props.treeView.outlineMode}
focus={this.refocus}
+ ContentScaling={returnOne}
hideLinkButton={BoolCast(this.props.treeView.props.Document.childHideLinkButton)}
dontRegisterView={BoolCast(this.props.treeView.props.Document.childDontRegisterViews, this.props.dontRegisterView)}
ScreenToLocalTransform={this.docTransform}
renderDepth={this.props.renderDepth + 1}
+ treeViewDoc={this.props.treeView?.props.Document}
rootSelected={returnTrue}
layerProvider={returnTrue}
docViewPath={this.props.treeView.props.docViewPath}
@@ -832,7 +841,7 @@ export class TreeView extends React.Component<TreeViewProps> {
dontRegisterView: boolean | undefined,
observerHeight: (ref: any) => void,
unobserveHeight: (ref: any) => void,
- contextMenuItems: ({ script: ScriptField, filter: ScriptField, label: string }[])
+ contextMenuItems: ({ script: ScriptField, filter: ScriptField, label: string, icon: string }[])
) {
const viewSpecScript = Cast(conainerCollection.viewSpecScript, ScriptField);
if (viewSpecScript) {
@@ -849,6 +858,7 @@ export class TreeView extends React.Component<TreeViewProps> {
}
const dentDoc = (editTitle: boolean, newParent: Doc, addAfter: Doc | undefined, parent: TreeView | CollectionTreeView | undefined) => {
+ if (parent instanceof TreeView && parent.props.treeView.fileSysMode && !newParent.isFolder) return;
const fieldKey = Doc.LayoutFieldKey(newParent);
if (remove && fieldKey && Cast(newParent[fieldKey], listSpec(Doc)) !== undefined) {
remove(child);
@@ -865,7 +875,7 @@ export class TreeView extends React.Component<TreeViewProps> {
const childLayout = Doc.Layout(pair.layout);
const rowHeight = () => {
const aspect = Doc.NativeAspect(childLayout);
- return aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]();
+ return (aspect ? Math.min(childLayout[WidthSym](), rowWidth()) / aspect : childLayout[HeightSym]());
};
return <TreeView key={child[Id]} ref={r => treeViewRefs.set(child, r ? r : undefined)}
document={pair.layout}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index a8f5e6dd2..3b3e069d8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -2,14 +2,16 @@ import { action, computed, IReactionDisposer, observable, reaction } from "mobx"
import { observer } from "mobx-react";
import { Doc } from "../../../../fields/Doc";
import { Id } from "../../../../fields/FieldSymbols";
+import { List } from "../../../../fields/List";
import { NumCast, StrCast } from "../../../../fields/Types";
import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../Utils';
-import { DocumentType } from "../../../documents/DocumentTypes";
+import { LinkManager } from "../../../util/LinkManager";
import { SnappingManager } from "../../../util/SnappingManager";
import { DocumentView } from "../../nodes/DocumentView";
import "./CollectionFreeFormLinkView.scss";
import React = require("react");
+
export interface CollectionFreeFormLinkViewProps {
A: DocumentView;
B: DocumentView;
@@ -40,8 +42,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
if (SnappingManager.GetIsDragging() || !A.ContentDiv || !B.ContentDiv) return;
setTimeout(action(() => this._opacity = 1), 0); // since the render code depends on querying the Dom through getBoudndingClientRect, we need to delay triggering render()
setTimeout(action(() => (!LinkDocs.length || !linkDoc.linkDisplay) && (this._opacity = 0.05)), 750); // this will unhighlight the link line.
- const acont = A.rootDoc.type === DocumentType.LINK ? A.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : [];
- const bcont = B.rootDoc.type === DocumentType.LINK ? B.ContentDiv.getElementsByClassName("linkAnchorBox-cont") : [];
+ const acont = A.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
+ const bcont = B.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
const adiv = acont.length ? acont[0] : A.ContentDiv;
const bdiv = bcont.length ? bcont[0] : B.ContentDiv;
const a = adiv.getBoundingClientRect();
@@ -66,8 +68,10 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
} else {
const m = targetAhyperlink.getBoundingClientRect();
const mp = A.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
- linkDoc.anchor1_x = Math.min(1, mp[0] / A.props.PanelWidth()) * 100;
- linkDoc.anchor1_y = Math.min(1, mp[1] / A.props.PanelHeight()) * 100;
+ const mpx = mp[0] / A.props.PanelWidth();
+ const mpy = mp[1] / A.props.PanelHeight();
+ if (mpx >= 0 && mpx <= 1) linkDoc.anchor1_x = mpx * 100;
+ if (mpy >= 0 && mpy <= 1) linkDoc.anchor1_y = mpy * 100;
}
if (!targetBhyperlink) {
if (linkDoc.linkAutoMove) {
@@ -77,8 +81,10 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
} else {
const m = targetBhyperlink.getBoundingClientRect();
const mp = B.props.ScreenToLocalTransform().transformPoint(m.right, m.top + 5);
- linkDoc.anchor2_x = Math.min(1, mp[0] / B.props.PanelWidth()) * 100;
- linkDoc.anchor2_y = Math.min(1, mp[1] / B.props.PanelHeight()) * 100;
+ const mpx = mp[0] / B.props.PanelWidth();
+ const mpy = mp[1] / B.props.PanelHeight();
+ if (mpx >= 0 && mpx <= 1) linkDoc.anchor2_x = mpx * 100;
+ if (mpy >= 0 && mpy <= 1) linkDoc.anchor2_y = mpy * 100;
}
}
@@ -137,8 +143,8 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
if (!A.ContentDiv || !B.ContentDiv || !LinkDocs.length) return undefined;
const acont = A.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
const bcont = B.ContentDiv.getElementsByClassName("linkAnchorBox-cont");
- const adiv = (acont.length ? acont[0] : A.ContentDiv);
- const bdiv = (bcont.length ? bcont[0] : B.ContentDiv);
+ const adiv = acont.length ? acont[0] : A.ContentDiv;
+ const bdiv = bcont.length ? bcont[0] : B.ContentDiv;
for (let apdiv = adiv; apdiv; apdiv = apdiv.parentElement as any) if ((apdiv as any).hidden) return;
for (let bpdiv = bdiv; bpdiv; bpdiv = bpdiv.parentElement as any) if ((bpdiv as any).hidden) return;
const a = adiv.getBoundingClientRect();
@@ -173,8 +179,14 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
render() {
if (!this.renderData) return (null);
const { a, b, pt1norm, pt2norm, aActive, bActive, textX, textY, pt1, pt2 } = this.renderData;
+ LinkManager.currentLink = this.props.LinkDocs[0];
+ const linkRelationship = StrCast(LinkManager.currentLink?.linkRelationship); //get string representing relationship
+ const linkRelationshipList = Doc.UserDoc().linkRelationshipList as List<string>;
+ const linkColorList = Doc.UserDoc().linkColorList as List<string>;
+ //access stroke color using index of the relationship in the color list (default black)
+ const strokeColor = linkRelationshipList.indexOf(linkRelationship) === -1 ? "black" : linkColorList[linkRelationshipList.indexOf(linkRelationship)];
return !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<>
- <path className="collectionfreeformlinkview-linkLine" style={{ opacity: this._opacity, strokeDasharray: "2 2" }}
+ <path className="collectionfreeformlinkview-linkLine" style={{ opacity: this._opacity, strokeDasharray: "2 2", stroke: strokeColor }}
d={`M ${pt1[0]} ${pt1[1]} C ${pt1[0] + pt1norm[0]} ${pt1[1] + pt1norm[1]}, ${pt2[0] + pt2norm[0]} ${pt2[1] + pt2norm[1]}, ${pt2[0]} ${pt2[1]}`} />
{textX === undefined ? (null) : <text className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown} >
{StrCast(this.props.LinkDocs[0].description)}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
index 5e0b31754..dacbb3508 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.tsx
@@ -1,29 +1,17 @@
import { computed } from "mobx";
import { observer } from "mobx-react";
-import { Doc } from "../../../../fields/Doc";
import { Id } from "../../../../fields/FieldSymbols";
-import { Utils } from "../../../../Utils";
import { DocumentManager } from "../../../util/DocumentManager";
-import { DocumentView } from "../../nodes/DocumentView";
import "./CollectionFreeFormLinksView.scss";
import { CollectionFreeFormLinkView } from "./CollectionFreeFormLinkView";
import React = require("react");
-import { DocumentType } from "../../../documents/DocumentTypes";
@observer
export class CollectionFreeFormLinksView extends React.Component {
@computed get uniqueConnections() {
- const connections = DocumentManager.Instance.LinkedDocumentViews
- .filter(c => c.a.props.Document.type === DocumentType.LINK || c.b.props.Document.type === DocumentType.LINK)
- .reduce((drawnPairs, connection) => {
- const matchingPairs = drawnPairs.filter(pair => connection.a === pair.a && connection.b === pair.b);
- matchingPairs.forEach(drawnPair => drawnPair.l.add(connection.l));
- if (!matchingPairs.length) drawnPairs.push({ a: connection.a, b: connection.b, l: new Set<Doc>([connection.l]) });
- return drawnPairs;
- }, [] as { a: DocumentView, b: DocumentView, l: Set<Doc> }[]);
- const set = new Map<Doc, { a: DocumentView, b: DocumentView, l: Doc[] }>();
- connections.map(c => !set.has(Array.from(c.l)[0]) && set.set(Array.from(c.l)[0], { a: c.a, b: c.b, l: Array.from(c.l) }));
- return Array.from(set.values()).map(c => <CollectionFreeFormLinkView key={c.l[0][Id]} A={c.a} B={c.b} LinkDocs={c.l} />);
+ return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews)).map(c =>
+ <CollectionFreeFormLinkView key={c.l[Id]} A={c.a} B={c.b} LinkDocs={[c.l]} />
+ );
}
render() {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index d09d9b9d7..be0b078ec 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1,6 +1,7 @@
import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx";
import { observer } from "mobx-react";
import { computedFn } from "mobx-utils";
+import { DateField } from "../../../../fields/DateField";
import { Doc, HeightSym, Opt, StrListCast, WidthSym } from "../../../../fields/Doc";
import { collectionSchema, documentSchema } from "../../../../fields/documentSchemas";
import { Id } from "../../../../fields/FieldSymbols";
@@ -17,6 +18,7 @@ import { aggregateBounds, emptyFunction, intersectRect, returnFalse, setupMoveUp
import { CognitiveServices } from "../../../cognitive_services/CognitiveServices";
import { DocServer } from "../../../DocServer";
import { Docs, DocUtils } from "../../../documents/Documents";
+import { DocumentType } from "../../../documents/DocumentTypes";
import { CurrentUserUtils } from "../../../util/CurrentUserUtils";
import { DocumentManager } from "../../../util/DocumentManager";
import { DragManager, dropActionType } from "../../../util/DragManager";
@@ -48,8 +50,7 @@ import { CollectionFreeFormRemoteCursors } from "./CollectionFreeFormRemoteCurso
import "./CollectionFreeFormView.scss";
import { MarqueeView } from "./MarqueeView";
import React = require("react");
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { DateField } from "../../../../fields/DateField";
+import Color = require("color");
export const panZoomSchema = createSchema({
_panX: "number",
@@ -74,6 +75,8 @@ export type collectionFreeformViewProps = {
scaleField?: string;
noOverlay?: boolean; // used to suppress docs in the overlay (z) layer (ie, for minimap since overlay doesn't scale)
engineProps?: any;
+ dontRenderDocuments?: boolean; // used for annotation overlays which need to distribute documents into different freeformviews with different mixBlendModes depending on whether they are trnasparent or not.
+ // However, this screws up interactions since only the top layer gets events. so we render the freeformview a 3rd time with all documents in order to get interaction events (eg., marquee) but we don't actually want to display the documents.
};
@observer
@@ -115,7 +118,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
@computed get views() { return this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z).map(ele => ele.ele); }
@computed get backgroundEvents() { return this.props.layerProvider?.(this.layoutDoc) === false && SnappingManager.GetIsDragging(); }
- @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && (this.props.ContainingCollectionView?.isContentActive() || this.props.isContentActive()); }
+ @computed get backgroundActive() { return this.props.layerProvider?.(this.layoutDoc) === false && this.props.isContentActive(); }
@computed get fitToContentVals() {
return {
bounds: { ...this.contentBounds, cx: (this.contentBounds.x + this.contentBounds.r) / 2, cy: (this.contentBounds.y + this.contentBounds.b) / 2 },
@@ -168,6 +171,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
getContainerTransform = () => this.cachedGetContainerTransform.copy();
getTransformOverlay = () => this.getContainerTransform().translate(1, 1);
getActiveDocuments = () => this.childLayoutPairs.filter(pair => this.isCurrent(pair.layout)).map(pair => pair.layout);
+ isAnyChildContentActive = () => this.props.isAnyChildContentActive();
addLiveTextBox = (newBox: Doc) => {
FormattedTextBox.SelectOnLoad = newBox[Id];// track the new text box so we can give it a prop that tells it to focus itself when it's displayed
this.addDocument(newBox);
@@ -179,7 +183,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
addDocument = (newBox: Doc | Doc[]) => {
let retVal = false;
if (newBox instanceof Doc) {
- if (retVal = this.props.addDocument?.(newBox) || false) {
+ if (retVal = (this.props.addDocument?.(newBox) || false)) {
this.bringToFront(newBox);
this.updateCluster(newBox);
}
@@ -205,25 +209,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
return retVal;
}
- updateGroupBounds = () => {
- if (!this.props.Document._isGroup) return;
- const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() }));
- const cbounds = aggregateBounds(clist, 0, 0);
- const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2];
- const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)];
- const pbounds = {
- x: (cbounds.x - p[0]) * this.zoomScaling() + c[0], y: (cbounds.y - p[1]) * this.zoomScaling() + c[1],
- r: (cbounds.r - p[0]) * this.zoomScaling() + c[0], b: (cbounds.b - p[1]) * this.zoomScaling() + c[1]
- };
-
- this.layoutDoc._width = (pbounds.r - pbounds.x);
- this.layoutDoc._height = (pbounds.b - pbounds.y);
- this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2;
- this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2;
- this.layoutDoc.x = pbounds.x;
- this.layoutDoc.y = pbounds.y;
- }
-
isCurrent(doc: Doc) {
const dispTime = NumCast(doc._timecodeToShow, -1);
const endTime = NumCast(doc._timecodeToHide, dispTime + 1.5);
@@ -265,8 +250,6 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
!StrListCast(d._layerTags).includes(StyleLayers.Background) && (d._raiseWhenDragged === undefined ? Doc.UserDoc()._raiseWhenDragged : d._raiseWhenDragged) && (d.zIndex = zsorted.length + 1 + i); // bringToFront
}
- this.updateGroupBounds();
-
(docDragData.droppedDocuments.length === 1 || de.shiftKey) && this.updateClusterDocs(docDragData.droppedDocuments);
return true;
}
@@ -506,7 +489,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
const points = ge.points;
const B = this.getTransform().transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
const inkDoc = Docs.Create.InkDocument(ActiveInkColor(), CurrentUserUtils.SelectedTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), points,
- { title: "ink stroke", x: B.x - Number(ActiveInkWidth()) / 2, y: B.y - Number(ActiveInkWidth()) / 2, _width: B.width + Number(ActiveInkWidth()), _height: B.height + Number(ActiveInkWidth()) });
+ { title: "ink stroke", x: B.x - ActiveInkWidth() / 2, y: B.y - ActiveInkWidth() / 2, _width: B.width + ActiveInkWidth(), _height: B.height + ActiveInkWidth() });
this.addDocument(inkDoc);
e.stopPropagation();
break;
@@ -1031,7 +1014,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
docFilters={this.childDocFilters}
docRangeFilters={this.childDocRangeFilters}
searchFilterDocs={this.searchFilterDocs}
- isContentActive={this.isAnnotationOverlay ? this.props.isContentActive : returnFalse}
+ isContentActive={returnFalse}
isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
focus={this.focusDocument}
addDocTab={this.addDocTab}
@@ -1048,7 +1031,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
freezeDimensions={this.props.childFreezeDimensions}
dropAction={StrCast(this.props.Document.childDropAction) as dropActionType}
bringToFront={this.bringToFront}
- dontRegisterView={this.props.dontRegisterView}
+ showTitle={this.props.childShowTitle}
+ dontRegisterView={this.props.dontRenderDocuments || this.props.dontRegisterView}
pointerEvents={this.backgroundActive || this.props.childPointerEvents ? "all" :
(this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : undefined}
jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0}
@@ -1231,6 +1215,30 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
{ fireImmediately: true, name: "doLayout" });
this._marqueeRef.current?.addEventListener("dashDragAutoScroll", this.onDragAutoScroll as any);
+
+ this._disposers.groupBounds = reaction(() => {
+ if (this.props.Document._isGroup && this.childDocs.length === this.childDocList?.length) {
+ const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: cd[WidthSym](), height: cd[HeightSym]() }));
+ return aggregateBounds(clist, NumCast(this.layoutDoc._xPadding), NumCast(this.layoutDoc._yPadding));
+ }
+ return undefined;
+ },
+ (cbounds) => {
+ if (cbounds) {
+ const c = [NumCast(this.layoutDoc.x) + this.layoutDoc[WidthSym]() / 2, NumCast(this.layoutDoc.y) + this.layoutDoc[HeightSym]() / 2];
+ const p = [NumCast(this.layoutDoc._panX), NumCast(this.layoutDoc._panY)];
+ const pbounds = {
+ x: (cbounds.x - p[0]) * this.zoomScaling() + c[0], y: (cbounds.y - p[1]) * this.zoomScaling() + c[1],
+ r: (cbounds.r - p[0]) * this.zoomScaling() + c[0], b: (cbounds.b - p[1]) * this.zoomScaling() + c[1]
+ };
+ this.layoutDoc._width = (pbounds.r - pbounds.x);
+ this.layoutDoc._height = (pbounds.b - pbounds.y);
+ this.layoutDoc._panX = (cbounds.r + cbounds.x) / 2;
+ this.layoutDoc._panY = (cbounds.b + cbounds.y) / 2;
+ this.layoutDoc.x = pbounds.x;
+ this.layoutDoc.y = pbounds.y;
+ }
+ }, { fireImmediately: true });
}
componentWillUnmount() {
@@ -1322,8 +1330,10 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
!options && ContextMenu.Instance.addItem({ description: "Options...", subitems: optionItems, icon: "eye" });
const mores = ContextMenu.Instance.findByDescription("More...");
const moreItems = mores && "subitems" in mores ? mores.subitems : [];
- moreItems.push({ description: "Export collection", icon: "download", event: async () => Doc.Zip(this.props.Document) });
- moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(x, y) });
+ if (!Doc.UserDoc().noviceMode) {
+ moreItems.push({ description: "Export collection", icon: "download", event: async () => Doc.Zip(this.props.Document) });
+ moreItems.push({ description: "Import exported collection", icon: "upload", event: ({ x, y }) => this.importDocument(x, y) });
+ }
!mores && ContextMenu.Instance.addItem({ description: "More...", subitems: moreItems, icon: "eye" });
}
@@ -1395,6 +1405,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
}
chooseGridSpace = (gridSpace: number): number => {
+ if (!this.zoomScaling()) return 50;
const divisions = this.props.PanelWidth() / this.zoomScaling() / gridSpace + 3;
return divisions < 60 ? gridSpace : this.chooseGridSpace(gridSpace * 10);
}
@@ -1452,8 +1463,8 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
getContainerTransform={this.getContainerTransform}
getTransform={this.getTransform}
isAnnotationOverlay={this.isAnnotationOverlay}>
- <div ref={this._marqueeRef}>
- {this.layoutDoc["_backgroundGrid-show"] ? this.backgroundGrid : (null)}
+ <div ref={this._marqueeRef} style={{ display: this.props.dontRenderDocuments ? "none" : undefined }}>
+ {this.layoutDoc._backgroundGridShow ? this.backgroundGrid : (null)}
<CollectionFreeFormViewPannableContents
isAnnotationOverlay={this.isAnnotationOverlay}
transform={this.contentTransform}
@@ -1479,6 +1490,14 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
return wscale < hscale ? wscale : hscale;
}
+ private groupDropDisposer?: DragManager.DragDropDisposer;
+ protected createGroupEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+ this.groupDropDisposer?.();
+ if (ele) {
+ this.groupDropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc, this.onInternalPreDrop.bind(this));
+ }
+ }
+
render() {
TraceMobx();
const clientRect = this._mainCont?.getBoundingClientRect();
@@ -1522,7 +1541,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P
</div>}
{this.props.Document._isGroup && SnappingManager.GetIsDragging() && (this.ChildDrag || this.props.layerProvider?.(this.props.Document) === false) ?
- <div className="collectionFreeForm-groupDropper" ref={this.createDashEventsTarget} style={{
+ <div className="collectionFreeForm-groupDropper" ref={this.createGroupEventsTarget} style={{
width: this.ChildDrag ? "10000" : "100%",
height: this.ChildDrag ? "10000" : "100%",
left: this.ChildDrag ? "-5000" : 0,
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index d0243850f..81f6307d1 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -41,7 +41,7 @@ interface MarqueeViewProps {
trySelectCluster: (addToSel: boolean) => boolean;
nudge?: (x: number, y: number, nudgeTime?: number) => boolean;
ungroup?: () => void;
- setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void;
+ setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
}
@observer
export class MarqueeView extends React.Component<SubCollectionViewProps & MarqueeViewProps>
@@ -211,7 +211,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
// allow marquee if right click OR alt+left click
if (e.button === 2 || (e.button === 0 && e.altKey)) {
// if (e.altKey || (MarqueeView.DragMarquee && this.props.active(true))) {
- this.setPreviewCursor(e.clientX, e.clientY, true);
+ this.setPreviewCursor(e.clientX, e.clientY, true, false);
// (!e.altKey) && e.stopPropagation(); // bcz: removed so that you can alt-click on button in a collection to switch link following behaviors.
e.preventDefault();
// }
@@ -284,8 +284,13 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
else if (document.getSelection()) { document.getSelection()?.empty(); }
}
- setPreviewCursor = action((x: number, y: number, drag: boolean) => {
- if (drag) {
+ setPreviewCursor = action((x: number, y: number, drag: boolean, hide: boolean) => {
+ if (hide) {
+ this._downX = this._lastX = x;
+ this._downY = this._lastY = y;
+ this._commandExecuted = false;
+ PreviewCursor.Visible = false;
+ } else if (drag) {
this._downX = this._lastX = x;
this._downY = this._lastY = y;
this._commandExecuted = false;
@@ -313,7 +318,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
if (!(e.nativeEvent as any).marqueeHit) {
(e.nativeEvent as any).marqueeHit = true;
if (!this.props.trySelectCluster(e.shiftKey)) {
- this.setPreviewCursor(e.clientX, e.clientY, false);
+ this.setPreviewCursor(e.clientX, e.clientY, false, false);
} else e.stopPropagation();
}
}
@@ -443,8 +448,8 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque
syntaxHighlight = (e: KeyboardEvent | React.PointerEvent | undefined) => {
const selected = this.marqueeSelect(false);
if (e instanceof KeyboardEvent ? e.key === "i" : true) {
- const inks = selected.filter(s => s.proto?.type === DocumentType.INK);
- const setDocs = selected.filter(s => s.proto?.type === DocumentType.RTF && s.color);
+ const inks = selected.filter(s => s.type === DocumentType.INK);
+ const setDocs = selected.filter(s => s.type === DocumentType.RTF && s.color);
const sets = setDocs.map((sd) => Cast(sd.data, RichTextField)?.Text as string);
const colors = setDocs.map(sd => FieldValue(sd.color) as string);
const wordToColor = new Map<string, string>();
diff --git a/src/client/views/collections/collectionFreeForm/index.ts b/src/client/views/collections/collectionFreeForm/index.ts
new file mode 100644
index 000000000..702dc8d42
--- /dev/null
+++ b/src/client/views/collections/collectionFreeForm/index.ts
@@ -0,0 +1,7 @@
+export * from "./CollectionFreeFormLayoutEngines";
+export * from "./CollectionFreeFormLinkView";
+export * from "./CollectionFreeFormLinksView";
+export * from "./CollectionFreeFormRemoteCursors";
+export * from "./CollectionFreeFormView";
+export * from "./MarqueeOptionsMenu";
+export * from "./MarqueeView"; \ No newline at end of file
diff --git a/src/client/views/collections/collectionGrid/index.ts b/src/client/views/collections/collectionGrid/index.ts
new file mode 100644
index 000000000..be5d5667a
--- /dev/null
+++ b/src/client/views/collections/collectionGrid/index.ts
@@ -0,0 +1,2 @@
+export * from "./Grid";
+export * from "./CollectionGridView"; \ No newline at end of file
diff --git a/src/client/views/collections/CollectionLinearView.scss b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
index 46e40489b..8fe804466 100644
--- a/src/client/views/collections/CollectionLinearView.scss
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
@@ -1,15 +1,31 @@
-@import "../global/globalCssVariables";
-@import "../_nodeModuleOverrides";
+@import "../../global/globalCssVariables";
+@import "../../_nodeModuleOverrides";
.collectionLinearView-outer {
overflow: visible;
height: 100%;
pointer-events: none;
+ &.true {
+ padding-left: 5px;
+ padding-right: 5px;
+ border-left: $standard-border;
+ background-color: $medium-blue-alt;
+ }
+
+ >input:not(:checked)~&.true {
+ background-color: transparent;
+ }
+
.collectionLinearView {
display: flex;
height: 100%;
align-items: center;
+ gap: 5px;
+
+ .collectionView {
+ overflow: visible !important;
+ }
>span {
background: $dark-gray;
@@ -41,7 +57,7 @@
}
.bottomPopup-descriptions {
- cursor:pointer;
+ cursor: pointer;
display: inline;
white-space: nowrap;
padding-left: 8px;
@@ -54,7 +70,7 @@
}
.bottomPopup-exit {
- cursor:pointer;
+ cursor: pointer;
display: inline;
white-space: nowrap;
margin-right: 10px;
@@ -67,29 +83,26 @@
}
>label {
- margin-top: "auto";
- margin-bottom: "auto";
- background: $dark-gray;
- color: $white;
- display: inline-block;
- border-radius: 18px;
- font-size: 12.5px;
- width: 18px;
- height: 18px;
- margin-top: auto;
- margin-bottom: auto;
- margin-right: 3px;
+ pointer-events: all;
cursor: pointer;
+ background-color: $medium-blue;
+ padding: 5;
+ border-radius: 2px;
+ height: 25;
+ min-width: 25;
+ margin: 0;
+ color: $white;
+ display: flex;
+ font-weight: 100;
+ width: fit-content;
transition: transform 0.2s;
- }
-
- label p {
- padding-left: 5px;
- }
+ align-items: center;
+ justify-content: center;
+ transition: 0.2s;
- label:hover {
- background: $medium-gray;
- transform: scale(1.15);
+ &:hover{
+ filter: brightness(0.85);
+ }
}
>input {
@@ -110,13 +123,11 @@
display: flex;
opacity: 1;
position: relative;
- margin-top: auto;
.collectionLinearView-docBtn,
.collectionLinearView-docBtn-scalable {
position: relative;
margin: auto;
- margin-left: 3px;
transform-origin: center 80%;
}
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
new file mode 100644
index 000000000..7fe95fef0
--- /dev/null
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -0,0 +1,226 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@material-ui/core';
+import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { Doc, HeightSym, Opt, WidthSym } from '../../../../fields/Doc';
+import { documentSchema } from '../../../../fields/documentSchemas';
+import { Id } from '../../../../fields/FieldSymbols';
+import { makeInterface } from '../../../../fields/Schema';
+import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
+import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
+import { DragManager } from '../../../util/DragManager';
+import { Transform } from '../../../util/Transform';
+import { Colors, Shadows } from '../../global/globalEnums';
+import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
+import { DocumentView } from '../../nodes/DocumentView';
+import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
+import { StyleProp } from '../../StyleProvider';
+import { CollectionSubView } from '../CollectionSubView';
+import { CollectionViewType } from '../CollectionView';
+import "./CollectionLinearView.scss";
+
+
+type LinearDocument = makeInterface<[typeof documentSchema,]>;
+const LinearDocument = makeInterface(documentSchema);
+
+@observer
+export class CollectionLinearView extends CollectionSubView(LinearDocument) {
+ @observable public addMenuToggle = React.createRef<HTMLInputElement>();
+ @observable private _selectedIndex = -1;
+ private _dropDisposer?: DragManager.DragDropDisposer;
+ private _widthDisposer?: IReactionDisposer;
+ private _selectedDisposer?: IReactionDisposer;
+
+ componentWillUnmount() {
+ this._dropDisposer?.();
+ this._widthDisposer?.();
+ this._selectedDisposer?.();
+ this.childLayoutPairs.map((pair, ind) => ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log));
+ }
+
+ componentDidMount() {
+ this._widthDisposer = reaction(() => 5 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * (this.rootDoc[HeightSym]()) : 10),
+ width => this.childDocs.length && (this.layoutDoc._width = width),
+ { fireImmediately: true }
+ );
+
+ this._selectedDisposer = reaction(
+ () => NumCast(this.layoutDoc.selectedIndex),
+ (i) => runInAction(() => {
+ this._selectedIndex = i;
+ let selected: any = undefined;
+ this.childLayoutPairs.map(async (pair, ind) => {
+ const isSelected = this._selectedIndex === ind;
+ if (isSelected) {
+ selected = pair;
+ }
+ else {
+ ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log);
+ }
+ });
+ if (selected && selected.layout) {
+ ScriptCast(selected.layout.proto?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log);
+ }
+ }),
+ { fireImmediately: true }
+ );
+ }
+ protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view
+ this._dropDisposer && this._dropDisposer();
+ if (ele) {
+ this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
+ }
+ }
+
+ dimension = () => NumCast(this.rootDoc._height); // 2 * the padding
+ getTransform = (ele: Opt<HTMLDivElement>) => {
+ if (!ele) return Transform.Identity();
+ const { scale, translateX, translateY } = Utils.GetScreenTransform(ele);
+ return new Transform(-translateX, -translateY, 1);
+ }
+
+ @action
+ exitLongLinks = () => {
+ if (DocumentLinksButton.StartLink) {
+ if (DocumentLinksButton.StartLink.Document) {
+ action((e: React.PointerEvent<HTMLDivElement>) => {
+ Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc);
+ });
+ }
+ }
+ DocumentLinksButton.StartLink = undefined;
+ DocumentLinksButton.StartLinkView = undefined;
+ }
+
+ @action
+ changeDescriptionSetting = () => {
+ if (LinkDescriptionPopup.showDescriptions) {
+ if (LinkDescriptionPopup.showDescriptions === "ON") {
+ LinkDescriptionPopup.showDescriptions = "OFF";
+ LinkDescriptionPopup.descriptionPopup = false;
+ } else {
+ LinkDescriptionPopup.showDescriptions = "ON";
+ }
+ } else {
+ LinkDescriptionPopup.showDescriptions = "OFF";
+ LinkDescriptionPopup.descriptionPopup = false;
+ }
+ }
+
+ myContextMenu = (e: React.MouseEvent) => {
+ console.log("STOPPING");
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+
+ getDisplayDoc = (doc: Doc) => {
+ const nested = doc._viewType === CollectionViewType.Linear;
+ const hidden = doc.hidden === true;
+
+ let dref: Opt<HTMLDivElement>;
+ const docXf = () => this.getTransform(dref);
+ // const scalable = pair.layout.onClick || pair.layout.onDragStart;
+ return hidden ? (null) : <div className={`collectionLinearView-docBtn`} key={doc[Id]} ref={r => dref = r || undefined}
+ style={{
+ pointerEvents: "all",
+ width: nested ? undefined : NumCast(doc._width),
+ height: nested ? undefined : NumCast(doc._height),
+ marginLeft: !nested ? 2.5 : 0,
+ marginRight: !nested ? 2.5 : 0,
+ // width: NumCast(pair.layout._width),
+ // height: NumCast(pair.layout._height),
+ }} >
+ <DocumentView
+ Document={doc}
+ isContentActive={this.props.isContentActive}
+ isDocumentActive={returnTrue}
+ addDocument={this.props.addDocument}
+ moveDocument={this.props.moveDocument}
+ addDocTab={this.props.addDocTab}
+ pinToPres={emptyFunction}
+ rootSelected={this.props.isSelected}
+ removeDocument={this.props.removeDocument}
+ ScreenToLocalTransform={docXf}
+ PanelWidth={nested ? doc[WidthSym] : this.dimension}
+ PanelHeight={nested ? doc[HeightSym] : this.dimension}
+ renderDepth={this.props.renderDepth + 1}
+ focus={emptyFunction}
+ styleProvider={this.props.styleProvider}
+ layerProvider={this.props.layerProvider}
+ docViewPath={returnEmptyDoclist}
+ whenChildContentsActiveChanged={emptyFunction}
+ bringToFront={emptyFunction}
+ docFilters={this.props.docFilters}
+ docRangeFilters={this.props.docRangeFilters}
+ searchFilterDocs={this.props.searchFilterDocs}
+ ContainingCollectionView={undefined}
+ ContainingCollectionDoc={undefined} />
+ </div>;
+ }
+
+ render() {
+ const guid = Utils.GenerateGuid(); // Generate a unique ID to use as the label
+ const flexDir: any = StrCast(this.Document.flexDirection); // Specify direction of linear view content
+ const flexGap: number = NumCast(this.Document.flexGap); // Specify the gap between linear view content
+ const expandable: boolean = BoolCast(this.props.Document.linearViewExpandable); // Specify whether it is expandable or not
+ const floating: boolean = BoolCast(this.props.Document.linearViewFloating); // Specify whether it is expandable or not
+
+ const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor);
+ const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color);
+ const icon: string = StrCast(this.props.Document.icon); // Menu opener toggle
+ const menuOpener = <label htmlFor={`${guid}`}
+ style={{
+ boxShadow: floating ? Shadows.STANDARD_SHADOW : undefined,
+ color: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.BLACK,
+ backgroundColor: backgroundColor === color ? "black" : BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.LIGHT_GRAY
+ }}
+ onPointerDown={e => e.stopPropagation()} >
+ <div className="collectionLinearView-menuOpener">
+ {BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : <FontAwesomeIcon icon={"minus"} /> : icon ? icon : <FontAwesomeIcon icon={"plus"} />}
+ </div>
+ </label>;
+
+ return <div className={`collectionLinearView-outer ${this.layoutDoc.linearViewSubMenu}`} style={{ backgroundColor: BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : "transparent" }}>
+ <div className="collectionLinearView" ref={this.createDashEventsTarget}
+ onContextMenu={this.myContextMenu} >
+ {!expandable ? (null) : <Tooltip title={<><div className="dash-tooltip">{BoolCast(this.props.Document.linearViewIsExpanded) ? "Close" : "Open"}</div></>} placement="top">
+ {menuOpener}
+ </Tooltip>}
+ <input id={`${guid}`} type="checkbox" checked={BoolCast(this.props.Document.linearViewIsExpanded)} ref={this.addMenuToggle}
+ onChange={action(() => this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
+
+ <div className="collectionLinearView-content"
+ style={{
+ height: this.dimension(),
+ flexDirection: flexDir,
+ gap: flexGap
+ }}>
+ {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))}
+ </div>
+ {DocumentLinksButton.StartLink && StrCast(this.layoutDoc.title) === "docked buttons" ? <span className="bottomPopup-background" style={{
+ pointerEvents: "all"
+ }}
+ onPointerDown={e => e.stopPropagation()} >
+ <span className="bottomPopup-text" >
+ Creating link from: <b>{DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'}</b>
+ </span>
+
+ <Tooltip title={<><div className="dash-tooltip">{"Toggle description pop-up"} </div></>} placement="top">
+ <span className="bottomPopup-descriptions" onClick={this.changeDescriptionSetting}>
+ Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"}
+ </span>
+ </Tooltip>
+
+ <Tooltip title={<><div className="dash-tooltip">Exit linking mode</div></>} placement="top">
+ <span className="bottomPopup-exit" onClick={this.exitLongLinks}>
+ Stop
+ </span>
+ </Tooltip>
+
+ </span> : null}
+ </div>
+ </div>;
+ }
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionLinear/index.ts b/src/client/views/collections/collectionLinear/index.ts
new file mode 100644
index 000000000..ff73e14ae
--- /dev/null
+++ b/src/client/views/collections/collectionLinear/index.ts
@@ -0,0 +1 @@
+export * from "./CollectionLinearView"; \ No newline at end of file
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
index fd99abce5..ed196349e 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaCells.tsx
@@ -293,12 +293,12 @@ export class CollectionSchemaCell extends React.Component<CellProps> {
}
}
const script = CompileScript(inputSansCommas, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
- const changeMade = value.length !== value.length || value.length - 2 !== value.length;
+ const changeMade = value.length - 2 !== value.length;
script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
// handle booleans
} else if (inputIsBool) {
const script = CompileScript(value, { requiredType: type, typecheck: false, editable: true, addReturn: true, params: { this: Doc.name, $r: "number", $c: "number", $: "any" } });
- const changeMade = value.length !== value.length || value.length - 2 !== value.length;
+ const changeMade = value.length - 2 !== value.length;
script.compiled && (retVal = this.applyToDoc(changeMade ? this._rowDoc : this._rowDataDoc, this.props.row, this.props.col, script.run));
}
}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
index aaa50ba67..a25f962df 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaHeaders.tsx
@@ -191,7 +191,7 @@ export class CollectionSchemaColumnMenu extends React.Component<ColumnMenuProps>
{this.renderSorting()}
{this.renderColors()}
<div className="collectionSchema-headerMenu-group">
- <button onClick={() => this.props.deleteColumn(this.props.columnField.heading)}>Delete Column</button>
+ <button onClick={() => this.props.deleteColumn(this.props.columnField.heading)}>Hide Column</button>
</div>
</>
}
@@ -413,7 +413,7 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
bool = fields ? fields[1] === "check" : false;
}
return <div key={key} className="key-option" style={{
- border: "1px solid lightgray", paddingLeft: 5, textAlign: "left",
+ paddingLeft: 5, textAlign: "left",
width: this.props.width, maxWidth: this.props.width, overflowX: "hidden", background: "white", backgroundColor: "white",
}}
>
@@ -489,8 +489,10 @@ export class KeysDropdown extends React.Component<KeysDropdownProps> {
}
render() {
return (
- <div style={{ display: "flex" }} ref={this.setNode}>
- <FontAwesomeIcon onClick={e => { this.props.openHeader(this.props.col, e.clientX, e.clientY); e.stopPropagation(); }} icon={this.props.icon} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} />
+ <div style={{ display: "flex", width: '100%', alignContent: 'center', alignItems: 'center' }} ref={this.setNode}>
+ <div className="schema-icon" onClick={e => { this.props.openHeader(this.props.col, e.clientX, e.clientY); e.stopPropagation(); }}>
+ <FontAwesomeIcon icon={this.props.icon} size="lg" style={{ display: "inline" }} />
+ </div>
{/* <FontAwesomeIcon icon={fa.faSearchMinus} size="lg" style={{ display: "inline", paddingBottom: "1px", paddingTop: "4px", cursor: "hand" }} onClick={e => {
runInAction(() => { this._isOpen === undefined ? this._isOpen = true : this._isOpen = !this._isOpen })
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx
index f48906ba5..0e19ef3d9 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaMovableRow.tsx
@@ -134,9 +134,9 @@ export class MovableRow extends React.Component<MovableRowProps> {
<div className="collectionSchema-row-wrapper" onKeyPress={this.onKeyDown} ref={this._header} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}>
<ReactTableDefaults.TrComponent onKeyPress={this.onKeyDown} >
<div className="row-dragger">
- <div className="row-option" style={{ left: 5 }} onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
- <div className="row-option" style={{ cursor: "grab", left: 25 }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
- <div className="row-option" style={{ left: 40 }} onClick={() => this.props.addDocTab(this.props.rowInfo.original, "add:right")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
+ <div className="row-option" onClick={undoBatch(() => this.props.removeDoc(this.props.rowInfo.original))}><FontAwesomeIcon icon="trash" size="sm" /></div>
+ <div className="row-option" style={{ cursor: "grab" }} ref={reference} onPointerDown={onItemDown}><FontAwesomeIcon icon="grip-vertical" size="sm" /></div>
+ <div className="row-option" onClick={() => this.props.addDocTab(this.props.rowInfo.original, "add:right")}><FontAwesomeIcon icon="external-link-alt" size="sm" /></div>
</div>
{children}
</ReactTableDefaults.TrComponent>
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 40cdcd14b..3074ce66e 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -108,9 +108,7 @@
}
.rt-th {
padding: 0;
- border: solid lightgray;
- border-width: 0 1px;
- border-bottom: 2px solid lightgray;
+ border-left: solid 1px $light-gray;
}
}
.rt-th {
@@ -213,6 +211,8 @@
}
}
+
+
.collectionSchemaView-header {
height: 100%;
color: gray;
@@ -227,6 +227,15 @@ button.add-column {
width: 28px;
}
+.collectionSchemaView-menuOptions-wrapper {
+ background: rgb(241, 239, 235);
+ display: flex;
+ cursor: default;
+ height: 100%;
+ align-content: center;
+ align-items: center;
+}
+
.collectionSchema-header-menuOptions {
color: black;
width: 180px;
@@ -272,6 +281,9 @@ button.add-column {
width: 10px;
}
}
+
+
+
.keys-dropdown {
position: relative;
//width: 100%;
@@ -287,26 +299,7 @@ button.add-column {
font-weight: normal;
}
}
- .keys-options-wrapper {
- width: 100%;
- max-height: 150px;
- overflow-y: scroll;
- position: absolute;
- top: 28px;
- box-shadow: 0 10px 16px rgba(0, 0, 0, 0.1);
- background-color: white;
- .key-option {
- background-color: white;
- border: 1px solid lightgray;
- padding: 2px 3px;
- &:not(:first-child) {
- border-top: 0;
- }
- &:hover {
- background-color: $light-gray;
- }
- }
- }
+
}
.columnMenu-colors {
display: flex;
@@ -325,11 +318,53 @@ button.add-column {
}
}
+.schema-icon {
+ cursor: pointer;
+ width: 25px;
+ height: 25px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ align-content: center;
+ background-color: $medium-blue;
+ color: white;
+ margin-right: 5px;
+ font-size: 10px;
+ border-radius: 3px;
+
+}
+
+.keys-options-wrapper {
+ position: absolute;
+ text-align: left;
+ height: fit-content;
+ top: 100%;
+ z-index: 21;
+ background-color: #ffffff;
+ box-shadow: 0px 3px 4px rgba(0,0,0,30%);
+ padding: 1px;
+ .key-option {
+ cursor: pointer;
+ color: #000000;
+ width: 100%;
+ height: 25px;
+ font-weight: 400;
+ display: flex;
+ justify-content: left;
+ align-items: center;
+ padding-left: 5px;
+ &:hover {
+ background-color: $light-gray;
+ }
+ }
+}
+
.collectionSchema-row {
height: 100%;
background-color: white;
&.row-focused .rt-td {
- background-color: #bfffc0; //$light-gray;
+ background-color: $light-blue; //$light-gray;
+ overflow: visible;
}
&.row-wrapped {
.rt-td {
@@ -338,39 +373,40 @@ button.add-column {
}
.row-dragger {
display: flex;
- justify-content: space-around;
- //flex: 50 0 auto;
- width: 0;
- max-width: 50px;
- //height: 100%;
+ justify-content: space-evenly;
+ width: 58px;
+ position: absolute;
+ /* max-width: 50px; */
min-height: 30px;
align-items: center;
color: lightgray;
background-color: white;
transition: color 0.1s ease;
.row-option {
- // padding: 5px;
+ color: black;
cursor: pointer;
- position: absolute;
+ position: relative;
transition: color 0.1s ease;
display: flex;
flex-direction: column;
justify-content: center;
z-index: 2;
+ border-radius: 3px;
+ padding: 3px;
&:hover {
- color: gray;
+ background-color: $light-gray;
}
}
}
.collectionSchema-row-wrapper {
&.row-above {
- border-top: 1px solid red;
+ border-top: 1px solid $medium-blue;
}
&.row-below {
- border-bottom: 1px solid red;
+ border-bottom: 1px solid $medium-blue;
}
&.row-inside {
- border: 1px solid red;
+ border: 2px dashed $medium-blue;
}
.row-dragging {
background-color: blue;
@@ -383,24 +419,32 @@ button.add-column {
height: unset;
}
+.collectionSchemaView-cellContents {
+ width: 100%;
+}
+
.collectionSchemaView-cellWrapper {
+ display: flex;
height: 100%;
- padding: 4px;
text-align: left;
padding-left: 19px;
position: relative;
+ align-items: center;
+ align-content: center;
&:focus {
outline: none;
}
&.editing {
padding: 0;
+ box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3);
+ transform: scale(1.1);
+ z-index: 40;
input {
outline: 0;
border: none;
- background-color: rgb(255, 217, 217);
+ background-color: $white;
width: 100%;
height: 100%;
- padding: 2px 3px;
min-height: 26px;
}
}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index fed64b620..dfe99ffc8 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -338,7 +338,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) {
{this.renderColors(this._col)}
<div className="collectionSchema-headerMenu-group">
<button onClick={() => { this.deleteColumn(this._col.heading); }}
- >Delete Column</button>
+ >Hide Column</button>
</div>
</div>;
}
diff --git a/src/client/views/collections/collectionSchema/SchemaTable.tsx b/src/client/views/collections/collectionSchema/SchemaTable.tsx
index abe549072..3833f968b 100644
--- a/src/client/views/collections/collectionSchema/SchemaTable.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTable.tsx
@@ -194,10 +194,10 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
const sortIcon = col.desc === undefined ? "caret-right" : col.desc === true ? "caret-down" : "caret-up";
const header = <div className="collectionSchemaView-menuOptions-wrapper" style={{ background: col.color, padding: "2px", display: "flex", cursor: "default", height: "100%", }}>
{keysDropdown}
- <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}>
+ <div onClick={e => this.changeSorting(col)} style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "pointer" }}>
<FontAwesomeIcon icon={sortIcon} size="lg" />
</div>
- {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>}
+ {/* {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>} */}
</div>;
return {
@@ -562,7 +562,7 @@ export class SchemaTable extends React.Component<SchemaTableProps> {
onPointerDown={this.props.onPointerDown} onClick={this.props.onClick} onWheel={e => this.props.active(true) && e.stopPropagation()}
onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} >
{this.reactTable}
- {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>}
+ {this.props.Document._chromeHidden || this.props.addDocument === returnFalse ? undefined : <div className="collectionSchemaView-addRow" onClick={this.createRow}>+ new</div>}
{!this._showDoc ? (null) :
<div className="collectionSchemaView-documentPreview" ref="overlay"
style={{