aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2025-03-06 16:17:47 -0500
committerbobzel <zzzman@gmail.com>2025-03-06 16:17:47 -0500
commit5ad858090f3006631062877d90120e3cc505fada (patch)
tree9f87a8e1e7098a1025f6f4aac332dbc854db5be3 /src/client/views/collections
parent9c2a7c14fd9d0e44609aab30c6323583162009db (diff)
parentadaa107aac8558fa6f46e6ba1263c650c212d506 (diff)
Merge branch 'master' into aarav_edit
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionCardDeckView.scss9
-rw-r--r--src/client/views/collections/CollectionCardDeckView.tsx180
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.scss12
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx2
-rw-r--r--src/client/views/collections/CollectionDockingView.scss22
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx23
-rw-r--r--src/client/views/collections/CollectionMasonryViewFieldRow.tsx1
-rw-r--r--src/client/views/collections/CollectionMenu.scss6
-rw-r--r--src/client/views/collections/CollectionNoteTakingView.scss22
-rw-r--r--src/client/views/collections/CollectionPivotView.tsx147
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.scss26
-rw-r--r--src/client/views/collections/CollectionStackingView.scss206
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx71
-rw-r--r--src/client/views/collections/CollectionSubView.tsx54
-rw-r--r--src/client/views/collections/CollectionTimeView.tsx158
-rw-r--r--src/client/views/collections/CollectionTreeView.scss10
-rw-r--r--src/client/views/collections/CollectionView.scss6
-rw-r--r--src/client/views/collections/CollectionView.tsx11
-rw-r--r--src/client/views/collections/TabDocView.scss2
-rw-r--r--src/client/views/collections/TreeView.scss6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx6
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss16
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx54
-rw-r--r--src/client/views/collections/collectionFreeForm/ImageLabelHandler.tsx14
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.scss13
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx135
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.scss22
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.scss40
28 files changed, 588 insertions, 686 deletions
diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index 79c53db08..e6cc398af 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -1,5 +1,3 @@
-@import '../global/globalCssVariables.module.scss';
-
.collectionCardView-outer {
height: 100%;
width: 100%;
@@ -11,6 +9,7 @@
display: flex;
transform-origin: top left;
align-items: center;
+ position: relative;
}
button {
@@ -35,6 +34,12 @@
.collectionCardView-cardSizeDragger {
position: absolute;
top: 0;
+ width: 28px;
+ height: 28px;
+ > svg {
+ width: 100%;
+ height: 100%;
+ }
}
.no-card-span {
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 43464e50c..756b37f99 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -1,22 +1,22 @@
-import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import * as CSS from 'csstype';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import * as React from 'react';
-import * as CSS from 'csstype';
-import { ClientUtils, imageUrlToBase64, returnFalse, returnNever, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
+import { ClientUtils, returnFalse, returnNever, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { Animation, DocData } from '../../../fields/DocSymbols';
+import { Animation } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { URLField } from '../../../fields/URLField';
-import { gptImageLabel } from '../../apis/gpt/GPT';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { DocumentType } from '../../documents/DocumentTypes';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { dropActionType } from '../../util/DropActionTypes';
+import { SettingsManager } from '../../util/SettingsManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoable, UndoManager } from '../../util/UndoManager';
@@ -25,11 +25,8 @@ import { StyleProp } from '../StyleProp';
import { TagItem } from '../TagsView';
import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
import { FocusViewOptions } from '../nodes/FocusViewOptions';
-import { GPTPopup } from '../pdf/GPTPopup/GPTPopup';
import './CollectionCardDeckView.scss';
-import { CollectionSubView, docSortings, SubCollectionViewProps } from './CollectionSubView';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { SettingsManager } from '../../util/SettingsManager';
+import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
/**
* New view type specifically for studying more dynamically. Allows you to reorder docs however you see fit, easily
@@ -42,7 +39,6 @@ import { SettingsManager } from '../../util/SettingsManager';
export class CollectionCardView extends CollectionSubView() {
private _dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [key: string]: IReactionDisposer } = {};
- private _textToDoc = new Map<string, Doc>();
private _oldWheel: HTMLElement | null = null;
private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
private _setCurDocScript = () => ScriptField.MakeScript('scriptContext.layoutDoc._card_curDoc=this', { scriptContext: 'any' })!;
@@ -74,22 +70,7 @@ export class CollectionCardView extends CollectionSubView() {
return Math.ceil(this.cardDeckWidth / this.cardWidth);
}
- /**
- * update's gpt's doc-text list and initializes callbacks
- */
- childPairStringListAndUpdateSortDesc = () =>
- this.childPairStringList().then(sortDesc => {
- GPTPopup.Instance.setSortDesc(sortDesc.join());
- GPTPopup.Instance.onSortComplete = this.processGptOutput;
- GPTPopup.Instance.onQuizRandom = this.quizMode;
- });
-
componentDidMount() {
- this._disposers.chatVis = reaction(
- () => GPTPopup.Instance.Visible,
- vis => !vis && this.onGptHide()
- );
- GPTPopup.Instance.setRegenerateCallback(this.Document, this.childPairStringListAndUpdateSortDesc);
this._props.setContentViewBox?.(this);
// if card deck moves, then the child doc views are hidden so their screen to local transforms will return empty rectangles
// when inquired from the dom (below in childScreenToLocal). When the doc is actually rendered, we need to act like the
@@ -110,12 +91,7 @@ export class CollectionCardView extends CollectionSubView() {
);
}
- onGptHide = () => Doc.setDocFilter(this.Document, 'tags', '#chat', 'remove');
componentWillUnmount() {
- GPTPopup.Instance.setSortDesc('');
- GPTPopup.Instance.onSortComplete = undefined;
- GPTPopup.Instance.onQuizRandom = undefined;
- GPTPopup.Instance.setRegenerateCallback(undefined, null);
Object.keys(this._disposers).forEach(key => this._disposers[key]?.());
this._dropDisposer?.();
}
@@ -130,7 +106,7 @@ export class CollectionCardView extends CollectionSubView() {
* Circle arc size, in radians, to layout cards
*/
@computed get archAngle() {
- return NumCast(this.layoutDoc.card_arch, 90) * (Math.PI / 180) * (this.childCards.length < this._maxRowCount ? this.childCards.length / this._maxRowCount : 1);
+ return NumCast(this.layoutDoc.card_arch, 90) * (Math.PI / 180) * (this.childDocsNoInk.length < this._maxRowCount ? this.childDocsNoInk.length / this._maxRowCount : 1);
}
/**
* Spacing card rows as a percent of Doc size. 100 means rows spread out to fill 100% of the Doc vertically. Default is 60%
@@ -142,7 +118,7 @@ export class CollectionCardView extends CollectionSubView() {
/**
* The child documents to be rendered-- everything other than ink/link docs (which are marks as being svg's)
*/
- @computed get childCards() {
+ @computed get childDocsNoInk() {
return this.childLayoutPairs.filter(pair => !pair.layout.layout_isSvg);
}
@@ -150,8 +126,8 @@ export class CollectionCardView extends CollectionSubView() {
* how much to scale down the contents of the view so that everything will fit
*/
@computed get fitContentScale() {
- const length = Math.min(this.childCards.length, this._maxRowCount);
- return (this.childPanelWidth() * length) / this._props.PanelWidth();
+ const length = Math.min(this.childDocsNoInk.length, this._maxRowCount);
+ return (this.childPanelWidth() * length) / (this._props.PanelWidth() - 2 * this.xMargin);
}
@computed get nativeScaling() {
@@ -162,23 +138,20 @@ export class CollectionCardView extends CollectionSubView() {
return NumCast(this.layoutDoc._xMargin, Math.max(3, 0.05 * this._props.PanelWidth()));
}
+ @computed get yMargin() {
+ return this._props.yPadding || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
+ }
+
@computed get cardDeckWidth() {
return this._props.PanelWidth() - 2 * this.xMargin;
}
- /**
- * When in quiz mode, randomly selects a document
- */
- quizMode = () => {
- this.layoutDoc._card_curDoc = this.childDocs[Math.floor(Math.random() * this.childDocs.length)];
- };
-
setHoveredNodeIndex = action((index: number) => {
if (!SnappingManager.IsDragging) this._hoveredNodeIndex = index;
});
isSelected = (doc: Doc) => this._docRefs.get(doc)?.IsSelected;
- childPanelWidth = () => NumCast(this.layoutDoc.childPanelWidth, Math.max(150, this._props.PanelWidth() / (this.childCards.length > this._maxRowCount ? this._maxRowCount : this.childCards.length) / this.nativeScaling));
+ childPanelWidth = () => NumCast(this.layoutDoc.childPanelWidth, Math.max(150, this._props.PanelWidth() / (this.childDocsNoInk.length > this._maxRowCount ? this._maxRowCount : this.childDocsNoInk.length) / this.nativeScaling));
childPanelHeight = () => this._props.PanelHeight() * this.fitContentScale;
onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this.isAnyChildContentActive();
@@ -322,10 +295,10 @@ export class CollectionCardView extends CollectionSubView() {
* @returns number of cards in row that contains index
*/
cardsInRowThatIncludesCardIndex = (index: number) => {
- if (this.childCards.length < this._maxRowCount) {
- return this.childCards.length;
+ if (this.childDocsNoInk.length < this._maxRowCount) {
+ return this.childDocsNoInk.length;
}
- const totalCards = this.childCards.length;
+ const totalCards = this.childDocsNoInk.length;
if (index < totalCards - (totalCards % this._maxRowCount)) {
return this._maxRowCount;
}
@@ -389,103 +362,6 @@ export class CollectionCardView extends CollectionSubView() {
: this.translateY(index);
};
- /**
- * A list of the text content of all the child docs. RTF documents will have just their text and pdf documents will have the first 50 words.
- * Image documents are converted to bse64 and gpt generates a description for them. all other documents use their title. This string is
- * inputted into the gpt prompt to sort everything together
- * @returns
- */
- childPairStringList = () => {
- const docToText = (doc: Doc) => {
- switch (doc.type) {
- case DocumentType.PDF: return StrCast(doc.text).split(/\s+/).slice(0, 50).join(' '); // first 50 words of pdf text
- case DocumentType.IMG: return this.getImageDesc(doc);
- case DocumentType.RTF: return StrCast(RTFCast(doc.text).Text);
- default: return StrCast(doc.title);
- } // prettier-ignore
- };
- const docTextPromises = this.childCards
- .map(pair => pair.layout)
- .map(async doc => {
- const docText = (await docToText(doc)) ?? '';
- doc.gptInputText = docText;
- this._textToDoc.set(docText.replace(/\n/g, ' ').trim(), doc);
- return `======${docText.replace(/\n/g, ' ').trim()}======`;
- });
- return Promise.all<string>(docTextPromises);
- };
-
- /**
- * Calls the gpt API to generate descriptions for the images in the view
- * @param image
- * @returns
- */
- getImageDesc = async (image: Doc) => {
- if (StrCast(image.description)) return StrCast(image.description); // Return existing description
- const { href } = (image.data as URLField).url;
- const hrefParts = href.split('.');
- const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
- try {
- const hrefBase64 = await imageUrlToBase64(hrefComplete);
- const response = await gptImageLabel(hrefBase64, 'Give three to five labels to describe this image.');
- image[DocData].description = response.trim();
- return response; // Return the response from gptImageLabel
- } catch (error) {
- console.log(error);
- }
- return '';
- };
-
- /**
- * Processes gpt's output depending on the type of question the user asked. Converts gpt's string output to
- * usable code
- * @param gptOutput
- * @param questionType
- * @param tag
- */
- processGptOutput = (gptOutput: string, questionType: string, tag?: string) =>
- undoable(() => {
- // Split the string into individual list items
- const listItems = gptOutput.split('======').filter(item => item.trim() !== '');
-
- if (questionType === '2' || questionType === '4') {
- this.childDocs.forEach(d => {
- TagItem.removeTagFromDoc(d, '#chat');
- });
- }
-
- if (questionType === '6') {
- this.Document[this._props.fieldKey + '_sort'] = docSortings.Chat;
- }
-
- listItems.forEach((item, index) => {
- const normalizedItem = item.trim();
- // find the corresponding Doc in the textToDoc map
- const doc = this._textToDoc.get(normalizedItem);
- if (doc) {
- switch (questionType) {
- case '6':
- doc.chatIndex = index;
- break;
- case '1':
- if (tag) {
- const hashTag = tag.startsWith('#') ? tag : '#' + tag[0].toLowerCase() + tag.slice(1);
- const filterTag = Doc.MyFilterHotKeys.map(key => StrCast(key.toolType)).find(key => key.includes(tag)) ?? hashTag;
- TagItem.addTagToDoc(doc, filterTag);
- }
- break;
- case '2':
- case '4':
- TagItem.addTagToDoc(doc, '#chat');
- Doc.setDocFilter(this.Document, 'tags', '#chat', 'check');
- break;
- }
- } else {
- console.warn(`No matching document found for item: ${normalizedItem}`);
- }
- });
- }, '')();
-
childScreenToLocal = computedFn((doc: Doc, index: number, isSelected: boolean) => () => {
// need to explicitly trigger an invalidation since we're reading everything from the Dom
this._forceChildXf;
@@ -531,8 +407,8 @@ export class CollectionCardView extends CollectionSubView() {
setupMoveUpEvents(
this,
e,
- (emove: PointerEvent, down: number[], delta: number[]) => {
- this.layoutDoc._cardWidth = Math.max(10, delta[0] < 0 ? Math.floor(this.cardWidth + delta[0]) : Math.ceil(this.cardWidth + delta[0]));
+ (emove: PointerEvent) => {
+ this.layoutDoc._cardWidth = Math.max(10, this.ScreenToLocalBoxXf().transformPoint(emove.clientX, 0)[0] - this.xMargin);
return false;
},
action(() => {
@@ -625,7 +501,7 @@ export class CollectionCardView extends CollectionSubView() {
curDoc = () => DocCast(this.layoutDoc._card_curDoc);
render() {
- const fitContentScale = this.childCards.length === 0 ? 1 : this.fitContentScale;
+ const fitContentScale = this.childDocsNoInk.length === 0 ? 1 : this.fitContentScale;
return (
<div
className="collectionCardView-outer"
@@ -637,6 +513,8 @@ export class CollectionCardView extends CollectionSubView() {
style={{
background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string,
color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string,
+ paddingLeft: this.xMargin,
+ paddingRight: this.xMargin,
}}>
<div
className="collectionCardView-inner"
@@ -644,6 +522,7 @@ export class CollectionCardView extends CollectionSubView() {
transform: `scale(${1 / fitContentScale})`,
height: `${100 * fitContentScale}%`,
width: `${100 * fitContentScale}%`,
+ top: this.yMargin,
}}>
<div
className="collectionCardView-cardwrapper"
@@ -657,15 +536,14 @@ export class CollectionCardView extends CollectionSubView() {
<div
className="collectionCardView-flashcardUI"
style={{
- pointerEvents: this.childCards.length === 0 ? undefined : 'none',
+ pointerEvents: this.childDocsNoInk.length === 0 ? undefined : 'none',
height: `${100 / this.nativeScaling / fitContentScale}%`,
width: `${100 / this.nativeScaling / fitContentScale}%`,
transform: `scale(${this.nativeScaling * fitContentScale})`,
- }}>
- {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
- </div>
+ }}></div>
</div>
+ {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
<div
className="collectionCardView-cardSizeDragger"
onPointerDown={this.cardSizerDown}
diff --git a/src/client/views/collections/CollectionCarousel3DView.scss b/src/client/views/collections/CollectionCarousel3DView.scss
index 42e112906..13e6b54c2 100644
--- a/src/client/views/collections/CollectionCarousel3DView.scss
+++ b/src/client/views/collections/CollectionCarousel3DView.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.module.scss';
+@use '../global/globalCssVariables.module.scss' as global;
.collectionCarousel3DView-outer {
height: 100%;
position: relative;
@@ -10,8 +10,8 @@
.carousel-wrapper {
display: flex;
position: absolute;
- top: $CAROUSEL3D_TOP * 1%;
- height: ($CAROUSEL3D_SIDE_SCALE * 100) * 1%;
+ top: global.$CAROUSEL3D_TOP * 1%;
+ height: (global.$CAROUSEL3D_SIDE_SCALE * 100) * 1%;
align-items: center;
transition: transform 0.3s cubic-bezier(0.455, 0.03, 0.515, 0.955);
@@ -24,7 +24,7 @@
pointer-events: none;
opacity: 0.5;
z-index: 1;
- transform: scale($CAROUSEL3D_SIDE_SCALE);
+ transform: scale(global.$CAROUSEL3D_SIDE_SCALE);
user-select: none;
}
@@ -32,7 +32,7 @@
pointer-events: unset;
opacity: 1;
z-index: 2;
- transform: scale($CAROUSEL3D_CENTER_SCALE);
+ transform: scale(global.$CAROUSEL3D_CENTER_SCALE);
}
}
@@ -80,7 +80,7 @@
.carousel3DView-back {
top: 0;
background: transparent;
- width: calc((1 - #{$CAROUSEL3D_CENTER_SCALE} * 0.33) / 2 * 100%);
+ width: calc((1 - #{global.$CAROUSEL3D_CENTER_SCALE} * 0.33) / 2 * 100%);
height: 100%;
}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 87c6e3e5c..a7d217076 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -249,9 +249,9 @@ export class CollectionCarouselView extends CollectionSubView() {
position: 'relative',
}}>
{this.content}
- {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
{this.navButtons}
</div>
+ {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
</div>
);
}
diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss
index a747ef45f..7c19d39da 100644
--- a/src/client/views/collections/CollectionDockingView.scss
+++ b/src/client/views/collections/CollectionDockingView.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.module.scss';
+@use '../global/globalCssVariables.module.scss' as global;
.lm_root {
position: relative;
@@ -285,7 +285,7 @@
background: transparent;
border: solid 0px transparent;
cursor: grab;
- color: $black;
+ color: global.$black;
}
.collectiondockingview-container .lm_splitter {
opacity: 0.2;
@@ -378,7 +378,7 @@ ul.lm_tabs::before {
z-index: 1;
text-align: center;
font-size: 18;
- color: $dark-gray;
+ color: global.$dark-gray;
img {
position: relative;
@@ -491,7 +491,7 @@ ul.lm_tabs::before {
}
.lm_content {
- background: $white;
+ background: global.$white;
}
.lm_controls {
@@ -557,7 +557,7 @@ ul.lm_tabs::before {
}
.flexlayout__splitter {
- background-color: $dark-gray;
+ background-color: global.$dark-gray;
}
.flexlayout__splitter:hover {
@@ -626,7 +626,7 @@ ul.lm_tabs::before {
position: absolute;
box-sizing: border-box;
background-color: #222;
- color: $dark-gray;
+ color: global.$dark-gray;
}
.flexlayout__tab_button {
@@ -709,7 +709,7 @@ ul.lm_tabs::before {
}
.flexlayout__tab_header_outer {
- background-color: $dark-gray;
+ background-color: global.$dark-gray;
position: absolute;
left: 0;
right: 0;
@@ -769,28 +769,28 @@ ul.lm_tabs::before {
}
.flexlayout__border_top {
- background-color: $dark-gray;
+ background-color: global.$dark-gray;
border-bottom: 1px solid #ddd;
box-sizing: border-box;
overflow: hidden;
}
.flexlayout__border_bottom {
- background-color: $dark-gray;
+ background-color: global.$dark-gray;
border-top: 1px solid #333;
box-sizing: border-box;
overflow: hidden;
}
.flexlayout__border_left {
- background-color: $dark-gray;
+ background-color: global.$dark-gray;
border-right: 1px solid #333;
box-sizing: border-box;
overflow: hidden;
}
.flexlayout__border_right {
- background-color: $dark-gray;
+ background-color: global.$dark-gray;
border-left: 1px solid #333;
box-sizing: border-box;
overflow: hidden;
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 539b49c86..e51bc18ef 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -48,8 +48,7 @@ export class CollectionDockingView extends CollectionSubView() {
// eslint-disable-next-line no-use-before-define
@observable public static Instance: CollectionDockingView | undefined = undefined;
- private _reactionDisposer?: IReactionDisposer;
- private _lightboxReactionDisposer?: IReactionDisposer;
+ private _disposers: { [key: string]: IReactionDisposer } = {};
private _containerRef = React.createRef<HTMLDivElement>();
private _flush: UndoManager.Batch | undefined;
private _unmounting = false;
@@ -341,12 +340,12 @@ export class CollectionDockingView extends CollectionSubView() {
this._unmounting = false;
SetPropSetterCb('title', this.titleChanged); // this overrides any previously assigned callback for the property
if (this._containerRef.current) {
- this._lightboxReactionDisposer = reaction(
+ this._disposers.lightbox = reaction(
() => DocumentView.LightboxDoc(),
doc => setTimeout(() => !doc && this.onResize())
);
new ResizeObserver(this.onResize).observe(this._containerRef.current);
- this._reactionDisposer = reaction(
+ this._disposers.docking = reaction(
() => StrCast(this.Document.dockingConfig),
config => {
if (!this._goldenLayout || this._ignoreStateChange !== config) {
@@ -356,12 +355,16 @@ export class CollectionDockingView extends CollectionSubView() {
this._ignoreStateChange = '';
}
);
- reaction(
+ this._disposers.panel = reaction(
() => this._props.PanelWidth(),
- width => !this._goldenLayout && width > 20 && setTimeout(() => this.setupGoldenLayout()), // need to wait for the collectiondockingview-container to have it's width/height since golden layout reads that to configure its windows
+ width => {
+ if (!this._goldenLayout && width > 20) {
+ setTimeout(() => this.setupGoldenLayout());
+ }
+ }, // need to wait for the collectiondockingview-container to have it's width/height since golden layout reads that to configure its windows
{ fireImmediately: true }
);
- reaction(
+ this._disposers.color = reaction(
() => [SnappingManager.userBackgroundColor, SnappingManager.userBackgroundColor],
() => {
clearStyleSheetRules(CollectionDockingView._highlightStyleSheet);
@@ -376,6 +379,7 @@ export class CollectionDockingView extends CollectionSubView() {
componentWillUnmount: () => void = () => {
this._unmounting = true;
+ Object.values(this._disposers).forEach(d => d());
try {
this._goldenLayout.unbind('stackCreated', this.stackCreated);
this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
@@ -385,9 +389,6 @@ export class CollectionDockingView extends CollectionSubView() {
setTimeout(() => this._goldenLayout?.destroy());
window.removeEventListener('resize', this.onResize);
window.removeEventListener('mouseup', this.onPointerUp);
-
- this._reactionDisposer?.();
- this._lightboxReactionDisposer?.();
};
// ViewBoxInterface overrides
@@ -426,7 +427,7 @@ export class CollectionDockingView extends CollectionSubView() {
@action
onPointerUp = (): void => {
- window.removeEventListener('pointerup', this.onPointerUp);
+ window.removeEventListener('mouseup', this.onPointerUp);
DragManager.CompleteWindowDrag = undefined;
setTimeout(this.endUndoBatch, 100);
};
diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
index 710c00841..a0d5f9f70 100644
--- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
+++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx
@@ -258,7 +258,6 @@ export class CollectionMasonryViewFieldRow extends ObservableReactComponent<CMVF
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 `;
return this.collapsed ? null : (
<div style={{ position: 'relative' }}>
- {this._props.showHandle && this._props.parent._props.isContentActive() ? this._props.parent.columnDragger : null}
{showChrome ? (
<div
className="collectionStackingView-addDocumentButton"
diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss
index 45d9394ed..11fce720c 100644
--- a/src/client/views/collections/CollectionMenu.scss
+++ b/src/client/views/collections/CollectionMenu.scss
@@ -1,13 +1,13 @@
-@import '../global/globalCssVariables.module.scss';
+@use '../global/globalCssVariables.module.scss' as global;
.collectionMenu-container {
display: flex;
position: relative;
align-content: center;
justify-content: space-between;
- background-color: $dark-gray;
+ background-color: global.$dark-gray;
height: 40px;
- border-bottom: $standard-border;
+ border-bottom: global.$standard-border;
padding: 0 10px;
align-items: center;
overflow-x: auto;
diff --git a/src/client/views/collections/CollectionNoteTakingView.scss b/src/client/views/collections/CollectionNoteTakingView.scss
index 95fda7b0a..0d24a56b5 100644
--- a/src/client/views/collections/CollectionNoteTakingView.scss
+++ b/src/client/views/collections/CollectionNoteTakingView.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.module.scss';
+@use '../global/globalCssVariables.module.scss' as global;
.collectionNoteTakingView-DocumentButtons {
opacity: 0;
@@ -58,7 +58,7 @@
.documentButtonMenu {
position: relative;
height: fit-content;
- border-bottom: $standard-border;
+ border-bottom: global.$standard-border;
display: flex;
justify-content: center;
flex-direction: column;
@@ -70,11 +70,11 @@
width: 90%;
margin: 5px;
font-size: 11px;
- background-color: $light-blue;
- color: $medium-blue;
+ background-color: global.$light-blue;
+ color: global.$medium-blue;
padding: 10px;
border-radius: 10px;
- border: solid 2px $medium-blue;
+ border: solid 2px global.$medium-blue;
}
}
@@ -146,9 +146,9 @@
padding: 10px;
height: 2vw;
width: 100%;
- font-family: $sans-serif;
- background: $dark-gray;
- color: $white;
+ font-family: global.$sans-serif;
+ background: global.$dark-gray;
+ color: global.$white;
}
.collectionNoteTakingView-columnDragger {
@@ -206,7 +206,7 @@
margin-left: 2px;
margin-right: 2px;
margin-top: 2px;
- background: $medium-gray;
+ background: global.$medium-gray;
height: 5px;
&.active {
@@ -258,7 +258,7 @@
text-align: center;
margin: auto;
margin-bottom: 10px;
- background: $medium-gray;
+ background: global.$medium-gray;
// overflow: hidden; overflow is visible so the color menu isn't hidden -ftong
.editableView-input:hover,
@@ -279,7 +279,7 @@
display: flex;
align-items: center;
justify-content: center;
- color: $dark-gray;
+ color: global.$dark-gray;
.editableView-container-editing-oneLine,
.editableView-container-editing {
diff --git a/src/client/views/collections/CollectionPivotView.tsx b/src/client/views/collections/CollectionPivotView.tsx
new file mode 100644
index 000000000..2600c0f57
--- /dev/null
+++ b/src/client/views/collections/CollectionPivotView.tsx
@@ -0,0 +1,147 @@
+import { action, computed, makeObservable, observable, runInAction } from 'mobx';
+import { observer } from 'mobx-react';
+import * as React from 'react';
+import { returnTrue } from '../../../ClientUtils';
+import { Doc, Opt, StrListCast } from '../../../fields/Doc';
+import { List } from '../../../fields/List';
+import { ObjectField } from '../../../fields/ObjectField';
+import { ComputedField, ScriptField } from '../../../fields/ScriptField';
+import { NumCast, StrCast } from '../../../fields/Types';
+import { Docs } from '../../documents/Documents';
+import { ScriptingGlobals } from '../../util/ScriptingGlobals';
+import { FieldsDropdown } from '../FieldsDropdown';
+import { PinDocView } from '../PinFuncs';
+import { DocumentView } from '../nodes/DocumentView';
+import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import './CollectionTimeView.scss';
+import { ViewDefBounds, computePivotLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
+import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
+
+@observer
+export class CollectionPivotView extends CollectionSubView() {
+ _changing = false;
+ @observable _collapsed: boolean = false;
+ @observable _childClickedScript: Opt<ScriptField> = undefined;
+ @observable _viewDefDivClick: Opt<ScriptField> = undefined;
+ @observable _focusPivotField: Opt<string> = undefined;
+
+ constructor(props: SubCollectionViewProps) {
+ super(props);
+ makeObservable(this);
+ }
+
+ componentDidMount() {
+ this._props.setContentViewBox?.(this);
+ runInAction(() => {
+ this._childClickedScript = ScriptField.MakeScript('openInLightbox(this)', { this: Doc.name });
+ this._viewDefDivClick = ScriptField.MakeScript('pivotColumnClick(this,payload)', { payload: 'any' });
+ });
+ }
+
+ get pivotField() {
+ return this._focusPivotField || StrCast(this.layoutDoc._pivotField);
+ }
+
+ getAnchor = (addAsAnnotation: boolean) => {
+ const anchor = Docs.Create.ConfigDocument({
+ title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as unknown as string, // title can take a functiono or a string
+ annotationOn: this.Document,
+ });
+ PinDocView(anchor, { pinData: { collectionType: true, pivot: true, filters: true } }, this.Document);
+ addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
+ return anchor;
+ };
+
+ @action
+ scrollPreview = (docView: DocumentView, anchor: Doc /* , focusSpeed: number, options: FocusViewOptions */) => {
+ // if in preview, then override document's fields with view spec
+ this._focusFilters = StrListCast(anchor.config_docFilters);
+ this._focusRangeFilters = StrListCast(anchor.config_docRangeFilters);
+ this._focusPivotField = StrCast(anchor.config_pivotField);
+ return undefined;
+ };
+
+ toggleVisibility = action(() => {
+ this._collapsed = !this._collapsed;
+ });
+
+ goTo = (prevFilterIndex: number) => {
+ this.layoutDoc._pivotField = this.layoutDoc['_prevPivotFields' + prevFilterIndex];
+ this.layoutDoc._childFilters = ObjectField.MakeCopy(this.layoutDoc['_prevDocFilter' + prevFilterIndex] as ObjectField);
+ this.layoutDoc._childFiltersByRanges = ObjectField.MakeCopy(this.layoutDoc['_prevDocRangeFilters' + prevFilterIndex] as ObjectField);
+ this.layoutDoc._prevFilterIndex = prevFilterIndex;
+ };
+
+ @action
+ contentsDown = () => {
+ const prevFilterIndex = NumCast(this.layoutDoc._prevFilterIndex);
+ if (prevFilterIndex > 0) {
+ this.goTo(prevFilterIndex - 1);
+ } else {
+ this.layoutDoc._childFilters = new List([]);
+ }
+ };
+ layoutEngine = () => computePivotLayout.name;
+ @computed get contents() {
+ return (
+ <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this._props.isContentActive() ? undefined : 'none' }} onClick={this.contentsDown}>
+ <CollectionFreeFormView
+ {...this._props}
+ engineProps={{ pivotField: this.pivotField, childFilters: this.childDocFilters, childFiltersByRanges: this.childDocRangeFilters }}
+ fitContentsToBox={returnTrue}
+ childClickScript={this._childClickedScript}
+ viewDefDivClick={this._viewDefDivClick}
+ layoutEngine={this.layoutEngine}
+ />
+ </div>
+ );
+ }
+
+ render() {
+ return (
+ <div className="collectionTimeView-pivot" style={{ width: this._props.PanelWidth(), height: '100%' }}>
+ {this.contents}
+ <div style={{ right: 0, top: 0, position: 'absolute' }}>
+ <FieldsDropdown
+ Document={this.Document}
+ selectFunc={fieldKey => {
+ this.layoutDoc._pivotField = fieldKey;
+ }}
+ placeholder={StrCast(this.layoutDoc._pivotField)}
+ />
+ </div>
+ </div>
+ );
+ }
+}
+
+// eslint-disable-next-line prefer-arrow-callback
+ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) {
+ const pivotField = StrCast(pivotDoc._pivotField, 'author');
+ let prevFilterIndex = NumCast(pivotDoc._prevFilterIndex);
+ const originalFilter = StrListCast(ObjectField.MakeCopy(pivotDoc._childFilters as ObjectField));
+ pivotDoc['_prevDocFilter' + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._childFilters as ObjectField);
+ pivotDoc['_prevDocRangeFilters' + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._childFiltersByRanges as ObjectField);
+ pivotDoc['_prevPivotFields' + prevFilterIndex] = pivotField;
+ pivotDoc._prevFilterIndex = ++prevFilterIndex;
+ pivotDoc._childFilters = new List();
+ setTimeout(
+ action(() => {
+ const filterVals = bounds.payload as string[];
+ filterVals.map(filterVal => Doc.setDocFilter(pivotDoc, pivotField, filterVal, 'check'));
+ const pivotView = DocumentView.getDocumentView(pivotDoc);
+ if (pivotDoc && pivotView?.ComponentView instanceof CollectionPivotView && filterVals.length === 1) {
+ if (pivotView?.ComponentView.childDocs.length && pivotView.ComponentView.childDocs[0][filterVals[0]]) {
+ pivotDoc._pivotField = filterVals[0];
+ }
+ }
+ const newFilters = StrListCast(pivotDoc._childFilters);
+ if (newFilters.length && originalFilter.length && newFilters.lastElement() === originalFilter.lastElement()) {
+ pivotDoc._prevFilterIndex = --prevFilterIndex;
+ pivotDoc['_prevDocFilter' + prevFilterIndex] = undefined;
+ pivotDoc['_prevDocRangeFilters' + prevFilterIndex] = undefined;
+ pivotDoc['_prevPivotFields' + prevFilterIndex] = undefined;
+ }
+ })
+ );
+});
diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss
index 0ced3f9e3..d05c0ffde 100644
--- a/src/client/views/collections/CollectionStackedTimeline.scss
+++ b/src/client/views/collections/CollectionStackedTimeline.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.module.scss';
+@use '../global/globalCssVariables.module.scss' as global;
.collectionStackedTimeline-timelineContainer {
height: 100%;
@@ -6,7 +6,7 @@
overflow-x: auto;
overflow-y: hidden;
border: none;
- background-color: $white;
+ background-color: global.$white;
border-width: 0 2px 0 2px;
&:hover {
@@ -28,7 +28,7 @@
.collectionStackedTimeline {
position: absolute;
- background: $off-white;
+ background: global.$off-white;
z-index: 1000;
height: 100%;
overflow: hidden;
@@ -36,7 +36,7 @@
.collectionStackedTimeline-trim-shade {
position: absolute;
height: 100%;
- background-color: $dark-gray;
+ background-color: global.$dark-gray;
opacity: 0.3;
top: 0;
}
@@ -45,7 +45,7 @@
height: 100%;
position: absolute;
box-sizing: border-box;
- border: 2px solid $medium-blue;
+ border: 2px solid global.$medium-blue;
display: flex;
justify-content: space-between;
max-width: 100%;
@@ -53,7 +53,7 @@
left: 0;
.collectionStackedTimeline-trim-handle {
- background-color: $medium-blue;
+ background-color: global.$medium-blue;
height: 100%;
width: 5px;
cursor: ew-resize;
@@ -65,12 +65,12 @@
width: 10px;
top: 2.5%;
height: 95%;
- background: $light-blue;
+ background: global.$light-blue;
border-radius: 3px;
opacity: 0.3;
z-index: 500;
border-style: solid;
- border-color: $medium-blue;
+ border-color: global.$medium-blue;
border-width: 1px;
}
@@ -84,12 +84,12 @@
}
.collectionStackedTimeline-current {
- background-color: $pink;
+ background-color: global.$pink;
}
.collectionStackedTimeline-hover {
display: none;
- background-color: $medium-blue;
+ background-color: global.$medium-blue;
}
.collectionStackedTimeline-marker-timeline {
@@ -97,14 +97,14 @@
top: 2.5%;
height: 95%;
border-radius: 4px;
- //background: $light-gray;
+ //background: global.$light-gray;
&:hover {
opacity: 1;
}
.collectionStackedTimeline-left-resizer,
.collectionStackedTimeline-resizer {
- background: $dark-gray;
+ background: global.$dark-gray;
position: absolute;
top: 0;
height: 100%;
@@ -141,7 +141,7 @@
.hoverTime {
position: absolute;
- color: $dark-gray;
+ color: global.$dark-gray;
transform: translate(0, -100%);
font-weight: bold;
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 6400a0a8e..05ac52ff9 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.module.scss';
+@use '../global/globalCssVariables.module.scss' as global;
.collectionMasonryView {
display: inline;
@@ -12,120 +12,124 @@
position: relative;
height: 100%;
width: 100%;
-}
-
-// TODO:glr Turn this into a seperate class
-.documentButtonMenu {
- position: relative;
- height: fit-content;
- border-bottom: $standard-border;
- display: flex;
- justify-content: center;
- flex-direction: column;
- align-items: center;
- align-content: center;
- padding: 5px 0 5px 0;
- .documentExplanation {
- width: 90%;
- margin: 5px;
- font-size: 11px;
- color: $medium-blue;
- padding: 10px;
- border-radius: 5px;
- border: solid 0.5px $medium-blue;
+ .collectionStackingView-columnDragger {
+ width: 28;
+ height: 28;
+ position: absolute;
+ margin-left: -5;
+ z-index: 10;
+ > svg {
+ width: 100%;
+ height: 100%;
+ }
}
-}
-.collectionStackingView,
-.collectionMasonryView {
- height: 100%;
- width: 100%;
- position: absolute;
- top: 0;
- overflow-y: auto;
- overflow-x: hidden;
- flex-wrap: wrap;
- transition: top 0.5s;
-
- > div {
+ // TODO:glr Turn this into a seperate class
+ .documentButtonMenu {
position: relative;
- display: block;
- }
-
- .collectionStackingViewFieldColumn {
+ height: fit-content;
+ border-bottom: global.$standard-border;
display: flex;
+ justify-content: center;
flex-direction: column;
+ align-items: center;
+ align-content: center;
+ padding: 5px 0 5px 0;
+
+ .documentExplanation {
+ width: 90%;
+ margin: 5px;
+ font-size: 11px;
+ color: global.$medium-blue;
+ padding: 10px;
+ border-radius: 5px;
+ border: solid 0.5px global.$medium-blue;
+ }
}
- .collectionSchemaView-previewDoc {
+ .collectionStackingView,
+ .collectionMasonryView {
height: 100%;
+ width: 100%;
position: absolute;
- }
+ top: 0;
+ overflow-y: auto;
+ overflow-x: hidden;
+ flex-wrap: wrap;
+ transition: top 0.5s;
- .collectionStackingView-docView-container {
- width: 45%;
- margin: 5% 2.5%;
- padding-left: 2.5%;
- height: auto;
- }
+ > div {
+ position: relative;
+ display: block;
+ }
- .collectionStackingView-flexCont {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- align-items: center;
- }
+ .collectionStackingViewFieldColumn {
+ display: flex;
+ flex-direction: column;
+ }
- .collectionStackingView-masonrySingle,
- .collectionStackingView-masonryGrid {
- width: 100%;
- display: grid;
- top: 0;
- left: 0;
- }
+ .collectionSchemaView-previewDoc {
+ height: 100%;
+ position: absolute;
+ }
- .collectionStackingView-masonrySingle {
- height: 100%;
- position: absolute;
- }
+ .collectionStackingView-docView-container {
+ width: 45%;
+ margin: 5% 2.5%;
+ padding-left: 2.5%;
+ height: auto;
+ }
- .collectionStackingView-masonryGrid {
- margin: auto;
- height: max-content;
- position: relative;
- grid-auto-rows: 0px;
- }
+ .collectionStackingView-flexCont {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ align-items: center;
+ }
- .collectionStackingView-masonrySingle {
- width: 100%;
- height: 100%;
- position: absolute;
- display: flex;
- flex-direction: column;
- top: 0;
- left: 0;
- width: 100%;
- position: absolute;
- }
+ .collectionStackingView-masonrySingle,
+ .collectionStackingView-masonryGrid {
+ width: 100%;
+ display: grid;
+ top: 0;
+ left: 0;
+ }
- .collectionStackingView-description {
- font-size: 100%;
- margin-bottom: 1vw;
- padding: 10px;
- height: 2vw;
- width: 100%;
- font-family: $sans-serif;
- background: $dark-gray;
- color: $white;
- }
+ .collectionStackingView-masonrySingle {
+ height: 100%;
+ position: absolute;
+ }
- .collectionStackingView-columnDragger {
- width: 15;
- height: 15;
- position: absolute;
- margin-left: -5;
- z-index: 10;
+ .collectionStackingView-masonryGrid {
+ margin: auto;
+ height: max-content;
+ position: relative;
+ grid-auto-rows: 0px;
+ }
+
+ .collectionStackingView-masonrySingle {
+ width: 100%;
+ height: 100%;
+ position: absolute;
+ display: flex;
+ flex-direction: column;
+ top: 0;
+ left: 0;
+ width: 100%;
+ position: absolute;
+ }
+
+ .collectionStackingView-description {
+ font-size: 100%;
+ margin-bottom: 1vw;
+ padding: 10px;
+ height: 2vw;
+ width: 100%;
+ font-family: global.$sans-serif;
+ background: global.$dark-gray;
+ color: global.$white;
+ }
}
// Documents in stacking view
@@ -149,7 +153,7 @@
.collectionStackingView-collapseBar {
margin-top: 2px;
- background: $medium-gray;
+ background: global.$medium-gray;
height: 5px;
width: 100%;
display: none;
@@ -207,11 +211,11 @@
text-align: center;
margin: auto;
margin-bottom: 10px;
- background: $medium-gray;
+ background: global.$medium-gray;
// overflow: hidden; overflow is visible so the color menu isn't hidden -ftong
.editableView-input {
- color: $dark-gray;
+ color: global.$dark-gray;
}
.editableView-input:hover,
@@ -232,7 +236,7 @@
display: flex;
align-items: center;
justify-content: center;
- color: $dark-gray;
+ color: global.$dark-gray;
.editableView-container-editing-oneLine,
.editableView-container-editing {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index a57256424..96125f2c2 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -110,7 +110,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
// columnWidth handles the margin on the left and right side of the documents
@computed get columnWidth() {
- return Math.min(this._props.PanelWidth() - 2 * this.xMargin, this.isStackingView ? Number.MAX_VALUE : this.layoutDoc._columnWidth === -1 ? this._props.PanelWidth() - 2 * this.xMargin : NumCast(this.layoutDoc._columnWidth, 250));
+ const availableWidth = this._props.PanelWidth() - 2 * this.xMargin;
+ 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 ? Number.MAX_VALUE : cwid - this.gridGap);
}
@computed get NodeWidth() {
@@ -221,6 +223,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
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() {
super.componentDidMount?.();
this._props.setContentViewBox?.(this);
@@ -232,10 +237,21 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
this.dataDoc['_' + this.fieldKey + '_columnHeaders'] = new List();
}
);
+ // reset section headers when a new filter is inputted
+ this._disposers.width = reaction(
+ () => [this._props.PanelWidth() - 2 * this.xMargin, NumCast(this.Document._layout_columnWidth, 250)],
+ ([pw, cw]) => {
+ if (!this.isStackingView && Math.round(pw / cw)) {
+ this.layoutDoc._layout_columnCount = Math.round(pw / cw);
+ }
+ }
+ );
+
this._disposers.autoHeight = reaction(
- () => this.layoutDoc._layout_autoHeight,
- layoutAutoHeight => layoutAutoHeight && this._props.setHeight?.(this.headerMargin + (this.isStackingView ? Math.max(...this._refList.map(DivHeight)) : this._refList.reduce((p, r) => p + DivHeight(r), 0)))
+ () => [this.layoutDoc._layout_autoHeight, this.yMargin],
+ ([autoH]) => autoH && this.setAutoHeight()
);
+
this._disposers.refList = reaction(
() => ({ refList: this._refList.slice(), autoHeight: this.layoutDoc._layout_autoHeight && !DocumentView.LightboxContains(this.DocumentView?.()) }),
({ refList, autoHeight }) => {
@@ -435,8 +451,8 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
);
};
@action
- onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => {
- this.layoutDoc._columnWidth = Math.max(10, this.columnWidth + delta[0]);
+ onDividerMove = (e: PointerEvent) => {
+ this.Document._layout_columnWidth = Math.max(10, (this._props.DocumentView?.().screenToViewTransform().transformPoint(e.clientX, 0)[0] ?? 0) - this.xMargin);
return false;
};
@@ -446,7 +462,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
className="collectionStackingView-columnDragger"
onPointerDown={this.columnDividerDown}
ref={this._draggerRef}
- style={{ cursor: this._cursor, color: SettingsManager.userColor, left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }}>
+ style={{ cursor: this._cursor, color: SettingsManager.userColor, left: `${NumCast(this.Document._layout_columnWidth) + this.xMargin}px` }}>
<FontAwesomeIcon icon="arrows-alt-h" />
</div>
);
@@ -590,24 +606,29 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
const rows = () => (!this.isStackingView ? 1 : Math.max(1, Math.min(docList.length, Math.floor((this._props.PanelWidth() - 2 * this.xMargin) / (this.columnWidth + this.gridGap)))));
return (
- <CollectionMasonryViewFieldRow
- showHandle={first}
- Document={this.Document}
- chromeHidden={this.chromeHidden}
- pivotField={this.pivotField}
- refList={this._refList}
- key={heading ? heading.heading : ''}
- rows={rows}
- headings={this.headings}
- heading={heading ? heading.heading : ''}
- headingObject={heading}
- docList={docList}
- parent={this}
- type={type}
- createDropTarget={this.createDashEventsTarget}
- screenToLocalTransform={this.ScreenToLocalBoxXf}
- setDocHeight={this.setDocHeight}
- />
+ <div key={(heading?.heading ?? '') + 'head'}>
+ {this._props.isContentActive() && !this.isStackingView && !this.chromeHidden ? this.columnDragger : null}
+ <div style={{ top: this.yMargin }}>
+ <CollectionMasonryViewFieldRow
+ showHandle={first}
+ Document={this.Document}
+ chromeHidden={this.chromeHidden}
+ pivotField={this.pivotField}
+ refList={this._refList}
+ key={heading ? heading.heading : ''}
+ rows={rows}
+ headings={this.headings}
+ heading={heading ? heading.heading : ''}
+ headingObject={heading}
+ docList={docList}
+ parent={this}
+ type={type}
+ createDropTarget={this.createDashEventsTarget}
+ screenToLocalTransform={this.ScreenToLocalBoxXf}
+ setDocHeight={this.setDocHeight}
+ />
+ </div>
+ </div>
);
};
@@ -699,8 +720,6 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
return this._props.isContentActive() === false ? 'none' : undefined;
}
- observer = new ResizeObserver(() => this._props.setHeight?.(this.headerMargin + (this.isStackingView ? Math.max(...this._refList.map(DivHeight)) : this._refList.reduce((p, r) => p + DivHeight(r), 0))));
-
onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
render() {
TraceMobx();
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5e99bec39..655894e40 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -28,6 +28,7 @@ import { FieldViewProps } from '../nodes/FieldView';
import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
import { FlashcardPracticeUI } from './FlashcardPracticeUI';
import { OpenWhere, OpenWhereMod } from '../nodes/OpenWhere';
+import { Upload } from '../../../server/SharedMediaTypes';
export enum docSortings {
Time = 'time',
@@ -37,6 +38,9 @@ export enum docSortings {
Tag = 'tag',
None = '',
}
+
+export const ChatSortField = 'chat_sortIndex';
+
export interface CollectionViewProps extends React.PropsWithChildren<FieldViewProps> {
isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc)
isAnnotationOverlayScrollable?: boolean; // whether the annotation overlay can be vertically scrolled (just for tree views, currently)
@@ -121,6 +125,7 @@ export function CollectionSubView<X>() {
return this.dataDoc[this._props.fieldKey]; // this used to be 'layoutDoc', but then template fields will get ignored since the template is not a proto of the layout. hopefully nothing depending on the previous code.
}
+ hasChildDocs = () => this.childLayoutPairs.map(pair => pair.layout);
@computed get childLayoutPairs(): { layout: Doc; data: Doc }[] {
const { Document, TemplateDataDocument } = this._props;
const validPairs = this.childDocs
@@ -228,23 +233,21 @@ export function CollectionSubView<X>() {
childSortedDocs = (docsIn: Doc[], dragIndex: number) => {
const sortType = StrCast(this.Document[this._props.fieldKey + '_sort']) as docSortings;
- const isDesc = BoolCast(this.Document[this._props.fieldKey + '_sort_desc']);
+ const isDesc = BoolCast(this.Document[this._props.fieldKey + '_sort_reverse']);
const docs = docsIn.slice();
- if (sortType) {
- docs.sort((docA, docB) => {
- const [typeA, typeB] = (() => {
- switch (sortType) {
- default:
- case docSortings.Type: return [StrCast(docA.type), StrCast(docB.type)];
- case docSortings.Chat: return [NumCast(docA.chatIndex, 9999), NumCast(docB.chatIndex,9999)];
- case docSortings.Time: return [DateCast(docA.author_date)?.date ?? Date.now(), DateCast(docB.author_date)?.date ?? Date.now()];
- case docSortings.Color:return [DashColor(StrCast(docA.backgroundColor)).hsv().hue(), DashColor(StrCast(docB.backgroundColor)).hsv().hue()];
- case docSortings.Tag: return [StrListCast(docA.tags).join(""), StrListCast(docB.tags).join("")];
- }
- })(); //prettier-ignore
- return (typeA < typeB ? -1 : typeA > typeB ? 1 : 0) * (isDesc ? 1 : -1);
- });
- }
+ sortType && docs.sort((docA, docB) => {
+ const [typeA, typeB] = (() => {
+ switch (sortType) {
+ default:
+ case docSortings.Type: return [StrCast(docA.type), StrCast(docB.type)];
+ case docSortings.Chat: return [NumCast(docA[ChatSortField], 9999), NumCast(docB[ChatSortField], 9999)];
+ case docSortings.Time: return [DateCast(docA.author_date)?.date ?? Date.now(), DateCast(docB.author_date)?.date ?? Date.now()];
+ case docSortings.Color:return [DashColor(StrCast(docA.backgroundColor)).hsv().hue(), DashColor(StrCast(docB.backgroundColor)).hsv().hue()];
+ case docSortings.Tag: return [StrListCast(docA.tags).join(""), StrListCast(docB.tags).join("")];
+ }
+ })();
+ return (typeA < typeB ? -1 : typeA > typeB ? 1 : 0) * (isDesc ? -1 : 1);
+ }); //prettier-ignore
if (dragIndex !== -1) {
const draggedDoc = DragManager.docsBeingDragged[0];
const originalIndex = docs.findIndex(doc => doc === draggedDoc);
@@ -405,7 +408,7 @@ export function CollectionSubView<X>() {
const imgSrc = img.split('src="')[1].split('"')[0];
const imgOpts = { ...options, _width: 300 };
if (imgSrc.startsWith('data:image') && imgSrc.includes('base64')) {
- const result = (await Networking.PostToServer('/uploadRemoteImage', { sources: [imgSrc] })).lastElement();
+ const result = ((await Networking.PostToServer('/uploadRemoteImage', { sources: [imgSrc] })) as Upload.ImageInformation[]).lastElement();
const newImgSrc =
result.accessPaths.agnostic.client.indexOf('dashblobstore') === -1 //
? ClientUtils.prepend(result.accessPaths.agnostic.client)
@@ -538,12 +541,17 @@ export function CollectionSubView<X>() {
DocUtils.uploadYoutubeVideoLoading(files, {}, loading);
} else {
generatedDocuments.push(
- ...files.map(file => {
- const loading = Docs.Create.LoadingDocument(file, options);
- Doc.addCurrentlyLoading(loading);
- DocUtils.uploadFileToDoc(file, {}, loading);
- return loading;
- })
+ ...(await Promise.all(
+ files.map(async file => {
+ if (file.name.endsWith('svg')) {
+ return (await DocUtils.openSVGfile(file, options)) as Doc;
+ }
+ const loading = Docs.Create.LoadingDocument(file, options);
+ Doc.addCurrentlyLoading(loading);
+ DocUtils.uploadFileToDoc(file, {}, loading);
+ return loading;
+ })
+ ))
);
}
if (generatedDocuments.length) {
diff --git a/src/client/views/collections/CollectionTimeView.tsx b/src/client/views/collections/CollectionTimeView.tsx
index d75c633ac..98bd06221 100644
--- a/src/client/views/collections/CollectionTimeView.tsx
+++ b/src/client/views/collections/CollectionTimeView.tsx
@@ -1,33 +1,22 @@
-import { action, computed, makeObservable, observable, runInAction } from 'mobx';
+import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { returnFalse, returnTrue, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
import { Doc, Opt, StrListCast } from '../../../fields/Doc';
-import { List } from '../../../fields/List';
-import { ObjectField } from '../../../fields/ObjectField';
-import { listSpec } from '../../../fields/Schema';
-import { ComputedField, ScriptField } from '../../../fields/ScriptField';
-import { Cast, NumCast, StrCast } from '../../../fields/Types';
+import { NumCast, StrCast } from '../../../fields/Types';
import { Docs } from '../../documents/Documents';
-import { ScriptingGlobals } from '../../util/ScriptingGlobals';
-import { ContextMenu } from '../ContextMenu';
-import { ContextMenuProps } from '../ContextMenuItem';
import { FieldsDropdown } from '../FieldsDropdown';
import { PinDocView } from '../PinFuncs';
import { DocumentView } from '../nodes/DocumentView';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
import './CollectionTimeView.scss';
-import { ViewDefBounds, computePivotLayout, computeTimelineLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
+import { computeTimelineLayout } from './collectionFreeForm/CollectionFreeFormLayoutEngines';
import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView';
@observer
export class CollectionTimeView extends CollectionSubView() {
- _changing = false;
- @observable _layoutEngine = computePivotLayout.name;
@observable _collapsed: boolean = false;
- @observable _childClickedScript: Opt<ScriptField> = undefined;
- @observable _viewDefDivClick: Opt<ScriptField> = undefined;
@observable _focusPivotField: Opt<string> = undefined;
constructor(props: SubCollectionViewProps) {
@@ -37,10 +26,6 @@ export class CollectionTimeView extends CollectionSubView() {
componentDidMount() {
this._props.setContentViewBox?.(this);
- runInAction(() => {
- this._childClickedScript = ScriptField.MakeScript('openInLightbox(this)', { this: Doc.name });
- this._viewDefDivClick = ScriptField.MakeScript('pivotColumnClick(this,payload)', { payload: 'any' });
- });
}
get pivotField() {
@@ -49,19 +34,10 @@ export class CollectionTimeView extends CollectionSubView() {
getAnchor = (addAsAnnotation: boolean) => {
const anchor = Docs.Create.ConfigDocument({
- title: ComputedField.MakeFunction(`"${this.pivotField}"])`) as unknown as string, // title can take a functiono or a string
annotationOn: this.Document,
});
PinDocView(anchor, { pinData: { collectionType: true, pivot: true, filters: true } }, this.Document);
-
- if (addAsAnnotation) {
- // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
- if (Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), null) !== undefined) {
- Cast(this.dataDoc[this._props.fieldKey + '_annotations'], listSpec(Doc), []).push(anchor);
- } else {
- this.dataDoc[this._props.fieldKey + '_annotations'] = new List<Doc>([anchor]);
- }
- }
+ addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
return anchor;
};
@@ -74,7 +50,6 @@ export class CollectionTimeView extends CollectionSubView() {
return undefined;
};
- layoutEngine = () => this._layoutEngine;
toggleVisibility = action(() => {
this._collapsed = !this._collapsed;
});
@@ -126,105 +101,24 @@ export class CollectionTimeView extends CollectionSubView() {
);
};
- goTo = (prevFilterIndex: number) => {
- this.layoutDoc._pivotField = this.layoutDoc['_prevPivotFields' + prevFilterIndex];
- this.layoutDoc._childFilters = ObjectField.MakeCopy(this.layoutDoc['_prevDocFilter' + prevFilterIndex] as ObjectField);
- this.layoutDoc._childFiltersByRanges = ObjectField.MakeCopy(this.layoutDoc['_prevDocRangeFilters' + prevFilterIndex] as ObjectField);
- this.layoutDoc._prevFilterIndex = prevFilterIndex;
- };
-
- @action
- contentsDown = () => {
- const prevFilterIndex = NumCast(this.layoutDoc._prevFilterIndex);
- if (prevFilterIndex > 0) {
- this.goTo(prevFilterIndex - 1);
- } else {
- this.layoutDoc._childFilters = new List([]);
- }
- };
-
+ layoutEngine = () => computeTimelineLayout.name;
@computed get contents() {
return (
- <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this._props.isContentActive() ? undefined : 'none' }} onClick={this.contentsDown}>
+ <div className="collectionTimeView-innards" key="timeline" style={{ pointerEvents: this._props.isContentActive() ? undefined : 'none' }}>
<CollectionFreeFormView
{...this._props}
engineProps={{ pivotField: this.pivotField, childFilters: this.childDocFilters, childFiltersByRanges: this.childDocRangeFilters }}
fitContentsToBox={returnTrue}
- childClickScript={this._childClickedScript}
- viewDefDivClick={this.layoutEngine() === computeTimelineLayout.name ? undefined : this._viewDefDivClick}
layoutEngine={this.layoutEngine}
/>
</div>
);
}
- public static SyncTimelineToPresentation(doc: Doc) {
- const fieldKey = Doc.LayoutFieldKey(doc);
- doc[fieldKey + '-timelineCur'] = ComputedField.MakeFunction("(activePresentationItem()[this._pivotField || 'year'] || 0)");
- }
- specificMenu = () => {
- const layoutItems: ContextMenuProps[] = [];
- const doc = this.layoutDoc;
-
- layoutItems.push({
- description: 'Force Timeline',
- event: () => {
- doc._forceRenderEngine = computeTimelineLayout.name;
- },
- icon: 'compress-arrows-alt',
- });
- layoutItems.push({
- description: 'Force Pivot',
- event: () => {
- doc._forceRenderEngine = computePivotLayout.name;
- },
- icon: 'compress-arrows-alt',
- });
- layoutItems.push({
- description: 'Auto Time/Pivot layout',
- event: () => {
- doc._forceRenderEngine = undefined;
- },
- icon: 'compress-arrows-alt',
- });
- layoutItems.push({ description: 'Sync with presentation', event: () => CollectionTimeView.SyncTimelineToPresentation(doc), icon: 'compress-arrows-alt' });
-
- ContextMenu.Instance.addItem({ description: 'Options...', subitems: layoutItems, icon: 'eye' });
- };
-
render() {
- let nonNumbers = 0;
- this.childDocs.forEach(doc => {
- const num = NumCast(doc[this.pivotField], Number(StrCast(doc[this.pivotField])));
- if (isNaN(num)) {
- nonNumbers++;
- }
- });
- const forceLayout = StrCast(this.layoutDoc._forceRenderEngine);
- const doTimeline = forceLayout ? forceLayout === computeTimelineLayout.name : nonNumbers / this.childDocs.length < 0.1 && this._props.PanelWidth() / this._props.PanelHeight() > 6;
- if (doTimeline !== (this._layoutEngine === computeTimelineLayout.name)) {
- if (!this._changing) {
- this._changing = true;
- setTimeout(
- action(() => {
- this._layoutEngine = doTimeline ? computeTimelineLayout.name : computePivotLayout.name;
- this._changing = false;
- }),
- 0
- );
- }
- }
-
return (
- <div className={'collectionTimeView' + (doTimeline ? '' : '-pivot')} onContextMenu={this.specificMenu} style={{ width: this._props.PanelWidth(), height: '100%' }}>
+ <div className="collectionTimeView" style={{ width: this._props.PanelWidth(), height: '100%' }}>
{this.contents}
- {!this._props.isSelected() || !doTimeline ? null : (
- <>
- <div className="collectionTimeView-thumb-min collectionTimeView-thumb" key="min" onPointerDown={this.onMinDown} />
- <div className="collectionTimeView-thumb-max collectionTimeView-thumb" key="mid" onPointerDown={this.onMaxDown} />
- <div className="collectionTimeView-thumb-mid collectionTimeView-thumb" key="max" onPointerDown={this.onMidDown} />
- </>
- )}
<div style={{ right: 0, top: 0, position: 'absolute' }}>
<FieldsDropdown
Document={this.Document}
@@ -234,38 +128,14 @@ export class CollectionTimeView extends CollectionSubView() {
placeholder={StrCast(this.layoutDoc._pivotField)}
/>
</div>
+ {!this._props.isSelected() ? null : (
+ <>
+ <div className="collectionTimeView-thumb-min collectionTimeView-thumb" key="min" onPointerDown={this.onMinDown} />
+ <div className="collectionTimeView-thumb-max collectionTimeView-thumb" key="mid" onPointerDown={this.onMaxDown} />
+ <div className="collectionTimeView-thumb-mid collectionTimeView-thumb" key="max" onPointerDown={this.onMidDown} />
+ </>
+ )}
</div>
);
}
}
-
-// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function pivotColumnClick(pivotDoc: Doc, bounds: ViewDefBounds) {
- const pivotField = StrCast(pivotDoc._pivotField, 'author');
- let prevFilterIndex = NumCast(pivotDoc._prevFilterIndex);
- const originalFilter = StrListCast(ObjectField.MakeCopy(pivotDoc._childFilters as ObjectField));
- pivotDoc['_prevDocFilter' + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._childFilters as ObjectField);
- pivotDoc['_prevDocRangeFilters' + prevFilterIndex] = ObjectField.MakeCopy(pivotDoc._childFiltersByRanges as ObjectField);
- pivotDoc['_prevPivotFields' + prevFilterIndex] = pivotField;
- pivotDoc._prevFilterIndex = ++prevFilterIndex;
- pivotDoc._childFilters = new List();
- setTimeout(
- action(() => {
- const filterVals = bounds.payload as string[];
- filterVals.map(filterVal => Doc.setDocFilter(pivotDoc, pivotField, filterVal, 'check'));
- const pivotView = DocumentView.getDocumentView(pivotDoc);
- if (pivotDoc && pivotView?.ComponentView instanceof CollectionTimeView && filterVals.length === 1) {
- if (pivotView?.ComponentView.childDocs.length && pivotView.ComponentView.childDocs[0][filterVals[0]]) {
- pivotDoc._pivotField = filterVals[0];
- }
- }
- const newFilters = StrListCast(pivotDoc._childFilters);
- if (newFilters.length && originalFilter.length && newFilters.lastElement() === originalFilter.lastElement()) {
- pivotDoc._prevFilterIndex = --prevFilterIndex;
- pivotDoc['_prevDocFilter' + prevFilterIndex] = undefined;
- pivotDoc['_prevDocRangeFilters' + prevFilterIndex] = undefined;
- pivotDoc['_prevPivotFields' + prevFilterIndex] = undefined;
- }
- })
- );
-});
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss
index bbbef78b4..2a03ea708 100644
--- a/src/client/views/collections/CollectionTreeView.scss
+++ b/src/client/views/collections/CollectionTreeView.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.module.scss';
+@use '../global/globalCssVariables.module.scss' as global;
.collectionTreeView-container {
transform-origin: top left;
@@ -12,7 +12,7 @@
width: 100%;
position: relative;
top: 0;
- // background: $light-gray;
+ // background: global.$light-gray;
font-size: 13px;
overflow: auto;
user-select: none;
@@ -21,7 +21,7 @@
ul {
list-style: none;
- padding-left: $TREE_BULLET_WIDTH;
+ padding-left: global.$TREE_BULLET_WIDTH;
margin-bottom: 1px; // otherwise vertical scrollbars may pop up for no apparent reason....
> .contentFittingDocumentView {
width: unset;
@@ -47,7 +47,7 @@
}
.delete-button {
- color: $medium-gray;
+ color: global.$medium-gray;
// float: right;
margin-left: 15px;
// margin-top: 3px;
@@ -90,7 +90,7 @@
.collectionTreeView-subtitle {
font-style: italic;
font-size: 8pt;
- color: $medium-gray;
+ color: global.$medium-gray;
}
.docContainer {
diff --git a/src/client/views/collections/CollectionView.scss b/src/client/views/collections/CollectionView.scss
index de53a2c62..06c324bd0 100644
--- a/src/client/views/collections/CollectionView.scss
+++ b/src/client/views/collections/CollectionView.scss
@@ -1,10 +1,10 @@
-@import '../global/globalCssVariables.module.scss';
+@use '../global/globalCssVariables.module.scss' as global;
.collectionView {
border-width: 0;
- border-color: $light-gray;
+ border-color: global.$light-gray;
border-style: solid;
- border-radius: 0 0 $border-radius $border-radius;
+ border-radius: 0 0 global.$border-radius global.$border-radius;
box-sizing: border-box;
border-radius: inherit;
width: 100%;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index 6f0833a22..7ba8989ce 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -15,12 +15,14 @@ import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent } from '../DocComponent';
import { FieldView } from '../nodes/FieldView';
import { OpenWhere } from '../nodes/OpenWhere';
+import { CalendarBox } from '../nodes/calendarBox/CalendarBox';
import { CollectionCardView } from './CollectionCardDeckView';
import { CollectionCarousel3DView } from './CollectionCarousel3DView';
import { CollectionCarouselView } from './CollectionCarouselView';
import { CollectionDockingView } from './CollectionDockingView';
import { CollectionNoteTakingView } from './CollectionNoteTakingView';
import { CollectionPileView } from './CollectionPileView';
+import { CollectionPivotView } from './CollectionPivotView';
import { CollectionStackingView } from './CollectionStackingView';
import { CollectionViewProps, SubCollectionViewProps } from './CollectionSubView';
import { CollectionTimeView } from './CollectionTimeView';
@@ -32,7 +34,6 @@ import { CollectionLinearView } from './collectionLinear';
import { CollectionMulticolumnView } from './collectionMulticolumn/CollectionMulticolumnView';
import { CollectionMultirowView } from './collectionMulticolumn/CollectionMultirowView';
import { CollectionSchemaView } from './collectionSchema/CollectionSchemaView';
-import { CalendarBox } from '../nodes/calendarBox/CalendarBox';
@observer
export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewProps>() {
@@ -89,6 +90,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
TraceMobx();
if (type === undefined) return null;
switch (type) {
+ default:
+ case CollectionViewType.Freeform: return <CollectionFreeFormView key="collview" {...props} />;
case CollectionViewType.Schema: return <CollectionSchemaView key="collview" {...props} />;
case CollectionViewType.Calendar: return <CalendarBox key="collview" {...props} />;
case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
@@ -103,10 +106,9 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
case CollectionViewType.NoteTaking: return <CollectionNoteTakingView key="collview" {...props} />;
case CollectionViewType.Masonry: return <CollectionStackingView key="collview" {...props} />;
case CollectionViewType.Time: return <CollectionTimeView key="collview" {...props} />;
+ case CollectionViewType.Pivot: return <CollectionPivotView key="collview" {...props} />;
case CollectionViewType.Grid: return <CollectionGridView key="collview" {...props} />;
case CollectionViewType.Card: return <CollectionCardView key="collview" {...props} />;
- case CollectionViewType.Freeform:
- default: return <CollectionFreeFormView key="collview" {...props} />;
}
};
@@ -126,7 +128,8 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
{ description: 'Carousel', event: () => func(CollectionViewType.Carousel), icon: 'columns' },
{ description: '3D Carousel', event: () => func(CollectionViewType.Carousel3D), icon: 'columns' },
{ description: 'Calendar', event: () => func(CollectionViewType.Calendar), icon: 'columns' },
- { description: 'Pivot/Time', event: () => func(CollectionViewType.Time), icon: 'columns' },
+ { description: 'Pivot', event: () => func(CollectionViewType.Pivot), icon: 'columns' },
+ { description: 'Time', event: () => func(CollectionViewType.Time), icon: 'columns' },
{ description: 'Map', event: () => func(CollectionViewType.Map), icon: 'globe-americas' },
{ description: 'Grid', event: () => func(CollectionViewType.Grid), icon: 'th-list' },
];
diff --git a/src/client/views/collections/TabDocView.scss b/src/client/views/collections/TabDocView.scss
index dd4c0b881..397e35ca9 100644
--- a/src/client/views/collections/TabDocView.scss
+++ b/src/client/views/collections/TabDocView.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.module.scss';
+@use '../global/globalCssVariables.module.scss' as global;
.tabDocView-content {
height: 100%;
diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss
index 2ab1a5ac1..78794d112 100644
--- a/src/client/views/collections/TreeView.scss
+++ b/src/client/views/collections/TreeView.scss
@@ -1,4 +1,4 @@
-@import '../global/globalCssVariables.module.scss';
+@use '../global/globalCssVariables.module.scss' as global;
.treeView-label {
max-height: 1.5em;
@@ -14,7 +14,7 @@
.bullet-outline {
position: relative;
width: fit-content;
- color: $medium-gray;
+ color: global.$medium-gray;
transform: scale(0.5);
display: inline-flex;
align-items: center;
@@ -66,7 +66,7 @@
min-height: 20px;
min-width: 15px;
margin-right: 3px;
- color: $medium-gray;
+ color: global.$medium-gray;
border: #80808030 1px solid;
border-radius: 5px;
z-index: 1;
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
index bebdbd731..241a56a88 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx
@@ -4,7 +4,7 @@ import { Id, ToString } from '../../../../fields/FieldSymbols';
import { ObjectField } from '../../../../fields/ObjectField';
import { RefField } from '../../../../fields/RefField';
import { listSpec } from '../../../../fields/Schema';
-import { Cast, NumCast, StrCast } from '../../../../fields/Types';
+import { Cast, DateCast, NumCast, StrCast } from '../../../../fields/Types';
import { aggregateBounds } from '../../../../Utils';
export interface ViewDefBounds {
@@ -107,7 +107,7 @@ export function computePassLayout(poolData: Map<string, PoolData>, pivotDoc: Doc
}
function toNumber(val: FieldResult<FieldType>) {
- return val === undefined ? undefined : NumCast(val, Number(StrCast(val)));
+ return val === undefined ? undefined : DateCast(val) ? DateCast(val).date.getMilliseconds() : NumCast(val, Number(StrCast(val)));
}
export function computeStarburstLayout(poolData: Map<string, PoolData>, pivotDoc: Doc, childPairs: { layout: Doc; data?: Doc }[], panelDim: number[], viewDefsToJSX: (views: ViewDefBounds[]) => ViewDefResult[] /* , engineProps: any */) {
@@ -295,7 +295,7 @@ export function computeTimelineLayout(poolData: Map<string, PoolData>, pivotDoc:
let minTime = minTimeReq === undefined ? Number.MAX_VALUE : minTimeReq;
let maxTime = maxTimeReq === undefined ? -Number.MAX_VALUE : maxTimeReq;
childPairs.forEach(pair => {
- const num = NumCast(pair.layout[timelineFieldKey], Number(StrCast(pair.layout[timelineFieldKey])));
+ const num = toNumber(pair.layout[timelineFieldKey]) ?? 0;
if (!isNaN(num) && (!minTimeReq || num >= minTimeReq) && (!maxTimeReq || num <= maxTimeReq)) {
!pivotDateGroups.get(num) && pivotDateGroups.set(num, []);
pivotDateGroups.get(num)!.push(pair.layout);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
index 46bd37f6d..cce0ff684 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss
@@ -1,4 +1,4 @@
-@import '../../global/globalCssVariables.module.scss';
+@use '../../global/globalCssVariables.module.scss' as global;
.collectionfreeformview-none {
position: inherit;
@@ -32,9 +32,9 @@
.collectionfreeformview-mask-empty,
.collectionfreeformview-mask {
z-index: 5000;
- width: $INK_MASK_SIZE;
- height: $INK_MASK_SIZE;
- transform: translate($INK_MASK_SIZE_HALF, $INK_MASK_SIZE_HALF);
+ width: global.$INK_MASK_SIZE;
+ height: global.$INK_MASK_SIZE;
+ transform: translate(global.$INK_MASK_SIZE_HALF, global.$INK_MASK_SIZE_HALF);
pointer-events: none;
position: absolute;
background-color: transparent;
@@ -211,11 +211,11 @@
//nested freeform views
// .collectionfreeformview-container {
- // background-image: linear-gradient(to right, $light-color-secondary 1px, transparent 1px),
- // linear-gradient(to bottom, $light-color-secondary 1px, transparent 1px);
+ // background-image: linear-gradient(to right, global.$light-color-secondary 1px, transparent 1px),
+ // linear-gradient(to bottom, global.$light-color-secondary 1px, transparent 1px);
// background-size: 30px 30px;
// }
- border: 0px solid $light-gray;
+ border: 0px solid global.$light-gray;
border-radius: inherit;
box-sizing: border-box;
position: absolute;
@@ -233,7 +233,7 @@
box-sizing: border-box;
width: 98%;
height: 98%;
- border-radius: $border-radius;
+ border-radius: global.$border-radius;
}
//this is an animation for the blinking cursor!
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index 3c31b584e..43addfc29 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -30,7 +30,7 @@ import { CompileScript } from '../../../util/Scripting';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { freeformScrollMode, SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
-import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager';
+import { undoable, UndoManager } from '../../../util/UndoManager';
import { Timeline } from '../../animationtimeline/Timeline';
import { ContextMenu } from '../../ContextMenu';
import { InkingStroke } from '../../InkingStroke';
@@ -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) {
- const dfltScale = this.isAnnotationOverlay ? 1 : 0.5;
+ const dfltScale = this.isAnnotationOverlay ? 1 : 0.25;
if (this.layoutDoc[this.scaleFieldKey] !== dfltScale) {
this.zoomSmoothlyAboutPt(this.screenToFreeformContentsXf.transformPoint(pointFocus.X, pointFocus.Y), dfltScale, zoomTime);
options.didMove = true;
@@ -1230,47 +1230,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
};
@action
- showSmartDraw = (x: number, y: number) => {
- SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc;
- SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing;
- SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
- SmartDrawHandler.Instance.displaySmartDrawHandler(x, y, NumCast(this.layoutDoc[this.scaleFieldKey]));
+ showSmartDraw = (x: number, y: number, regenerate?: boolean) => {
+ const sm = SmartDrawHandler.Instance;
+ sm.RemoveDrawing = this.removeDrawing;
+ sm.AddDrawing = this.addDrawing;
+ (regenerate ? sm.displayRegenerate : sm.displaySmartDrawHandler)(x, y, NumCast(this.layoutDoc[this.scaleFieldKey]));
};
_drawing: Doc[] = [];
_drawingContainer: Doc | undefined = undefined;
- /**
- * Function that creates a drawing--a group of ink strokes--to go with the smart draw function.
- */
- @undoBatch
- createDrawingDoc = (strokeData: [InkData, string, string][], opts: DrawingOptions) => {
- this._drawing = [];
- const xf = this.screenToFreeformContentsXf;
- strokeData.forEach((stroke: [InkData, string, string]) => {
- const bounds = InkField.getBounds(stroke[0]);
- const B = xf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
- const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
- const inkDoc = Docs.Create.InkDocument(
- stroke[0],
- { title: 'stroke',
- x: B.x - inkWidth / 2,
- y: B.y - inkWidth / 2,
- _width: B.width + inkWidth,
- _height: B.height + inkWidth,
- stroke_showLabel: BoolCast(Doc.UserDoc().activeHideTextLabels)}, // prettier-ignore
- inkWidth,
- opts.autoColor ? stroke[1] : ActiveInkColor(),
- ActiveInkBezierApprox(),
- stroke[2] === 'none' ? ActiveInkFillColor() : stroke[2],
- ActiveInkArrowStart(),
- ActiveInkArrowEnd(),
- ActiveInkDash(),
- ActiveIsInkMask()
- );
- this._drawing.push(inkDoc);
- });
- return MarqueeView.getCollection(this._drawing, undefined, true, { left: opts.x, top: opts.y, width: 1, height: 1 });
- };
/**
* Part of regenerating a drawing--deletes the old drawing.
@@ -1291,9 +1259,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
/**
* Adds the created drawing to the freeform canvas and sets the metadata.
*/
- addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string) => {
+ addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string, x?: number, y?: number) => {
const docData = doc[DocData];
- docData.title = opts.text.match(/^(.*?)~~~.*$/)?.[1] || opts.text;
+ docData.title = opts.text;
docData._width = opts.size;
docData.ai_drawing_input = opts.text;
docData.ai_drawing_complexity = opts.complexity;
@@ -1302,6 +1270,9 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
docData.ai_drawing_data = gptRes;
docData.ai = 'gpt';
this._drawingContainer = doc;
+ if (x !== undefined && y !== undefined) {
+ [doc.x, doc.y] = this.screenToFreeformContentsXf.transformPoint(x, y);
+ }
this.addDocument(doc);
this._batch?.end();
};
@@ -2003,7 +1974,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
optionItems.push({
description: 'Regenerate AI Drawing',
event: action(() => {
- SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc;
SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing;
!SmartDrawHandler.Instance.ShowRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10) : SmartDrawHandler.Instance.hideRegenerate();
diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelHandler.tsx b/src/client/views/collections/collectionFreeForm/ImageLabelHandler.tsx
index f050b9846..1a44e094d 100644
--- a/src/client/views/collections/collectionFreeForm/ImageLabelHandler.tsx
+++ b/src/client/views/collections/collectionFreeForm/ImageLabelHandler.tsx
@@ -9,7 +9,8 @@ import { MarqueeOptionsMenu } from './MarqueeOptionsMenu';
import './ImageLabelHandler.scss';
@observer
-export class ImageLabelHandler extends ObservableReactComponent<{}> {
+export class ImageLabelHandler extends ObservableReactComponent<object> {
+ // eslint-disable-next-line no-use-before-define
static Instance: ImageLabelHandler;
@observable _display: boolean = false;
@@ -19,11 +20,10 @@ export class ImageLabelHandler extends ObservableReactComponent<{}> {
@observable _currentLabel: string = '';
@observable _labelGroups: string[] = [];
- constructor(props: any) {
+ constructor(props: object) {
super(props);
makeObservable(this);
ImageLabelHandler.Instance = this;
- console.log('Instantiated label handler!');
}
@action
@@ -41,8 +41,8 @@ export class ImageLabelHandler extends ObservableReactComponent<{}> {
};
@action
- addLabel = (label: string) => {
- label = label.toUpperCase().trim();
+ addLabel = (labelIn: string) => {
+ const label = labelIn.toUpperCase().trim();
if (label.length > 0) {
if (!this._labelGroups.includes(label)) {
this._labelGroups = [...this._labelGroups, label];
@@ -96,10 +96,10 @@ export class ImageLabelHandler extends ObservableReactComponent<{}> {
<div>
{this._labelGroups.map(group => {
return (
- <div>
+ <div key={group}>
<p>{group}</p>
<IconButton
- tooltip={'Remove Label'}
+ tooltip="Remove Label"
onPointerDown={() => {
this.removeLabel(group);
}}
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.scss b/src/client/views/collections/collectionGrid/CollectionGridView.scss
index 845b07c51..4edaf9745 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.scss
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.scss
@@ -21,11 +21,11 @@
width: 100%;
}
- .react-grid-item>.react-resizable-handle {
+ .react-grid-item > .react-resizable-handle {
z-index: 4; // doesn't work on prezi otherwise
}
- .react-grid-item>.react-resizable-handle::after {
+ .react-grid-item > .react-resizable-handle::after {
// grey so it can be seen on the audiobox
border-right: 2px solid slategrey;
border-bottom: 2px solid slategrey;
@@ -41,7 +41,6 @@
position: absolute;
height: 3;
left: 5;
- top: 30;
transform-origin: left;
transform: rotate(90deg);
outline: none;
@@ -103,7 +102,7 @@
span::before,
span::after {
- content: "";
+ content: '';
width: 50%;
position: relative;
display: inline-block;
@@ -128,10 +127,8 @@
}
}
}
-
}
-
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
@@ -140,6 +137,6 @@ input::-webkit-inner-spin-button {
}
/* Firefox */
-input[type=number] {
+input[type='number'] {
-moz-appearance: textfield;
-} \ No newline at end of file
+}
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 6dffb80f1..80bf4bd12 100644
--- a/src/client/views/collections/collectionGrid/CollectionGridView.tsx
+++ b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
@@ -9,7 +9,7 @@ import { emptyFunction } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
import { Transform } from '../../../util/Transform';
-import { undoable, undoBatch } from '../../../util/UndoManager';
+import { undoable } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { ContextMenuProps } from '../../ContextMenuItem';
import { DocumentView } from '../../nodes/DocumentView';
@@ -22,9 +22,9 @@ export class CollectionGridView extends CollectionSubView() {
private _containerRef: React.RefObject<HTMLDivElement> = React.createRef();
private _changeListenerDisposer: Opt<Lambda>; // listens for changes in this.childLayoutPairs
private _resetListenerDisposer: Opt<Lambda>; // listens for when the reset button is clicked
+ private _dropLocation: object = {}; // sets the drop location for external drops
@observable private _rowHeight: Opt<number> = undefined; // temporary store of row height to make change undoable
@observable private _scroll: number = 0; // required to make sure the decorations box container updates on scroll
- private dropLocation: object = {}; // sets the drop location for external drops
constructor(props: SubCollectionViewProps) {
super(props);
@@ -48,14 +48,20 @@ export class CollectionGridView extends CollectionSubView() {
}
@computed get colWidthPlusGap() {
- return (this._props.PanelWidth() - this.margin) / this.numCols;
+ return (this._props.PanelWidth() - 2 * this.xMargin - this.gridGap) / this.numCols;
}
@computed get rowHeightPlusGap() {
- return this.rowHeight + this.margin;
+ return this.rowHeight + this.gridGap;
}
- @computed get margin() {
- return NumCast(this.Document.margin, 10);
+ @computed get xMargin() {
+ return NumCast(this.layoutDoc._xMargin, Math.max(3, 0.05 * this._props.PanelWidth()));
+ }
+ @computed get yMargin() {
+ return this._props.yPadding || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
+ }
+ @computed get gridGap() {
+ return NumCast(this.Document._gridGap, 10);
} // sets the margin between grid nodes
@computed get flexGrid() {
@@ -77,10 +83,10 @@ export class CollectionGridView extends CollectionSubView() {
pairs.forEach((pair, i) => {
const existing = oldLayouts.find(l => l.i === pair.layout[Id]);
if (existing) newLayouts.push(existing);
- else if (Object.keys(this.dropLocation).length) {
+ else if (Object.keys(this._dropLocation).length) {
// external drop
- this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.dropLocation as { x: number; y: number }, !this.flexGrid));
- this.dropLocation = {};
+ this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this._dropLocation as { x: number; y: number }, !this.flexGrid));
+ this._dropLocation = {};
} else {
// internal drop
this.addLayoutItem(newLayouts, this.makeLayoutItem(pair.layout, this.unflexedPosition(i), !this.flexGrid));
@@ -115,30 +121,29 @@ export class CollectionGridView extends CollectionSubView() {
* @returns the default location of the grid node (i.e. when the grid is static)
* @param index
*/
- unflexedPosition(index: number): Omit<Layout, 'i'> {
- return {
- x: (index % (Math.floor(this.numCols / this.defaultW) || 1)) * this.defaultW,
- y: Math.floor(index / (Math.floor(this.numCols / this.defaultH) || 1)) * this.defaultH,
- w: this.defaultW,
- h: this.defaultH,
- static: true,
- };
- }
+ unflexedPosition = (index: number): Omit<Layout, 'i'> => ({
+ x: (index % (Math.floor(this.numCols / this.defaultW) || 1)) * this.defaultW,
+ y: Math.floor(index / (Math.floor(this.numCols / this.defaultH) || 1)) * this.defaultH,
+ w: this.defaultW,
+ h: this.defaultH,
+ static: true,
+ });
/**
* Maps the x- and y- coordinates of the event to a grid cell.
*/
- screenToCell(sx: number, sy: number) {
- const pt = this.ScreenToLocalBoxXf().transformPoint(sx, sy);
- const x = Math.floor(pt[0] / this.colWidthPlusGap);
- const y = Math.floor((pt[1] + this._scroll) / this.rowHeight);
+ screenToCell = (sx: number, sy: number) => {
+ const [ptx, pty] = this.ScreenToLocalBoxXf().transformPoint(sx, sy);
+ const x = Math.floor((ptx + this.xMargin) / this.colWidthPlusGap);
+ const y = Math.floor((pty + this.yMargin + this._scroll) / this.rowHeight);
return { x, y };
- }
+ };
/**
* Creates a layout object for a grid item
*/
- makeLayoutItem = (doc: Doc, pos: { x: number; y: number }, Static: boolean = false, w: number = this.defaultW, h: number = this.defaultH) => ({ i: doc[Id], w, h, x: pos.x, y: pos.y, static: Static });
+ makeLayoutItem = (doc: Doc, pos: { x: number; y: number }, Static: boolean = false, w: number = this.defaultW, h: number = this.defaultH) =>
+ ({ i: doc[Id], w, h, x: pos.x, y: pos.y, static: Static }); // prettier-ignore
/**
* Adds a layout to the list of layouts.
@@ -152,9 +157,9 @@ export class CollectionGridView extends CollectionSubView() {
/**
* @returns the transform that will correctly place the document decorations box.
*/
- private lookupIndividualTransform = (layout: Layout) => {
+ lookupIndividualTransform = (layout: Layout) => {
const xypos = this.flexGrid ? layout : this.unflexedPosition(this.renderedLayoutList.findIndex(l => l.i === layout.i));
- const pos = { x: xypos.x * this.colWidthPlusGap + this.margin, y: xypos.y * this.rowHeightPlusGap + this.margin - this._scroll };
+ const pos = { x: xypos.x * this.colWidthPlusGap + this.gridGap + this.xMargin, y: xypos.y * this.rowHeightPlusGap + this.gridGap - this._scroll + this.yMargin };
return this.ScreenToLocalBoxXf().translate(-pos.x, -pos.y);
};
@@ -169,9 +174,9 @@ export class CollectionGridView extends CollectionSubView() {
/**
* Stores the layout list on the Document as JSON
*/
- setLayoutList(layouts: Layout[]) {
+ setLayoutList = (layouts: Layout[]) => {
this.Document.gridLayoutString = JSON.stringify(layouts);
- }
+ };
isContentActive = () => this._props.isSelected() || this._props.isContentActive();
isChildContentActive = () => (this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive)) ? true : undefined);
@@ -183,29 +188,27 @@ export class CollectionGridView extends CollectionSubView() {
* @param height
* @returns the `ContentFittingDocumentView` of the node
*/
- getDisplayDoc(layout: Doc, dxf: () => Transform, width: () => number, height: () => number) {
- return (
- <DocumentView
- {...this._props}
- Document={layout}
- TemplateDataDocument={layout.resolvedDataDoc as Doc}
- NativeWidth={returnZero}
- NativeHeight={returnZero}
- fitWidth={this._props.childLayoutFitWidth}
- containerViewPath={this.childContainerViewPath}
- renderDepth={this._props.renderDepth + 1}
- isContentActive={this.isChildContentActive}
- PanelWidth={width}
- PanelHeight={height}
- ScreenToLocalTransform={dxf}
- setContentViewBox={emptyFunction}
- whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
- onClickScript={this.onChildClickHandler}
- dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'}
- showTags={BoolCast(this.layoutDoc.showChildTags) || BoolCast(this.Document._layout_showTags)}
- />
- );
- }
+ getDisplayDoc = (layout: Doc, dxf: () => Transform, width: () => number, height: () => number) => (
+ <DocumentView
+ {...this._props}
+ Document={layout}
+ TemplateDataDocument={layout.resolvedDataDoc as Doc}
+ NativeWidth={returnZero}
+ NativeHeight={returnZero}
+ fitWidth={this._props.childLayoutFitWidth}
+ containerViewPath={this.childContainerViewPath}
+ renderDepth={this._props.renderDepth + 1}
+ isContentActive={this.isChildContentActive}
+ PanelWidth={width}
+ PanelHeight={height}
+ ScreenToLocalTransform={dxf}
+ setContentViewBox={emptyFunction}
+ whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+ onClickScript={this.onChildClickHandler}
+ dontCenter={StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'}
+ showTags={BoolCast(this.layoutDoc.showChildTags) || BoolCast(this.Document._layout_showTags)}
+ />
+ );
/**
* Saves the layouts received from the Grid to the Document.
@@ -238,15 +241,14 @@ export class CollectionGridView extends CollectionSubView() {
* @returns a list of `ContentFittingDocumentView`s inside wrapper divs.
* The key of the wrapper div must be the same as the `i` value of the corresponding layout.
*/
- @computed
- private get contents(): JSX.Element[] {
+ @computed get contents(): JSX.Element[] {
const collector: JSX.Element[] = [];
if (this.renderedLayoutList.length === this.childLayoutPairs.length) {
this.renderedLayoutList.forEach(l => {
const child = this.childLayoutPairs.find(c => c.layout[Id] === l.i);
const dxf = () => this.lookupIndividualTransform(l);
- const width = () => (this.flexGrid ? l.w : this.defaultW) * this.colWidthPlusGap - this.margin;
- const height = () => (this.flexGrid ? l.h : this.defaultH) * this.rowHeightPlusGap - this.margin;
+ const width = () => (this.flexGrid ? l.w : this.defaultW) * this.colWidthPlusGap - this.gridGap;
+ const height = () => (this.flexGrid ? l.h : this.defaultH) * this.rowHeightPlusGap - this.gridGap;
child &&
collector.push(
<div key={child.layout[Id]} className={'document-wrapper' + (this.flexGrid && this._props.isSelected() ? '' : ' static')}>
@@ -295,7 +297,7 @@ export class CollectionGridView extends CollectionSubView() {
* Handles external drop of images/PDFs etc from outside Dash.
*/
onExternalDrop = async (e: React.DragEvent): Promise<void> => {
- this.dropLocation = this.screenToCell(e.clientX, e.clientY);
+ this._dropLocation = this.screenToCell(e.clientX, e.clientY);
super.onExternalDrop(e, {});
};
@@ -316,12 +318,13 @@ export class CollectionGridView extends CollectionSubView() {
this,
e,
returnFalse,
- action(() => {
- undoable(() => {
+ undoable(
+ action(() => {
this.Document.gridRowHeight = this._rowHeight;
- }, 'changing row height')();
- this._rowHeight = undefined;
- }),
+ this._rowHeight = undefined;
+ }),
+ 'changing row height'
+ ),
emptyFunction,
false,
false
@@ -391,14 +394,14 @@ export class CollectionGridView extends CollectionSubView() {
<div
className="collectionGridView-gridContainer"
ref={this._containerRef}
- style={{ backgroundColor: StrCast(this.layoutDoc._backgroundColor, 'white') }}
+ style={{ backgroundColor: StrCast(this.layoutDoc._backgroundColor, 'white'), padding: `${this.yMargin} ${this.xMargin}` }}
onWheel={e => e.stopPropagation()}
onScroll={action(e => {
if (!this._props.isSelected()) e.currentTarget.scrollTop = this._scroll;
else this._scroll = e.currentTarget.scrollTop;
})}>
<Grid
- width={this._props.PanelWidth()}
+ width={this._props.PanelWidth() - 2 * this.xMargin}
nodeList={this.contents.length ? this.contents : null}
layout={this.contents.length ? this.renderedLayoutList : undefined}
childrenDraggable={!!this._props.isSelected()}
@@ -408,15 +411,15 @@ export class CollectionGridView extends CollectionSubView() {
transformScale={this.ScreenToLocalBoxXf().Scale}
compactType={this.compaction} // determines whether nodes should remain in position, be bound to the top, or to the left
preventCollision={BoolCast(this.Document.gridPreventCollision)} // determines whether nodes should move out of the way (i.e. collide) when other nodes are dragged over them
- margin={this.margin}
+ margin={this.gridGap}
/>
<input
className="rowHeightSlider"
type="range"
- style={{ width: this._props.PanelHeight() - 30 }}
+ style={{ width: this._props.PanelHeight() - 2 * this.yMargin }}
min={1}
value={this.rowHeight}
- max={this._props.PanelHeight() - 30}
+ max={this._props.PanelHeight() - 2 * this.yMargin}
onPointerDown={this.onSliderDown}
onChange={this.onSliderChange}
/>
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.scss b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
index b8ceec139..0dfaed38a 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.scss
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.scss
@@ -1,12 +1,12 @@
-@import '../../global/globalCssVariables.module.scss';
-@import '../../_nodeModuleOverrides';
+@use '../../global/globalCssVariables.module.scss' as global;
+// bcz fix @import '../../_nodeModuleOverrides';
.collectionLinearView {
width: 100%;
}
.collectionLinearView-label {
color: black;
- background-color: $light-gray;
+ background-color: global.$light-gray;
width: 100%;
}
.collectionLinearView-outer {
@@ -32,8 +32,8 @@
}
> span {
- background: $dark-gray;
- color: $white;
+ background: global.$dark-gray;
+ color: global.$white;
border-radius: 18px;
margin-right: 6px;
cursor: pointer;
@@ -44,7 +44,7 @@
}
.bottomPopup-background {
- background: $medium-blue;
+ background: global.$medium-blue;
display: flex;
border-radius: 10px;
height: 35;
@@ -55,7 +55,7 @@
}
.bottomPopup-text {
- color: $white;
+ color: global.$white;
display: inline;
white-space: nowrap;
padding-left: 8px;
@@ -72,7 +72,7 @@
padding-left: 8px;
padding-right: 8px;
vertical-align: middle;
- background-color: $light-gray;
+ background-color: global.$light-gray;
border-radius: 3px;
color: black;
margin-right: 5px;
@@ -86,7 +86,7 @@
padding-left: 8px;
padding-right: 8px;
vertical-align: middle;
- background-color: $close-red;
+ background-color: global.$close-red;
border-radius: 3px;
color: black;
}
@@ -94,13 +94,13 @@
> label {
pointer-events: all;
cursor: pointer;
- background-color: $medium-blue;
+ background-color: global.$medium-blue;
padding: 5;
border-radius: 2px;
height: 100%;
min-width: 25;
margin: 0;
- color: $white;
+ color: global.$white;
display: flex;
font-weight: 100;
transition: transform 0.2s;
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
index 817ceac97..0bf78f57c 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss
@@ -1,4 +1,4 @@
-@import '../../global/globalCssVariables.module.scss';
+@use '../../global/globalCssVariables.module.scss' as global;
.collectionSchemaView {
cursor: default;
@@ -7,7 +7,7 @@
flex-direction: row;
.schema-table {
- background-color: $white;
+ background-color: global.$white;
cursor: grab;
width: 100%;
@@ -49,10 +49,10 @@
.schema-column-menu,
.schema-filter-menu {
- background: $light-gray;
+ background: global.$light-gray;
position: absolute;
- border: 1px solid $medium-gray;
- border-bottom: 2px solid $medium-gray;
+ border: 1px solid global.$medium-gray;
+ border-bottom: 2px solid global.$medium-gray;
max-height: 201px;
display: flex;
overflow: hidden;
@@ -66,7 +66,7 @@
width: 100%;
&:hover {
- background-color: $medium-gray;
+ background-color: global.$medium-gray;
}
.schema-search-result-type {
font-family: 'Courier New', Courier, monospace;
@@ -74,8 +74,8 @@
.schema-search-result-type,
.schema-search-result-desc {
- color: $dark-gray;
- font-size: $body-text;
+ color: global.$dark-gray;
+ font-size: global.$body-text;
}
.schema-search-result-desc {
font-style: italic;
@@ -120,9 +120,9 @@
.schema-column-menu-button {
cursor: pointer;
padding: 2px 5px;
- background: $medium-blue;
+ background: global.$medium-blue;
border-radius: 9999px;
- color: $white;
+ color: global.$white;
width: fit-content;
margin: 5px;
align-self: center;
@@ -141,7 +141,7 @@
}
.schema-column-header {
- background-color: $light-gray;
+ background-color: global.$light-gray;
font-weight: bold;
display: flex;
flex-direction: row;
@@ -149,7 +149,7 @@
align-items: center;
padding: 0;
z-index: 1;
- border: 1px solid $medium-gray;
+ border: 1px solid global.$medium-gray;
.schema-column-title {
flex-grow: 2;
@@ -175,7 +175,7 @@
cursor: ew-resize;
&:hover {
- background-color: $light-blue;
+ background-color: global.$light-blue;
}
}
@@ -188,7 +188,7 @@
min-width: 5px;
transform: translate(-3px, 0px);
align-self: flex-start;
- background-color: $medium-gray;
+ background-color: global.$medium-gray;
}*/ // creates awkward thick gray borders between colheaders
}
@@ -202,7 +202,7 @@
}
.schema-header-row {
- background-color: $light-gray;
+ background-color: global.$light-gray;
overflow: hidden;
}
@@ -226,7 +226,7 @@
.schema-table-cell,
.row-menu {
- border: 1px solid $medium-gray;
+ border: 1px solid global.$medium-gray;
overflow-x: hidden;
overflow-y: auto;
display: inline-flex;
@@ -264,7 +264,7 @@
.row-menu-infos {
position: absolute;
top: 3;
- left: 3;
+ left: 3;
z-index: 1;
display: flex;
justify-content: flex-end;
@@ -278,7 +278,7 @@
.schema-row-button,
.schema-header-button {
- color: $dark-gray;
+ color: global.$dark-gray;
margin: 3px;
cursor: pointer;
display: flex;
@@ -294,7 +294,7 @@
width: 17px;
height: 17px;
border-radius: 30%;
- background-color: $dark-gray;
+ background-color: global.$dark-gray;
color: white;
margin: 3px;
cursor: pointer;
@@ -311,5 +311,3 @@
outline: none;
height: 100%;
}
-
-