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/CollectionCardDeckView.scss4
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.scss12
-rw-r--r--src/client/views/collections/CollectionCarouselView.scss16
-rw-r--r--src/client/views/collections/CollectionDockingView.scss132
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx44
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.scss30
-rw-r--r--src/client/views/collections/CollectionPivotView.tsx8
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.scss16
-rw-r--r--src/client/views/collections/CollectionStackingView.scss39
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx167
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx32
-rw-r--r--src/client/views/collections/CollectionTimeView.scss13
-rw-r--r--src/client/views/collections/CollectionTreeView.scss6
-rw-r--r--src/client/views/collections/CollectionView.scss8
-rw-r--r--src/client/views/collections/CollectionView.tsx2
-rw-r--r--src/client/views/collections/FlashcardPracticeUI.scss8
-rw-r--r--src/client/views/collections/FlashcardPracticeUI.tsx9
-rw-r--r--src/client/views/collections/KeyRestrictionRow.tsx72
-rw-r--r--src/client/views/collections/TabDocView.scss4
-rw-r--r--src/client/views/collections/TabDocView.tsx4
-rw-r--r--src/client/views/collections/TreeView.scss4
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss32
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx52
-rw-r--r--src/client/views/collections/collectionFreeForm/FaceCollectionBox.scss14
-rw-r--r--src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx5
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.scss8
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx126
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.scss6
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.scss8
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss8
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss4
-rw-r--r--src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx1
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx1
-rw-r--r--src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx3
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss10
37 files changed, 443 insertions, 469 deletions
diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index e6cc398af..981e528cc 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -18,7 +18,7 @@
}
.collectionCardView-flashcardUI {
- top: 0;
+ top: 0px;
position: absolute;
width: 100%;
height: 100%;
@@ -33,7 +33,7 @@
}
.collectionCardView-cardSizeDragger {
position: absolute;
- top: 0;
+ top: 0px;
width: 28px;
height: 28px;
> svg {
diff --git a/src/client/views/collections/CollectionCarousel3DView.scss b/src/client/views/collections/CollectionCarousel3DView.scss
index 13e6b54c2..361d88cb6 100644
--- a/src/client/views/collections/CollectionCarousel3DView.scss
+++ b/src/client/views/collections/CollectionCarousel3DView.scss
@@ -12,7 +12,7 @@
position: absolute;
top: global.$CAROUSEL3D_TOP * 1%;
height: (global.$CAROUSEL3D_SIDE_SCALE * 100) * 1%;
- align-items: center;
+ //align-items: center;
transition: transform 0.3s cubic-bezier(0.455, 0.03, 0.515, 0.955);
.collectionCarousel3DView-item,
@@ -67,8 +67,8 @@
.carousel3DView-fwd-scroll-hidden {
position: absolute;
display: flex;
- width: 30;
- height: 30;
+ width: 30px;
+ height: 30px;
align-items: center;
border-radius: 5px;
justify-content: center;
@@ -78,7 +78,7 @@
.carousel3DView-fwd,
.carousel3DView-back {
- top: 0;
+ top: 0px;
background: transparent;
width: calc((1 - #{global.$CAROUSEL3D_CENTER_SCALE} * 0.33) / 2 * 100%);
height: 100%;
@@ -94,13 +94,13 @@
.carousel3DView-fwd,
.carousel3DView-fwd-scroll,
.carousel3DView-fwd-scroll-hidden {
- right: 0;
+ right: 0px;
}
.carousel3DView-back,
.carousel3DView-back-scroll,
.carousel3DView-back-scroll-hidden {
- left: 0;
+ left: 0px;
}
.carousel3DView-fwd-scroll-hidden,
diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss
index 962b590c8..4c999b6dd 100644
--- a/src/client/views/collections/CollectionCarouselView.scss
+++ b/src/client/views/collections/CollectionCarouselView.scss
@@ -6,10 +6,10 @@
transform-origin: top left;
.collectionCarouselView-caption {
- height: 50;
+ height: 50px;
display: inline-block;
width: 100%;
- bottom: 0;
+ bottom: 0px;
position: absolute;
}
.collectionCarouselView-image {
@@ -18,8 +18,8 @@
width: 100%;
user-select: none;
position: absolute;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
}
}
.collectionCarouselView-recentlyMissed {
@@ -34,8 +34,8 @@
.carouselView-fwd {
position: absolute;
display: flex;
- width: 30;
- height: 30;
+ width: 30px;
+ height: 30px;
align-items: center;
border-radius: 5px;
justify-content: center;
@@ -47,12 +47,12 @@
}
.carouselView-fwd {
top: calc(50% - 15px);
- right: 0;
+ right: 0px;
transform-origin: right top;
}
.carouselView-back {
top: calc(50% - 15px);
- left: 0;
+ left: 0px;
transform-origin: top left;
}
.carouselView-back:hover,
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index 7c19d39da..de214e2ef 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -17,8 +17,8 @@
}
.lm_maximised {
position: absolute;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
z-index: 40;
}
.lm_maximise_placeholder {
@@ -62,8 +62,8 @@
text-align: center;
}
.lm_header ul {
- margin: 0;
- padding: 0;
+ margin: 0px;
+ padding: 0px;
list-style-type: none;
}
.lm_header .lm_tab {
@@ -81,11 +81,11 @@
position: absolute;
}
.lm_header .lm_tab i.lm_left {
- top: 0;
+ top: 0px;
left: -2px;
}
.lm_header .lm_tab i.lm_right {
- top: 0;
+ top: 0px;
right: -2px;
}
.lm_header .lm_tab .lm_title {
@@ -97,8 +97,8 @@
width: 14px;
height: 14px;
position: absolute;
- top: 0;
- right: 0;
+ top: 0px;
+ right: 0px;
text-align: center;
}
.lm_stack.lm_left .lm_header,
@@ -118,14 +118,14 @@
.lm_stack.lm_left .lm_header .lm_tabs,
.lm_stack.lm_right .lm_header .lm_tabs {
transform-origin: left top;
- top: 0;
+ top: 0px;
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;
+ bottom: 0px;
}
.lm_dragProxy.lm_left .lm_items,
.lm_dragProxy.lm_right .lm_items,
@@ -136,7 +136,7 @@
.lm_dragProxy.lm_left .lm_header .lm_tabs,
.lm_stack.lm_left .lm_header .lm_tabs {
transform: rotate(-90deg) scaleX(-1);
- left: 0;
+ left: 0px;
}
.lm_dragProxy.lm_left .lm_header .lm_tabs .lm_tab,
.lm_stack.lm_left .lm_header .lm_tabs .lm_tab {
@@ -156,7 +156,7 @@
.lm_stack.lm_right .lm_header .lm_tabs {
transform: rotate(90deg) scaleX(1);
left: 100%;
- margin-left: 0;
+ margin-left: 0px;
}
.lm_dragProxy.lm_right .lm_header .lm_controls,
.lm_stack.lm_right .lm_header .lm_controls {
@@ -169,7 +169,7 @@
}
.lm_dragProxy.lm_bottom .lm_header .lm_tab,
.lm_stack.lm_bottom .lm_header .lm_tab {
- margin-top: 0;
+ margin-top: 0px;
border-top: none;
}
.lm_dragProxy.lm_bottom .lm_header .lm_controls,
@@ -189,8 +189,8 @@
}
.lm_header .lm_controls .lm_tabdropdown:before {
content: '';
- width: 0;
- height: 0;
+ width: 0px;
+ height: 0px;
vertical-align: middle;
display: inline-block;
border-top: 5px dashed;
@@ -201,14 +201,14 @@
.lm_header .lm_tabdropdown_list {
position: absolute;
top: 20px;
- right: 0;
+ right: 0px;
z-index: 5;
overflow: hidden;
}
.lm_header .lm_tabdropdown_list .lm_tab {
clear: both;
padding-right: 10px;
- margin: 0;
+ margin: 0px;
}
.lm_header .lm_tabdropdown_list .lm_tab .lm_title {
width: 100px;
@@ -218,8 +218,8 @@
}
.lm_dragProxy {
position: absolute;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
z-index: 30;
}
.lm_dragProxy .lm_header {
@@ -238,32 +238,32 @@
width: 100%;
height: 100%;
position: relative;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
}
.lm_transition_indicator {
display: none;
width: 20px;
height: 20px;
position: absolute;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
z-index: 20;
}
.lm_popin {
width: 20px;
height: 20px;
position: absolute;
- bottom: 0;
- right: 0;
+ bottom: 0px;
+ right: 0px;
z-index: 9999;
}
.lm_popin > * {
width: 100%;
height: 100%;
position: absolute;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
}
.lm_popin > .lm_bg {
z-index: 10;
@@ -307,7 +307,7 @@
width: max-content;
height: 100%;
display: flex;
- max-width: 100;
+ max-width: 100px;
text-overflow: ellipsis;
}
@@ -328,7 +328,7 @@
ul.lm_tabs::before {
content: ' ';
position: absolute;
- bottom: 0;
+ bottom: 0px;
width: 100%;
z-index: 1;
pointer-events: none;
@@ -349,9 +349,9 @@ ul.lm_tabs::before {
}
}
.lm_header .lm_tab.lm_active {
- padding: 0;
+ padding: 0px;
opacity: 1;
- margin: 0;
+ margin: 0px;
box-shadow: none;
height: 27px;
margin-right: 2px;
@@ -405,7 +405,7 @@ ul.lm_tabs::before {
}
.lm_drag_tab {
- padding: 0;
+ padding: 0px;
width: 15px !important;
height: 15px !important;
position: relative !important;
@@ -418,7 +418,7 @@ ul.lm_tabs::before {
.lm_close_tab {
display: inline-flex !important;
- padding: 0;
+ padding: 0px;
opacity: 1 !important;
align-self: center;
margin-right: 5px;
@@ -455,7 +455,7 @@ ul.lm_tabs::before {
content: 'x';
margin: auto;
position: relative;
- top: -2;
+ top: -2px;
font-size: medium;
font-family: sans-serif;
}
@@ -478,8 +478,8 @@ ul.lm_tabs::before {
width: 100%;
height: 100%;
position: absolute;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
.collectionDockingView-drag {
touch-action: none;
@@ -514,7 +514,7 @@ ul.lm_tabs::before {
border-bottom-left-radius: 10px;
background: #93939347;
z-index: 100;
- //left: -3;
+ //left: -3px;
&:hover {
background: gray;
color: white !important;
@@ -524,7 +524,7 @@ ul.lm_tabs::before {
content: '+';
margin: auto;
font-size: x-large;
- top: -4;
+ top: -4px;
position: relative;
}
.lm_maximise {
@@ -548,10 +548,10 @@ ul.lm_tabs::before {
}
.flexlayout__layout {
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
+ left: 0px;
+ top: 0px;
+ right: 0px;
+ bottom: 0px;
position: absolute;
overflow: hidden;
}
@@ -691,8 +691,8 @@ ul.lm_tabs::before {
.flexlayout__tabset_header {
position: absolute;
- left: 0;
- right: 0;
+ left: 0px;
+ right: 0px;
color: #eee;
background-color: #212121;
padding: 3px 3px 3px 5px;
@@ -702,17 +702,17 @@ ul.lm_tabs::before {
.flexlayout__tab_header_inner {
position: absolute;
- left: 0;
- top: 0;
- bottom: 0;
+ left: 0px;
+ top: 0px;
+ bottom: 0px;
width: 10000px;
}
.flexlayout__tab_header_outer {
background-color: global.$dark-gray;
position: absolute;
- left: 0;
- right: 0;
+ left: 0px;
+ right: 0px;
/*top: 0px;*/
/*height: 100px;*/
overflow: hidden;
@@ -731,23 +731,23 @@ ul.lm_tabs::before {
display: flex;
flex-direction: row-reverse;
align-items: center;
- top: 0;
- bottom: 0;
- right: 0;
+ top: 0px;
+ bottom: 0px;
+ right: 0px;
}
.flexlayout__tab_toolbar_button-min {
width: 20px;
height: 20px;
border: none;
- outline-width: 0;
+ outline-width: 0px;
}
.flexlayout__tab_toolbar_button-max {
width: 20px;
height: 20px;
border: none;
- outline-width: 0;
+ outline-width: 0px;
}
.flexlayout__popup_menu_item {
@@ -870,9 +870,9 @@ ul.lm_tabs::before {
display: flex;
flex-direction: column-reverse;
align-items: center;
- bottom: 0;
- left: 0;
- right: 0;
+ bottom: 0px;
+ left: 0px;
+ right: 0px;
}
.flexlayout__border_toolbar_right {
@@ -880,9 +880,9 @@ ul.lm_tabs::before {
display: flex;
flex-direction: column-reverse;
align-items: center;
- bottom: 0;
- left: 0;
- right: 0;
+ bottom: 0px;
+ left: 0px;
+ right: 0px;
}
.flexlayout__border_toolbar_top {
@@ -890,9 +890,9 @@ ul.lm_tabs::before {
display: flex;
flex-direction: row-reverse;
align-items: center;
- top: 0;
- bottom: 0;
- right: 0;
+ top: 0px;
+ bottom: 0px;
+ right: 0px;
}
.flexlayout__border_toolbar_bottom {
@@ -900,8 +900,8 @@ ul.lm_tabs::before {
display: flex;
flex-direction: row-reverse;
align-items: center;
- top: 0;
- bottom: 0;
- right: 0;
+ top: 0px;
+ bottom: 0px;
+ right: 0px;
}
}
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 89ccf5a0f..164c6e831 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -7,6 +7,7 @@ import { emptyFunction, numberRange } from '../../../Utils';
import { Doc } from '../../../fields/Doc';
import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { ScriptField } from '../../../fields/ScriptField';
+import { StrCast } from '../../../fields/Types';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { CompileScript } from '../../util/Scripting';
@@ -16,6 +17,7 @@ import { undoBatch, undoable } from '../../util/UndoManager';
import { EditableView } from '../EditableView';
import { ObservableReactComponent } from '../ObservableReactComponent';
import { DocumentView } from '../nodes/DocumentView';
+import { ImportElementBox } from '../nodes/importBox/ImportElementBox';
import { CollectionStackingView } from './CollectionStackingView';
import './CollectionStackingView.scss';
@@ -28,12 +30,14 @@ interface CMVFieldRowProps {
headingObject: SchemaHeaderField | undefined;
docList: Doc[];
parent: CollectionStackingView;
+ panelWidth: () => number;
+ columnWidth: () => number;
pivotField: string;
type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined;
createDropTarget: (ele: HTMLDivElement) => void;
screenToLocalTransform: () => Transform;
setDocHeight: (key: string, thisHeight: number) => void;
- refList: Element[];
+ sectionRefs: Element[];
showHandle: boolean;
}
@@ -74,7 +78,7 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
createRowDropRef = (ele: HTMLDivElement | null) => {
this._dropDisposer?.();
if (ele) this._dropDisposer = DragManager.MakeDropTarget(ele, this.rowDrop.bind(this), this._props.Doc);
- else if (this._ele) this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
+ else if (this._ele) this.props.sectionRefs.splice(this.props.sectionRefs.indexOf(this._ele), 1);
this._ele = ele;
};
@action
@@ -82,10 +86,10 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
this.heading = this._props.headingObject?.heading || '';
this.color = this._props.headingObject?.color || '#f1efeb';
this.collapsed = this._props.headingObject?.collapsed || false;
- this._ele && this.props.refList.push(this._ele);
+ this._ele && this.props.sectionRefs.push(this._ele);
}
componentWillUnmount() {
- this._ele && this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
+ this._ele && this.props.sectionRefs.splice(this.props.sectionRefs.indexOf(this._ele), 1);
this._ele = null;
}
@@ -128,10 +132,8 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
const key = this._props.pivotField;
const castedValue = this.getValue(value);
if (castedValue) {
- if (this._props.parent.colHeaderData) {
- if (this._props.parent.colHeaderData.map(i => i.heading).indexOf(castedValue.toString()) > -1) {
- return false;
- }
+ if (this._props.parent.colHeaderData?.map(i => i.heading).indexOf(castedValue.toString()) || 0 > -1) {
+ return false;
}
key && this._props.docList.forEach(d => Doc.SetInPlace(d, key, castedValue, true));
this._heading = castedValue.toString();
@@ -251,20 +253,11 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
textCallback = (/* char: string */) => this.addDocument('', false);
@computed get contentLayout() {
- const rows = Math.max(1, Math.min(this._props.docList.length, Math.floor((this._props.parent._props.PanelWidth() - 2 * this._props.parent.xMargin) / (this._props.parent.columnWidth + this._props.parent.gridGap))));
- const showChrome = !this._props.chromeHidden;
- const stackPad = showChrome ? `0px ${this._props.parent.xMargin}px` : `${this._props.parent.yMargin}px ${this._props.parent.xMargin}px 0px ${this._props.parent.xMargin}px `;
+ const rows = Math.max(1, Math.min(this._props.docList.length, Math.floor(this._props.panelWidth() / this._props.columnWidth())));
return this.collapsed ? null : (
<div style={{ position: 'relative' }}>
- {showChrome ? (
- <div
- className="collectionStackingView-addDocumentButton"
- style={
- {
- // width: style.columnWidth / style.numGroupColumns,
- // padding: `${NumCast(this._props.parent.layoutDoc._yPadding, this._props.parent.yMargin)}px 0px 0px 0px`,
- }
- }>
+ {!this._props.chromeHidden && !StrCast(this._props.Doc.childLayoutString).includes(ImportElementBox.name) ? (
+ <div className="collectionStackingView-addDocumentButton">
<EditableView GetValue={returnEmptyString} SetValue={this.addDocument} textCallback={this.textCallback} contents="+ NEW" />
</div>
) : null}
@@ -272,11 +265,9 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
className="collectionStackingView-masonryGrid"
ref={this._contRef}
style={{
- padding: stackPad,
minHeight: this._props.showHandle && this._props.parent._props.isContentActive() ? '10px' : undefined,
- width: this._props.parent.NodeWidth,
gridGap: this._props.parent.gridGap,
- gridTemplateColumns: numberRange(rows).reduce(list => list + ` ${this._props.parent.columnWidth}px`, ''),
+ gridTemplateColumns: numberRange(rows).reduce(list => list + ` ${this._props.columnWidth()}px`, ''),
}}>
{this._props.parent.children(this._props.docList)}
</div>
@@ -339,7 +330,12 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
render() {
const background = this._background;
return (
- <div className="collectionStackingView-masonrySection" style={{ width: this._props.parent.NodeWidth, background }} ref={this.createRowDropRef} onPointerEnter={this.pointerEnteredRow} onPointerLeave={this.pointerLeaveRow}>
+ <div
+ className="collectionStackingView-masonrySection"
+ style={{ width: this._props.pivotField ? this._props.panelWidth() : '100%', background }}
+ ref={this.createRowDropRef}
+ onPointerEnter={this.pointerEnteredRow}
+ onPointerLeave={this.pointerLeaveRow}>
{this.headingView}
{this.contentLayout}
</div>
diff --git a/src/client/views/collections/CollectionNoteTakingView.scss b/src/client/views/collections/CollectionNoteTakingView.scss
index 0d24a56b5..231085338 100644
--- a/src/client/views/collections/CollectionNoteTakingView.scss
+++ b/src/client/views/collections/CollectionNoteTakingView.scss
@@ -82,7 +82,7 @@
height: 100%;
width: 100%;
position: absolute;
- top: 0;
+ top: 0px;
overflow-y: auto;
overflow-x: hidden;
transition: top 0.5s;
@@ -130,8 +130,8 @@
display: flex;
flex-direction: column;
align-items: center;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
width: 100%;
position: absolute;
margin: auto;
@@ -152,20 +152,20 @@
}
.collectionNoteTakingView-columnDragger {
- width: 15;
- height: 15;
+ width: 15px;
+ height: 15px;
position: absolute;
- margin-left: -5;
+ margin-left: -5px;
}
.collectionNoteTakingView-sectionDelete {
display: none;
position: absolute;
- right: 0;
+ right: 0px;
width: max-content;
height: max-content;
- top: 10;
- padding: 2;
+ top: 10px;
+ padding: 2px;
}
// Documents in NoteTaking view
@@ -210,8 +210,8 @@
height: 5px;
&.active {
- margin-left: 0;
- margin-right: 0;
+ margin-left: 0px;
+ margin-right: 0px;
background: red;
}
}
@@ -303,8 +303,8 @@
.collectionNoteTakingView-sectionColor {
position: absolute;
- left: 0;
- top: 0;
+ left: 0px;
+ top: 0px;
height: 100%;
display: none;
@@ -345,8 +345,8 @@
.collectionNoteTakingView-sectionOptions {
position: absolute;
- right: 0;
- top: 0;
+ right: 0px;
+ top: 0px;
height: 100%;
display: none;
diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx
index 4736070c3..5487315f0 100644
--- a/src/client/views/collections/CollectionPivotView.tsx
+++ b/src/client/views/collections/CollectionPivotView.tsx
@@ -102,13 +102,7 @@ export class CollectionPivotView extends CollectionSubView() {
<div className="collectionTimeView-pivot" style={{ width: this._props.PanelWidth(), height: '100%' }}>
{this.contents}
<div style={{ right: 0, top: 0, position: 'absolute' }}>
- <FieldsDropdown
- Doc={this.Document}
- selectFunc={fieldKey => {
- this.layoutDoc._pivotField = fieldKey;
- }}
- placeholder={StrCast(this.layoutDoc._pivotField)}
- />
+ <FieldsDropdown Doc={this.Document} isInactive={!this._props.isContentActive()} selectFunc={fieldKey => (this.layoutDoc._pivotField = fieldKey)} placeholder={StrCast(this.layoutDoc._pivotField)} />
</div>
</div>
);
diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss
index d05c0ffde..e71df2164 100644
--- a/src/client/views/collections/CollectionStackedTimeline.scss
+++ b/src/client/views/collections/CollectionStackedTimeline.scss
@@ -38,7 +38,7 @@
height: 100%;
background-color: global.$dark-gray;
opacity: 0.3;
- top: 0;
+ top: 0px;
}
.collectionStackedTimeline-trim-controls {
@@ -49,8 +49,8 @@
display: flex;
justify-content: space-between;
max-width: 100%;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
.collectionStackedTimeline-trim-handle {
background-color: global.$medium-blue;
@@ -106,18 +106,18 @@
.collectionStackedTimeline-resizer {
background: global.$dark-gray;
position: absolute;
- top: 0;
+ top: 0px;
height: 100%;
width: 10px;
pointer-events: all;
z-index: 100;
}
.collectionStackedTimeline-resizer {
- right: 0;
+ right: 0px;
cursor: e-resize;
}
.collectionStackedTimeline-left-resizer {
- left: 0;
+ left: 0px;
cursor: w-resize;
}
}
@@ -126,8 +126,8 @@
position: absolute;
width: 100%;
height: 100%;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
pointer-events: none;
}
}
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 05ac52ff9..2cf361847 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -2,10 +2,12 @@
.collectionMasonryView {
display: inline;
+ flex-wrap: wrap;
}
.collectionStackingView {
display: flex;
+ justify-content: space-between;
}
.collectionStackingMasonry-cont {
@@ -14,10 +16,10 @@
width: 100%;
.collectionStackingView-columnDragger {
- width: 28;
- height: 28;
- position: absolute;
- margin-left: -5;
+ width: 28px;
+ height: 28px;
+ position: relative;
+ margin-left: -5px;
z-index: 10;
> svg {
width: 100%;
@@ -53,10 +55,9 @@
height: 100%;
width: 100%;
position: absolute;
- top: 0;
+ top: 0px;
overflow-y: auto;
overflow-x: hidden;
- flex-wrap: wrap;
transition: top 0.5s;
> div {
@@ -92,8 +93,8 @@
.collectionStackingView-masonryGrid {
width: 100%;
display: grid;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
}
.collectionStackingView-masonrySingle {
@@ -114,8 +115,8 @@
position: absolute;
display: flex;
flex-direction: column;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
width: 100%;
position: absolute;
}
@@ -158,12 +159,12 @@
width: 100%;
display: none;
position: absolute;
- top: 0;
+ top: 0px;
cursor: default;
&.active {
- margin-left: 0;
- margin-right: 0;
+ margin-left: 0px;
+ margin-right: 0px;
background: red;
}
}
@@ -210,7 +211,6 @@
.collectionStackingView-sectionHeader {
text-align: center;
margin: auto;
- margin-bottom: 10px;
background: global.$medium-gray;
// overflow: hidden; overflow is visible so the color menu isn't hidden -ftong
@@ -262,8 +262,8 @@
.collectionStackingView-sectionColor {
position: absolute;
- left: 0;
- top: 0;
+ left: 0px;
+ top: 0px;
height: 100%;
display: none;
@@ -304,8 +304,8 @@
.collectionStackingView-sectionOptions {
position: absolute;
- right: 0;
- top: 0;
+ right: 0px;
+ top: 0px;
height: 100%;
display: none;
@@ -339,7 +339,7 @@
.collectionStackingView-sectionDelete {
position: absolute;
right: 0px;
- top: 0;
+ top: 0px;
height: 100%;
display: none;
}
@@ -367,7 +367,6 @@
.collectionStackingView-addGroupButton {
display: flex;
overflow: hidden;
- margin: auto;
width: 90%;
overflow: ellipses;
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 25a222cbb..bdeb7d944 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -4,7 +4,7 @@ import { action, computed, IReactionDisposer, makeObservable, observable, Observ
import { observer } from 'mobx-react';
import * as React from 'react';
import { ClientUtils, DivHeight, returnNone, returnZero, setupMoveUpEvents, smoothScroll } from '../../../ClientUtils';
-import { Doc, Opt } from '../../../fields/Doc';
+import { Doc, Field, Opt } from '../../../fields/Doc';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { listSpec } from '../../../fields/Schema';
@@ -35,6 +35,7 @@ import { CollectionStackingViewFieldColumn } from './CollectionStackingViewField
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
import { computedFn } from 'mobx-utils';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
+import { FieldsDropdown } from '../FieldsDropdown';
export type collectionStackingViewProps = {
sortFunc?: (a: Doc, b: Doc) => number;
@@ -48,28 +49,43 @@ export type collectionStackingViewProps = {
@observer
export class CollectionStackingView extends CollectionSubView<Partial<collectionStackingViewProps>>() {
_disposers: { [key: string]: IReactionDisposer } = {};
+ _addGroupRef = React.createRef<HTMLDivElement>();
_masonryGridRef: HTMLDivElement | null = null;
// used in a column dragger, likely due for the masonry grid view. We want to use this
_draggerRef = React.createRef<HTMLDivElement>();
// keeping track of documents. Updated on internal and external drops. What's the difference?
_docXfs: { height: () => number; width: () => number; stackedDocTransform: () => Transform }[] = [];
- // Doesn't look like this field is being used anywhere. Obsolete?
- _columnStart: number = 0;
- @observable _refList: HTMLElement[] = [];
+ @observable _colStackRefs: HTMLElement[] = [];
+ @observable _colHdrRefs: HTMLElement[] = [];
// map of node headers to their heights. Used in Masonry
@observable _heightMap = new Map<string, number>();
// Assuming that this is the current css cursor style
@observable _cursor: CSS.Property.Cursor = 'ew-resize';
// gets reset whenever we scroll. Not sure what it is
@observable _scroll = 0; // used to force the document decoration to update when scrolling
- // does this mean whether the browser is hidden? Or is chrome something else entirely?
+ // whether ui/editing controls are shown
@computed get chromeHidden() {
return this._props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden);
}
- // it looks like this gets the column headers that Mehek was showing just now
@computed get colHeaderData() {
- return Cast(this.dataDoc['_' + this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null);
+ return Cast(this.dataDoc[this.fieldKey + '_columnHeaders'], listSpec(SchemaHeaderField), null);
+ }
+
+ @computed get Sections() {
+ return this.filteredChildren.reduce(
+ (map, d) => {
+ const docHeader = d[this.pivotField] ? d[this.pivotField] : `NO ${this.pivotField.toUpperCase()} VALUE`;
+ const docHeaderString = docHeader !== undefined ? Field.toString(docHeader) : `NO ${this.pivotField.toUpperCase()} VALUE`;
+
+ // find existing header or create
+ const existingHeader = Array.from(map.keys()).find(sh => sh.heading === docHeaderString);
+ if (!existingHeader) map.set(new SchemaHeaderField(docHeaderString), [d]);
+ else map.get(existingHeader)!.push(d);
+ return map;
+ },
+ new ObservableMap<SchemaHeaderField, Doc[]>(this.colHeaderData?.map(hdata => [hdata, []] as [SchemaHeaderField, Doc[]]) ?? [])
+ );
}
// Still not sure what a pivot is, but it appears that we can actually filter docs somehow?
@computed get pivotField() {
@@ -107,9 +123,12 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
@computed get showAddAGroup() {
return this.pivotField && !this.chromeHidden;
}
+ @computed get availableWidth() {
+ return this._props.PanelWidth() - 2 * this.xMargin - (this.isStackingView ? this.gridGap * ((this.numGroupColumns || 1) - 1) : 0);
+ }
// columnWidth handles the margin on the left and right side of the documents
@computed get columnWidth() {
- const availableWidth = this._props.PanelWidth() - 2 * this.xMargin;
+ const availableWidth = this.availableWidth;
const cwid = availableWidth / (NumCast(this.Document._layout_columnCount) || this._props.PanelWidth() / NumCast(this.Document._layout_columnWidth, this._props.PanelWidth() / 4));
return Math.min(availableWidth, this.isStackingView ? availableWidth / (this.numGroupColumns || 1) : cwid - this.gridGap);
}
@@ -121,28 +140,17 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
constructor(props: SubCollectionViewProps) {
super(props);
makeObservable(this);
- 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.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>();
- }
}
+ availableWidthFn = () => this.availableWidth;
columnWidthFn = () => this.columnWidth;
columnDocHeightFn = (doc: Doc) => () => (this.isStackingView ? this.getDocHeight(doc)() : Math.min(this.getDocHeight(doc)(), this._props.PanelHeight()));
- // TODO: plj - these are the children
children = (docs: Doc[]) => {
- // TODO: can somebody explain me to what exactly TraceMobX is?
TraceMobx();
- // appears that we are going to reset the _docXfs. TODO: what is Xfs?
this._docXfs.length = 0;
- this._renderCount < docs.length &&
- setTimeout(
- action(() => {
- this._renderCount = Math.min(docs.length, this._renderCount + 5);
- })
- );
+ this._renderCount < docs.length &&
+ setTimeout(action(() => (this._renderCount = Math.min(docs.length, this._renderCount + 5)))); // prettier-ignore
return docs.map((d, i) => {
// 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((this.getDocHeight(d)() + this.gridGap) / this.gridGap);
@@ -153,76 +161,28 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
margin: undefined,
transition: this.getDocTransition(d)(),
width: this.columnWidth,
- marginTop: i ? this.gridGap : 0,
height: this.getDocHeight(d)(),
zIndex: DocumentView.getFirstDocumentView(d)?.IsSelected ? 1000 : 0,
}
: { gridRowEnd: `span ${rowSpan}`, zIndex: DocumentView.getFirstDocumentView(d)?.IsSelected ? 1000 : 0 };
// 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}>
+ <div className={`collectionStackingView${this.isStackingView ? '-columnDoc' : '-masonryDoc'}`} key={d[Id]} style={style}>
{this.getDisplayDoc(d, this.getDocTransition(d), i)}
</div>
);
});
};
@action
- setDocHeight = (key: string, sectionHeight: number) => {
- this._heightMap.set(key, sectionHeight);
+ setDocHeight = (key: string, sectionHeight: number) => this._heightMap.set(key, sectionHeight);
+
+ setAutoHeight = () => {
+ const maxHeader = this.isStackingView ? this._colHdrRefs.reduce((p, r) => Math.max(p, DivHeight(r)), 0) + (this._colHdrRefs.length ? this.gridGap : 0) : 0;
+ const maxCol = this.isStackingView
+ ? this._colStackRefs.reduce((p, r) => Math.max(p, DivHeight(r)), 0) + this.gridGap
+ : this._colStackRefs.reduce((p, r) => p + DivHeight(r), this._addGroupRef.current ? DivHeight(this._addGroupRef.current) : 0);
+ this._props.setHeight?.(this.headerMargin + 2 * this.yMargin + maxCol + maxHeader);
};
-
- // is sections that all collections inherit? I think this is how we show the masonry/columns
- // TODO: this seems important
- get Sections() {
- // appears that pivot field IS actually for sorting
- if (!this.pivotField || this.colHeaderData instanceof Promise) return new Map<SchemaHeaderField, Doc[]>();
-
- if (this.colHeaderData === undefined) {
- setTimeout(() => {
- this.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List<SchemaHeaderField>();
- });
- return new Map<SchemaHeaderField, Doc[]>();
- }
- const colHeaderData = Array.from(this.colHeaderData);
- const fields = new Map<SchemaHeaderField, Doc[]>(colHeaderData.map(sh => [sh, []] as [SchemaHeaderField, []]));
- let changed = false;
- this.filteredChildren.forEach(d => {
- const sectionValue = (d[this.pivotField] ? d[this.pivotField] : `NO ${this.pivotField.toUpperCase()} VALUE`) as object;
- // the next five lines ensures that floating point rounding errors don't create more than one section -syip
- const parsed = parseInt(sectionValue.toString());
- const castedSectionValue = !isNaN(parsed) ? parsed : sectionValue;
-
- // look for if header exists already
- const existingHeader = colHeaderData.find(sh => sh.heading === (castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`));
- if (existingHeader) {
- fields.get(existingHeader)!.push(d);
- } else {
- const newSchemaHeader = new SchemaHeaderField(castedSectionValue ? castedSectionValue.toString() : `NO ${this.pivotField.toUpperCase()} VALUE`);
- fields.set(newSchemaHeader, [d]);
- colHeaderData.push(newSchemaHeader);
- changed = true;
- }
- });
- // remove all empty columns if hideHeadings is set
- // we will want to have something like this, so that we can hide columns and add them back in
- if (this.layoutDoc._columnsHideIfEmpty) {
- Array.from(fields.keys())
- .filter(key => !fields.get(key)!.length)
- .forEach(header => {
- fields.delete(header);
- colHeaderData.splice(colHeaderData.indexOf(header), 1);
- changed = true;
- });
- }
- changed &&
- setTimeout(
- action(() => this.colHeaderData?.splice(0, this.colHeaderData.length, ...colHeaderData)),
- 0
- );
- return fields;
- }
-
- setAutoHeight = () => this._props.setHeight?.(this.headerMargin + (this.isStackingView ? Math.max(...this._refList.map(DivHeight)) : 2 * this.yMargin + this._refList.reduce((p, r) => p + DivHeight(r), 0)));
observer = new ResizeObserver(this.setAutoHeight);
componentDidMount() {
@@ -232,9 +192,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
// reset section headers when a new filter is inputted
this._disposers.pivotField = reaction(
() => this.pivotField,
- () => {
- this.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List();
- }
+ () => (this.dataDoc[this.fieldKey + '_columnHeaders'] = new List())
);
// reset section headers when a new filter is inputted
this._disposers.width = reaction(
@@ -252,7 +210,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
);
this._disposers.refList = reaction(
- () => ({ refList: this._refList.slice(), autoHeight: this.layoutDoc._layout_autoHeight && !DocumentView.LightboxContains(this.DocumentView?.()) }),
+ () => ({ refList: this._colStackRefs.slice(), autoHeight: this.layoutDoc._layout_autoHeight && !DocumentView.LightboxContains(this.DocumentView?.()) }),
({ refList, autoHeight }) => {
this.observer.disconnect();
if (autoHeight) refList.forEach(r => this.observer.observe(r));
@@ -409,11 +367,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
getDocWidth = computedFn((d?: Doc) => () => {
if (!d) return 0;
const childLayoutDoc = Doc.LayoutDoc(d, this._props.childLayoutTemplate?.());
- const maxWidth = this.columnWidth / this.numGroupColumns;
if (!this.layoutDoc._columnsFill && !this.childFitWidth(childLayoutDoc)) {
- return Math.min(NumCast(d._width), maxWidth);
+ return Math.min(NumCast(d._width), this.columnWidth);
}
- return maxWidth;
+ return this.columnWidth;
});
getDocTransition = computedFn((d?: Doc) => () => StrCast(d?.dataTransition));
getDocHeight = computedFn((d?: Doc) => () => {
@@ -424,7 +381,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._width) : 0);
const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._height) : 0);
if (nw && nh) {
- const colWid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
+ const colWid = this.columnWidth;
const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d)(), colWid);
return Math.min(maxHeight, (docWid * nh) / nw);
}
@@ -571,7 +528,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
return (
<CollectionStackingViewFieldColumn
- refList={this._refList}
+ colStackRefs={this._colStackRefs}
+ colHeaderRefs={this._colHdrRefs}
addDocument={this.addDocument}
chromeHidden={this.chromeHidden}
colHeaderData={this.colHeaderData}
@@ -608,13 +566,15 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
return (
<div key={(heading?.heading ?? '') + 'head'}>
{this._props.isContentActive() && !this.isStackingView && !this.chromeHidden ? this.columnDragger : null}
- <div style={{ top: this.yMargin }}>
+ <div style={{ position: 'relative' }}>
<CollectionMasonryViewFieldRow
showHandle={first}
Doc={this.Document}
chromeHidden={this.chromeHidden}
+ panelWidth={this.availableWidthFn}
+ columnWidth={this.columnWidthFn}
pivotField={this.pivotField}
- refList={this._refList}
+ sectionRefs={this._colStackRefs}
key={heading ? heading.heading : ''}
rows={rows}
headings={this.headings}
@@ -635,9 +595,11 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
/// add a new group category (column) to the active set of note categories. (e.g., if the pivot field is 'transportation', groups might be 'car', 'plane', 'bike', etc)
@action
addGroup = (value: string) => {
+ if (!this.colHeaderData) {
+ this.dataDoc[this.fieldKey + '_columnHeaders'] = new List();
+ }
if (value && this.colHeaderData) {
- const schemaHdrField = new SchemaHeaderField(value);
- this.colHeaderData.push(schemaHdrField);
+ this.colHeaderData.push(new SchemaHeaderField(value));
return true;
}
return false;
@@ -745,22 +707,25 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
this.fixWheelEvents(ele, this._props.isContentActive);
}}
style={{
- overflowY: this.isContentActive() ? 'auto' : 'hidden',
+ paddingBottom: this.yMargin,
+ paddingTop: this.yMargin,
+ paddingLeft: this.xMargin,
+ paddingRight: this.xMargin,
+ overflowY: this.isContentActive() && !this.layoutDoc._layout_autoHeight ? 'auto' : 'hidden',
background: this._props.styleProvider?.(this.Document, this._props, StyleProp.BackgroundColor) as string,
pointerEvents: this._props.pointerEvents?.() ?? this.backgroundEvents,
}}
- onScroll={action(e => {
- this._scroll = e.currentTarget.scrollTop;
- })}
+ onScroll={action(e => (this._scroll = e.currentTarget.scrollTop))}
onDrop={this.onExternalDrop.bind(this)}
onContextMenu={this.onContextMenu}
onWheel={e => this.isContentActive() && e.stopPropagation()}>
{this.renderedSections}
- {!this.showAddAGroup ? null : (
- <div key={`${this.Document[Id]}-addGroup`} className="collectionStackingView-addGroupButton" style={{ width: !this.isStackingView ? '100%' : this.columnWidth / this.numGroupColumns - 10, marginTop: 10 }}>
- <EditableView {...editableViewProps} />
- </div>
- )}
+ <div className="collectionStackingView-addGroupButton" ref={this._addGroupRef} style={{ width: !this.isStackingView ? '100%' : this.columnWidth, display: this.showAddAGroup ? undefined : 'none' }}>
+ <EditableView {...editableViewProps} />
+ </div>
+ <div style={{ right: 0, top: 0, position: 'absolute', display: !this.layoutDoc._pivotField ? 'none' : undefined }}>
+ <FieldsDropdown Doc={this.Document} isInactive={!this._props.isContentActive()} selectFunc={fieldKey => (this.layoutDoc._pivotField = fieldKey)} placeholder={StrCast(this.layoutDoc._pivotField)} />
+ </div>
</div>
</div>
</>
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 345f60e75..8c535534a 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -1,9 +1,10 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { DivHeight, DivWidth, returnEmptyString, returnTrue, setupMoveUpEvents } from '../../../ClientUtils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { DocData } from '../../../fields/DocSymbols';
import { RichTextField } from '../../../fields/RichTextField';
import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField';
import { ScriptField } from '../../../fields/ScriptField';
@@ -26,7 +27,6 @@ import { EditableView } from '../EditableView';
import { DocumentView } from '../nodes/DocumentView';
import { ObservableReactComponent } from '../ObservableReactComponent';
import './CollectionStackingView.scss';
-import { DocData } from '../../../fields/DocSymbols';
// So this is how we are storing a column
interface CSVFieldColumnProps {
@@ -50,14 +50,14 @@ interface CSVFieldColumnProps {
addDocument: (doc: Doc | Doc[]) => boolean;
createDropTarget: (ele: HTMLDivElement) => void;
screenToLocalTransform: () => Transform;
- refList: HTMLElement[];
+ colStackRefs: HTMLElement[];
+ colHeaderRefs: HTMLElement[];
}
@observer
export class CollectionStackingViewFieldColumn extends ObservableReactComponent<CSVFieldColumnProps> {
private dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [name: string]: IReactionDisposer } = {};
- private _headerRef: React.RefObject<HTMLDivElement> = React.createRef();
@observable _background = 'inherit';
@observable _paletteOn = false;
@observable _heading = '';
@@ -72,6 +72,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
_ele: HTMLElement | null = null;
_eleMasonrySingle = React.createRef<HTMLDivElement>();
+ _headerRef: HTMLDivElement | null = null;
protected onInternalPreDrop = (e: Event, de: DragManager.DropEvent, targetDropAction: dropActionType) => {
const dragData = de.complete.docDragData;
@@ -93,13 +94,13 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
createColumnDropRef = (ele: HTMLDivElement | null) => {
this.dropDisposer?.();
if (ele) this.dropDisposer = DragManager.MakeDropTarget(ele, this.columnDrop.bind(this), this._props.Doc, this.onInternalPreDrop.bind(this));
- else if (this._eleMasonrySingle.current) this.props.refList.splice(this.props.refList.indexOf(this._eleMasonrySingle.current), 1);
+ else if (this._eleMasonrySingle.current) runInAction(() => this.props.colStackRefs.splice(this.props.colStackRefs.indexOf(this._eleMasonrySingle.current!), 1));
this._ele = ele;
};
@action
componentDidMount() {
- this._eleMasonrySingle.current && this.props.refList.push(this._eleMasonrySingle.current);
+ this._eleMasonrySingle.current && this.props.colStackRefs.push(this._eleMasonrySingle.current);
this._disposers.collapser = reaction(
() => this._props.headingObject?.collapsed,
collapsed => { this.collapsed = collapsed !== undefined ? BoolCast(collapsed) : false; }, // prettier-ignore
@@ -108,7 +109,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
}
componentWillUnmount() {
this._disposers.collapser?.();
- this._ele && this.props.refList.splice(this.props.refList.indexOf(this._ele), 1);
+ this._ele && runInAction(() => this.props.colStackRefs.splice(this.props.colStackRefs.indexOf(this._ele!), 1));
this._ele = null;
}
@@ -192,7 +193,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
value = typeof value === 'string' ? `"${value}"` : value;
embedding.viewSpecScript = ScriptField.MakeFunction(`doc.${this._props.pivotField} === ${value}`, { doc: Doc.name });
if (embedding.viewSpecScript) {
- DragManager.StartDocumentDrag([this._headerRef.current!], new DragManager.DocumentDragData([embedding]), e.clientX, e.clientY);
+ DragManager.StartDocumentDrag([this._headerRef!], new DragManager.DocumentDragData([embedding]), e.clientX, e.clientY);
return true;
}
return false;
@@ -314,9 +315,14 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
<div
key={heading}
className="collectionStackingView-sectionHeader"
- ref={this._headerRef}
+ ref={r => {
+ if (this._headerRef && this._props.colHeaderRefs.includes(this._headerRef)) this._props.colHeaderRefs.splice(this._props.colStackRefs.indexOf(this._headerRef), 1);
+ r && this._props.colHeaderRefs.push(r);
+ this._headerRef = r;
+ }}
style={{
- marginTop: this._props.yMargin,
+ marginTop: 0,
+ marginBottom: this._props.gridGap,
width: this._props.columnWidth,
}}>
{/* the default bucket (no key value) has a tooltip that describes what it is.
@@ -367,7 +373,7 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
ref={this._eleMasonrySingle}
className="collectionStackingView-masonrySingle"
style={{
- padding: `${columnYMargin}px ${0}px ${this._props.yMargin}px ${0}px`,
+ padding: `${columnYMargin}px ${0}px ${0}px ${0}px`,
margin: this._props.dontCenter.includes('x') ? undefined : 'auto',
height: 'max-content',
position: 'relative',
@@ -400,15 +406,13 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
render() {
TraceMobx();
- const headings = this._props.headings();
const heading = this._heading;
- const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
return (
<div
className="collectionStackingViewFieldColumn"
key={heading}
style={{
- width: `${100 / (uniqueHeadings.length + (this._props.chromeHidden ? 0 : 1) || 1)}%`,
+ width: this._props.columnWidth,
height: undefined,
background: this._background,
}}
diff --git a/src/client/views/collections/CollectionTimeView.scss b/src/client/views/collections/CollectionTimeView.scss
index d995cbcd2..d56999974 100644
--- a/src/client/views/collections/CollectionTimeView.scss
+++ b/src/client/views/collections/CollectionTimeView.scss
@@ -27,7 +27,7 @@
transform: rotate(45deg);
display: inline-block;
background: gray;
- bottom: 0;
+ bottom: 0px;
margin-bottom: -17px;
border-radius: 9px;
opacity: 0.25;
@@ -67,9 +67,9 @@
pointer-events: all;
padding: 5px;
border: 1px solid black;
- display:none;
+ display: none;
span {
- margin-left : 10px;
+ margin-left: 10px;
}
}
@@ -86,8 +86,9 @@
}
}
-.collectionTimeView:hover, .collectionTimeView-pivot:hover {
+.collectionTimeView:hover,
+.collectionTimeView-pivot:hover {
.pivotKeyEntry {
- display:unset;
+ display: unset;
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index 2a03ea708..95faaa3f0 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -11,7 +11,7 @@
height: 100%;
width: 100%;
position: relative;
- top: 0;
+ top: 0px;
// background: global.$light-gray;
font-size: 13px;
overflow: auto;
@@ -33,12 +33,12 @@
}
.no-indent {
- padding-left: 0;
+ padding-left: 0px;
//width: max-content;
}
.no-indent-outline {
- padding-left: 0;
+ padding-left: 0px;
width: 100%;
}
diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss
index 06c324bd0..837219e1d 100644
--- a/src/client/views/collections/CollectionView.scss
+++ b/src/client/views/collections/CollectionView.scss
@@ -1,7 +1,7 @@
@use '../global/globalCssVariables.module.scss' as global;
.collectionView {
- border-width: 0;
+ border-width: 0px;
border-color: global.$light-gray;
border-style: solid;
border-radius: 0 0 global.$border-radius global.$border-radius;
@@ -18,7 +18,7 @@
position: absolute;
top: 55%;
border: 1px black solid;
- border-radius: 0;
+ border-radius: 0px;
border-top-left-radius: 20px;
border-bottom-left-radius: 20px;
border-right: unset;
@@ -31,8 +31,8 @@
width: 200px;
height: 100%;
position: absolute;
- right: 0;
- top: 0;
+ right: 0px;
+ top: 0px;
border-left: solid 1px;
z-index: 1;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index eb9caf29d..72c8c3f8c 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -113,7 +113,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
};
setupViewTypes(category: string, func: (type_collection: CollectionViewType) => Doc) {
- if (!Doc.IsSystem(this.Document) && this.Document._type_collection !== CollectionViewType.Docking && !this.dataDoc.isGroup && !this.Document.annotationOn) {
+ if (!Doc.IsSystem(this.Document) && this.Document._type_collection !== CollectionViewType.Docking && !this.Document.annotationOn) {
// prettier-ignore
const subItems: ContextMenuProps[] = [
{ description: 'Freeform', event: () => func(CollectionViewType.Freeform), icon: 'signature' },
diff --git a/src/client/views/collections/FlashcardPracticeUI.scss b/src/client/views/collections/FlashcardPracticeUI.scss
index 210c6798f..0cc4711b3 100644
--- a/src/client/views/collections/FlashcardPracticeUI.scss
+++ b/src/client/views/collections/FlashcardPracticeUI.scss
@@ -8,8 +8,8 @@
.FlashcardPracticeUI-check {
position: absolute;
display: flex;
- width: 30;
- height: 30;
+ width: 30px;
+ height: 30px;
align-items: center;
border-radius: 5px;
justify-content: center;
@@ -39,7 +39,7 @@
display: flex;
top: 0px;
left: 0px;
- width: 30;
+ width: 30px;
transform-origin: top left;
border-radius: 5px;
color: rgba(255, 255, 255, 0.5);
@@ -49,7 +49,7 @@
width: 100%;
display: flex;
flex-direction: column;
- top: 0;
+ top: 0px;
position: relative;
.FlashcardPracticeUI-quiz,
.FlashcardPracticeUI-practice {
diff --git a/src/client/views/collections/FlashcardPracticeUI.tsx b/src/client/views/collections/FlashcardPracticeUI.tsx
index 2f46c00bd..17b65334c 100644
--- a/src/client/views/collections/FlashcardPracticeUI.tsx
+++ b/src/client/views/collections/FlashcardPracticeUI.tsx
@@ -61,7 +61,8 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
get practiceField() { return this._props.fieldKey + "_practice"; } // prettier-ignore
@computed get filterDoc() { return DocListCast(Doc.MyContextMenuBtns?.data).find(doc => doc.title === 'Filter'); } // prettier-ignore
- @computed get practiceMode() { return this._props.allChildDocs().some(doc => doc._layout_flashcardType) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
+ @computed get hasFlashcards() { return this._props.allChildDocs().some(doc => doc._layout_flashcardType); } // prettier-ignore
+ @computed get practiceMode() { return this.hasFlashcards ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore
btnHeight = () => NumCast(this.filterDoc?.height);
btnWidth = () => (!this.filterDoc ? 1 : NumCast(this.filterDoc._width));
@@ -130,7 +131,7 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
const setColor = (mode: practiceMode) => (StrCast(this.practiceMode) === mode ? 'white' : 'lightgray');
const togglePracticeMode = (mode: practiceMode) => this.setPracticeMode(mode === this.practiceMode ? undefined : mode);
- return !this._props.allChildDocs().some(doc => doc._layout_flashcardType) ? null : (
+ return !this.hasFlashcards ? null : (
<div
className="FlashcardPracticeUI-practiceModes"
style={{
@@ -141,8 +142,8 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
type={Type.PRIM}
color={SnappingManager.userColor}
background={SnappingManager.userVariantColor}
+ showUntilToggle={false}
multiSelect={false}
- toggleStatus={!!this.practiceMode}
label="Practice"
items={[
[practiceMode.QUIZ, 'file-pen', 'Practice flashcards using GPT'],
@@ -160,8 +161,8 @@ export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProp
type={Type.PRIM}
color={SnappingManager.userColor}
background={SnappingManager.userVariantColor}
+ showUntilToggle={false}
multiSelect={false}
- toggleStatus={!!this.practiceMode}
label={StrCast(this._props.layoutDoc.revealOp, flashcardRevealOp.FLIP)}
items={[
['reveal', StrCast(this._props.layoutDoc.revealOp) === flashcardRevealOp.SLIDE ? 'expand' : 'question', StrCast(this._props.layoutDoc.revealOp, flashcardRevealOp.FLIP)],
diff --git a/src/client/views/collections/KeyRestrictionRow.tsx b/src/client/views/collections/KeyRestrictionRow.tsx
deleted file mode 100644
index 7dc08389b..000000000
--- a/src/client/views/collections/KeyRestrictionRow.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-/* eslint-disable react/button-has-type */
-import { observable, runInAction } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-
-interface IKeyRestrictionProps {
- contains: boolean;
- script: (value: string) => void;
- field: string;
- value: string;
-}
-
-@observer
-export default class KeyRestrictionRow extends React.Component<IKeyRestrictionProps> {
- @observable private _key = this.props.field;
- @observable private _value = this.props.value;
- @observable private _contains = this.props.contains;
-
- render() {
- if (this._key && this._value) {
- let parsedValue: string | number = `"${this._value}"`;
- const parsed = parseInt(this._value);
- let type = 'string';
- if (!isNaN(parsed)) {
- parsedValue = parsed;
- type = 'number';
- }
- const scriptText = `${this._contains ? '' : '!'}(((doc.${this._key} && (doc.${this._key} as ${type})${type === 'string' ? '.includes' : '<='}(${parsedValue}))) ||
- ((doc.data_ext && doc.data_ext.${this._key}) && (doc.data_ext.${this._key} as ${type})${type === 'string' ? '.includes' : '<='}(${parsedValue}))))`;
- // let doc = new Doc();
- // ((doc.data_ext && doc.data_ext!.text) && (doc.data_ext!.text as string).includes("hello"));
- this.props.script(scriptText);
- } else {
- this.props.script('');
- }
-
- return (
- <div className="collectionViewBaseChrome-viewSpecsMenu-row">
- <input
- className="collectionViewBaseChrome-viewSpecsMenu-rowLeft"
- value={this._key}
- onChange={e =>
- runInAction(() => {
- this._key = e.target.value;
- })
- }
- placeholder="KEY"
- />
- <button
- className="collectionViewBaseChrome-viewSpecsMenu-rowMiddle"
- style={{ background: this._contains ? '#77dd77' : '#ff6961' }}
- onClick={() =>
- runInAction(() => {
- this._contains = !this._contains;
- })
- }>
- {this._contains ? 'CONTAINS' : 'DOES NOT CONTAIN'}
- </button>
- <input
- className="collectionViewBaseChrome-viewSpecsMenu-rowRight"
- value={this._value}
- onChange={e =>
- runInAction(() => {
- this._value = e.target.value;
- })
- }
- placeholder="VALUE"
- />
- </div>
- );
- }
-}
diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss
index 931cdac2b..b705d17f3 100644
--- a/src/client/views/collections/TabDocView.scss
+++ b/src/client/views/collections/TabDocView.scss
@@ -92,6 +92,6 @@ input.lm_title {
.miniMap-hidden {
cursor: pointer;
position: absolute;
- bottom: 5;
- right: 5;
+ bottom: 5px;
+ right: 5px;
}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx
index c4373aaa7..897a86e7f 100644
--- a/src/client/views/collections/TabDocView.tsx
+++ b/src/client/views/collections/TabDocView.tsx
@@ -584,9 +584,7 @@ export class TabDocView extends ObservableReactComponent<TabDocViewProps> {
miniMapColor = () => Colors.MEDIUM_GRAY;
tabView = () => this._view;
disableMinimap = () => !this._document;
- whenChildContentActiveChanges = (isActive: boolean) => {
- this._isAnyChildContentActive = isActive;
- };
+ whenChildContentActiveChanges = (isActive: boolean) => (this._isAnyChildContentActive = isActive);
isContentActiveFunc = () => this.isContentActive;
waitForDoubleClick = () => (SnappingManager.ExploreMode ? 'never' : undefined);
renderDocView = (doc: Doc) => (
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index 78794d112..542b0cc87 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -136,8 +136,8 @@
width: 100%;
height: 100%;
position: absolute;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
z-index: 0;
filter: opacity(0);
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
index 7951aff65..32cf3586f 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormRemoteCursors.scss
@@ -18,5 +18,5 @@
color: black;
// fontStyle: "italic",
margin-left: -12;
- margin-top: 4;
+ margin-top: 4px;
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 6c47a71b0..ac1ef7d65 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -2,8 +2,8 @@
.collectionfreeformview-none {
position: inherit;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
width: 100%;
height: 100%;
transform-origin: left top;
@@ -12,10 +12,10 @@
border-radius: inherit;
}
.collectionFreeForm-groupDropper {
- width: 10000;
- height: 10000;
- left: -5000;
- top: -5000;
+ width: 10000px;
+ height: 10000px;
+ left: -5000px;
+ top: -5000px;
position: absolute;
background: transparent;
pointer-events: all;
@@ -24,8 +24,8 @@
.collectionfreeformview-grid {
transform-origin: top left;
position: absolute;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
pointer-events: none;
}
@@ -219,8 +219,8 @@
border-radius: inherit;
box-sizing: border-box;
position: absolute;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
width: 100%;
height: 100%;
align-items: center;
@@ -264,7 +264,7 @@
.collectionFreeform-infoUI {
position: absolute;
display: block;
- top: 0;
+ top: 0px;
color: white;
background-color: #5075ef;
@@ -275,19 +275,19 @@
padding: 10px;
.collectionFreeform-infoUI-close {
position: absolute;
- top: -10;
- left: -10;
+ top: -10px;
+ left: -10px;
}
.collectionFreeform-infoUI-msg {
position: relative;
- max-width: 500;
- margin: 10;
+ max-width: 500px;
+ margin: 10px;
}
.collectionFreeform-infoUI-button {
border-radius: 50px;
font-size: 12px;
- padding: 6;
+ padding: 6px;
position: relative;
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 5bbe93a90..8f9b132e8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -8,7 +8,7 @@ import { computedFn } from 'mobx-utils';
import * as React from 'react';
import { AiOutlineSend } from 'react-icons/ai';
import ReactLoading from 'react-loading';
-import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils';
+import { ClientUtils, DashColor, lightOrDark, OmitKeys, returnFalse, returnTrue, returnZero, setupMoveUpEvents, UpdateIcon } from '../../../../ClientUtils';
import { DateField } from '../../../../fields/DateField';
import { Doc, DocListCast, Field, FieldType, Opt, StrListCast } from '../../../../fields/Doc';
import { DocData, DocLayout, Height, Width } from '../../../../fields/DocSymbols';
@@ -178,7 +178,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return renderableEles;
}
@computed get fitContentsToBox() {
- return (this._props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox) && !this.isAnnotationOverlay;
+ return (this._props.fitContentsToBox?.() || this.Document._freeform_fitContentsToBox || this.Document.freeform_isGroup) && !this.isAnnotationOverlay;
}
@computed get nativeWidth() {
return this._props.NativeWidth?.() || Doc.NativeWidth(this.Document);
@@ -344,7 +344,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
*/
focusOnPoint = (options: FocusViewOptions) => {
const { pointFocus, zoomTime, didMove } = options;
- if (!this.Document.isGroup && pointFocus && !didMove) {
+ if (!this.Document.freeform_isGroup && pointFocus && !didMove) {
const dfltScale = this.isAnnotationOverlay ? 1 : 0.25;
if (this.layoutDoc[this.scaleFieldKey] !== dfltScale) {
this.zoomSmoothlyAboutPt(this.screenToFreeformContentsXf.transformPoint(pointFocus.X, pointFocus.Y), dfltScale, zoomTime);
@@ -382,7 +382,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
* @returns
*/
focus = (anchor: Doc, options: FocusViewOptions) => {
- if (anchor.isGroup && !options.docTransform && options.contextPath?.length) {
+ if (Doc.IsFreeformGroup(anchor) && !options.docTransform && options.contextPath?.length) {
// don't focus on group if there's a context path because we're about to focus on a group item
// which will override any group focus. (If we allowed the group to focus, it would mark didMove even if there were no net movement)
return undefined;
@@ -397,7 +397,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
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.Document.isGroup || this.layoutDoc._lockedTransform) && !DocumentView.LightboxDoc());
+ const cantTransform = this.fitContentsToBox || ((this.Document.freeform_isGroup || this.layoutDoc._lockedTransform) && !DocumentView.LightboxDoc());
const { panX, panY, scale } = cantTransform || (!options.willPan && !options.willZoomCentered) ? savedState : this.calculatePanIntoView(anchor, xfToCollection, options?.willZoomCentered ? (options?.zoomScale ?? 0.75) : undefined);
// focus on the document in the collection
@@ -516,7 +516,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._downTime = Date.now();
const scrollMode = e.altKey ? (Doc.UserDoc().freeformScrollMode === freeformScrollMode.Pan ? freeformScrollMode.Zoom : freeformScrollMode.Pan) : Doc.UserDoc().freeformScrollMode;
if (e.button === 0 && (!(e.ctrlKey && !e.metaKey) || scrollMode !== freeformScrollMode.Pan) && this._props.isContentActive()) {
- if (!this.Document.isGroup) {
+ if (!this.Document.freeform_isGroup) {
// group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
// prettier-ignore
const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
@@ -1273,7 +1273,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
- if (this.Document.isGroup || this.Document[(this._props.viewField ?? '_') + 'freeform_noZoom']) return;
+ if (this.Document.freeform_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.screenToFreeformContentsXf.transformPoint(pointX, pointY);
@@ -1300,7 +1300,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
onPointerWheel = (e: React.WheelEvent): void => {
- if (this.Document.isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom
+ if (this.Document.freeform_isGroup || !this.isContentActive()) return; // group style collections neither pan nor zoom
SnappingManager.TriggerUserPanned();
if (this.layoutDoc._Transform || this.Document.treeView_OutlineMode === TreeViewType.outline) return;
e.stopPropagation();
@@ -1424,7 +1424,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@action
zoomSmoothlyAboutPt(docpt: number[], scale: number, transitionTime = 500) {
- if (this.Document.isGroup) return;
+ if (this.Document.freeform_isGroup) return;
this.setPanZoomTransition(transitionTime);
const screenXY = this.screenToFreeformContentsXf.inverse().transformPoint(docpt[0], docpt[1]);
this.layoutDoc[this.scaleFieldKey] = scale;
@@ -1507,7 +1507,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
removeDocument = (docs: Doc | Doc[], annotationKey?: string | undefined) => {
const ret = !!this._props.removeDocument?.(docs, annotationKey);
// if this is a group and we have fewer than 2 Docs, then just promote what's left to our parent and get rid of the group.
- if (ret && DocListCast(this.dataDoc[annotationKey ?? this.fieldKey]).length < 2 && this.Document.isGroup) {
+ if (ret && DocListCast(this.dataDoc[annotationKey ?? this.fieldKey]).length < 2 && this.Document.freeform_isGroup) {
this.promoteCollection();
}
return ret;
@@ -1550,7 +1550,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
searchFilterDocs={this.searchFilterDocs}
isDocumentActive={childLayout.pointerEvents === 'none' ? returnFalse : this._props.childDocumentsActive?.() ? this._props.isDocumentActive : this.isContentActive}
isContentActive={this.childContentsActive}
- focus={this.Document.isGroup ? this.groupFocus : this.isAnnotationOverlay ? this._props.focus : this.focus}
+ focus={this.Document.freeform_isGroup ? this.groupFocus : this.isAnnotationOverlay ? this._props.focus : this.focus}
addDocTab={this.addDocTab}
addDocument={this._props.addDocument}
removeDocument={this.removeDocument}
@@ -1735,15 +1735,16 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
});
PinDocView(
anchor,
- { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ? { ...pinProps.pinData, poslayoutview: pinProps.pinData.dataview } : {}), pannable: !this.Document.isGroup, collectionType: true, filters: true } },
+ { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ? { ...pinProps.pinData, poslayoutview: pinProps.pinData.dataview } : {}), pannable: !this.Document.freeform_isGroup, collectionType: true, filters: true } },
this.Document
);
if (addAsAnnotation) {
- if (Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
- Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), [])?.push(anchor);
+ const fieldKey = this._props.isAnnotationOverlay ? this._props.fieldKey : this._props.fieldKey + '_annotations';
+ if (Cast(this.dataDoc[fieldKey], listSpec(Doc), null) !== undefined) {
+ Cast(this.dataDoc[fieldKey], listSpec(Doc), [])?.push(anchor);
} else {
- this.dataDoc[this._props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
+ this.dataDoc[fieldKey] = new List<Doc>([anchor]);
}
}
return anchor;
@@ -1806,7 +1807,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
this._firstRender = false;
this._disposers.groupBounds = reaction(
() => {
- if (this.Document.isGroup && this.childDocs.length === this.childDocList?.length) {
+ if (this.Document.freeform_isGroup && this.childDocs.length === this.childDocList?.length) {
const clist = this.childDocs.map(cd => ({ x: NumCast(cd.x), y: NumCast(cd.y), width: NumCast(cd._width), height: NumCast(cd._height) }));
return aggregateBounds(clist, NumCast(this.layoutDoc._xMargin), NumCast(this.layoutDoc._yMargin));
}
@@ -1968,8 +1969,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const appearance = ContextMenu.Instance.findByDescription('Appearance...');
const appearanceItems = appearance?.subitems ?? [];
- !this.Document.isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' });
- !this.Document.isGroup && appearanceItems.push({ description: 'Toggle Auto Reset View', event: this.toggleResetView, icon: 'compress-arrows-alt' });
+ !this.Document.freeform_isGroup && appearanceItems.push({ description: 'Reset View', event: this.resetView, icon: 'compress-arrows-alt' });
+ !this.Document.freeform_isGroup && appearanceItems.push({ description: 'Toggle Auto Reset View', event: this.toggleResetView, icon: 'compress-arrows-alt' });
if (this._props.setContentViewBox === emptyFunction) {
!appearance && ContextMenu.Instance.addItem({ description: 'Appearance...', subitems: appearanceItems, icon: 'eye' });
return;
@@ -1987,7 +1988,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
!Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: () => this.updateIcon(), icon: 'compress-arrows-alt' });
this._props.renderDepth && appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' });
- this.Document.isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: this.transcribeStrokes, icon: 'font' });
+ this.Document.freeform_isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: this.transcribeStrokes, icon: 'font' });
!Doc.noviceMode ? appearanceItems.push({ description: 'Arrange contents in grid', event: this.layoutDocsInGrid, icon: 'table' }) : null;
!Doc.noviceMode ? appearanceItems.push({ description: (this.Document._freeform_useClusters ? 'Hide' : 'Show') + ' Clusters', event: () => this._clusters.updateClusters(!this.Document._freeform_useClusters), icon: 'braille' }) : null;
@@ -2046,7 +2047,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
transcribeStrokes = undoable(() => {
- if (this.Document.isGroup && this.Document.transcription) {
+ if (this.Document.freeform_isGroup && this.Document.transcription) {
const text = StrCast(this.Document.transcription);
const lines = text.split('\n');
const height = 30 + 15 * lines.length;
@@ -2064,7 +2065,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
dragStarting = (snapToDraggedDoc: boolean = false, showGroupDragTarget: boolean = true, visited = new Set<Doc>()) => {
if (visited.has(this.Document)) return;
visited.add(this.Document);
- showGroupDragTarget && (this.GroupChildDrag = BoolCast(this.Document.isGroup));
+ showGroupDragTarget && (this.GroupChildDrag = BoolCast(this.Document.freeform_isGroup));
const activeDocs = this.getActiveDocuments();
const size = this.screenToFreeformContentsXf.transformDirection(this._props.PanelWidth(), this._props.PanelHeight());
const selRect = { left: this.panX() - size[0] / 2, top: this.panY() - size[1] / 2, width: size[0], height: size[1] };
@@ -2072,13 +2073,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const isDocInView = (doc: Doc, rect: { left: number; top: number; width: number; height: number }) => intersectRect(docDims(doc), rect);
const snappableDocs = activeDocs.filter(doc => doc.z === undefined && isDocInView(doc, selRect)); // first see if there are any foreground docs to snap to
- activeDocs.filter(doc => doc.isGroup && SnappingManager.IsResizing !== doc[Id] && !DragManager.docsBeingDragged.includes(doc)).forEach(doc => DocumentView.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited));
+ activeDocs
+ .filter(doc => Doc.IsFreeformGroup(doc) && SnappingManager.IsResizing !== doc[Id] && !DragManager.docsBeingDragged.includes(doc))
+ .forEach(doc => DocumentView.getDocumentView(doc)?.ComponentView?.dragStarting?.(snapToDraggedDoc, false, visited));
const horizLines: number[] = [];
const vertLines: number[] = [];
const invXf = this.screenToFreeformContentsXf.inverse();
snappableDocs
- .filter(doc => !doc.isGroup && (snapToDraggedDoc || (SnappingManager.IsResizing !== doc[Id] && !DragManager.docsBeingDragged.includes(doc))))
+ .filter(doc => !Doc.IsFreeformGroup(doc) && (snapToDraggedDoc || (SnappingManager.IsResizing !== doc[Id] && !DragManager.docsBeingDragged.includes(doc))))
.forEach(doc => {
const { left, top, width, height } = docDims(doc);
const topLeftInScreen = invXf.transformPoint(left, top);
@@ -2102,6 +2105,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}
this.childDocs.some(doc => !this._renderCutoffData.get(doc[Id])) && setTimeout(this.incrementalRender, 1);
});
+ showBorderRounding = returnTrue;
showPresPaths = () => SnappingManager.ShowPresPaths;
brushedView = () => this._brushedView;
gridColor = () => DashColor(lightOrDark(this.backgroundColor)).fade(0.5).toString(); // prettier-ignore
@@ -2170,7 +2174,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
{...this._props}
ref={this._marqueeViewRef}
Doc={this.Document}
- ungroup={this.Document.isGroup ? this.promoteCollection : undefined}
+ ungroup={this.Document.freeform_isGroup ? this.promoteCollection : undefined}
nudge={this.isAnnotationOverlay || this._props.renderDepth > 0 ? undefined : this.nudge}
addDocTab={this.addDocTab}
slowLoadDocuments={this.slowLoadDocuments}
diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.scss b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.scss
index 0a001d84c..d0685e419 100644
--- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.scss
+++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.scss
@@ -2,7 +2,7 @@
display: flex;
height: max-content;
flex-direction: column;
- top: 0;
+ top: 0px;
position: absolute;
width: 100%;
height: 100%;
@@ -31,9 +31,9 @@
}
.face-document-top {
position: relative;
- top: 0;
+ top: 0px;
width: 100%;
- left: 0;
+ left: 0px;
}
.face-document-image-container {
@@ -69,8 +69,8 @@
.remove-item {
position: absolute;
- bottom: -5;
- right: -5;
+ bottom: -5px;
+ right: -5px;
background-color: rgba(0, 0, 0, 0.5); // Optional: to add a background behind the icon for better visibility
border-radius: 30%;
width: 10px; // Adjust size as needed
@@ -98,7 +98,7 @@
.faceCollectionBox {
width: 100%;
height: 100%;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
position: absolute;
}
diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
index ff9fb14e7..e3a3f9b05 100644
--- a/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/ImageLabelBox.tsx
@@ -160,15 +160,16 @@ export class ImageLabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
classifyImagesInBox = async () => {
this.startLoading();
+ const selectedImages = this._selectedImages;
// Converts the images into a Base64 format, afterwhich the information is sent to GPT to label them.
- const imageInfos = this._selectedImages.map(async doc => {
+ const imageInfos = selectedImages.map(async doc => {
if (!doc.$tags_chat) {
const url = ImageCastWithSuffix(doc[Doc.LayoutDataKey(doc)], '_o') ?? '';
return imageUrlToBase64(url).then(hrefBase64 =>
!hrefBase64 ? undefined :
- gptImageLabel(hrefBase64,'Give three labels to describe this image.').then(labels =>
+ gptImageLabel(hrefBase64, 'Give three labels to describe this image.').then(labels =>
({ doc, labels }))) ; // prettier-ignore
}
});
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index abd828945..2ec59e5d5 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -16,6 +16,7 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean, selection?: Doc[]) => Doc | void = unimplementedFunction;
public delete: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
public summarize: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
+ public generateScrapbook: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
public showMarquee: () => void = unimplementedFunction;
public hideMarquee: () => void = unimplementedFunction;
public pinWithView: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
@@ -38,6 +39,7 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
<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="Generate Scrapbook" onPointerDown={this.generateScrapbook} icon={<FontAwesomeIcon icon="palette" />} 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="Classify and Sort Images" onPointerDown={this.classifyImages} icon={<FontAwesomeIcon icon="object-group" />} color={this.userColor} />
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
index 7c9d0f6e1..135f4deac 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss
@@ -1,7 +1,7 @@
.marqueeView {
position: inherit;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
width: 100%;
height: 100%;
overflow: hidden;
@@ -20,7 +20,7 @@
pointer-events: none;
.marquee-legend {
bottom: -18px;
- left: 0;
+ left: 0px;
position: absolute;
font-size: 9;
white-space: nowrap;
@@ -28,4 +28,4 @@
.marquee-legend::after {
content: 'Press <space> for lasso';
}
-}
+} \ No newline at end of file
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 3cc7c0f2d..ff78b332a 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -28,6 +28,9 @@ import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { SubCollectionViewProps } from '../CollectionSubView';
import { ImageLabelBoxData } from './ImageLabelBox';
import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
+import { StrListCast } from '../../../../fields/Doc';
+import { requestAiGeneratedPreset, DocumentDescriptor } from '../../nodes/scrapbook/AIPresetGenerator';
+import { buildPlaceholdersFromConfigs, slotRealDocIntoPlaceholders } from '../../nodes/scrapbook/ScrapbookBox';
import './MarqueeView.scss';
interface MarqueeViewProps {
@@ -76,6 +79,11 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
@observable _labelsVisibile: boolean = false;
@observable _lassoPts: [number, number][] = [];
@observable _lassoFreehand: boolean = false;
+ // ─── New Observables for “Pick 1 of N AI Scrapbook” ───
+ @observable aiChoices: Doc[] = []; // temporary hidden Scrapbook docs
+ @observable pickerX = 0; // popup x coordinate
+ @observable pickerY = 0; // popup y coordinate
+ @observable pickerVisible = false; // show/hide ScrapbookPicker
@computed get Transform() {
return this._props.getTransform();
@@ -190,6 +198,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this._props.childLayoutString ? e.key : '';
FormattedTextBox.LiveTextUndo = UndoManager.StartBatch('type new note');
this._props.addLiveTextDocument(DocUtils.GetNewTextDoc('-typed text-', x, y, 200, 100));
+ setTimeout(() => FormattedTextBox.LiveTextUndo?.end(), 100);
e.stopPropagation();
}
};
@@ -275,6 +284,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
MarqueeOptionsMenu.Instance.createCollection = this.collection;
MarqueeOptionsMenu.Instance.delete = this.delete;
MarqueeOptionsMenu.Instance.summarize = this.summary;
+ MarqueeOptionsMenu.Instance.generateScrapbook = this.generateScrapbook;
MarqueeOptionsMenu.Instance.showMarquee = this.showMarquee;
MarqueeOptionsMenu.Instance.hideMarquee = this.hideMarquee;
MarqueeOptionsMenu.Instance.jumpTo(e.clientX, e.clientY);
@@ -372,7 +382,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
? creator(selected, { title: 'nested stack' })
: ((doc: Doc) => {
doc.$data = new List<Doc>(selected);
- doc.$isGroup = makeGroup;
+ doc.$freeform_isGroup = makeGroup;
doc.$title = makeGroup ? 'grouping' : 'nested freeform';
doc._freeform_panX = doc._freeform_panY = 0;
return doc;
@@ -508,7 +518,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
_layout_showSidebar: true,
title: 'overview',
});
- const portal = Docs.Create.FreeformDocument(selected, { title: 'summarized documents', x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: 'transparent' });
+ const portal = Docs.Create.FreeformDocument(selected, { title: 'summarized documents', x: this.Bounds.left + 200, y: this.Bounds.top, freeform_isGroup: true, backgroundColor: 'transparent' });
DocUtils.MakeLink(summary, portal, { link_relationship: 'summary of:summarized by' });
portal.hidden = true;
@@ -517,6 +527,77 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
MarqueeOptionsMenu.Instance.fadeOut(true);
});
+ getAiPresetsDescriptors = (): DocumentDescriptor[] =>
+ this.marqueeSelect(false).map(doc => ({
+ type: typeof doc.$type === 'string' ? doc.$type : 'UNKNOWN',
+ tags: Array.from(new Set(StrListCast(doc.$tags_chat))),
+ }));
+
+ generateScrapbook = action(async () => {
+ const selectedDocs = this.marqueeSelect(false);
+ if (!selectedDocs.length) return;
+
+ const descriptors = this.getAiPresetsDescriptors();
+ if (descriptors.length === 0) {
+ alert('No documents selected to generate a scrapbook from!');
+ return;
+ }
+
+ const aiPreset = await requestAiGeneratedPreset(descriptors);
+ if (!aiPreset.length) {
+ alert('Failed to generate preset');
+ return;
+ }
+ const scrapbookPlaceholders: Doc[] = buildPlaceholdersFromConfigs(aiPreset);
+ /*
+ const scrapbookPlaceholders: Doc[] = aiPreset.map(cfg => {
+ const placeholderDoc = Docs.Create.TextDocument(cfg.tag);
+ placeholderDoc.placeholder_docType = cfg.type as DocumentType;
+ placeholderDoc.placeholder_acceptTags = new List<string>(cfg.acceptTags ?? [cfg.tag]);
+
+ const placeholder = new Doc();
+ placeholder.proto = placeholderDoc;
+ placeholder.original = placeholderDoc;
+ placeholder.x = cfg.x;
+ placeholder.y = cfg.y;
+ if (cfg.width != null) placeholder._width = cfg.width;
+ if (cfg.height != null) placeholder._height = cfg.height;
+
+ return placeholder;
+ });*/
+
+ const scrapbook = Docs.Create.ScrapbookDocument(scrapbookPlaceholders, {
+ backgroundColor: '#e2ad32',
+ x: this.Bounds.left,
+ y: this.Bounds.top,
+ _width: 500,
+ _height: 500,
+ title: 'AI-generated Scrapbook',
+ });
+
+ // 3) Now grab that new scrapbook’s flat placeholders
+ const allPlaceholders = DocUtils.unwrapPlaceholders(scrapbookPlaceholders);
+
+ // 4) Slot each selectedDocs[i] into the first matching placeholder
+ selectedDocs.forEach(realDoc => slotRealDocIntoPlaceholders(realDoc, allPlaceholders));
+
+ const selected = selectedDocs.map(d => {
+ this._props.removeDocument?.(d);
+ d.x = NumCast(d.x) - this.Bounds.left;
+ d.y = NumCast(d.y) - this.Bounds.top;
+ return d;
+ });
+
+ this._props.addDocument?.(scrapbook);
+ const portal = Docs.Create.FreeformDocument(selected, { title: 'docs in scrapbook', x: this.Bounds.left + 200, y: this.Bounds.top, isGroup: true, backgroundColor: 'transparent' });
+ DocUtils.MakeLink(scrapbook, portal, { link_relationship: 'scrapbook of:in scrapbook' });
+
+ portal.hidden = true;
+ this._props.addDocument?.(portal);
+ MarqueeOptionsMenu.Instance.fadeOut(true);
+ this.hideMarquee();
+ });
+
@action
marqueeCommand = (e: KeyboardEvent) => {
const ee = e as unknown as KeyboardEvent & { propagationIsStopped?: boolean };
@@ -538,6 +619,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
if (e.key === 'g') this.collection(e, true);
if (e.key === 'c' || e.key === 't') this.collection(e);
if (e.key === 's' || e.key === 'S') this.summary();
+ if (e.key === 'g' || e.key === 'G') this.generateScrapbook(); // ← scrapbook shortcut
if (e.key === 'p') this.pileup();
this.cleanupInteractions(false);
}
@@ -682,25 +764,27 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
};
render() {
return (
- <div
- className="marqueeView"
- ref={r => {
- r?.addEventListener('dashDragMovePause', this.onDragMovePause as EventListenerOrEventListenerObject);
- this.MarqueeRef = r;
- }}
- style={{
- overflow: StrCast(this._props.Document._overflow),
- cursor: Doc.ActiveTool === InkTool.Ink || this._visible ? 'crosshair' : 'pointer',
- }}
- onDragOver={e => e.preventDefault()}
- onScroll={e => {
- e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0;
- }}
- onClick={this.onClick}
- onPointerDown={this.onPointerDown}>
- {this._visible ? this.marqueeDiv : null}
- {this.props.children}
- </div>
+ <>
+ <div
+ className="marqueeView"
+ ref={r => {
+ r?.addEventListener('dashDragMovePause', this.onDragMovePause as EventListenerOrEventListenerObject);
+ this.MarqueeRef = r;
+ }}
+ style={{
+ overflow: StrCast(this._props.Document._overflow),
+ cursor: Doc.ActiveTool === InkTool.Ink || this._visible ? 'crosshair' : 'pointer',
+ }}
+ onDragOver={e => e.preventDefault()}
+ onScroll={e => {
+ e.currentTarget.scrollTop = e.currentTarget.scrollLeft = 0;
+ }}
+ onClick={this.onClick}
+ onPointerDown={this.onPointerDown}>
+ {this._visible ? this.marqueeDiv : null}
+ {this.props.children}
+ </div>
+ </>
);
}
}
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss
index 4edaf9745..b95d3ea44 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.scss
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss
@@ -39,8 +39,8 @@
background: #d3d3d3;
position: absolute;
- height: 3;
- left: 5;
+ height: 3px;
+ left: 5px;
transform-origin: left;
transform: rotate(90deg);
outline: none;
@@ -133,7 +133,7 @@
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
- margin: 0;
+ margin: 0px;
}
/* Firefox */
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.scss b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
index 0dfaed38a..0053d3e60 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.scss
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
@@ -47,7 +47,7 @@
background: global.$medium-blue;
display: flex;
border-radius: 10px;
- height: 35;
+ height: 35px;
transform: translate3d(6px, 0px, 0px);
align-content: center;
justify-content: center;
@@ -95,11 +95,11 @@
pointer-events: all;
cursor: pointer;
background-color: global.$medium-blue;
- padding: 5;
+ padding: 5px;
border-radius: 2px;
height: 100%;
- min-width: 25;
- margin: 0;
+ min-width: 25px;
+ margin: 0px;
color: global.$white;
display: flex;
font-weight: 100;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
index 9ed247d50..1dc46102f 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
@@ -1,8 +1,8 @@
.collectionMulticolumnView_drop {
height: 100%;
width: 100%;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
position: absolute;
.collectionMulticolumnView_contents {
@@ -18,8 +18,8 @@
align-items: center;
position: relative;
> .iconButton-container {
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
position: absolute;
}
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
index 91779065d..eb157d155 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
@@ -1,8 +1,8 @@
.collectionMultirowView_drop {
height: 100%;
width: 100%;
- top: 0;
- left: 0;
+ top: 0px;
+ left: 0px;
position: absolute;
.collectionMultirowView_contents {
diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
index 10a6fa2e9..2ff99f134 100644
--- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/require-default-props */
import { action } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
index 66215f109..4f57e1656 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowHeightLabel.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/require-default-props */
import { computed } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
index 918365700..1954b4743 100644
--- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
+++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/require-default-props */
import { action } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -66,7 +65,7 @@ export default class ResizeBar extends React.Component<ResizerProps> {
style={{
pointerEvents: this.props.isContentActive?.() ? 'all' : 'none',
height: this.props.height,
- backgroundColor: !this.props.isContentActive?.() ? '' : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor) as string,
+ backgroundColor: !this.props.isContentActive?.() ? '' : (this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor) as string),
}}>
<div className="multiRowResizer-hdl" onPointerDown={e => this.registerResizing(e)} />
</div>
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 53c0823ea..e975ae6f6 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -18,7 +18,7 @@
.schema-add {
position: relative;
- height: 35;
+ height: 35px;
display: flex;
align-items: center;
top: -10px;
@@ -147,7 +147,7 @@
flex-direction: row;
justify-content: space-between;
align-items: center;
- padding: 0;
+ padding: 0px;
z-index: 1;
border: 1px solid global.$medium-gray;
@@ -231,7 +231,7 @@
overflow-x: hidden;
overflow-y: auto;
display: inline-flex;
- padding: 0;
+ padding: 0px;
align-items: center;
input[type='text'] {
border: unset;
@@ -272,8 +272,8 @@
.row-menu-infos {
position: absolute;
- top: 3;
- left: 3;
+ top: 3px;
+ left: 3px;
z-index: 1;
display: flex;
justify-content: flex-end;