aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.scss7
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.tsx34
-rw-r--r--src/client/views/collections/CollectionCarouselView.scss23
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx20
-rw-r--r--src/client/views/collections/CollectionDockingView.scss391
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx58
-rw-r--r--src/client/views/collections/CollectionMenu.tsx9
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.tsx36
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewColumn.tsx10
-rw-r--r--src/client/views/collections/CollectionNoteTakingViewDivider.tsx11
-rw-r--r--src/client/views/collections/CollectionPileView.tsx11
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx47
-rw-r--r--src/client/views/collections/CollectionStackingView.scss9
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx14
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx20
-rw-r--r--src/client/views/collections/CollectionSubView.tsx12
-rw-r--r--src/client/views/collections/CollectionTreeView.tsx4
-rw-r--r--src/client/views/collections/CollectionView.tsx4
-rw-r--r--src/client/views/collections/TabDocView.tsx179
-rw-r--r--src/client/views/collections/TreeView.scss25
-rw-r--r--src/client/views/collections/TreeView.tsx87
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx24
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss9
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx134
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx38
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx4
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx12
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx16
-rw-r--r--src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx74
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx74
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss28
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx32
-rw-r--r--src/client/views/collections/collectionSchema/SchemaRowBox.tsx54
-rw-r--r--src/client/views/collections/collectionSchema/SchemaTableCell.tsx30
35 files changed, 1028 insertions, 514 deletions
diff --git a/src/client/views/collections/CollectionCarousel3DView.scss b/src/client/views/collections/CollectionCarousel3DView.scss
index 6bd1d9f5f..8319f19ca 100644
--- a/src/client/views/collections/CollectionCarousel3DView.scss
+++ b/src/client/views/collections/CollectionCarousel3DView.scss
@@ -74,7 +74,10 @@
.carousel3DView-fwd,
.carousel3DView-back {
- top: 50%;
+ top: 0;
+ background: transparent;
+ width: calc((1 - #{$CAROUSEL3D_CENTER_SCALE} * 0.33) / 2 * 100%);
+ height: 100%;
}
.carousel3DView-fwd-scroll,
@@ -108,8 +111,6 @@
opacity: 1;
}
-.carousel3DView-back:hover,
-.carousel3DView-fwd:hover,
.carousel3DView-back-scroll:hover,
.carousel3DView-fwd-scroll:hover {
background: lightgray;
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index d94e552b4..a8d080953 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -2,14 +2,15 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { computed } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc } from '../../../fields/Doc';
+import { Doc, DocListCast } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
-import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { returnFalse, returnZero, Utils } from '../../../Utils';
+import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
import { SelectionManager } from '../../util/SelectionManager';
import { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } from '../global/globalCssVariables.scss';
-import { DocumentView } from '../nodes/DocumentView';
+import { DocFocusOptions, DocumentView } from '../nodes/DocumentView';
import { StyleProp } from '../StyleProvider';
import './CollectionCarousel3DView.scss';
import { CollectionSubView } from './CollectionSubView';
@@ -27,7 +28,6 @@ export class CollectionCarousel3DView extends CollectionSubView() {
}
protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
- //used for stacking and masonry view
this._dropDisposer?.();
if (ele) {
this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
@@ -46,6 +46,15 @@ export class CollectionCarousel3DView extends CollectionSubView() {
.translate(-this.panelWidth() + ((this.centerScale - 1) * this.panelWidth()) / 2, -((Number(CAROUSEL3D_TOP) / 100) * this.props.PanelHeight()) + ((this.centerScale - 1) * this.panelHeight()) / 2)
.scale(1 / this.centerScale);
+ focus = (anchor: Doc, options: DocFocusOptions) => {
+ const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
+ if (anchor.type !== DocumentType.CONFIG && !docs.includes(anchor)) return;
+ options.didMove = true;
+ const target = DocCast(anchor.annotationOn) ?? anchor;
+ const index = docs.indexOf(target);
+ index !== -1 && (this.layoutDoc._carousel_index = index);
+ return undefined;
+ };
@computed get content() {
const currentIndex = NumCast(this.layoutDoc._carousel_index);
const displayDoc = (childPair: { layout: Doc; data: Doc }) => {
@@ -61,6 +70,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
LayoutTemplateString={this.props.childLayoutString}
Document={childPair.layout}
DataDoc={childPair.data}
+ focus={this.focus}
ScreenToLocalTransform={this.childScreenToLocal}
isContentActive={this.isChildContentActive}
isDocumentActive={this.props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this.props.isDocumentActive : this.isContentActive}
@@ -85,8 +95,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
this.layoutDoc._carousel_index = (NumCast(this.layoutDoc._carousel_index) + direction + this.childLayoutPairs.length) % this.childLayoutPairs.length;
};
- onArrowClick = (e: React.MouseEvent, direction: number) => {
- e.stopPropagation();
+ onArrowClick = (direction: number) => {
this.changeSlide(direction);
!this.layoutDoc.autoScrollOn && (this.layoutDoc.showScrollButton = direction === 1 ? 'fwd' : 'back'); // while autoscroll is on, keep the other autoscroll button hidden
!this.layoutDoc.autoScrollOn && this.fadeScrollButton(); // keep pause button visible while autoscroll is on
@@ -117,16 +126,11 @@ export class CollectionCarousel3DView extends CollectionSubView() {
};
@computed get buttons() {
- if (!this.props.isContentActive()) return null;
return (
<div className="arrow-buttons">
- <div key="back" className="carousel3DView-back" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={e => this.onArrowClick(e, -1)}>
- <FontAwesomeIcon icon="angle-left" size={'2x'} />
- </div>
- <div key="fwd" className="carousel3DView-fwd" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={e => this.onArrowClick(e, 1)}>
- <FontAwesomeIcon icon="angle-right" size={'2x'} />
- </div>
- {this.autoScrollButton}
+ <div title="click to go back" key="back" className="carousel3DView-back" onClick={e => this.onArrowClick(-1)} />
+ <div title="click to advance" key="fwd" className="carousel3DView-fwd" onClick={e => this.onArrowClick(1)} />
+ {/* {this.autoScrollButton} */}
</div>
);
}
@@ -165,7 +169,7 @@ export class CollectionCarousel3DView extends CollectionSubView() {
<div className="carousel-wrapper" style={{ transform: `translateX(${this.translateX}px)` }}>
{this.content}
</div>
- {this.props.Document._chromeHidden ? null : this.buttons}
+ {this.buttons}
<div className="dot-bar">{this.dots}</div>
</div>
);
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index 8660113cd..130b31325 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -1,8 +1,7 @@
-
.collectionCarouselView-outer {
- height : 100%;
+ height: 100%;
.collectionCarouselView-caption {
- height: 50;
+ height: 50;
display: inline-block;
width: 100%;
}
@@ -13,7 +12,8 @@
user-select: none;
}
}
-.carouselView-back, .carouselView-fwd {
+.carouselView-back,
+.carouselView-fwd {
position: absolute;
display: flex;
top: 42.5%;
@@ -22,18 +22,19 @@
align-items: center;
border-radius: 5px;
justify-content: center;
- color: rgba(255,255,255,0.5);
- background : rgba(0,0,0, 0.1);
+ color: rgba(255, 255, 255, 0.5);
+ background: rgba(0, 0, 0, 0.1);
&:hover {
- color:white;
+ color: white;
}
}
.carouselView-fwd {
- right: 0;
+ right: 20;
}
.carouselView-back {
- left: 0;
+ left: 20;
}
-.carouselView-back:hover, .carouselView-fwd:hover {
+.carouselView-back:hover,
+.carouselView-fwd:hover {
background: lightgray;
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index ea02bcd4c..33a92d406 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -4,7 +4,7 @@ import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, Opt } from '../../../fields/Doc';
import { NumCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnFalse, returnZero, StopEvent } from '../../../Utils';
+import { emptyFunction, returnFalse, returnOne, returnZero, StopEvent } from '../../../Utils';
import { DragManager } from '../../util/DragManager';
import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
@@ -21,7 +21,6 @@ export class CollectionCarouselView extends CollectionSubView() {
}
protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
- //used for stacking and masonry view
this._dropDisposer?.();
if (ele) {
this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
@@ -44,12 +43,14 @@ export class CollectionCarouselView extends CollectionSubView() {
panelHeight = () => this.props.PanelHeight() - (StrCast(this.layoutDoc._layout_showCaption) ? 50 : 0);
onContentDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
onContentClick = () => ScriptCast(this.layoutDoc.onChildClick);
+ @computed get marginX() {
+ return NumCast(this.layoutDoc.caption_xMargin, 50);
+ }
+ captionWidth = () => this.props.PanelWidth() - 2 * this.marginX;
@computed get content() {
const index = NumCast(this.layoutDoc._carousel_index);
const curDoc = this.childLayoutPairs?.[index];
- const captionProps = { ...this.props, fieldKey: 'caption', setHeight: undefined };
- const marginX = NumCast(this.layoutDoc['caption_xMargin']);
- const marginY = NumCast(this.layoutDoc['caption_yMargin']);
+ const captionProps = { ...this.props, NativeScaling: returnOne, PanelWidth: this.captionWidth, fieldKey: 'caption', setHeight: undefined, setContentView: undefined };
const show_captions = StrCast(this.layoutDoc._layout_showCaption);
return !(curDoc?.layout instanceof Doc) ? null : (
<>
@@ -58,6 +59,7 @@ export class CollectionCarouselView extends CollectionSubView() {
{...this.props}
NativeWidth={returnZero}
NativeHeight={returnZero}
+ setContentView={undefined}
onDoubleClick={this.onContentDoubleClick}
onClick={this.onContentClick}
isDocumentActive={this.props.childDocumentsActive?.() ? this.props.isDocumentActive : this.props.isContentActive}
@@ -79,11 +81,11 @@ export class CollectionCarouselView extends CollectionSubView() {
style={{
display: show_captions ? undefined : 'none',
borderRadius: this.props.styleProvider?.(this.layoutDoc, captionProps, StyleProp.BorderRounding),
- marginRight: marginX,
- marginLeft: marginX,
- width: `calc(100% - ${marginX * 2}px)`,
+ marginRight: this.marginX,
+ marginLeft: this.marginX,
+ width: `calc(100% - ${this.marginX * 2}px)`,
}}>
- <FormattedTextBox key={index} {...captionProps} allowScroll={true} fieldKey={show_captions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} DataDoc={undefined} />
+ <FormattedTextBox key={index} {...captionProps} fieldKey={show_captions} styleProvider={this.captionStyleProvider} Document={curDoc.layout} DataDoc={undefined} />
</div>
</>
);
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index b13753025..c0530ab81 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -1,5 +1,285 @@
@import '../global/globalCssVariables.scss';
-@import '../../../../node_modules/golden-layout/src/css/goldenlayout-base.css';
+
+.lm_root {
+ position: relative;
+}
+.lm_row > .lm_item {
+ float: left;
+}
+.lm_content {
+ overflow: hidden;
+ position: relative;
+}
+.lm_dragging,
+.lm_dragging * {
+ cursor: move !important;
+ user-select: none;
+}
+.lm_maximised {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 40;
+}
+.lm_maximise_placeholder {
+ display: none;
+}
+.lm_splitter {
+ position: relative;
+ z-index: 20;
+}
+.lm_splitter:hover,
+.lm_splitter.lm_dragging {
+ background: orange;
+}
+.lm_splitter.lm_vertical .lm_drag_handle {
+ width: 100%;
+ position: absolute;
+ cursor: ns-resize;
+}
+.lm_splitter.lm_horizontal {
+ float: left;
+ height: 100%;
+}
+.lm_splitter.lm_horizontal .lm_drag_handle {
+ height: 100%;
+ position: absolute;
+ cursor: ew-resize;
+}
+.lm_header {
+ overflow: visible;
+ position: relative;
+ z-index: 1;
+}
+// .lm_header [class^='lm_'] {
+// box-sizing: content-box !important;
+// }
+.lm_header .lm_controls {
+ position: absolute;
+ right: 3px;
+}
+.lm_header .lm_controls > li {
+ cursor: pointer;
+ float: left;
+ width: 18px;
+ height: 18px;
+ text-align: center;
+ top: 3px;
+}
+.lm_header ul {
+ margin: 0;
+ padding: 0;
+ list-style-type: none;
+}
+.lm_header .lm_tabs {
+ position: absolute;
+}
+.lm_header .lm_tab {
+ cursor: pointer;
+ float: left;
+ height: 25px;
+ padding: 0 10px 5px;
+ padding-right: 25px;
+ position: relative;
+ box-shadow: unset !important;
+}
+.lm_header .lm_tab i {
+ width: 2px;
+ height: 19px;
+ position: absolute;
+}
+.lm_header .lm_tab i.lm_left {
+ top: 0;
+ left: -2px;
+}
+.lm_header .lm_tab i.lm_right {
+ top: 0;
+ right: -2px;
+}
+.lm_header .lm_tab .lm_title {
+ display: inline-block;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.lm_header .lm_tab .lm_close_tab {
+ width: 14px;
+ height: 14px;
+ position: absolute;
+ top: 0;
+ right: 0;
+ text-align: center;
+}
+.lm_stack.lm_left .lm_header,
+.lm_stack.lm_right .lm_header {
+ height: 100%;
+}
+.lm_dragProxy.lm_left .lm_header,
+.lm_dragProxy.lm_right .lm_header,
+.lm_stack.lm_left .lm_header,
+.lm_stack.lm_right .lm_header {
+ width: 20px;
+ float: left;
+ vertical-align: top;
+}
+.lm_dragProxy.lm_left .lm_header .lm_tabs,
+.lm_dragProxy.lm_right .lm_header .lm_tabs,
+.lm_stack.lm_left .lm_header .lm_tabs,
+.lm_stack.lm_right .lm_header .lm_tabs {
+ transform-origin: left top;
+ top: 0;
+ width: 1000px;
+}
+.lm_dragProxy.lm_left .lm_header .lm_controls,
+.lm_dragProxy.lm_right .lm_header .lm_controls,
+.lm_stack.lm_left .lm_header .lm_controls,
+.lm_stack.lm_right .lm_header .lm_controls {
+ bottom: 0;
+}
+.lm_dragProxy.lm_left .lm_items,
+.lm_dragProxy.lm_right .lm_items,
+.lm_stack.lm_left .lm_items,
+.lm_stack.lm_right .lm_items {
+ float: left;
+}
+.lm_dragProxy.lm_left .lm_header .lm_tabs,
+.lm_stack.lm_left .lm_header .lm_tabs {
+ transform: rotate(-90deg) scaleX(-1);
+ left: 0;
+}
+.lm_dragProxy.lm_left .lm_header .lm_tabs .lm_tab,
+.lm_stack.lm_left .lm_header .lm_tabs .lm_tab {
+ transform: scaleX(-1);
+ margin-top: 1px;
+}
+.lm_dragProxy.lm_left .lm_header .lm_tabdropdown_list,
+.lm_stack.lm_left .lm_header .lm_tabdropdown_list {
+ top: initial;
+ right: initial;
+ left: 20px;
+}
+.lm_dragProxy.lm_right .lm_content {
+ float: left;
+}
+.lm_dragProxy.lm_right .lm_header .lm_tabs,
+.lm_stack.lm_right .lm_header .lm_tabs {
+ transform: rotate(90deg) scaleX(1);
+ left: 100%;
+ margin-left: 0;
+}
+.lm_dragProxy.lm_right .lm_header .lm_controls,
+.lm_stack.lm_right .lm_header .lm_controls {
+ left: 3px;
+}
+.lm_dragProxy.lm_right .lm_header .lm_tabdropdown_list,
+.lm_stack.lm_right .lm_header .lm_tabdropdown_list {
+ top: initial;
+ right: 20px;
+}
+.lm_dragProxy.lm_bottom .lm_header .lm_tab,
+.lm_stack.lm_bottom .lm_header .lm_tab {
+ margin-top: 0;
+ border-top: none;
+}
+.lm_dragProxy.lm_bottom .lm_header .lm_controls,
+.lm_stack.lm_bottom .lm_header .lm_controls {
+ top: 3px;
+}
+.lm_dragProxy.lm_bottom .lm_header .lm_tabdropdown_list,
+.lm_stack.lm_bottom .lm_header .lm_tabdropdown_list {
+ top: initial;
+ bottom: 20px;
+}
+.lm_drop_tab_placeholder {
+ float: left;
+ width: 100px;
+ height: 10px;
+ visibility: hidden;
+}
+.lm_header .lm_controls .lm_tabdropdown:before {
+ content: '';
+ width: 0;
+ height: 0;
+ vertical-align: middle;
+ display: inline-block;
+ border-top: 5px dashed;
+ border-right: 5px solid transparent;
+ border-left: 5px solid transparent;
+ color: white;
+}
+.lm_header .lm_tabdropdown_list {
+ position: absolute;
+ top: 20px;
+ right: 0;
+ z-index: 5;
+ overflow: hidden;
+}
+.lm_header .lm_tabdropdown_list .lm_tab {
+ clear: both;
+ padding-right: 10px;
+ margin: 0;
+}
+.lm_header .lm_tabdropdown_list .lm_tab .lm_title {
+ width: 100px;
+}
+.lm_header .lm_tabdropdown_list .lm_close_tab {
+ display: none !important;
+}
+.lm_dragProxy {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 30;
+}
+.lm_dragProxy .lm_header {
+ background: transparent;
+}
+.lm_dragProxy .lm_content {
+ border-top: none;
+ overflow: hidden;
+}
+.lm_dropTargetIndicator {
+ display: none;
+ position: absolute;
+ z-index: 20;
+}
+.lm_dropTargetIndicator .lm_inner {
+ width: 100%;
+ height: 100%;
+ position: relative;
+ top: 0;
+ left: 0;
+}
+.lm_transition_indicator {
+ display: none;
+ width: 20px;
+ height: 20px;
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 20;
+}
+.lm_popin {
+ width: 20px;
+ height: 20px;
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ z-index: 9999;
+}
+.lm_popin > * {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+.lm_popin > .lm_bg {
+ z-index: 10;
+}
+.lm_popin > .lm_icon {
+ z-index: 20;
+} /*# sourceMappingURL=goldenlayout-base.css.map */
+
@import '../../../../node_modules/golden-layout/src/css/goldenlayout-dark-theme.css';
.lm_title {
@@ -35,6 +315,8 @@
width: max-content;
height: 100%;
display: flex;
+ max-width: 100;
+ text-overflow: ellipsis;
}
.lm_active {
@@ -46,18 +328,33 @@
// font-weight: 700;
}
+.lm_header .lm_tabs {
+ overflow-y: hidden;
+ width: 100%;
+}
+ul.lm_tabs::before {
+ content: ' ';
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ z-index: 1;
+ pointer-events: none;
+ border: solid 1px black;
+}
.lm_header .lm_tab {
// padding: 0px; // moved to MainView.scss, othwerise they get overridden by default stylings
// opacity: 0.7;
// box-shadow: none;
// height: 25px;
// border-bottom: black solid;
+ border-bottom: unset !important;
+ border-top-right-radius: 5px;
+ border-top-left-radius: 5px;
.collectionDockingView-gear {
display: none;
}
}
-
.lm_header .lm_tab.lm_active {
padding: 0;
opacity: 1;
@@ -65,7 +362,11 @@
box-shadow: none;
height: 27px;
margin-right: 2px;
- // border-bottom: unset;
+ z-index: 2 !important;
+ border-right: solid 2px;
+ border-left: solid 2px;
+ border-top: solid 2px;
+ border-color: black;
.collectionDockingView-gear {
display: inline-block;
@@ -123,20 +424,55 @@
}
.lm_close_tab {
+ display: inline-flex !important;
padding: 0;
+ opacity: 1 !important;
align-self: center;
margin-right: 5px;
- background-color: black;
border-radius: 3px;
- opacity: 1 !important;
width: 15px !important;
height: 15px !important;
position: relative !important;
- display: inline-flex !important;
align-items: center;
top: 0 !important;
right: unset !important;
left: 0 !important;
+ background-image: unset !important;
+ &::before {
+ content: '\a0x\a0';
+ color: rgb(50, 50, 50);
+ margin: auto;
+ position: relative;
+ top: -2px;
+ }
+ &:hover {
+ &::before {
+ background: gray;
+ color: white;
+ }
+ }
+}
+.lm_close {
+ background-image: unset !important;
+ &:hover {
+ background: gray;
+ color: white !important;
+ }
+ &::before {
+ content: 'x';
+ margin: auto;
+ position: relative;
+ top: -2;
+ font-size: medium;
+ font-family: sans-serif;
+ }
+}
+
+.lm_iconWrap {
+ &:hover {
+ background: gray;
+ color: white !important;
+ }
}
.lm_tab,
@@ -154,14 +490,6 @@
top: 0;
left: 0;
- // overflow: hidden; // bcz: menus don't show up when this is on (e.g., the parentSelectorMenu)
- .collectionDockingView-gear {
- padding-left: 5px;
- height: 15px;
- width: 18px;
- margin: auto;
- }
-
.collectionDockingView-drag {
touch-action: none;
position: absolute;
@@ -180,7 +508,6 @@
display: flex;
align-content: center;
justify-content: center;
- background: $dark-gray;
}
.lm_controls > li {
@@ -190,14 +517,38 @@
}
.lm_controls .lm_popout {
- transform: rotate(45deg);
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAQUlEQVR4nHXOQQ4AMAgCQeT/f6aXpsGK3jSTuCVJAAr7iBdoAwCKd0nwfaAdHbYERw5b44+E8JoBjEYGMBq5gAYP3usUDu2IvoUAAAAASUVORK5CYII=);
+ background-image: unset;
+ left: -3;
+ &:hover {
+ background: gray;
+ color: white !important;
+ }
+ }
+ li.lm_popout::before {
+ content: '+';
+ margin: auto;
+ font-size: x-large;
+ top: -6;
+ position: relative;
+ }
+ .lm_maximise {
+ background-image: unset !important;
+ &::before {
+ content: '\25A3';
+ margin: auto;
+ font-size: medium;
+ position: relative;
+ }
+ &:hover {
+ background: gray;
+ color: white !important;
+ }
}
.lm_maximised .lm_controls .lm_maximise {
- opacity: 1;
- transform: scale(0.8);
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAKElEQVR4nGP8////fwYCgImQAgYGBgYWKM2IR81/okwajIpgvsMbVgAwgQYRVakEKQAAAABJRU5ErkJggg==) !important;
+ &::before {
+ content: '\25A2';
+ }
}
.flexlayout__layout {
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 519b7c905..4873a61ff 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -10,7 +10,7 @@ import { List } from '../../../fields/List';
import { ImageCast, NumCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { GetEffectiveAcl, inheritParentAcls } from '../../../fields/util';
-import { emptyFunction, incrementTitleCopy } from '../../../Utils';
+import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, incrementTitleCopy } from '../../../Utils';
import { DocServer } from '../../DocServer';
import { Docs } from '../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes';
@@ -31,6 +31,7 @@ import { CollectionFreeFormView } from './collectionFreeForm';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
import { TabDocView } from './TabDocView';
import React = require('react');
+import { SettingsManager } from '../../util/SettingsManager';
const _global = (window /* browser */ || global) /* node */ as any;
@observer
@@ -60,7 +61,7 @@ export class CollectionDockingView extends CollectionSubView() {
return this._goldenLayout._maximisedItem !== null;
}
private _goldenLayout: any = null;
-
+ static _highlightStyleSheet: any = addStyleSheet();
constructor(props: SubCollectionViewProps) {
super(props);
if (this.props.renderDepth < 0) runInAction(() => (CollectionDockingView.Instance = this));
@@ -118,6 +119,7 @@ export class CollectionDockingView extends CollectionSubView() {
const j = tab.header.parent.contentItems.indexOf(tab.contentItem);
if (j !== -1) {
tab.header.parent.contentItems[j].remove();
+ CollectionDockingView.Instance.endUndoBatch();
return CollectionDockingView.Instance.layoutChanged();
}
}
@@ -329,6 +331,16 @@ export class CollectionDockingView extends CollectionSubView() {
width => !this._goldenLayout && width > 20 && setTimeout(() => this.setupGoldenLayout()), // need to wait for the collectiondockingview-container to have it's width/height since golden layout reads that to configure its windows
{ fireImmediately: true }
);
+ reaction(
+ () => [SettingsManager.userBackgroundColor, SettingsManager.userBackgroundColor],
+ () => {
+ clearStyleSheetRules(CollectionDockingView._highlightStyleSheet);
+ addStyleSheetRule(CollectionDockingView._highlightStyleSheet, 'lm_controls', { background: `${SettingsManager.userBackgroundColor} !important` });
+ addStyleSheetRule(CollectionDockingView._highlightStyleSheet, 'lm_controls', { color: `${SettingsManager.userColor} !important` });
+ addStyleSheetRule(SettingsManager._settingsStyle, 'lm_header', { background: `${SettingsManager.userBackgroundColor} !important` });
+ },
+ { fireImmediately: true }
+ );
}
};
@@ -504,6 +516,23 @@ export class CollectionDockingView extends CollectionSubView() {
}
});
+ let addNewDoc = action(() => {
+ const dashboard = Doc.ActiveDashboard;
+ if (dashboard) {
+ dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1;
+ const docToAdd = Docs.Create.FreeformDocument([], {
+ _width: this.props.PanelWidth(),
+ _height: this.props.PanelHeight(),
+ _layout_fitWidth: true,
+ _freeform_backgroundGrid: true,
+ title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
+ });
+ Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd);
+ inheritParentAcls(this.dataDoc, docToAdd, false);
+ CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
+ }
+ });
+
stack.header?.controlsContainer
.find('.lm_close') //get the close icon
.off('click') //unbind the current click handler
@@ -523,31 +552,18 @@ export class CollectionDockingView extends CollectionSubView() {
})
);
+ stack.element.click((e: any) => {
+ if (stack.contentItems.length === 0 && Array.from(document.elementsFromPoint(e.originalEvent.x, e.originalEvent.y)).some(ele => ele?.className === 'empty-tabs-message')) {
+ addNewDoc();
+ }
+ });
stack.header?.controlsContainer
.find('.lm_maximise') //get the close icon
.click(() => setTimeout(this.stateChanged));
stack.header?.controlsContainer
.find('.lm_popout') //get the popout icon
.off('click') //unbind the current click handler
- .click(
- action(() => {
- // stack.config.fixed = !stack.config.fixed; // force the stack to have a fixed size
- const dashboard = Doc.ActiveDashboard;
- if (dashboard) {
- dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1;
- const docToAdd = Docs.Create.FreeformDocument([], {
- _width: this.props.PanelWidth(),
- _height: this.props.PanelHeight(),
- _layout_fitWidth: true,
- _freeform_backgroundGrid: true,
- title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`,
- });
- Doc.AddDocToList(Doc.MyHeaderBar, 'data', docToAdd);
- inheritParentAcls(this.dataDoc, docToAdd, false);
- CollectionDockingView.AddSplit(docToAdd, OpenWhereMod.none, stack);
- }
- })
- );
+ .click(addNewDoc);
};
render() {
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx
index 5c9dd2058..ec9d86c1a 100644
--- a/src/client/views/collections/CollectionMenu.tsx
+++ b/src/client/views/collections/CollectionMenu.tsx
@@ -41,6 +41,7 @@ import { COLLECTION_BORDER_WIDTH } from './CollectionView';
import { TabDocView } from './TabDocView';
import { CollectionFreeFormView } from './collectionFreeForm';
import { CollectionLinearView } from './collectionLinear';
+import { media_state } from '../nodes/AudioBox';
interface CollectionMenuProps {
panelHeight: () => number;
@@ -157,7 +158,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
<Toggle
toggleType={ToggleType.BUTTON}
type={Type.PRIM}
- color={StrCast(Doc.UserDoc().userColor)}
+ color={SettingsManager.userColor}
onClick={this.toggleTopBar}
toggleStatus={SettingsManager.headerBarHeight > 0}
icon={<FontAwesomeIcon icon={headerIcon} size="lg" />}
@@ -166,7 +167,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
<Toggle
toggleType={ToggleType.BUTTON}
type={Type.PRIM}
- color={StrCast(Doc.UserDoc().userColor)}
+ color={SettingsManager.userColor}
onClick={this.toggleProperties}
toggleStatus={SettingsManager.propertiesWidth > 0}
icon={<FontAwesomeIcon icon={propIcon} size="lg" />}
@@ -182,7 +183,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> {
className="collectionMenu-container"
style={{
background: SettingsManager.userBackgroundColor,
- // borderColor: StrCast(Doc.UserDoc().userColor)
+ // borderColor: SettingsManager.userColor
}}>
{this.contMenuButtons}
{hardCodedButtons}
@@ -579,7 +580,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionViewMenu
@undoBatch
@action
startRecording = () => {
- const doc = Docs.Create.ScreenshotDocument({ title: 'screen recording', _layout_fitWidth: true, _width: 400, _height: 200, mediaState: 'pendingRecording' });
+ const doc = Docs.Create.ScreenshotDocument({ title: 'screen recording', _layout_fitWidth: true, _width: 400, _height: 200, mediaState: media_state.PendingRecording });
CollectionDockingView.AddSplit(doc, OpenWhereMod.right);
};
diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx
index 53a42d2a6..c7ad80f11 100644
--- a/src/client/views/collections/CollectionNoteTakingView.tsx
+++ b/src/client/views/collections/CollectionNoteTakingView.tsx
@@ -1,10 +1,10 @@
import React = require('react');
import { CursorProperty } from 'csstype';
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, observable, reaction, trace } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, Field, Opt } from '../../../fields/Doc';
import { DocData, Height, Width } from '../../../fields/DocSymbols';
-import { Id } from '../../../fields/FieldSymbols';
+import { Copy, Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
@@ -93,7 +93,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
// we use availableWidth to convert from a percentage to a pixel count.
@computed get availableWidth() {
const numDividers = this.numGroupColumns - 1;
- return this.maxColWidth - numDividers * this.DividerWidth;
+ return this.maxColWidth - numDividers * this.DividerWidth - 2 * NumCast(this.layoutDoc.xMargin);
}
// children is passed as a prop to the NoteTakingField, which uses this function
@@ -216,7 +216,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
return this.props.styleProvider?.(doc, props, property);
};
- isContentActive = () => this.props.isSelected() || this.props.isContentActive();
+ isContentActive = () => this.props.isContentActive();
blockPointerEventsWhenDragging = () => (this.docsDraggedRowCol.length ? 'none' : undefined);
// getDisplayDoc returns the rules for displaying a document in this view (ie. DocumentView)
@@ -269,7 +269,6 @@ export class CollectionNoteTakingView extends CollectionSubView() {
whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
addDocTab={this.props.addDocTab}
bringToFront={returnFalse}
- scriptContext={this.props.scriptContext}
pinToPres={this.props.pinToPres}
/>
);
@@ -521,6 +520,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
this.observer.observe(ref);
}
}}
+ PanelWidth={this.props.PanelWidth}
select={this.props.select}
addDocument={this.addDocument}
chromeHidden={this.chromeHidden}
@@ -589,6 +589,8 @@ export class CollectionNoteTakingView extends CollectionSubView() {
const rightHeader = this.colHeaderData[colIndex + 1];
leftHeader.setWidth(leftHeader.width + movementX / this.availableWidth);
rightHeader.setWidth(rightHeader.width - movementX / this.availableWidth);
+ const headers = Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null);
+ headers.splice(headers.indexOf(leftHeader), 1, leftHeader[Copy]());
};
// renderedSections returns a list of all of the JSX elements used (columns and dividers). If the view
@@ -596,17 +598,15 @@ export class CollectionNoteTakingView extends CollectionSubView() {
// allows the user to adjust the column widths.
@computed get renderedSections() {
TraceMobx();
- const entries = Array.from(this.Sections.entries());
- const sections = entries;
- const eles: JSX.Element[] = [];
- for (let i = 0; i < sections.length; i++) {
- const col = this.sectionNoteTaking(sections[i][0], sections[i][1]);
- eles.push(col);
- if (i < sections.length - 1) {
- eles.push(<CollectionNoteTakingViewDivider key={`divider${i}`} index={i} setColumnStartXCoords={this.setColumnStartXCoords} xMargin={this.xMargin} />);
- }
- }
- return eles;
+ const sections = Array.from(this.Sections.entries());
+ return sections.map((sec, i) => (
+ <>
+ {this.sectionNoteTaking(sec[0], sec[1])}
+ {i === sections.length - 1 ? null : ( //
+ <CollectionNoteTakingViewDivider key={`divider${i}`} isContentActive={this.isContentActive} index={i} setColumnStartXCoords={this.setColumnStartXCoords} xMargin={this.xMargin} />
+ )}
+ </>
+ ));
}
@computed get nativeWidth() {
@@ -621,7 +621,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
}
@computed get backgroundEvents() {
- return this.props.isContentActive() === false ? 'none' : undefined;
+ return this.isContentActive() === false ? 'none' : undefined;
}
observer: any;
@@ -636,7 +636,7 @@ export class CollectionNoteTakingView extends CollectionSubView() {
style={{
overflowY: this.props.isContentActive() ? 'auto' : 'hidden',
background: this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor),
- pointerEvents: this.backgroundEvents ? 'all' : undefined,
+ pointerEvents: this.backgroundEvents,
}}
onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))}
onPointerLeave={action(e => (this.docsDraggedRowCol.length = 0))}
diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
index 3286d60bd..52cc21903 100644
--- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx
@@ -1,13 +1,13 @@
import React = require('react');
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, observable } from 'mobx';
+import { action, computed, observable, trace } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { Copy, Id } from '../../../fields/FieldSymbols';
import { RichTextField } from '../../../fields/RichTextField';
import { listSpec } from '../../../fields/Schema';
import { SchemaHeaderField } from '../../../fields/SchemaHeaderField';
-import { Cast } from '../../../fields/Types';
+import { Cast, NumCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { returnEmptyString } from '../../../Utils';
@@ -50,6 +50,7 @@ interface CSVFieldColumnProps {
maxColWidth: number;
dividerWidth: number;
availableWidth: number;
+ PanelWidth: () => number;
}
/**
@@ -62,9 +63,9 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
// columnWidth returns the width of a column in absolute pixels
@computed get columnWidth() {
- if (!this.props.colHeaderData || !this.props.headingObject || this.props.colHeaderData.length === 1) return '100%';
+ if (!this.props.colHeaderData || !this.props.headingObject || this.props.colHeaderData.length === 1) return `${(this.props.availableWidth / this.props.PanelWidth()) * 100}%`;
const i = this.props.colHeaderData.indexOf(this.props.headingObject);
- return this.props.colHeaderData[i].width * 100 + '%';
+ return ((this.props.colHeaderData[i].width * this.props.availableWidth) / this.props.PanelWidth()) * 100 + '%';
}
private dropDisposer?: DragManager.DragDropDisposer;
@@ -297,6 +298,7 @@ export class CollectionNoteTakingViewColumn extends React.Component<CSVFieldColu
style={{
width: this.columnWidth,
background: this._background,
+ marginLeft: this.props.headings().findIndex((h: any) => h[0] === this.props.headingObject) === 0 ? NumCast(this.props.Document.xMargin) : 0,
}}
ref={this.createColumnDropRef}
onPointerEnter={this.pointerEntered}
diff --git a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
index a1309b11f..af822d917 100644
--- a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
+++ b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx
@@ -1,4 +1,5 @@
-import { action, observable } from 'mobx';
+import { action, observable, trace } from 'mobx';
+import { observer } from 'mobx-react';
import * as React from 'react';
import { emptyFunction, setupMoveUpEvents } from '../../../Utils';
import { UndoManager } from '../../util/UndoManager';
@@ -7,6 +8,7 @@ interface DividerProps {
index: number;
xMargin: number;
setColumnStartXCoords: (movementX: number, colIndex: number) => void;
+ isContentActive: () => boolean | undefined;
}
/**
@@ -14,24 +16,26 @@ interface DividerProps {
* which only appear when there is more than 1 column in CollectionNoteTakingView. Dividers
* are two simple vertical lines that allow the user to alter the widths of CollectionNoteTakingViewColumns.
*/
+@observer
export class CollectionNoteTakingViewDivider extends React.Component<DividerProps> {
@observable private isHoverActive = false;
@observable private isResizingActive = false;
@action
private registerResizing = (e: React.PointerEvent<HTMLDivElement>) => {
- const batch = UndoManager.StartBatch('resizing');
+ let batch: UndoManager.Batch | undefined;
setupMoveUpEvents(
this,
e,
(e, down, delta) => {
+ if (!batch) batch = UndoManager.StartBatch('resizing');
this.props.setColumnStartXCoords(delta[0], this.props.index);
return false;
},
action(() => {
this.isResizingActive = false;
this.isHoverActive = false;
- batch.end();
+ batch?.end();
}),
emptyFunction
);
@@ -46,6 +50,7 @@ export class CollectionNoteTakingViewDivider extends React.Component<DividerProp
display: 'flex',
alignItems: 'center',
cursor: 'col-resize',
+ pointerEvents: this.props.isContentActive() ? 'all' : 'none',
}}
onPointerEnter={action(() => (this.isHoverActive = true))}
onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}>
diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx
index bbd528e13..91701b213 100644
--- a/src/client/views/collections/CollectionPileView.tsx
+++ b/src/client/views/collections/CollectionPileView.tsx
@@ -1,20 +1,19 @@
-import { action, computed, IReactionDisposer, reaction } from 'mobx';
+import { action, computed, IReactionDisposer } from 'mobx';
import { observer } from 'mobx-react';
import { Doc, DocListCast } from '../../../fields/Doc';
import { Height, Width } from '../../../fields/DocSymbols';
+import { ScriptField } from '../../../fields/ScriptField';
import { NumCast, StrCast } from '../../../fields/Types';
-import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents } from '../../../Utils';
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils';
import { DocUtils } from '../../documents/Documents';
import { SelectionManager } from '../../util/SelectionManager';
-import { SnappingManager } from '../../util/SnappingManager';
import { undoBatch, UndoManager } from '../../util/UndoManager';
+import { OpenWhere } from '../nodes/DocumentView';
+import { computePassLayout, computeStarburstLayout } from './collectionFreeForm';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
import './CollectionPileView.scss';
import { CollectionSubView } from './CollectionSubView';
import React = require('react');
-import { ScriptField } from '../../../fields/ScriptField';
-import { OpenWhere } from '../nodes/DocumentView';
-import { computePassLayout, computeStarburstLayout } from './collectionFreeForm';
@observer
export class CollectionPileView extends CollectionSubView() {
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index d2be70577..c4650647c 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -32,7 +32,7 @@ import './CollectionStackedTimeline.scss';
export type CollectionStackedTimelineProps = {
Play: () => void;
Pause: () => void;
- playLink: (linkDoc: Doc) => void;
+ playLink: (linkDoc: Doc, options: DocFocusOptions) => void;
playFrom: (seekTimeInSeconds: number, endTime?: number) => void;
playing: () => boolean;
setTime: (time: number) => void;
@@ -58,9 +58,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
@observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined;
@observable public static CurrentlyPlaying: DocumentView[];
- static RangeScript: ScriptField;
static LabelScript: ScriptField;
- static RangePlayScript: ScriptField;
static LabelPlayScript: ScriptField;
private _timeline: HTMLDivElement | null = null; // ref to actual timeline div
@@ -114,25 +112,6 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
return this._zoomFactor;
}
- 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',
- })!;
- }
-
componentDidMount() {
document.addEventListener('keydown', this.keyEvents, true);
}
@@ -161,6 +140,10 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
this.layoutDoc.clipEnd = this.trimEnd;
this._trimming = TrimScope.None;
}
+ @action
+ public CancelTrimming() {
+ this._trimming = TrimScope.None;
+ }
@action
public setZoom(zoom: number) {
@@ -175,8 +158,19 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack
return Math.max(this.clipStart, Math.min(this.clipEnd, (screen_delta / width) * this.clipDuration + this.clipStart));
};
- rangeClickScript = () => CollectionStackedTimeline.RangeScript;
- rangePlayScript = () => CollectionStackedTimeline.RangePlayScript;
+ @computed get rangeClick() {
+ // prettier-ignore
+ return ScriptField.MakeFunction('stackedTimeline.clickAnchor(this, clientX)',
+ { stackedTimeline: 'any', clientX: 'number' }, { stackedTimeline: this as any }
+ )!;
+ }
+ @computed get rangePlay() {
+ // prettier-ignore
+ return ScriptField.MakeFunction('stackedTimeline.playOnClick(this, clientX)',
+ { stackedTimeline: 'any', clientX: 'number' }, { stackedTimeline: this as any })!;
+ }
+ rangeClickScript = () => this.rangeClick;
+ rangePlayScript = () => this.rangePlay;
// handles key events for for creating key anchors, scrubbing, exiting trim
@action
@@ -677,7 +671,7 @@ interface StackedTimelineAnchorProps {
height: number;
toTimeline: (screen_delta: number, width: number) => number;
styleProvider?: (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string) => any;
- playLink: (linkDoc: Doc) => void;
+ playLink: (linkDoc: Doc, options: DocFocusOptions) => void;
setTime: (time: number) => void;
startTag: string;
endTag: string;
@@ -793,7 +787,7 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) {
const anchor = observable({ view: undefined as any });
const focusFunc = (doc: Doc, options: DocFocusOptions): number | undefined => {
- this.props.playLink(mark);
+ this.props.playLink(mark, options);
return undefined;
};
return {
@@ -831,7 +825,6 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps>
hideResizeHandles={true}
bringToFront={emptyFunction}
contextMenuItems={this.contextMenuItems}
- scriptContext={this.props.stackedTimeline}
/>
),
};
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 255bc3889..dddb3ec71 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -395,16 +395,23 @@
}
.collectionStackingView-addDocumentButton {
- font-size: 75%;
letter-spacing: 2px;
cursor: pointer;
+ .editableView-container-editing {
+ text-align: right;
+ }
.editableView-input {
outline-color: black;
letter-spacing: 2px;
color: grey;
border: 0px;
+ background: yellow;
+ text-align: right;
padding-top: 10px; // : 12px 10px 11px 10px;
+ input {
+ text-align: right;
+ }
}
}
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index e4a0d6dad..c43a9d2b8 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -31,6 +31,7 @@ import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow';
import './CollectionStackingView.scss';
import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
import { CollectionSubView } from './CollectionSubView';
+import { SettingsManager } from '../../util/SettingsManager';
const _global = (window /* browser */ || global) /* node */ as any;
export type collectionStackingViewProps = {
@@ -67,7 +68,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
// it looks like this gets the column headers that Mehek was showing just now
@computed get colHeaderData() {
- return Cast(this.layoutDoc['_' + this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null);
+ return Cast(this.dataDoc['_' + this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null);
}
// Still not sure what a pivot is, but it appears that we can actually filter docs somehow?
@computed get pivotField() {
@@ -120,7 +121,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
if (this.colHeaderData === undefined) {
// TODO: what is a layout doc? Is it literally how this document is supposed to be layed out?
// here we're making an empty list of column headers (again, what Mehek showed us)
- this.layoutDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>();
+ this.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>();
}
}
@@ -137,7 +138,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// assuming we need to get rowSpan because we might be dealing with many columns. Grid gap makes sense if multiple columns
const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
// just getting the style
- const style = this.isStackingView ? { width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
+ const style = this.isStackingView ? { margin: this.rootDoc._stacking_alignCenter ? 'auto' : undefined, width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
// So we're choosing whether we're going to render a column or a masonry doc
return (
<div className={`collectionStackingView-${this.isStackingView ? 'columnDoc' : 'masonryDoc'}`} key={d[Id]} style={style}>
@@ -158,7 +159,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
if (!this.pivotField || this.colHeaderData instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
if (this.colHeaderData === undefined) {
- setTimeout(() => (this.layoutDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>()), 0);
+ setTimeout(() => (this.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>()), 0);
return new Map<SchemaHeaderField, Doc[]>();
}
const colHeaderData = Array.from(this.colHeaderData);
@@ -207,7 +208,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// reset section headers when a new filter is inputted
this._pivotFieldDisposer = reaction(
() => this.pivotField,
- () => (this.layoutDoc['_' + this.fieldKey + '_columnHeaders'] = new List())
+ () => (this.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List())
);
this._layout_autoHeightDisposer = reaction(
() => this.layoutDoc._layout_autoHeight,
@@ -360,7 +361,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged}
addDocTab={this.props.addDocTab}
bringToFront={returnFalse}
- scriptContext={this.props.scriptContext}
pinToPres={this.props.pinToPres}
/>
);
@@ -426,7 +426,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
className="collectionStackingView-columnDragger"
onPointerDown={this.columnDividerDown}
ref={this._draggerRef}
- style={{ cursor: this._cursor, color: StrCast(Doc.UserDoc().userColor), left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }}>
+ style={{ cursor: this._cursor, color: SettingsManager.userColor, left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }}>
<FontAwesomeIcon icon={'arrows-alt-h'} />
</div>
);
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 7a22d4871..3598d548a 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -212,15 +212,16 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
const layoutItems: ContextMenuProps[] = [];
const docItems: ContextMenuProps[] = [];
const dataDoc = this.props.DataDoc || this.props.Document;
-
+ const width = this._ele ? Number(getComputedStyle(this._ele).width.replace('px', '')) : 0;
+ const height = this._ele ? Number(getComputedStyle(this._ele).height.replace('px', '')) : 0;
DocUtils.addDocumentCreatorMenuItems(
doc => {
FormattedTextBox.SelectOnLoad = doc[Id];
return this.props.addDocument?.(doc);
},
this.props.addDocument,
- x,
- y,
+ 0,
+ 0,
true
);
@@ -272,8 +273,11 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
this.props.addDocument?.(created);
}
});
- const pt = this.props.screenToLocalTransform().inverse().transformPoint(x, y);
- ContextMenu.Instance.displayMenu(x, y, undefined, true);
+ const pt = this.props
+ .screenToLocalTransform()
+ .inverse()
+ .transformPoint(width - 30, height);
+ ContextMenu.Instance.displayMenu(pt[0], pt[1], undefined, true);
};
@computed get innards() {
@@ -357,7 +361,11 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC
//TODO: would be great if there was additional space beyond the frame, so that you can actually see your
// bottom note
//TODO: ok, so we are using a single column, and this is it!
- <div key={`${heading}-add-document`} className="collectionStackingView-addDocumentButton" style={{ width: 'calc(100% - 25px)', maxWidth: this.props.columnWidth / this.props.numGroupColumns - 25, marginBottom: 10 }}>
+ <div
+ key={`${heading}-add-document`}
+ onKeyDown={e => e.stopPropagation()}
+ className="collectionStackingView-addDocumentButton"
+ style={{ width: 'calc(100% - 25px)', maxWidth: this.props.columnWidth / this.props.numGroupColumns - 25, marginBottom: 10 }}>
<EditableView
GetValue={returnEmptyString}
SetValue={this.addNewTextDoc}
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index eb4685834..158f9d8ee 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -31,7 +31,6 @@ export function CollectionSubView<X>(moreProps?: X) {
@observable _focusFilters: Opt<string[]>; // childFilters that are overridden when previewing a link to an anchor which has childFilters set on it
@observable _focusRangeFilters: Opt<string[]>; // childFiltersByRanges that are overridden when previewing a link to an anchor which has childFiltersByRanges set on it
protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
- //used for stacking and masonry view
this.dropDisposer?.();
this.gestureDisposer?.();
this._multiTouchDisposer?.();
@@ -210,11 +209,12 @@ export function CollectionSubView<X>(moreProps?: X) {
const targetDocments = DocListCast(this.dataDoc[this.props.fieldKey]);
const someMoved = !dropAction && 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 === 'same' || dropAction === 'move' || someMoved) && docDragData.moveDocument) {
+ if ((!dropAction || dropAction === 'inSame' || 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) {
- const canAdd = de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc);
+ const canAdd =
+ (de.embedKey || dropAction || Doc.AreProtosEqual(Cast(movedDocs[0].annotationOn, Doc, null), this.rootDoc)) && (dropAction !== 'inSame' || docDragData.draggedDocuments.every(d => d.embedContainer === this.rootDoc));
const moved = docDragData.moveDocument(movedDocs, this.rootDoc, canAdd ? this.addDocument : returnFalse);
added = canAdd || moved ? moved : undefined;
} else {
@@ -298,7 +298,7 @@ export function CollectionSubView<X>(moreProps?: X) {
let source = split;
if (split.startsWith('data:image') && split.includes('base64')) {
const [{ accessPaths }] = await Networking.PostToServer('/uploadRemoteImage', { sources: [split] });
- if (accessPaths.agnostic.client.indexOf("dashblobstore") === -1) {
+ if (accessPaths.agnostic.client.indexOf('dashblobstore') === -1) {
source = Utils.prepend(accessPaths.agnostic.client);
} else {
source = accessPaths.agnostic.client;
@@ -347,10 +347,10 @@ export function CollectionSubView<X>(moreProps?: X) {
}
if (uriList || text) {
- if ((uriList || text).includes('www.youtube.com/watch') || text.includes('www.youtube.com/embed')) {
+ if ((uriList || text).includes('www.youtube.com/watch') || text.includes('www.youtube.com/embed') || text.includes('www.youtube.com/shorts')) {
const batch = UndoManager.StartBatch('youtube upload');
const generatedDocuments: Doc[] = [];
- this.slowLoadDocuments((uriList || text).split('v=')[1].split('&')[0], options, generatedDocuments, text, completed, addDocument).then(batch.end);
+ this.slowLoadDocuments((uriList || text).split('v=').lastElement().split('&')[0].split('shorts/').lastElement(), options, generatedDocuments, text, completed, addDocument).then(batch.end);
return;
}
diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx
index eed04b3ee..9e5ac77d9 100644
--- a/src/client/views/collections/CollectionTreeView.tsx
+++ b/src/client/views/collections/CollectionTreeView.tsx
@@ -149,7 +149,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
if (isAlreadyInTree() !== sameTree) {
console.log('WHAAAT');
}
- dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree ? 'same' : dragData.dropAction;
+ dragData.dropAction = dropAction && !isAlreadyInTree() ? dropAction : sameTree && dragData.dropAction !== 'inSame' ? 'same' : dragData.dropAction;
e.stopPropagation();
}
};
@@ -438,7 +438,7 @@ export class CollectionTreeView extends CollectionSubView<Partial<collectionTree
render() {
TraceMobx();
- const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1) || 1;
+ const scale = this.props.NativeDimScaling?.() || 1;
return (
<div style={{ transform: `scale(${scale})`, transformOrigin: 'top left', width: `${100 / scale}%`, height: `${100 / scale}%` }}>
{!(this.doc instanceof Doc) || !this.treeChildren ? null : this.doc.treeView_HasOverlay ? (
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 88f892efc..ce19b3f9b 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,7 +1,7 @@
import { computed, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, DocListCast } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { ObjectField } from '../../../fields/ObjectField';
import { ScriptField } from '../../../fields/ScriptField';
import { BoolCast, Cast, ScriptCast, StrCast } from '../../../fields/Types';
@@ -40,7 +40,7 @@ interface CollectionViewProps_ extends FieldViewProps {
isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
layoutEngine?: () => string;
- setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void;
+ setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean, doc: Opt<Doc>) => void) => void;
setBrushViewer?: (func?: (view: { width: number; height: number; panX: number; panY: number }, transTime: number) => void) => void;
ignoreUnrendered?: boolean;
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index f6acafa95..26aa5a121 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -17,6 +17,7 @@ import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'
import { DocumentManager } from '../../util/DocumentManager';
import { DragManager, dropActionType } from '../../util/DragManager';
import { SelectionManager } from '../../util/SelectionManager';
+import { SettingsManager } from '../../util/SettingsManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoable, UndoManager } from '../../util/UndoManager';
@@ -131,6 +132,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
if (tab.element[0].children[1].children.length === 1) {
iconWrap.className = 'lm_iconWrap lm_moreInfo';
+ iconWrap.title = 'click for menu, drag to embed in document';
const dragBtnDown = (e: React.PointerEvent) => {
setupMoveUpEvents(
this,
@@ -215,15 +217,15 @@ export class TabDocView extends React.Component<TabDocViewProps> {
);
// highlight the tab when the tab document is brushed in any part of the UI
- tab._disposers.reactionDisposer = reaction(
- () => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }),
- ({ title, degree }) => {
- //titleEle.value = title;
- // titleEle.style.padding = degree ? 0 : 2;
- // titleEle.style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`;
- },
- { fireImmediately: true }
- );
+ // tab._disposers.reactionDisposer = reaction(
+ // () => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }),
+ // ({ title, degree }) => {
+ // titleEle.value = title;
+ // titleEle.style.padding = degree ? 0 : 2;
+ // titleEle.style.border = `${['gray', 'gray', 'gray'][degree]} ${['none', 'dashed', 'solid'][degree]} 2px`;
+ // },
+ // { fireImmediately: true }
+ // );
// clean up the tab when it is closed
tab.closeElement
@@ -278,10 +280,8 @@ export class TabDocView extends React.Component<TabDocViewProps> {
if (pinProps.pinViewport) PresBox.pinDocView(pinDoc, pinProps, anchorDoc ?? doc);
if (!pinProps?.audioRange && duration !== undefined) {
- pinDoc.mediaStart = 'manual';
- pinDoc.mediaStop = 'manual';
- pinDoc.config_clipStart = NumCast(doc.clipStart);
- pinDoc.config_clipEnd = NumCast(doc.clipEnd, duration);
+ pinDoc.presentation_mediaStart = 'manual';
+ pinDoc.presentation_mediaStop = 'manual';
}
if (pinProps?.activeFrame !== undefined) {
pinDoc.config_activeFrame = pinProps?.activeFrame;
@@ -350,7 +350,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
private onActiveContentItemChanged(contentItem: any) {
if (!contentItem || (this.stack === contentItem.parent && ((contentItem?.tab === this.tab && !this._isActive) || (contentItem?.tab !== this.tab && this._isActive)))) {
this._activated = this._isActive = !contentItem || contentItem?.tab === this.tab;
- if (!this._view) setTimeout(() => SelectionManager.SelectView(this._view, false));
+ if (!this._view && this.tab?.contentItem?.config?.props?.panelName !== 'dontSelectOnActivate') setTimeout(() => SelectionManager.SelectView(this._view, false));
!this._isActive && this._document && Doc.UnBrushDoc(this._document); // bcz: bad -- trying to simulate a pointer leave event when a new tab is opened up on top of an existing one.
}
}
@@ -382,7 +382,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
return LightboxView.AddDocTab(doc, location);
case OpenWhere.close: return CollectionDockingView.CloseSplit(doc, whereMods);
case OpenWhere.replace: return CollectionDockingView.ReplaceTab(doc, whereMods, this.stack, undefined, keyValue);
- case OpenWhere.toggle: return CollectionDockingView.ToggleSplit(doc, whereMods, this.stack, undefined, keyValue);
+ case OpenWhere.toggle: return CollectionDockingView.ToggleSplit(doc, whereMods, this.stack, "dontSelectOnActivate", keyValue);
case OpenWhere.add:default:return CollectionDockingView.AddSplit(doc, whereMods, this.stack, undefined, keyValue);
}
};
@@ -421,7 +421,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
PanelHeight = () => this._panelHeight;
miniMapColor = () => Colors.MEDIUM_GRAY;
tabView = () => this._view;
- disableMinimap = () => !this._document || this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._type_collection !== CollectionViewType.Freeform;
+ disableMinimap = () => !this._document;
whenChildContentActiveChanges = (isActive: boolean) => (this._isAnyChildContentActive = isActive);
isContentActive = () => this._isContentActive;
waitForDoubleClick = () => (DocumentView.ExploreMode ? 'never' : undefined);
@@ -462,9 +462,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
bringToFront={emptyFunction}
pinToPres={TabDocView.PinDoc}
/>
- {this.disableMinimap() || this._document._type_collection !== CollectionViewType.Freeform ? null : (
- <TabMinimapView key="minimap" addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} />
- )}
+ {this.disableMinimap() ? null : <TabMinimapView key="minimap" addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} />}
</>
);
}
@@ -476,7 +474,7 @@ export class TabDocView extends React.Component<TabDocViewProps> {
style={{
fontFamily: Doc.UserDoc().renderStyle === 'comic' ? 'Comic Sans MS' : undefined,
}}
- onPointerEnter={action(() => (this._hovering = true))}
+ onPointerOver={action(() => (this._hovering = true))}
onPointerLeave={action(() => (this._hovering = false))}
onDragOver={action(() => (this._hovering = true))}
onDragLeave={action(() => (this._hovering = false))}
@@ -505,6 +503,18 @@ interface TabMinimapViewProps {
PanelHeight: () => number;
background: () => string;
}
+interface TabMiniThumbProps {
+ miniWidth: () => number;
+ miniHeight: () => number;
+ miniTop: () => number;
+ miniLeft: () => number;
+}
+@observer
+class TabMiniThumb extends React.Component<TabMiniThumbProps> {
+ render() {
+ return <div className="miniThumb" style={{ width: `${this.props.miniWidth()}% `, height: `${this.props.miniHeight()}% `, left: `${this.props.miniLeft()}% `, top: `${this.props.miniTop()}% ` }} />;
+ }
+}
@observer
export class TabMinimapView extends React.Component<TabMinimapViewProps> {
static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => {
@@ -516,25 +526,17 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
return 'none';
case StyleProp.DocContents:
const background = ((type: DocumentType) => {
+ // prettier-ignore
switch (type) {
- case DocumentType.PDF:
- return 'pink';
- case DocumentType.AUDIO:
- return 'lightgreen';
- case DocumentType.WEB:
- return 'brown';
- case DocumentType.IMG:
- return 'blue';
- case DocumentType.MAP:
- return 'orange';
- case DocumentType.VID:
- return 'purple';
- case DocumentType.RTF:
- return 'yellow';
- case DocumentType.COL:
- return undefined;
- default:
- return 'gray';
+ case DocumentType.PDF: return 'pink';
+ case DocumentType.AUDIO: return 'lightgreen';
+ case DocumentType.WEB: return 'brown';
+ case DocumentType.IMG: return 'blue';
+ case DocumentType.MAP: return 'orange';
+ case DocumentType.VID: return 'purple';
+ case DocumentType.RTF: return 'yellow';
+ case DocumentType.COL: return undefined;
+ default: return 'gray';
}
})(doc.type as DocumentType);
return !background ? undefined : <div style={{ width: doc[Width](), height: doc[Height](), position: 'absolute', display: 'block', background }} />;
@@ -554,13 +556,13 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
returnMiniSize = () => NumCast(this.props.document._miniMapSize, 150);
miniDown = (e: React.PointerEvent) => {
const doc = this.props.document;
- const renderBounds = this.renderBounds ?? { l: 0, r: 0, t: 0, b: 0, dim: 1 };
const miniSize = this.returnMiniSize();
doc &&
setupMoveUpEvents(
this,
e,
action((e: PointerEvent, down: number[], delta: number[]) => {
+ const renderBounds = this.renderBounds ?? { l: 0, r: 0, t: 0, b: 0, dim: 1 };
doc._freeform_panX = clamp(NumCast(doc._freeform_panX) + (delta[0] / miniSize) * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim);
doc._freeform_panY = clamp(NumCast(doc._freeform_panY) + (delta[1] / miniSize) * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim);
return false;
@@ -569,60 +571,57 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> {
emptyFunction
);
};
- render() {
- if (!this.renderBounds) return null;
- const miniWidth = (this.props.PanelWidth() / NumCast(this.props.document._freeform_scale, 1) / this.renderBounds.dim) * 100;
- const miniHeight = (this.props.PanelHeight() / NumCast(this.props.document._freeform_scale, 1) / this.renderBounds.dim) * 100;
- const miniLeft = 50 + ((NumCast(this.props.document._freeform_panX) - this.renderBounds.cx) / this.renderBounds.dim) * 100 - miniWidth / 2;
- const miniTop = 50 + ((NumCast(this.props.document._freeform_panY) - this.renderBounds.cy) / this.renderBounds.dim) * 100 - miniHeight / 2;
+ popup = () => {
+ if (!this.renderBounds) return <></>;
+ const renderBounds = this.renderBounds;
+ const miniWidth = () => (this.props.PanelWidth() / NumCast(this.props.document._freeform_scale, 1) / renderBounds.dim) * 100;
+ const miniHeight = () => (this.props.PanelHeight() / NumCast(this.props.document._freeform_scale, 1) / renderBounds.dim) * 100;
+ const miniLeft = () => 50 + ((NumCast(this.props.document._freeform_panX) - renderBounds.cx) / renderBounds.dim) * 100 - miniWidth() / 2;
+ const miniTop = () => 50 + ((NumCast(this.props.document._freeform_panY) - renderBounds.cy) / renderBounds.dim) * 100 - miniHeight() / 2;
const miniSize = this.returnMiniSize();
return (
- <div className="miniMap-hidden">
- <Popup
- icon={<FontAwesomeIcon icon="globe-asia" size="lg" />}
- color={StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE)}
- type={Type.TERT}
- onPointerDown={e => e.stopPropagation()}
- placement={'top-end'}
- popup={
- <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
- <CollectionFreeFormView
- Document={this.props.document}
- docViewPath={returnEmptyDoclist}
- 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={emptyFunction}
- isAnyChildContentActive={returnFalse}
- select={emptyFunction}
- isSelected={returnFalse}
- dontRegisterView={true}
- fieldKey={Doc.LayoutFieldKey(this.props.document)}
- bringToFront={emptyFunction}
- rootSelected={returnTrue}
- addDocument={returnFalse}
- moveDocument={returnFalse}
- removeDocument={returnFalse}
- PanelWidth={this.returnMiniSize}
- PanelHeight={this.returnMiniSize}
- ScreenToLocalTransform={Transform.Identity}
- renderDepth={0}
- whenChildContentsActiveChanged={emptyFunction}
- focus={emptyFunction}
- styleProvider={TabMinimapView.miniStyleProvider}
- addDocTab={this.props.addDocTab}
- pinToPres={TabDocView.PinDoc}
- childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
- childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
- searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
- fitContentsToBox={returnTrue}
- />
- <div className="miniOverlay" onPointerDown={this.miniDown}>
- <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% ` }} />
- </div>
- </div>
- }
+ <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}>
+ <CollectionFreeFormView
+ Document={this.props.document}
+ docViewPath={returnEmptyDoclist}
+ 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={emptyFunction}
+ isAnyChildContentActive={returnFalse}
+ select={emptyFunction}
+ isSelected={returnFalse}
+ dontRegisterView={true}
+ fieldKey={Doc.LayoutFieldKey(this.props.document)}
+ bringToFront={emptyFunction}
+ rootSelected={returnTrue}
+ addDocument={returnFalse}
+ moveDocument={returnFalse}
+ removeDocument={returnFalse}
+ PanelWidth={this.returnMiniSize}
+ PanelHeight={this.returnMiniSize}
+ ScreenToLocalTransform={Transform.Identity}
+ renderDepth={0}
+ whenChildContentsActiveChanged={emptyFunction}
+ focus={emptyFunction}
+ styleProvider={TabMinimapView.miniStyleProvider}
+ addDocTab={this.props.addDocTab}
+ pinToPres={TabDocView.PinDoc}
+ childFilters={CollectionDockingView.Instance?.childDocFilters ?? returnEmptyDoclist}
+ childFiltersByRanges={CollectionDockingView.Instance?.childDocRangeFilters ?? returnEmptyDoclist}
+ searchFilterDocs={CollectionDockingView.Instance?.searchFilterDocs ?? returnEmptyDoclist}
+ fitContentsToBox={returnTrue}
/>
+ <div className="miniOverlay" onPointerDown={this.miniDown}>
+ <TabMiniThumb miniLeft={miniLeft} miniTop={miniTop} miniWidth={miniWidth} miniHeight={miniHeight} />
+ </div>
+ </div>
+ );
+ };
+ render() {
+ return this.props.document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this.props.document)) || this.props.document?._type_collection !== CollectionViewType.Freeform ? null : (
+ <div className="miniMap-hidden">
+ <Popup icon={<FontAwesomeIcon icon="globe-asia" size="lg" />} color={SettingsManager.userVariantColor} type={Type.TERT} onPointerDown={e => e.stopPropagation()} placement={'top-end'} popup={this.popup} />
</div>
);
}
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index d22e85880..cbcc7c710 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -21,8 +21,28 @@
}
.treeView-bulletIcons {
- width: 100%;
+ margin: auto;
height: 100%;
+ // changes start here.
+
+ .treeView-expandIcon {
+ display: none;
+ left: -8px;
+ position: absolute;
+ }
+
+ .treeView-checkIcon {
+ left: 3.5px;
+ top: 2px;
+ position: absolute;
+ }
+
+ &:hover {
+ .treeView-expandIcon {
+ display: unset;
+ }
+ }
+ // end changes
position: relative;
display: flex;
flex-direction: row;
@@ -44,6 +64,8 @@
position: relative;
width: fit-content;
min-height: 20px;
+ min-width: 15px;
+ margin-right: 3px;
color: $medium-gray;
border: #80808030 1px solid;
border-radius: 5px;
@@ -121,7 +143,6 @@
filter: opacity(0.2) !important;
}
}
-
//align-items: center;
::-webkit-scrollbar {
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index 701150769..f89aa065b 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -2,7 +2,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
-import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../fields/Doc';
+import { Doc, DocListCast, Field, FieldResult, Opt, StrListCast } from '../../../fields/Doc';
import { DocData, Height, Width } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
@@ -36,6 +36,7 @@ import './TreeView.scss';
import React = require('react');
import { IconButton, Size } from 'browndash-components';
import { TreeSort } from './TreeSort';
+import { SettingsManager } from '../../util/SettingsManager';
export interface TreeViewProps {
treeView: CollectionTreeView;
@@ -222,6 +223,38 @@ export class TreeView extends React.Component<TreeViewProps> {
}
};
+ @undoBatch
+ @action
+ recurToggle = (childList: Doc[]) => {
+ if (childList.length > 0) {
+ childList.forEach(child => {
+ child.runProcess = !!!child.runProcess;
+ TreeView.ToggleChildrenRun.get(child)?.();
+ });
+ }
+ };
+
+ @undoBatch
+ @action
+ getRunningChildren = (childList: Doc[]) => {
+ if (childList.length === 0) {
+ return [];
+ }
+
+ const runningChildren: FieldResult[] = [];
+ childList.forEach(child => {
+ if (child.runProcess && TreeView.GetRunningChildren.get(child)) {
+ if (child.runProcess) {
+ runningChildren.push(child);
+ }
+ runningChildren.push(...(TreeView.GetRunningChildren.get(child)?.() ?? []));
+ }
+ });
+ return runningChildren;
+ };
+
+ static GetRunningChildren = new Map<Doc, any>();
+ static ToggleChildrenRun = new Map<Doc, () => void>();
constructor(props: any) {
super(props);
if (!TreeView._openLevelScript) {
@@ -230,6 +263,17 @@ export class TreeView extends React.Component<TreeViewProps> {
}
this._openScript = Doc.IsSystem(this.props.document) ? undefined : () => TreeView._openLevelScript!;
this._editTitleScript = Doc.IsSystem(this.props.document) ? () => TreeView._openLevelScript! : () => TreeView._openTitleScript!;
+
+ // set for child processing highligting
+ this.dataDoc.hasChildren = this.childDocs.length > 0;
+ // this.dataDoc.children = this.childDocs;
+ TreeView.ToggleChildrenRun.set(this.doc, () => {
+ this.recurToggle(this.childDocs);
+ });
+
+ TreeView.GetRunningChildren.set(this.doc, () => {
+ return this.getRunningChildren(this.childDocs);
+ });
}
_treeEle: any;
@@ -289,7 +333,7 @@ export class TreeView extends React.Component<TreeViewProps> {
const pt = [e.clientX, e.clientY];
const rect = this._header.current!.getBoundingClientRect();
const before = pt[1] < rect.top + rect.height / 2;
- const inside = pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocs?.length);
+ const inside = pt[0] > rect.left + rect.width * 0.33 || (!before && this.treeViewOpen && this.childDocs?.length);
this._header.current!.className = 'treeView-header';
if (!this.props.treeView.outlineMode || DragManager.DocDragData?.treeViewDoc === this.props.treeView.rootDoc) {
if (inside) this._header.current!.className += ' treeView-header-inside';
@@ -349,7 +393,7 @@ export class TreeView extends React.Component<TreeViewProps> {
if (!this._header.current) return false;
const rect = this._header.current.getBoundingClientRect();
const before = pt[1] < rect.top + rect.height / 2;
- const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocs?.length ? true : false);
+ const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > rect.left + rect.width * 0.33 || (!before && this.treeViewOpen && this.childDocs?.length ? true : false);
if (de.complete.linkDragData) {
const sourceDoc = de.complete.linkDragData.linkSourceGetAnchor();
const destDoc = this.doc;
@@ -389,9 +433,9 @@ export class TreeView extends React.Component<TreeViewProps> {
return (doc instanceof Doc ? [doc] : doc).reduce((flg, doc) => flg && innerAdd(doc), true as boolean);
};
const addDoc = inside ? localAdd : parentAddDoc;
- const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same') && moveDocument;
+ const move = (!dropAction || dropAction === 'proto' || dropAction === 'move' || dropAction === 'same' || dropAction === 'inSame') && moveDocument;
const canAdd = (!this.props.treeView.outlineMode && !StrCast((inside ? this.props.document : this.props.treeViewParent)?.treeView_FreezeChildren).includes('add')) || forceAdd;
- if (canAdd) {
+ if (canAdd && (dropAction !== 'inSame' || droppedDocuments.every(d => d.embedContainer === this.props.parentTreeView?.doc))) {
this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = true);
const res = droppedDocuments.reduce((added, d) => (move ? move(d, undefined, addDoc) || (dropAction === 'proto' ? addDoc(d) : false) : addDoc(d)) || added, false);
this.props.parentTreeView instanceof TreeView && (this.props.parentTreeView.dropping = false);
@@ -554,7 +598,7 @@ export class TreeView extends React.Component<TreeViewProps> {
}
const dataIsComputed = ComputedField.WithoutComputed(() => FieldValue(this.dataDoc[key])) instanceof ComputedField;
const added = (!dataIsComputed || (this.dropping && this.moving)) && Doc.AddDocToList(this.dataDoc, key, doc, addBefore, before, false, true);
- !dataIsComputed && added && Doc.SetContainer(doc, DocCast(this.doc.embedContainer));
+ !dataIsComputed && added && Doc.SetContainer(doc, this.doc);
return added;
};
@@ -693,7 +737,7 @@ export class TreeView extends React.Component<TreeViewProps> {
@computed get renderBullet() {
TraceMobx();
const iconType = this.props.treeView.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ':open' : !this.childDocs.length ? ':empty' : '')) || 'question';
- const color = StrCast(Doc.UserDoc().userColor);
+ const color = SettingsManager.userColor;
const checked = this.onCheckedClick ? this.doc.treeView_Checked ?? 'unchecked' : undefined;
return (
<div
@@ -717,16 +761,15 @@ export class TreeView extends React.Component<TreeViewProps> {
<IconButton color={color} icon={<FontAwesomeIcon icon={[this.childDocs?.length && !this.treeViewOpen ? 'fas' : 'far', 'circle']} />} size={Size.XSMALL} />
)
) : (
- <div className="treeView-bulletIcons" style={{ color: Doc.IsSystem(DocCast(this.doc.proto)) ? 'red' : undefined }}>
- {this.onCheckedClick ? (
- <IconButton
- color={color}
- icon={<FontAwesomeIcon size="sm" icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'} />}
- size={Size.XSMALL}
+ <div className="treeView-bulletIcons">
+ <div className={`treeView-${this.onCheckedClick ? 'checkIcon' : 'expandIcon'}`}>
+ <FontAwesomeIcon
+ size="sm"
+ style={{ display: this.childDocs?.length >= 1 ? 'block' : 'none' }}
+ icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'}
/>
- ) : (
- <IconButton color={color} icon={<FontAwesomeIcon icon={iconType as IconProp} />} size={Size.XSMALL} />
- )}
+ </div>
+ {this.onCheckedClick ? null : typeof iconType === 'string' ? <FontAwesomeIcon icon={iconType as IconProp} /> : iconType}
</div>
)}
</div>
@@ -754,7 +797,7 @@ export class TreeView extends React.Component<TreeViewProps> {
@observable headerEleWidth = 0;
@computed get titleButtons() {
const customHeaderButtons = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Decorations);
- const color = StrCast(Doc.UserDoc().userColor);
+ const color = SettingsManager.userColor;
return this.props.treeViewHideHeaderFields() || this.doc.treeView_HideHeaderFields ? null : (
<>
{customHeaderButtons} {/* e.g.,. hide button is set by dashboardStyleProvider */}
@@ -924,8 +967,8 @@ export class TreeView extends React.Component<TreeViewProps> {
}
})}
Document={this.doc}
+ DataDoc={undefined} // or this.dataDoc?
layout_fitWidth={returnTrue}
- DataDoc={undefined}
scriptContext={this}
hideDecorationTitle={this.props.treeView.outlineMode}
hideResizeHandles={this.props.treeView.outlineMode}
@@ -972,8 +1015,8 @@ export class TreeView extends React.Component<TreeViewProps> {
ref={this._tref}
title="click to edit title. Double Click or Drag to Open"
style={{
- backgroundColor: Doc.IsSystem(this.props.document) || this.props.document.isFolder ? StrCast(Doc.UserDoc().userVariantColor) : undefined,
- color: Doc.IsSystem(this.props.document) || this.props.document.isFolder ? lightOrDark(StrCast(Doc.UserDoc().userVariantColor)) : undefined,
+ backgroundColor: Doc.IsSystem(this.props.document) || this.props.document.isFolder ? SettingsManager.userVariantColor : undefined,
+ color: Doc.IsSystem(this.props.document) || this.props.document.isFolder ? lightOrDark(SettingsManager.userVariantColor) : undefined,
fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? 'bold' : undefined,
textDecoration: Doc.GetT(this.doc, 'title', 'string', true) ? 'underline' : undefined,
outline: this.doc === Doc.ActiveDashboard ? 'dashed 1px #06123232' : undefined,
@@ -1002,7 +1045,7 @@ export class TreeView extends React.Component<TreeViewProps> {
<div
className="treeView-background"
style={{
- background: StrCast(Doc.UserDoc().userColor),
+ background: SettingsManager.userColor,
}}
/>
{contents}
@@ -1098,7 +1141,7 @@ export class TreeView extends React.Component<TreeViewProps> {
const pt = [de.clientX, de.clientY];
const rect = this._header.current!.getBoundingClientRect();
const before = pt[1] < rect.top + rect.height / 2;
- const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > Math.min(rect.left + 75, rect.left + rect.width * 0.75) || (!before && this.treeViewOpen && this.childDocs?.length ? true : false);
+ const inside = this.props.treeView.fileSysMode && !this.doc.isFolder ? false : pt[0] > rect.left + rect.width * 0.33 || (!before && this.treeViewOpen && this.childDocs?.length ? true : false);
const docs = this.props.treeView.onTreeDrop(de, (docs: Doc[]) => this.dropDocuments(docs, before, inside, 'copy', undefined, undefined, false));
};
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index 1e76d373c..15b6e1d37 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -350,7 +350,7 @@ export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc:
groupNames.push({ type: 'text', text: toLabel(Math.ceil(maxTime)), x: Math.ceil(maxTime - minTime) * scaling, y: 0, height: fontHeight, fontSize, payload: undefined });
}
- const divider = { type: 'div', color: Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'dimgray' : 'black', x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
+ const divider = { type: 'div', color: 'black', x: 0, y: 0, width: panelDim[0], height: -1, payload: undefined };
return normalizeResults(panelDim, fontHeight, docMap, poolData, viewDefsToJSX, groupNames, (maxTime - minTime) * scaling, [divider]);
function layoutDocsAtTime(keyDocs: Doc[], key: number) {
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
index 89deb733a..24a758d8c 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx
@@ -228,6 +228,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const textX = (Math.min(pt1[0], pt2[0]) + Math.max(pt1[0], pt2[0])) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetX);
const textY = (pt1[1] + pt2[1]) / 2 + NumCast(LinkDocs[0].link_relationship_OffsetY);
+ const link = this.props.LinkDocs[0];
return {
a,
b,
@@ -238,11 +239,11 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
textX,
textY,
// fully connected
- pt1,
- pt2,
+ // pt1,
+ // pt2,
// this code adds space between links
- // pt1: [pt1[0] + pt1normalized[0] * 13, pt1[1] + pt1normalized[1] * 13],
- // pt2: [pt2[0] + pt2normalized[0] * 13, pt2[1] + pt2normalized[1] * 13],
+ pt1: link.link_displayArrow ? [pt1[0] + pt1normalized[0] * 3*NumCast(link.link_displayArrow_scale, 4), pt1[1] + pt1normalized[1] * 3*NumCast(link.link_displayArrow_scale, 3)] : pt1,
+ pt2: link.link_displayArrow ? [pt2[0] + pt2normalized[0] * 3*NumCast(link.link_displayArrow_scale, 4), pt2[1] + pt2normalized[1] * 3*NumCast(link.link_displayArrow_scale, 3)] : pt2,
};
}
@@ -256,9 +257,9 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
const linkColorList = Doc.UserDoc().link_ColorList as List<string>;
const linkRelationshipSizes = Doc.UserDoc().link_relationshipSizes as List<number>;
const currRelationshipIndex = linkRelationshipList.indexOf(linkRelationship);
- const linkDescription = Field.toString(link.link_description as any as Field);
+ const linkDescription = Field.toString(link.link_description as any as Field).split('\n')[0];
- const linkSize = currRelationshipIndex === -1 || currRelationshipIndex >= linkRelationshipSizes.length ? -1 : linkRelationshipSizes[currRelationshipIndex];
+ const linkSize = Doc.noviceMode || currRelationshipIndex === -1 || currRelationshipIndex >= linkRelationshipSizes.length ? -1 : linkRelationshipSizes[currRelationshipIndex];
//access stroke color using index of the relationship in the color list (default black)
const stroke = currRelationshipIndex === -1 || currRelationshipIndex >= linkColorList.length ? StrCast(link._backgroundColor, 'black') : linkColorList[currRelationshipIndex];
@@ -268,15 +269,12 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
//thickness varies linearly from 3px to 12px for increasing link count
const strokeWidth = linkSize === -1 ? '3px' : Math.floor(2 + 10 * (linkSize / Math.max(...linkRelationshipSizes))) + 'px';
- if (link.link_displayArrow === undefined) {
- link.link_displayArrow = false;
- }
-
+ const arrowScale = NumCast(link.link_displayArrow_scale, 3)
return link.opacity === 0 || !a.width || !b.width || (!(Doc.UserDoc().showLinkLines || link.link_displayLine) && !aActive && !bActive) ? null : (
<>
<defs>
- <marker id={`${link[Id] + 'arrowhead'}`} markerWidth="4" markerHeight="3" refX="0" refY="1.5" orient="auto">
- <polygon points="0 0, 3 1.5, 0 3" fill={stroke} />
+ <marker id={`${link[Id] + 'arrowhead'}`} markerWidth={`${4*arrowScale}`} markerHeight={`${3*arrowScale}`} refX="0" refY={`${1.5*arrowScale}`} orient="auto">
+ <polygon points={`0 0, ${3*arrowScale} ${1.5*arrowScale}, 0 ${3*arrowScale}`} fill={stroke} />
</marker>
<filter id="outline">
<feMorphology in="SourceAlpha" result="expanded" operator="dilate" radius="1" />
@@ -306,7 +304,7 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo
{textX === undefined || !linkDescription ? null : (
<text filter={`url(#${link[Id] + 'background'})`} className="collectionfreeformlinkview-linkText" x={textX} y={textY} onPointerDown={this.pointerDown}>
<tspan>&nbsp;</tspan>
- <tspan dy="2">{linkDescription}</tspan>
+ <tspan dy="2">{linkDescription.substring(0, 50) + (linkDescription.length > 50 ? '...' : '')}</tspan>
<tspan dy="2">&nbsp;</tspan>
</text>
)}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index e4ae251c8..c90fdf013 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -11,6 +11,15 @@
touch-action: none;
border-radius: inherit;
}
+.collectionFreeForm-groupDropper {
+ width: 10000;
+ height: 10000;
+ left: -5000;
+ top: -5000;
+ position: absolute;
+ background: transparent;
+ pointer-events: all;
+}
.collectionfreeformview-grid {
transform-origin: top left;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 16d6f1270..676e96714 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -16,7 +16,7 @@ import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } fro
import { ImageField } from '../../../../fields/URLField';
import { TraceMobx } from '../../../../fields/util';
import { GestureUtils } from '../../../../pen-gestures/GestureUtils';
-import { aggregateBounds, emptyFunction, intersectRect, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
+import { aggregateBounds, emptyFunction, intersectRect, lightOrDark, returnFalse, returnNone, returnTrue, returnZero, setupMoveUpEvents, Utils } from '../../../../Utils';
import { CognitiveServices } from '../../../cognitive_services/CognitiveServices';
import { Docs, DocUtils } from '../../../documents/Documents';
import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes';
@@ -33,7 +33,6 @@ import { undoBatch, UndoManager } from '../../../util/UndoManager';
import { COLLECTION_BORDER_WIDTH } from '../../../views/global/globalCssVariables.scss';
import { Timeline } from '../../animationtimeline/Timeline';
import { ContextMenu } from '../../ContextMenu';
-import { DocumentDecorations } from '../../DocumentDecorations';
import { GestureOverlay } from '../../GestureOverlay';
import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke';
import { LightboxView } from '../../LightboxView';
@@ -52,6 +51,7 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso
import './CollectionFreeFormView.scss';
import { MarqueeView } from './MarqueeView';
import React = require('react');
+import { FollowLinkScript } from '../../../util/LinkFollower';
export type collectionFreeformViewProps = {
NativeWidth?: () => number;
@@ -97,7 +97,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
private _brushtimer: any;
private _brushtimer1: any;
- private get isAnnotationOverlay() {
+ public get isAnnotationOverlay() {
return this.props.isAnnotationOverlay;
}
public get scaleFieldKey() {
@@ -124,7 +124,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _timelineRef = React.createRef<Timeline>();
@observable _marqueeRef: HTMLDivElement | null = null;
@observable _marqueeViewRef = React.createRef<MarqueeView>();
- @observable ChildDrag: DocumentView | undefined; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
+ @observable GroupChildDrag: boolean = false; // child document view being dragged. needed to update drop areas of groups when a group item is dragged.
@observable _brushedView = { width: 0, height: 0, panX: 0, panY: 0, opacity: 0 }; // highlighted region of freeform canvas used by presentations to indicate a region
constructor(props: any) {
@@ -155,7 +155,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
? { x: cb[0], y: cb[1], r: cb[2], b: cb[3] }
: this.props.contentBounds?.() ??
aggregateBounds(
- this._layoutElements.filter(e => e.bounds && e.bounds.width && !e.bounds.z).map(e => e.bounds!),
+ this._layoutElements.filter(e => e.bounds?.width && !e.bounds.z).map(e => e.bounds!),
NumCast(this.layoutDoc._xPadding, 10),
NumCast(this.layoutDoc._yPadding, 10)
);
@@ -186,7 +186,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return this.props.ScreenToLocalTransform().translate(-this.borderWidth, -this.borderWidth);
}
@computed get cachedGetTransform(): Transform {
- return this.getContainerTransform().translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY).transform(this.cachedGetLocalTransform);
+ return this.getContainerTransform()
+ .scale(this.props.isAnnotationOverlay ? 1 : 1 / this.nativeDim())
+ .translate(-this.cachedCenteringShiftX, -this.cachedCenteringShiftY)
+ .transform(this.cachedGetLocalTransform);
}
public static gotoKeyframe(timer: NodeJS.Timeout | undefined, docs: Doc[], duration: number) {
@@ -311,6 +314,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
focus = (anchor: Doc, options: DocFocusOptions) => {
if (this._lightboxDoc) return;
+ if (anchor.type !== DocumentType.CONFIG && !DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]).includes(anchor)) return;
const xfToCollection = options?.docTransform ?? Transform.Identity();
const savedState = { panX: NumCast(this.Document[this.panXFieldKey]), panY: NumCast(this.Document[this.panYFieldKey]), scale: options?.willZoomCentered ? this.Document[this.scaleFieldKey] : undefined };
const cantTransform = this.fitContentsToBox || ((this.rootDoc._isGroup || this.layoutDoc._lockedTransform) && !LightboxView.LightboxDoc);
@@ -338,7 +342,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData, xp: number, yp: number) {
- if (!de.embedKey && !this.ChildDrag && this.rootDoc._isGroup) return false;
if (!super.onInternalDrop(e, de)) return false;
const refDoc = docDragData.droppedDocuments[0];
const [xpo, ypo] = this.getContainerTransform().transformPoint(de.x, de.y);
@@ -420,11 +423,27 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (linkDragData.linkDragView.props.docViewPath().includes(this.props.docViewPath().lastElement())) {
let added = false;
// do nothing if link is dropped into any freeform view parent of dragged document
- if (!linkDragData.dragDocument.embedContainer || linkDragData.dragDocument.embedContainer !== this.rootDoc) {
- const source = Docs.Create.TextDocument('', { _width: 200, _height: 75, x: xp, y: yp, title: 'dropped annotation' });
- added = this.props.addDocument?.(source) ? true : false;
- de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { link_relationship: 'annotated by:annotation of' }); // TODODO this is where in text links get passed
- }
+ const source =
+ !linkDragData.dragDocument.embedContainer || linkDragData.dragDocument.embedContainer !== this.rootDoc
+ ? Docs.Create.TextDocument('', { _width: 200, _height: 75, x: xp, y: yp, title: 'dropped annotation' })
+ : Docs.Create.FontIconDocument({
+ title: 'anchor',
+ icon_label: '',
+ followLinkToggle: true,
+ icon: 'map-pin',
+ x: xp,
+ y: yp,
+ backgroundColor: '#ACCEF7',
+ layout_hideAllLinks: true,
+ layout_hideLinkButton: true,
+ _width: 15,
+ _height: 15,
+ _xPadding: 0,
+ onClick: FollowLinkScript(),
+ });
+ added = this.props.addDocument?.(source) ? true : false;
+ de.complete.linkDocument = DocUtils.MakeLink(linkDragData.linkSourceGetAnchor(), source, { link_relationship: 'annotated by:annotation of' }); // TODODO this is where in text links get passed
+
e.stopPropagation();
!added && e.preventDefault();
return added;
@@ -844,7 +863,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// create a new curve by appending all curves of the current segment together in order to render a single new stroke.
if (!e.shiftKey) {
this._eraserLock++;
- this.segmentInkStroke(intersect.inkView, intersect.t).forEach(segment =>
+ const segments = this.segmentInkStroke(intersect.inkView, intersect.t);
+ segments.forEach(segment =>
this.forceStrokeGesture(
e,
GestureUtils.Gestures.Stroke,
@@ -1023,7 +1043,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
- if (this.Document._isGroup || this.Document._freeform_noZoom) return;
+ if (this.Document._isGroup || this.Document[(this.props.viewField ?? '_') + 'freeform_noZoom']) return;
let deltaScale = deltaY > 0 ? 1 / 1.05 : 1.05;
if (deltaScale < 0) deltaScale = -deltaScale;
const [x, y] = this.getTransform().transformPoint(pointX, pointY);
@@ -1031,12 +1051,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (deltaScale * invTransform.Scale > 20) {
deltaScale = 20 / invTransform.Scale;
}
- if (deltaScale < 1 && invTransform.Scale <= NumCast(this.rootDoc._freeform_scale_min, 1) && this.isAnnotationOverlay) {
+ if (deltaScale < 1 && invTransform.Scale <= NumCast(this.rootDoc[this.scaleFieldKey + '_min'])) {
this.setPan(0, 0);
return;
}
- if (deltaScale * invTransform.Scale < NumCast(this.rootDoc._freeform_scale_min, 1) && this.isAnnotationOverlay) {
- deltaScale = NumCast(this.rootDoc._freeform_scale_min, 1) / invTransform.Scale;
+ if (deltaScale * invTransform.Scale > NumCast(this.rootDoc[this.scaleFieldKey + '_max'], Number.MAX_VALUE)) {
+ deltaScale = NumCast(this.rootDoc[this.scaleFieldKey + '_max'], 1) / invTransform.Scale;
+ }
+ if (deltaScale * invTransform.Scale < NumCast(this.rootDoc[this.scaleFieldKey + '_min'], this.isAnnotationOverlay ? 1 : 0)) {
+ deltaScale = NumCast(this.rootDoc[this.scaleFieldKey + '_min'], 1) / invTransform.Scale;
}
const localTransform = invTransform.scaleAbout(deltaScale, x, y);
@@ -1526,12 +1549,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
});
});
- if (this.props.isAnnotationOverlay && this.props.Document[this.scaleFieldKey]) {
- // don't zoom out farther than 1-1 if it's a bounded item (image, video, pdf), otherwise don't allow zooming in closer than 1-1 if it's a text sidebar
- if (this.props.viewField) this.props.Document[this.scaleFieldKey] = Math.min(1, this.zoomScaling());
- else this.props.Document[this.scaleFieldKey] = Math.max(1, this.zoomScaling()); // NumCast(this.props.Document[this.scaleFieldKey]));
- }
-
this.Document._freeform_useClusters && !this._clusterSets.length && this.childDocs.length && this.updateClusters(true);
return elements;
}
@@ -1745,19 +1762,24 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@undoBatch
toggleNativeDimensions = () => Doc.toggleNativeDimensions(this.layoutDoc, 1, this.nativeWidth, this.nativeHeight);
+ ///
+ /// resetView restores a freeform collection to unit scale and centered at (0,0) UNLESS
+ /// the view is a group, in which case this does nothing (since Groups calculate their own scale and center)
+ ///
+ @undoBatch
+ resetView = () => {
+ if (!this.props.Document._isGroup) {
+ this.props.Document[this.panXFieldKey] = this.props.Document[this.panYFieldKey] = 0;
+ this.props.Document[this.scaleFieldKey] = 1;
+ }
+ };
+
onContextMenu = (e: React.MouseEvent) => {
if (this.props.isAnnotationOverlay || !ContextMenu.Instance) return;
const appearance = ContextMenu.Instance.findByDescription('Appearance...');
const appearanceItems = appearance && 'subitems' in appearance ? appearance.subitems : [];
- appearanceItems.push({
- description: 'Reset View',
- event: () => {
- this.props.Document[this.panXFieldKey] = this.props.Document[this.panYFieldKey] = 0;
- this.props.Document[this.scaleFieldKey] = 1;
- },
- icon: 'compress-arrows-alt',
- });
+ !this.props.Document._isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' });
!Doc.noviceMode &&
appearanceItems.push({
description: 'Toggle auto arrange',
@@ -1885,6 +1907,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
showPresPaths = () => (CollectionFreeFormView.ShowPresPaths ? PresBox.Instance.getPaths(this.rootDoc) : null);
brushedView = () => this._brushedView;
+ gridColor = () => {
+ const backColor = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor + ':box');
+ return lightOrDark(backColor);
+ };
@computed get marqueeView() {
TraceMobx();
return (
@@ -1917,6 +1943,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
PanelHeight={this.props.PanelHeight}
panX={this.panX}
panY={this.panY}
+ color={this.gridColor}
nativeDimScaling={this.nativeDim}
zoomScaling={this.zoomScaling}
layoutDoc={this.layoutDoc}
@@ -1954,15 +1981,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
nativeDim = () => this.nativeDimScaling;
- 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));
- }
- };
-
@action
brushView = (viewport: { width: number; height: number; panX: number; panY: number }, transTime: number) => {
this._brushtimer1 && clearTimeout(this._brushtimer1);
@@ -2047,30 +2065,13 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
{/* // uncomment to show snap lines */}
<div className="snapLines" style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', pointerEvents: 'none' }}>
<svg style={{ width: '100%', height: '100%' }}>
- {this._hLines?.map(l => (
- <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />
- ))}
- {this._vLines?.map(l => (
- <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />
- ))}
+ {(this._hLines ?? [])
+ .map(l => <line x1="0" y1={l} x2="1000" y2={l} stroke="black" />) //
+ .concat((this._vLines ?? []).map(l => <line y1="0" x1={l} y2="1000" x2={l} stroke="black" />)) ?? []}
</svg>
</div>
- {this.props.Document._isGroup && SnappingManager.GetIsDragging() && this.ChildDrag ? (
- <div
- className="collectionFreeForm-groupDropper"
- ref={this.createGroupEventsTarget}
- style={{
- width: this.ChildDrag ? '10000' : '100%',
- height: this.ChildDrag ? '10000' : '100%',
- left: this.ChildDrag ? '-5000' : 0,
- top: this.ChildDrag ? '-5000' : 0,
- position: 'absolute',
- background: '#0009930',
- pointerEvents: 'all',
- }}
- />
- ) : null}
+ {this.GroupChildDrag ? <div className="collectionFreeForm-groupDropper" /> : null}
</>
)}
</div>
@@ -2216,6 +2217,7 @@ interface CollectionFreeFormViewBackgroundGridProps {
panY: () => number;
PanelWidth: () => number;
PanelHeight: () => number;
+ color: () => string;
isAnnotationOverlay?: boolean;
nativeDimScaling: () => number;
zoomScaling: () => number;
@@ -2237,7 +2239,7 @@ class CollectionFreeFormBackgroundGrid extends React.Component<CollectionFreeFor
const renderGridSpace = gridSpace * this.props.zoomScaling();
const w = this.props.PanelWidth() / this.props.nativeDimScaling() + 2 * renderGridSpace;
const h = this.props.PanelHeight() / this.props.nativeDimScaling() + 2 * renderGridSpace;
- const strokeStyle = Doc.ActiveDashboard?.colorScheme === ColorScheme.Dark ? 'rgba(255,255,255,0.5)' : 'rgba(0, 0,0,0.5)';
+ const strokeStyle = this.props.color();
return !this.props.nativeDimScaling() ? null : (
<canvas
className="collectionFreeFormView-grid"
@@ -2285,7 +2287,7 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY
}
while (parFfview?.rootDoc._isGroup) parFfview = parFfview.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView;
const ffview = selfFfview && selfFfview.rootDoc[selfFfview.scaleFieldKey] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview
- ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5, browseTransitionTime);
+ ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), ffview?.isAnnotationOverlay ? 1 : 0.5, browseTransitionTime);
Doc.linkFollowHighlight(dv?.props.Document, false);
}
});
@@ -2320,9 +2322,3 @@ ScriptingGlobals.add(function bringToFront() {
ScriptingGlobals.add(function sendToBack(doc: Doc) {
SelectionManager.Views().forEach(view => view.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView.bringToFront(view.rootDoc, true));
});
-ScriptingGlobals.add(function resetView() {
- SelectionManager.Docs().forEach(doc => {
- doc._freeform_panX = doc._freeform_panY = 0;
- doc._freeform_scale = 1;
- });
-});
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 71900c63f..607f9fb95 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -8,6 +8,7 @@ import { IconButton } from 'browndash-components';
import { StrCast } from '../../../../fields/Types';
import { Doc } from '../../../../fields/Doc';
import { computed } from 'mobx';
+import { SettingsManager } from '../../../util/SettingsManager';
@observer
export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -27,43 +28,18 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
}
@computed get userColor() {
- return StrCast(Doc.UserDoc().userColor)
+ return SettingsManager.userColor;
}
render() {
const presPinWithViewIcon = <img src="/assets/pinWithView.png" style={{ width: 19 }} />;
const buttons = (
<>
- <IconButton
- tooltip={"Create a Collection"}
- onPointerDown={this.createCollection}
- icon={<FontAwesomeIcon icon="object-group"/>}
- color={this.userColor}
- />
- <IconButton
- tooltip={"Create a Grouping"}
- onPointerDown={e => this.createCollection(e, true)}
- icon={<FontAwesomeIcon icon="layer-group"/>}
- color={this.userColor}
- />
- <IconButton
- tooltip={"Summarize Documents"}
- onPointerDown={this.summarize}
- icon={<FontAwesomeIcon icon="compress-arrows-alt"/>}
- color={this.userColor}
- />
- <IconButton
- tooltip={"Delete Documents"}
- onPointerDown={this.delete}
- icon={<FontAwesomeIcon icon="trash-alt"/>}
- color={this.userColor}
- />
- <IconButton
- tooltip={"Pin selected region"}
- onPointerDown={this.pinWithView}
- icon={<FontAwesomeIcon icon="map-pin"/>}
- color={this.userColor}
- />
+ <IconButton tooltip={'Create a Collection'} onPointerDown={this.createCollection} icon={<FontAwesomeIcon icon="object-group" />} color={this.userColor} />
+ <IconButton tooltip={'Create a Grouping'} onPointerDown={e => this.createCollection(e, true)} icon={<FontAwesomeIcon icon="layer-group" />} color={this.userColor} />
+ <IconButton tooltip={'Summarize Documents'} onPointerDown={this.summarize} icon={<FontAwesomeIcon icon="compress-arrows-alt" />} color={this.userColor} />
+ <IconButton tooltip={'Delete Documents'} onPointerDown={this.delete} icon={<FontAwesomeIcon icon="trash-alt" />} color={this.userColor} />
+ <IconButton tooltip={'Pin selected region'} onPointerDown={this.pinWithView} icon={<FontAwesomeIcon icon="map-pin" />} color={this.userColor} />
</>
);
return this.getElement(buttons);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index cd7bd28e9..4c502021d 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -18,7 +18,7 @@ import { SelectionManager } from '../../../util/SelectionManager';
import { Transform } from '../../../util/Transform';
import { undoBatch, UndoManager } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
-import { OpenWhere } from '../../nodes/DocumentView';
+import { DocumentView, OpenWhere } from '../../nodes/DocumentView';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { pasteImageBitmap } from '../../nodes/WebBoxRenderer';
import { PreviewCursor } from '../../PreviewCursor';
@@ -335,7 +335,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, false, this.props.Document);
+ !DocumentView.ExploreMode && this.setPreviewCursor(e.clientX, e.clientY, false, false, this.props.Document);
} else e.stopPropagation();
}
}
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 4bb5c5adf..faf7501c4 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -1,5 +1,6 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@material-ui/core';
+import { Toggle, ToggleType, Type } from 'browndash-components';
import { action, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -7,22 +8,20 @@ import { Doc, Opt } from '../../../../fields/Doc';
import { Height, Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
-import { emptyFunction, returnEmptyDoclist, returnTrue, StopEvent, Utils } from '../../../../Utils';
+import { emptyFunction, returnEmptyDoclist, returnTrue, Utils } from '../../../../Utils';
import { CollectionViewType } from '../../../documents/DocumentTypes';
+import { BranchingTrailManager } from '../../../util/BranchingTrailManager';
import { DocumentManager } from '../../../util/DocumentManager';
import { DragManager, dropActionType } from '../../../util/DragManager';
+import { SettingsManager } from '../../../util/SettingsManager';
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 { UndoStack } from '../../UndoStack';
import { CollectionStackedTimeline } from '../CollectionStackedTimeline';
import { CollectionSubView } from '../CollectionSubView';
import './CollectionLinearView.scss';
-import { Button, Toggle, ToggleType, Type } from 'browndash-components';
-import { Colors } from '../../global/globalEnums';
-import { SettingsManager } from '../../../util/SettingsManager';
/**
* CollectionLinearView is the class for rendering the horizontal collection
@@ -146,6 +145,7 @@ export class CollectionLinearView extends CollectionSubView() {
case '<LinkingUI>': return this.getLinkUI();
case '<CurrentlyPlayingUI>': return this.getCurrentlyPlayingUI();
case '<UndoStack>': return <UndoStack key={doc[Id]} width={200} height={40} inline={true} />;
+ case '<Branching>': return Doc.UserDoc().isBranchingMode ? <BranchingTrailManager /> : null;
}
const nested = doc._type_collection === CollectionViewType.Linear;
@@ -227,7 +227,7 @@ export class CollectionLinearView extends CollectionSubView() {
<div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu} style={{ minHeight: this.dimension(), pointerEvents: 'all' }}>
{
<>
- {menuOpener}
+ {!this.layoutDoc.linearView_Expandable ? null : menuOpener}
{!this.layoutDoc.linearView_IsOpen ? null : (
<div
className="collectionLinearView-content"
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
index 80da4e1a2..81453c0b8 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx
@@ -1,4 +1,5 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Tooltip } from '@mui/material';
import { Button } from 'browndash-components';
import { action, computed } from 'mobx';
import { observer } from 'mobx-react';
@@ -7,6 +8,7 @@ import { Doc, DocListCast } from '../../../../fields/Doc';
import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types';
import { returnFalse } from '../../../../Utils';
import { DragManager, dropActionType } from '../../../util/DragManager';
+import { SettingsManager } from '../../../util/SettingsManager';
import { Transform } from '../../../util/Transform';
import { undoable, undoBatch } from '../../../util/UndoManager';
import { DocumentView } from '../../nodes/DocumentView';
@@ -301,13 +303,13 @@ export class CollectionMulticolumnView extends CollectionSubView() {
.scale(this.props.NativeDimScaling?.() || 1);
const shouldNotScale = () => this.props.fitContentsToBox?.() || BoolCast(layout.freeform_fitContentsToBox);
collector.push(
- <div className="document-wrapper" key={'wrapper' + i} style={{ width: width() }}>
- {this.getDisplayDoc(layout, dxf, docwidth, docheight, shouldNotScale)}
- <Button icon={<FontAwesomeIcon icon={'times'} size={'lg'} />} onClick={undoable(e => {
- this.props.removeDocument?.(layout);
- }, "close doc")} color={StrCast(Doc.UserDoc().userColor)} />
- <WidthLabel layout={layout} collectionDoc={Document} />
- </div>,
+ <Tooltip title={'Tab: ' + StrCast(layout.title)}>
+ <div className="document-wrapper" key={'wrapper' + i} style={{ width: width() }}>
+ {this.getDisplayDoc(layout, dxf, docwidth, docheight, shouldNotScale)}
+ <Button tooltip="Remove document from header bar" icon={<FontAwesomeIcon icon="times" size="lg" />} onClick={undoable(e => this.props.removeDocument?.(layout), 'close doc')} color={SettingsManager.userColor} />
+ <WidthLabel layout={layout} collectionDoc={Document} />
+ </div>
+ </Tooltip>,
<ResizeBar
width={resizerWidth}
key={'resizer' + i}
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index 273e609ca..868b1140d 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -1,12 +1,12 @@
-import * as React from "react";
-import { observer } from "mobx-react";
-import { observable, action } from "mobx";
-import { Doc } from "../../../../fields/Doc";
-import { NumCast, StrCast } from "../../../../fields/Types";
-import { DimUnit } from "./CollectionMulticolumnView";
-import { UndoManager } from "../../../util/UndoManager";
-import { StyleProviderFunc } from "../../nodes/DocumentView";
-import { StyleProp } from "../../StyleProvider";
+import * as React from 'react';
+import { observer } from 'mobx-react';
+import { observable, action } from 'mobx';
+import { Doc } from '../../../../fields/Doc';
+import { NumCast, StrCast } from '../../../../fields/Types';
+import { DimUnit } from './CollectionMulticolumnView';
+import { UndoManager } from '../../../util/UndoManager';
+import { StyleProviderFunc } from '../../nodes/DocumentView';
+import { StyleProp } from '../../StyleProvider';
interface ResizerProps {
width: number;
@@ -31,13 +31,13 @@ export default class ResizeBar extends React.Component<ResizerProps> {
this.props.select(false);
e.stopPropagation();
e.preventDefault();
- window.removeEventListener("pointermove", this.onPointerMove);
- window.removeEventListener("pointerup", this.onPointerUp);
- window.addEventListener("pointermove", this.onPointerMove);
- window.addEventListener("pointerup", this.onPointerUp);
+ window.removeEventListener('pointermove', this.onPointerMove);
+ window.removeEventListener('pointerup', this.onPointerUp);
+ window.addEventListener('pointermove', this.onPointerMove);
+ window.addEventListener('pointerup', this.onPointerUp);
this.isResizingActive = true;
- this._resizeUndo = UndoManager.StartBatch("multcol resizing");
- }
+ this._resizeUndo = UndoManager.StartBatch('multcol resizing');
+ };
private onPointerMove = ({ movementX }: PointerEvent) => {
const { toLeft, toRight, columnUnitLength } = this.props;
@@ -47,30 +47,30 @@ export default class ResizeBar extends React.Component<ResizerProps> {
const unitLength = columnUnitLength();
if (unitLength) {
if (toNarrow) {
- const scale = StrCast(toNarrow._dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
+ const scale = StrCast(toNarrow._dimUnit, '*') === DimUnit.Ratio ? unitLength : 1;
toNarrow._dimMagnitude = Math.max(0.05, NumCast(toNarrow._dimMagnitude, 1) - Math.abs(movementX) / scale);
}
if (toWiden) {
- const scale = StrCast(toWiden._dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
+ const scale = StrCast(toWiden._dimUnit, '*') === DimUnit.Ratio ? unitLength : 1;
toWiden._dimMagnitude = Math.max(0.05, NumCast(toWiden._dimMagnitude, 1) + Math.abs(movementX) / scale);
}
}
- }
+ };
private get isActivated() {
const { toLeft, toRight } = this.props;
if (toLeft && toRight) {
- if (StrCast(toLeft._dimUnit, "*") === DimUnit.Pixel && StrCast(toRight._dimUnit, "*") === DimUnit.Pixel) {
+ if (StrCast(toLeft._dimUnit, '*') === DimUnit.Pixel && StrCast(toRight._dimUnit, '*') === DimUnit.Pixel) {
return false;
}
return true;
} else if (toLeft) {
- if (StrCast(toLeft._dimUnit, "*") === DimUnit.Pixel) {
+ if (StrCast(toLeft._dimUnit, '*') === DimUnit.Pixel) {
return false;
}
return true;
} else if (toRight) {
- if (StrCast(toRight._dimUnit, "*") === DimUnit.Pixel) {
+ if (StrCast(toRight._dimUnit, '*') === DimUnit.Pixel) {
return false;
}
return true;
@@ -82,23 +82,25 @@ export default class ResizeBar extends React.Component<ResizerProps> {
private onPointerUp = () => {
this.isResizingActive = false;
this.isHoverActive = false;
- window.removeEventListener("pointermove", this.onPointerMove);
- window.removeEventListener("pointerup", this.onPointerUp);
+ window.removeEventListener('pointermove', this.onPointerMove);
+ window.removeEventListener('pointerup', this.onPointerUp);
this._resizeUndo?.end();
this._resizeUndo = undefined;
- }
+ };
render() {
- return <div className="multiColumnResizer"
- style={{
- width: this.props.width,
- backgroundColor: !this.props.isContentActive?.() ? "" : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor)
- }}
- onPointerEnter={action(() => this.isHoverActive = true)}
- onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}
- >
- <div className={"multiColumnResizer-hdl"} onPointerDown={e => this.registerResizing(e)} />
- </div>;
+ return (
+ <div
+ className="multiColumnResizer"
+ style={{
+ pointerEvents: this.props.isContentActive?.() ? 'all' : 'none',
+ width: this.props.width,
+ backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor),
+ }}
+ onPointerEnter={action(() => (this.isHoverActive = true))}
+ onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}>
+ <div className={'multiColumnResizer-hdl'} onPointerDown={e => this.registerResizing(e)} />
+ </div>
+ );
}
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index 006ef4df6..5a9d6a82c 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -1,12 +1,12 @@
-import * as React from "react";
-import { observer } from "mobx-react";
-import { observable, action } from "mobx";
-import { Doc } from "../../../../fields/Doc";
-import { NumCast, StrCast } from "../../../../fields/Types";
-import { DimUnit } from "./CollectionMultirowView";
-import { UndoManager } from "../../../util/UndoManager";
-import { StyleProp } from "../../StyleProvider";
-import { StyleProviderFunc } from "../../nodes/DocumentView";
+import * as React from 'react';
+import { observer } from 'mobx-react';
+import { observable, action } from 'mobx';
+import { Doc } from '../../../../fields/Doc';
+import { NumCast, StrCast } from '../../../../fields/Types';
+import { DimUnit } from './CollectionMultirowView';
+import { UndoManager } from '../../../util/UndoManager';
+import { StyleProp } from '../../StyleProvider';
+import { StyleProviderFunc } from '../../nodes/DocumentView';
interface ResizerProps {
height: number;
@@ -29,13 +29,13 @@ export default class ResizeBar extends React.Component<ResizerProps> {
private registerResizing = (e: React.PointerEvent<HTMLDivElement>) => {
e.stopPropagation();
e.preventDefault();
- window.removeEventListener("pointermove", this.onPointerMove);
- window.removeEventListener("pointerup", this.onPointerUp);
- window.addEventListener("pointermove", this.onPointerMove);
- window.addEventListener("pointerup", this.onPointerUp);
+ window.removeEventListener('pointermove', this.onPointerMove);
+ window.removeEventListener('pointerup', this.onPointerUp);
+ window.addEventListener('pointermove', this.onPointerMove);
+ window.addEventListener('pointerup', this.onPointerUp);
this.isResizingActive = true;
- this._resizeUndo = UndoManager.StartBatch("multcol resizing");
- }
+ this._resizeUndo = UndoManager.StartBatch('multcol resizing');
+ };
private onPointerMove = ({ movementY }: PointerEvent) => {
const { toTop: toTop, toBottom: toBottom, columnUnitLength } = this.props;
@@ -45,30 +45,30 @@ export default class ResizeBar extends React.Component<ResizerProps> {
const unitLength = columnUnitLength();
if (unitLength) {
if (toNarrow) {
- const scale = StrCast(toNarrow._dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
+ const scale = StrCast(toNarrow._dimUnit, '*') === DimUnit.Ratio ? unitLength : 1;
toNarrow._dimMagnitude = Math.max(0.05, NumCast(toNarrow._dimMagnitude, 1) - Math.abs(movementY) / scale);
}
if (toWiden) {
- const scale = StrCast(toWiden._dimUnit, "*") === DimUnit.Ratio ? unitLength : 1;
+ const scale = StrCast(toWiden._dimUnit, '*') === DimUnit.Ratio ? unitLength : 1;
toWiden._dimMagnitude = Math.max(0.05, NumCast(toWiden._dimMagnitude, 1) + Math.abs(movementY) / scale);
}
}
- }
+ };
private get isActivated() {
const { toTop, toBottom } = this.props;
if (toTop && toBottom) {
- if (StrCast(toTop._dimUnit, "*") === DimUnit.Pixel && StrCast(toBottom._dimUnit, "*") === DimUnit.Pixel) {
+ if (StrCast(toTop._dimUnit, '*') === DimUnit.Pixel && StrCast(toBottom._dimUnit, '*') === DimUnit.Pixel) {
return false;
}
return true;
} else if (toTop) {
- if (StrCast(toTop._dimUnit, "*") === DimUnit.Pixel) {
+ if (StrCast(toTop._dimUnit, '*') === DimUnit.Pixel) {
return false;
}
return true;
} else if (toBottom) {
- if (StrCast(toBottom._dimUnit, "*") === DimUnit.Pixel) {
+ if (StrCast(toBottom._dimUnit, '*') === DimUnit.Pixel) {
return false;
}
return true;
@@ -80,23 +80,25 @@ export default class ResizeBar extends React.Component<ResizerProps> {
private onPointerUp = () => {
this.isResizingActive = false;
this.isHoverActive = false;
- window.removeEventListener("pointermove", this.onPointerMove);
- window.removeEventListener("pointerup", this.onPointerUp);
+ window.removeEventListener('pointermove', this.onPointerMove);
+ window.removeEventListener('pointerup', this.onPointerUp);
this._resizeUndo?.end();
this._resizeUndo = undefined;
- }
+ };
render() {
- return <div className="multiRowResizer"
- style={{
- height: this.props.height,
- backgroundColor: !this.props.isContentActive?.() ? "" : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor)
- }}
- onPointerEnter={action(() => this.isHoverActive = true)}
- onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}
- >
- <div className={"multiRowResizer-hdl"} onPointerDown={e => this.registerResizing(e)} />
- </div>;
+ return (
+ <div
+ className="multiRowResizer"
+ style={{
+ pointerEvents: this.props.isContentActive?.() ? 'all' : 'none',
+ height: this.props.height,
+ backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor),
+ }}
+ onPointerEnter={action(() => (this.isHoverActive = true))}
+ onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))}>
+ <div className={'multiRowResizer-hdl'} onPointerDown={e => this.registerResizing(e)} />
+ </div>
+ );
}
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 52ebb7763..76bd392a5 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -9,12 +9,32 @@
.schema-table {
background-color: $white;
cursor: grab;
+ width: 100%;
.schema-table-content {
overflow: overlay;
scroll-behavior: smooth;
}
+ .schema-add {
+ position: relative;
+ height: 30;
+ display: flex;
+ align-items: center;
+ width: 100%;
+ text-align: right;
+ background: lightgray;
+ .editableView-container-editing {
+ width: 100%;
+ }
+ .editableView-input {
+ width: 100%;
+ float: right;
+ text-align: right;
+ background: yellow;
+ }
+ }
+
.schema-column-menu,
.schema-filter-menu {
background: $light-gray;
@@ -173,6 +193,12 @@
flex-direction: row;
height: 100%;
overflow: auto;
+
+ .schemaSelectionCell {
+ align-self: center;
+ width: 100%;
+ display: flex;
+ }
}
.schema-header-row > .schema-column-header:nth-child(2) > .left {
@@ -185,7 +211,7 @@
overflow-x: hidden;
overflow-y: auto;
padding: 5px;
- display: inline-block;
+ display: inline-flex;
}
.schema-row {
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index babe5c810..f73c037f4 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -63,7 +63,7 @@ export class CollectionSchemaView extends CollectionSubView() {
public static _minColWidth: number = 25;
public static _rowMenuWidth: number = 60;
public static _previewDividerWidth: number = 4;
- public static _newNodeInputHeight: number = 30;
+ public static _newNodeInputHeight: number = 20;
public fieldInfos = new ObservableMap<string, FInfo>();
@observable _menuKeys: string[] = [];
@@ -609,10 +609,10 @@ export class CollectionSchemaView extends CollectionSubView() {
this._menuKeys = this.documentKeys.filter(value => value.toLowerCase().includes(this._menuValue.toLowerCase()));
};
- getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(':')[0] == field);
+ getFieldFilters = (field: string) => StrListCast(this.Document._childFilters).filter(filter => filter.split(Doc.FilterSep)[0] == field);
removeFieldFilters = (field: string) => {
- this.getFieldFilters(field).forEach(filter => Doc.setDocFilter(this.Document, field, filter.split(':')[1], 'remove'));
+ this.getFieldFilters(field).forEach(filter => Doc.setDocFilter(this.Document, field, filter.split(Doc.FilterSep)[1], 'remove'));
};
onFilterKeyDown = (e: React.KeyboardEvent) => {
@@ -766,8 +766,8 @@ export class CollectionSchemaView extends CollectionSubView() {
return keyOptions.map(key => {
let bool = false;
if (filters !== undefined) {
- const ind = filters.findIndex(filter => filter.split(':')[1] === key);
- const fields = ind === -1 ? undefined : filters[ind].split(':');
+ const ind = filters.findIndex(filter => filter.split(Doc.FilterSep)[1] === key);
+ const fields = ind === -1 ? undefined : filters[ind].split(Doc.FilterSep);
bool = fields ? fields[2] === 'check' : false;
}
return (
@@ -836,6 +836,7 @@ export class CollectionSchemaView extends CollectionSubView() {
<div ref={this._menuTarget} style={{ background: 'red', top: 0, left: 0, position: 'absolute', zIndex: 10000 }}></div>
<div
className="schema-table"
+ style={{ width: `calc(100% - ${this.previewWidth + 4}px)` }}
onWheel={e => this.props.isContentActive() && e.stopPropagation()}
ref={r => {
// prevent wheel events from passively propagating up through containers
@@ -869,14 +870,18 @@ export class CollectionSchemaView extends CollectionSubView() {
{this._columnMenuIndex !== undefined && this.renderColumnMenu}
{this._filterColumnIndex !== undefined && this.renderFilterMenu}
<CollectionSchemaViewDocs schema={this} childDocs={this.sortedDocsFunc} rowHeight={this.rowHeightFunc} setRef={(ref: HTMLDivElement | null) => (this._tableContentRef = ref)} />
- <EditableView
- GetValue={returnEmptyString}
- SetValue={undoable(value => (value ? this.addRow(Docs.Create.TextDocument(value, { title: value, _layout_autoHeight: true })) : false), 'add text doc')}
- placeholder={"Type ':' for commands"}
- contents={'+ New Node'}
- menuCallback={this.menuCallback}
- height={CollectionSchemaView._newNodeInputHeight}
- />
+ {this.layoutDoc.chromeHidden ? null : (
+ <div className="schema-add">
+ <EditableView
+ GetValue={returnEmptyString}
+ SetValue={undoable(value => (value ? this.addRow(Docs.Create.TextDocument(value, { title: value, _layout_autoHeight: true })) : false), 'add text doc')}
+ placeholder={"Type text to create note or ':' to create specific type"}
+ contents={'+ New Node'}
+ menuCallback={this.menuCallback}
+ height={CollectionSchemaView._newNodeInputHeight}
+ />
+ </div>
+ )}
</div>
{this.previewWidth > 0 && <div className="schema-preview-divider" style={{ width: CollectionSchemaView._previewDividerWidth }} onPointerDown={this.onDividerDown}></div>}
{this.previewWidth > 0 && (
@@ -967,7 +972,6 @@ class CollectionSchemaViewDocs extends React.Component<CollectionSchemaViewDocsP
hideDocumentButtonBar={true}
hideLinkAnchors={true}
layout_fitWidth={returnTrue}
- scriptContext={this}
/>
</div>
);
diff --git a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
index e8e606030..4e418728f 100644
--- a/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaRowBox.tsx
@@ -16,6 +16,10 @@ import { CollectionSchemaView } from './CollectionSchemaView';
import './CollectionSchemaView.scss';
import { SchemaTableCell } from './SchemaTableCell';
import { Transform } from '../../../util/Transform';
+import { IconButton, Size } from 'browndash-components';
+import { CgClose } from 'react-icons/cg';
+import { FaExternalLinkAlt } from 'react-icons/fa';
+import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../../Utils';
@observer
export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
@@ -110,22 +114,40 @@ export class SchemaRowBox extends ViewBoxBaseComponent<FieldViewProps>() {
width: CollectionSchemaView._rowMenuWidth,
pointerEvents: !this.props.isContentActive() ? 'none' : undefined,
}}>
- <div
- className="schema-row-button"
- onPointerDown={undoable(e => {
- e.stopPropagation();
- this.props.removeDocument?.(this.rootDoc);
- }, 'Delete Row')}>
- <FontAwesomeIcon icon="times" />
- </div>
- <div
- className="schema-row-button"
- onPointerDown={undoable(e => {
- e.stopPropagation();
- this.props.addDocTab(this.rootDoc, OpenWhere.addRight);
- }, 'Open Doc on Right')}>
- <FontAwesomeIcon icon="external-link-alt" />
- </div>
+ <IconButton
+ tooltip="close"
+ icon={<CgClose size={'16px'} />}
+ size={Size.XSMALL}
+ onPointerDown={e =>
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ undoable(e => {
+ e.stopPropagation();
+ this.props.removeDocument?.(this.rootDoc);
+ }, 'Delete Row')
+ )
+ }
+ />
+ <IconButton
+ tooltip="open preview"
+ icon={<FaExternalLinkAlt />}
+ size={Size.XSMALL}
+ onPointerDown={e =>
+ setupMoveUpEvents(
+ this,
+ e,
+ returnFalse,
+ emptyFunction,
+ undoable(e => {
+ e.stopPropagation();
+ this.props.addDocTab(this.rootDoc, OpenWhere.addRight);
+ }, 'Open schema Doc preview')
+ )
+ }
+ />
</div>
<div className="row-cells">
{this.schemaView?.columnKeys?.map((key, index) => (
diff --git a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
index 1c9c0de53..9d5b533d1 100644
--- a/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
+++ b/src/client/views/collections/collectionSchema/SchemaTableCell.tsx
@@ -232,7 +232,7 @@ export class SchemaImageCell extends React.Component<SchemaTableCellProps> {
const height = this.props.rowHeight() ? this.props.rowHeight() - (this.props.padding || 6) * 2 : undefined;
const width = height ? height * aspect : undefined; // increase the width of the image if necessary to maintain proportionality
- return <img src={this.url} width={width} height={height} style={{}} draggable="false" onPointerEnter={this.showHoverPreview} onPointerMove={this.moveHoverPreview} onPointerLeave={this.removeHoverPreview} />;
+ return <img src={this.url} width={width ? width : undefined} height={height} style={{}} draggable="false" onPointerEnter={this.showHoverPreview} onPointerMove={this.moveHoverPreview} onPointerLeave={this.removeHoverPreview} />;
}
}
@@ -272,7 +272,7 @@ export class SchemaRTFCell extends React.Component<SchemaTableCellProps> {
fieldProps.isContentActive = this.selectedFunc;
return (
<div className="schemaRTFCell" style={{ display: 'flex', fontStyle: this.selected ? undefined : 'italic', width: '100%', height: '100%', position: 'relative', color, textDecoration, cursor, pointerEvents }}>
- {this.selected ? <FormattedTextBox allowScroll={true} {...fieldProps} DataDoc={this.props.Document} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))}
+ {this.selected ? <FormattedTextBox {...fieldProps} DataDoc={this.props.Document} /> : (field => (field ? Field.toString(field) : ''))(FieldValue(fieldProps.Document[fieldProps.fieldKey]))}
</div>
);
}
@@ -325,10 +325,34 @@ export class SchemaEnumerationCell extends React.Component<SchemaTableCellProps>
const { color, textDecoration, fieldProps, cursor, pointerEvents } = SchemaTableCell.renderProps(this.props);
const options = this.props.options?.map(facet => ({ value: facet, label: facet }));
return (
- <div className="schemaSelectionCell" style={{ display: 'flex', color, textDecoration, cursor, pointerEvents }}>
+ <div className="schemaSelectionCell" style={{ color, textDecoration, cursor, pointerEvents }}>
<div style={{ width: '100%' }}>
<Select
styles={{
+ container: base => ({
+ ...base,
+ height: 20,
+ }),
+ control: base => ({
+ ...base,
+ height: 20,
+ minHeight: 20,
+ }),
+ placeholder: base => ({
+ ...base,
+ top: '40%',
+ }),
+ input: base => ({
+ ...base,
+ height: 20,
+ minHeight: 20,
+ margin: 0,
+ }),
+ indicatorsContainer: base => ({
+ ...base,
+ height: 20,
+ transform: 'scale(0.75)',
+ }),
menuPortal: base => ({
...base,
left: 0,