aboutsummaryrefslogtreecommitdiff
path: root/src/client/views
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-09-11 10:37:03 -0400
committerbobzel <zzzman@gmail.com>2024-09-11 10:37:03 -0400
commit1ed378a79d545516a42121819d7049c20aecbc8c (patch)
treeb1f112b03b1dbf0c3b23ec7a019c7da06c1aed3f /src/client/views
parent62eda6aa6af765526fd065dc271b9d53e2b28002 (diff)
parentd3817d1c03b68ab2c69ab5ccfb5e8d6943df8f25 (diff)
Merge branch 'master' into zach-starter
Diffstat (limited to 'src/client/views')
-rw-r--r--src/client/views/DashboardView.tsx2
-rw-r--r--src/client/views/DocumentButtonBar.tsx18
-rw-r--r--src/client/views/FilterPanel.tsx26
-rw-r--r--src/client/views/MainView.tsx8
-rw-r--r--src/client/views/PropertiesButtons.tsx20
-rw-r--r--src/client/views/SidebarAnnos.tsx1
-rw-r--r--src/client/views/StyleProvider.tsx2
-rw-r--r--src/client/views/TagsView.tsx38
-rw-r--r--src/client/views/collections/CollectionCalendarView.tsx5
-rw-r--r--src/client/views/collections/CollectionCardDeckView.scss18
-rw-r--r--src/client/views/collections/CollectionStackingView.scss7
-rw-r--r--src/client/views/collections/CollectionStackingView.tsx62
-rw-r--r--src/client/views/collections/CollectionStackingViewFieldColumn.tsx26
-rw-r--r--src/client/views/collections/CollectionView.tsx4
-rw-r--r--src/client/views/collections/TreeView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx1
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx4
-rw-r--r--src/client/views/collections/collectionGrid/CollectionGridView.tsx17
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss1
-rw-r--r--src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss1
-rw-r--r--src/client/views/global/globalScripts.ts14
-rw-r--r--src/client/views/nodes/ComparisonBox.tsx7
-rw-r--r--src/client/views/nodes/FieldView.tsx1
-rw-r--r--src/client/views/nodes/FontIconBox/FontIconBox.tsx2
-rw-r--r--src/client/views/nodes/ImageBox.tsx14
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx2
-rw-r--r--src/client/views/nodes/PDFBox.tsx4
-rw-r--r--src/client/views/nodes/calendarBox/CalendarBox.scss25
-rw-r--r--src/client/views/nodes/calendarBox/CalendarBox.tsx258
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx3
31 files changed, 346 insertions, 249 deletions
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx
index 33e905a54..eced64524 100644
--- a/src/client/views/DashboardView.tsx
+++ b/src/client/views/DashboardView.tsx
@@ -465,7 +465,7 @@ export class DashboardView extends ObservableReactComponent<object> {
isSystem: true,
layout_explainer: 'All of the calendars that you have created will appear here.',
};
- const myCalendars = DocUtils.AssignScripts(Docs.Create.CalendarCollectionDocument([], reqdOpts));
+ const myCalendars = DocUtils.AssignScripts(Docs.Create.StackingDocument([], reqdOpts));
// { treeView_ChildDoubleClick: 'openPresentation(documentView.rootDoc)' }
return new PrefetchProxy(myCalendars);
}
diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx
index 58b7f207c..437ef045f 100644
--- a/src/client/views/DocumentButtonBar.tsx
+++ b/src/client/views/DocumentButtonBar.tsx
@@ -264,23 +264,6 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
}
@computed
- get calendarButton() {
- const targetDoc = this.view0?.Document;
- return !targetDoc ? null : (
- <Tooltip title={<div className="dash-calendar-button">Open calendar menu</div>}>
- <div
- className="documentButtonBar-icon"
- style={{ color: 'white' }}
- onClick={() => {
- CalendarManager.Instance.open(this.view0, targetDoc);
- }}>
- <FontAwesomeIcon className="documentdecorations-icon" icon={faCalendarDays as IconLookup} />
- </div>
- </Tooltip>
- );
- }
-
- @computed
get keywordButton() {
return !DocumentView.Selected().length ? null : (
<Tooltip title={<div className="dash-keyword-button">Open keyword menu</div>}>
@@ -460,7 +443,6 @@ export class DocumentButtonBar extends ObservableReactComponent<{ views: () => (
{!DocumentView.Selected().some(v => v.allLinks.length) ? null : <div className="documentButtonBar-button">{this.followLinkButton}</div>}
<div className="documentButtonBar-button">{this.pinButton}</div>
<div className="documentButtonBar-button">{this.recordButton}</div>
- <div className="documentButtonBar-button">{this.calendarButton}</div>
<div className="documentButtonBar-button">{this.keywordButton}</div>
{!Doc.UserDoc().documentLinksButton_fullMenu ? null : <div className="documentButtonBar-button">{this.shareButton}</div>}
<div className="documentButtonBar-button">{this.menuButton}</div>
diff --git a/src/client/views/FilterPanel.tsx b/src/client/views/FilterPanel.tsx
index b11fa3bd5..2f6d1fbaa 100644
--- a/src/client/views/FilterPanel.tsx
+++ b/src/client/views/FilterPanel.tsx
@@ -192,21 +192,17 @@ export class FilterPanel extends ObservableReactComponent<filterProps> {
const allCollectionDocs = new Set<Doc>();
SearchUtil.foreachRecursiveDoc(this.targetDocChildren, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
const set = new Set<string>([...StrListCast(this.Document.childFilters).map(filter => filter.split(Doc.FilterSep)[1]), Doc.FilterNone, Doc.FilterAny]);
- if (facetHeader === 'tags')
- allCollectionDocs.forEach(child =>
- StrListCast(child[facetHeader])
- .filter(h => h)
- .forEach(key => set.add(key))
- );
- else
- allCollectionDocs.forEach(child => {
- const fieldVal = child[facetHeader] as FieldType;
- if (!(fieldVal instanceof List)) {
- // currently we have no good way of filtering based on a field that is a list
- set.add(Field.toString(fieldVal));
- (fieldVal === true || fieldVal === false) && set.add((!fieldVal).toString());
- }
- });
+
+ allCollectionDocs.forEach(child => {
+ const fieldVal = child[facetHeader] as FieldType;
+ const fieldStrList = StrListCast(child[facetHeader]).filter(h => h);
+ if (fieldStrList.length) fieldStrList.forEach(key => set.add(key));
+ else if (!(fieldVal instanceof List)) {
+ // currently we have no good way of filtering based on a field that is a list
+ set.add(Field.toString(fieldVal));
+ (fieldVal === true || fieldVal === false) && set.add((!fieldVal).toString());
+ }
+ });
const facetValues = Array.from(set).filter(v => v);
let nonNumbers = 0;
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx
index 7deca234e..09173133f 100644
--- a/src/client/views/MainView.tsx
+++ b/src/client/views/MainView.tsx
@@ -3,7 +3,7 @@ import { faBuffer, faHireAHelper } from '@fortawesome/free-brands-svg-icons';
import * as far from '@fortawesome/free-regular-svg-icons';
import * as fa from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { action, computed, configure, makeObservable, observable, reaction, runInAction } from 'mobx';
+import { action, computed, configure, makeObservable, observable, reaction, runInAction, trace } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import ResizeObserver from 'resize-observer-polyfill';
@@ -708,8 +708,8 @@ export class MainView extends ObservableReactComponent<object> {
setupMoveUpEvents(
this,
e,
- action(() => {
- SnappingManager.SetPropertiesWidth(Math.max(0, this._dashUIWidth - e.clientX));
+ action(moveEv => {
+ SnappingManager.SetPropertiesWidth(Math.max(0, this._dashUIWidth - moveEv.clientX));
return !SnappingManager.PropertiesWidth;
}),
action(() => {
@@ -808,6 +808,7 @@ export class MainView extends ObservableReactComponent<object> {
childFilters={returnEmptyFilter}
childFiltersByRanges={returnEmptyFilter}
searchFilterDocs={returnEmptyDoclist}
+ dontCenter="y"
/>
</div>
);
@@ -833,6 +834,7 @@ export class MainView extends ObservableReactComponent<object> {
};
@computed get mainInnerContent() {
+ trace();
const leftMenuFlyoutWidth = this._leftMenuFlyoutWidth + this.leftMenuWidth();
const width = this.propertiesWidth() + leftMenuFlyoutWidth;
return (
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx
index f346d4ba8..f96a4a255 100644
--- a/src/client/views/PropertiesButtons.tsx
+++ b/src/client/views/PropertiesButtons.tsx
@@ -246,26 +246,6 @@ export class PropertiesButtons extends React.Component {
// );
// }
- // @computed get freezeThumb() {
- // return this.propertyToggleBtn(
- // 'FreezeThumb',
- // '_thumb-frozen',
- // on => `${on ? 'Freeze' : 'Unfreeze'} thumbnail`,
- // on => 'snowflake',
- // (dv, doc) => {
- // if (doc['thumb-frozen']) doc['thumb-frozen'] = undefined;
- // else {
- // document.body.focus(); // so that we can access the clipboard without an error
- // setTimeout(() =>
- // pasteImageBitmap((data_url: any, error: any) => {
- // error && console.log(error);
- // data_url && Utils.convertDataUri(data_url, doc[Id] + '-thumb-frozen', true).then(returnedfilename => (doc['thumb-frozen'] = new ImageField(returnedfilename)));
- // })
- // );
- // }
- // }
- // );
- // }
@computed get snapButton() {
// THESE ARE NOT COMING
return this.propertyToggleBtn(
diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx
index 8f0a35df0..dd60bfa65 100644
--- a/src/client/views/SidebarAnnos.tsx
+++ b/src/client/views/SidebarAnnos.tsx
@@ -246,6 +246,7 @@ export class SidebarAnnos extends ObservableReactComponent<FieldViewProps & Extr
isAnnotationOverlay={false}
select={emptyFunction}
NativeDimScaling={returnOne}
+ dontCenter="y"
// childlayout_showTitle={this.layout_showTitle}
isAnyChildContentActive={returnFalse}
childDocumentsActive={this._props.isContentActive}
diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx
index 76cb119ab..513953d17 100644
--- a/src/client/views/StyleProvider.tsx
+++ b/src/client/views/StyleProvider.tsx
@@ -365,7 +365,7 @@ export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps &
</Tooltip>
);
};
- const tags = () => props?.DocumentView?.() && CollectionFreeFormDocumentView.from(props.DocumentView()) ? <TagsView View={props.DocumentView()}/> : null;
+ const tags = () => props?.DocumentView?.() ? <TagsView View={props.DocumentView()}/> : null;
return (
<>
{paint()}
diff --git a/src/client/views/TagsView.tsx b/src/client/views/TagsView.tsx
index 0ac015b36..8047363d9 100644
--- a/src/client/views/TagsView.tsx
+++ b/src/client/views/TagsView.tsx
@@ -9,7 +9,7 @@ import { emptyFunction } from '../../Utils';
import { Doc, DocListCast, Field, Opt, StrListCast } from '../../fields/Doc';
import { DocData } from '../../fields/DocSymbols';
import { List } from '../../fields/List';
-import { DocCast, NumCast, StrCast } from '../../fields/Types';
+import { DocCast, StrCast } from '../../fields/Types';
import { DocumentType } from '../documents/DocumentTypes';
import { DragManager } from '../util/DragManager';
import { SnappingManager } from '../util/SnappingManager';
@@ -91,7 +91,7 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
const tagCollection = TagItem.findTagCollectionDoc(tag) ?? TagItem.createTagCollectionDoc(tag);
// If the document is of type COLLECTION, make it a smart collection, otherwise, add the tag to the document.
- if (doc.type === DocumentType.COL) {
+ if (doc.type === DocumentType.COL && !doc.annotationOn) {
Doc.AddDocToList(tagCollection[DocData], 'collections', doc);
// Iterate through the tag Doc collections and add a copy of the document to each collection
@@ -216,7 +216,17 @@ export class TagItem extends ObservableReactComponent<TagItemProps> {
{metadata ? (
<span>
<b style={{ fontSize: 'smaller' }}>{tag}&nbsp;</b>
- {Field.toString(this._props.doc[metadata])}
+ {typeof this._props.doc[metadata] === 'boolean' ? (
+ <input
+ type="checkbox"
+ onClick={e => e.stopPropagation()}
+ onPointerDown={e => e.stopPropagation()}
+ onChange={undoable(e => (this._props.doc[metadata] = !this._props.doc[metadata]), 'metadata toggle')}
+ checked={this._props.doc[metadata] as boolean}
+ />
+ ) : (
+ Field.toString(this._props.doc[metadata])
+ )}
</span>
) : (
tag
@@ -247,6 +257,7 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
super(props);
makeObservable(this);
}
+ InsetDist = 25; // how far tag panel is moved up to overlap DocumentView.
@observable _panelHeightDirty = 0;
@observable _currentInput = '';
@@ -266,7 +277,7 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
}
@computed get currentScale() {
- return NumCast(DocCast(this._props.View.Document.embedContainer)?._freeform_scale, 1);
+ return Math.max(1, 1 / this._props.View.screenToLocalScale());
}
@computed get isEditing() {
return this._isEditing && DocumentView.SelectedDocs().includes(this._props.View.Document);
@@ -283,15 +294,18 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
};
/**
- * Adds the specified tag to the Doc. If the tag is not prefixed with '#', then a '#' prefix is added.
- * Whne the tag (after the '#') begins with '@', then a metadata key/value pair is displayed instead of
- * just the tag.
- * @param tag tag string to add
+ * Adds the specified tag or metadata to the Doc. If the tag is not prefixed with '#', then a '#' prefix is added.
+ * When the tag (after the '#') begins with '@', then a metadata key/value pair is displayed instead of
+ * just the tag. In addition, a suffix of :<value> can be added to set a metadata value
+ * @param tag tag string to add (format: #<tag> | #@field(:(=)?value)? )
*/
submitTag = undoable(
action((tag: string) => {
- const submittedLabel = tag.trim();
- submittedLabel && TagItem.addTagToDoc(this._props.View.Document, '#' + submittedLabel.replace(/^#/, ''));
+ const submittedLabel = tag.trim().replace(/^#/, '').split(':');
+ if (submittedLabel[0]) {
+ TagItem.addTagToDoc(this._props.View.Document, '#' + submittedLabel[0]);
+ if (submittedLabel.length > 1) Doc.SetField(this._props.View.Document, submittedLabel[0].replace(/^@/, ''), ':' + submittedLabel[1]);
+ }
this._currentInput = ''; // Clear the input box
}),
'added doc label'
@@ -315,7 +329,7 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
return !this._props.View.Document.showTags ? null : (
<div
className="tagsView-container"
- ref={r => r && new ResizeObserver(action(() => (this._props.View.TagPanelHeight = r?.getBoundingClientRect().height ?? 0))).observe(r)}
+ ref={r => r && new ResizeObserver(action(() => (this._props.View.TagPanelHeight = Math.max(0, (r?.getBoundingClientRect().height ?? 0) - this.InsetDist)))).observe(r)}
style={{
transformOrigin: 'top left',
maxWidth: `${100 * this.currentScale}%`,
@@ -323,6 +337,8 @@ export class TagsView extends ObservableReactComponent<TagViewProps> {
transform: `scale(${1 / this.currentScale})`,
backgroundColor: this.isEditing ? Colors.LIGHT_GRAY : Colors.TRANSPARENT,
borderColor: this.isEditing ? Colors.BLACK : Colors.TRANSPARENT,
+ position: 'relative',
+ top: `calc(-${this.InsetDist} * ${1 / this.currentScale}px)`,
}}>
<div className="tagsView-content" style={{ width: '100%' }}>
<div className="tagsView-list">
diff --git a/src/client/views/collections/CollectionCalendarView.tsx b/src/client/views/collections/CollectionCalendarView.tsx
index 9eb16917b..0ea9f8ebc 100644
--- a/src/client/views/collections/CollectionCalendarView.tsx
+++ b/src/client/views/collections/CollectionCalendarView.tsx
@@ -38,8 +38,8 @@ export class CollectionCalendarView extends CollectionSubView() {
const aDateRangeStr = StrCast(DocListCast(calendarA.data).lastElement()?.date_range);
const bDateRangeStr = StrCast(DocListCast(calendarB.data).lastElement()?.date_range);
- const [aFromDate, aToDate] = dateRangeStrToDates(aDateRangeStr);
- const [bFromDate, bToDate] = dateRangeStrToDates(bDateRangeStr);
+ const { start: aFromDate, end: aToDate } = dateRangeStrToDates(aDateRangeStr);
+ const { start: bFromDate, end: bToDate } = dateRangeStrToDates(bDateRangeStr);
if (aFromDate > bFromDate) {
return -1; // a comes first
@@ -82,6 +82,7 @@ export class CollectionCalendarView extends CollectionSubView() {
isAnnotationOverlay={false}
// select={emptyFunction} What does this mean?
isAnyChildContentActive={returnTrue} // ??
+ dontCenter="y"
// childDocumentsActive={}
// whenChildContentsActiveChanged={}
childHideDecorationTitle={false}
diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index a089b248d..cc797d0bd 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -6,6 +6,15 @@
position: relative;
background-color: white;
overflow: hidden;
+
+ button {
+ width: 35px;
+ height: 35px;
+ border-radius: 50%;
+ background-color: $dark-gray;
+ // border-color: $medium-blue;
+ margin: 5px; // transform: translateY(-50px);
+ }
}
.card-wrapper {
@@ -34,15 +43,6 @@
justify-content: start; /* Centers buttons horizontally */
}
-button {
- width: 35px;
- height: 35px;
- border-radius: 50%;
- background-color: $dark-gray;
- // border-color: $medium-blue;
- margin: 5px; // transform: translateY(-50px);
-}
-
// button:hover {
// transform: translateY(-50px);
// }
diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss
index 6225cc52a..6400a0a8e 100644
--- a/src/client/views/collections/CollectionStackingView.scss
+++ b/src/client/views/collections/CollectionStackingView.scss
@@ -54,11 +54,8 @@
}
.collectionStackingViewFieldColumn {
- height: max-content;
- }
-
- .collectionStackingViewFieldColumnDragging {
- height: 100%;
+ display: flex;
+ flex-direction: column;
}
.collectionSchemaView-previewDoc {
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx
index 6402ef16c..e97ee713e 100644
--- a/src/client/views/collections/CollectionStackingView.tsx
+++ b/src/client/views/collections/CollectionStackingView.tsx
@@ -35,6 +35,7 @@ import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow';
import './CollectionStackingView.scss';
import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { computedFn } from 'mobx-utils';
export type collectionStackingViewProps = {
sortFunc?: (a: Doc, b: Doc) => number;
@@ -127,6 +128,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
}
+ columnWidthFn = () => this.columnWidth;
+ columnDocHeightFn = (doc: Doc) => () => (this.isStackingView ? this.getDocHeight(doc)() : Math.min(this.getDocHeight(doc)(), this._props.PanelHeight()));
+
// TODO: plj - these are the children
children = (docs: Doc[]) => {
// TODO: can somebody explain me to what exactly TraceMobX is?
@@ -140,17 +144,14 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
})
);
return docs.map((d, i) => {
- const height = () => this.getDocHeight(d);
- const width = () => this.getDocWidth(d);
- const trans = () => this.getDocTransition(d);
// assuming we need to get rowSpan because we might be dealing with many columns. Grid gap makes sense if multiple columns
- const rowSpan = Math.ceil((height() + this.gridGap) / this.gridGap);
+ const rowSpan = Math.ceil((this.getDocHeight(d)() + this.gridGap) / this.gridGap);
// just getting the style
- const style = this.isStackingView ? { margin: this.Document._stacking_alignCenter ? 'auto' : undefined, transition: trans(), width: width(), marginTop: i ? this.gridGap : 0, height: height() } : { gridRowEnd: `span ${rowSpan}` };
+ const style = this.isStackingView ? { margin: undefined, transition: this.getDocTransition(d)(), width: this.columnWidth, marginTop: i ? this.gridGap : 0, height: this.getDocHeight(d)() } : { gridRowEnd: `span ${rowSpan}` };
// So we're choosing whether we're going to render a column or a masonry doc
return (
<div className={`collectionStackingView-${this.isStackingView ? 'columnDoc' : 'masonryDoc'}`} key={d[Id]} style={style}>
- {this.getDisplayDoc(d, width, trans, i)}
+ {this.getDisplayDoc(d, this.getDocTransition(d), i)}
</div>
);
});
@@ -311,26 +312,23 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
: this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
? false
: undefined;
+
isChildButtonContentActive = () => (this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false ? false : undefined);
@observable docRefs = new ObservableMap<Doc, DocumentView>();
childFitWidth = (doc: Doc) => Cast(this.Document.childLayoutFitWidth, 'boolean', this._props.childLayoutFitWidth?.(doc) ?? Cast(doc.layout_fitWidth, 'boolean', null));
// this is what renders the document that you see on the screen
// called in Children: this actually adds a document to our children list
- getDisplayDoc(doc: Doc, width: () => number, trans: () => string, count: number) {
+ getDisplayDoc(doc: Doc, trans: () => string, count: number) {
const dataDoc = doc.isTemplateDoc || doc.isTemplateForField ? this._props.TemplateDataDocument : undefined;
- const height = () => this.getDocHeight(doc);
- const panelHeight = () => (this.isStackingView ? height() : Math.min(height(), this._props.PanelHeight()));
- const panelWidth = () => this.columnWidth;
- const stackedDocTransform = () => this.getDocTransform(doc);
- this._docXfs.push({ stackedDocTransform, width, height });
+ this._docXfs.push({ stackedDocTransform: this.getDocTransform(doc), width: this.getDocWidth(doc), height: this.getDocHeight(doc) });
return count > this._renderCount ? null : (
<DocumentView
ref={action((r: DocumentView) => r?.ContentDiv && this.docRefs.set(doc, r))}
Document={doc}
TemplateDataDocument={dataDoc}
renderDepth={this._props.renderDepth + 1}
- PanelWidth={panelWidth}
- PanelHeight={panelHeight}
+ PanelWidth={this.columnWidthFn}
+ PanelHeight={this.columnDocHeightFn(doc)}
pointerEvents={this.DocumentView?.()._props.onClickScript?.() ? returnNone : undefined} // if the stack has an onClick, then we don't want the contents to be interactive (see CollectionPileView)
styleProvider={this.styleProvider}
containerViewPath={this.childContainerViewPath}
@@ -341,16 +339,16 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
isDocumentActive={this.isContentActive}
LayoutTemplate={this._props.childLayoutTemplate}
LayoutTemplateString={this._props.childLayoutString}
- NativeWidth={this._props.childIgnoreNativeSize ? returnZero : this._props.childLayoutFitWidth?.(doc) || (this.childFitWidth(doc) && !Doc.NativeWidth(doc)) ? width : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
- NativeHeight={this._props.childIgnoreNativeSize ? returnZero : this._props.childLayoutFitWidth?.(doc) || (this.childFitWidth(doc) && !Doc.NativeHeight(doc)) ? height : undefined}
- dontCenter={this._props.childIgnoreNativeSize ? 'xy' : (StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy')}
+ NativeWidth={this._props.childIgnoreNativeSize ? returnZero : this._props.childLayoutFitWidth?.(doc) || (this.childFitWidth(doc) && !Doc.NativeWidth(doc)) ? this.getDocWidth(doc) : undefined} // explicitly ignore nativeWidth/height if childIgnoreNativeSize is set- used by PresBox
+ NativeHeight={this._props.childIgnoreNativeSize ? returnZero : this._props.childLayoutFitWidth?.(doc) || (this.childFitWidth(doc) && !Doc.NativeHeight(doc)) ? this.getDocHeight(doc) : undefined}
+ dontCenter={this.dontCenter}
dontRegisterView={BoolCast(this.layoutDoc.childDontRegisterViews, this._props.dontRegisterView)} // used to be true if DataDoc existed, but template textboxes won't layout_autoHeight resize if dontRegisterView is set, but they need to.
rootSelected={this.rootSelected}
showTitle={this._props.childlayout_showTitle}
dragAction={(this.layoutDoc.childDragAction ?? this._props.childDragAction) as dropActionType}
onClickScript={this.onChildClickHandler}
onDoubleClickScript={this.onChildDoubleClickHandler}
- ScreenToLocalTransform={stackedDocTransform}
+ ScreenToLocalTransform={this.getDocTransform(doc)}
focus={this.focusDocument}
childFilters={this.childDocFilters}
hideDecorationTitle={this._props.childHideDecorationTitle}
@@ -371,15 +369,18 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
);
}
- getDocTransform(doc: Doc) {
+ getDocTransform = computedFn((doc: Doc) => () => {
+ // these must be referenced for document decorations to update when the text box container is scrolled
+ this._scroll;
+ this._props.ScreenToLocalTransform();
+
const dref = this.docRefs.get(doc);
- this._scroll; // must be referenced for document decorations to update when the text box container is scrolled
const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(dref?.ContentDiv);
return new Transform(-translateX + (dref?.centeringX || 0) * scale,
-translateY + (dref?.centeringY || 0) * scale, 1)
.scale(1 / scale); // prettier-ignore
- }
- getDocWidth(d?: Doc) {
+ });
+ getDocWidth = computedFn((d?: Doc) => () => {
if (!d) return 0;
const childLayoutDoc = Doc.Layout(d, this._props.childLayoutTemplate?.());
const maxWidth = this.columnWidth / this.numGroupColumns;
@@ -387,12 +388,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
return Math.min(NumCast(d._width), maxWidth);
}
return maxWidth;
- }
- getDocTransition(d?: Doc) {
- if (!d) return '';
- return StrCast(d.dataTransition);
- }
- getDocHeight(d?: Doc) {
+ });
+ getDocTransition = computedFn((d?: Doc) => () => StrCast(d?.dataTransition));
+ getDocHeight = computedFn((d?: Doc) => () => {
if (!d || d.hidden) return 0;
const childLayoutDoc = Doc.Layout(d, this._props.childLayoutTemplate?.());
const childDataDoc = !d.isTemplateDoc && !d.isTemplateForField ? undefined : this._props.TemplateDataDocument;
@@ -401,13 +399,13 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!this.childFitWidth(childLayoutDoc) ? NumCast(d._height) : 0);
if (nw && nh) {
const colWid = this.columnWidth / (this.isStackingView ? this.numGroupColumns : 1);
- const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid);
+ const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d)(), colWid);
return Math.min(maxHeight, (docWid * nh) / nw);
}
const childHeight = NumCast(childLayoutDoc._height);
const panelHeight = this.childFitWidth(childLayoutDoc) ? Number.MAX_SAFE_INTEGER : this._props.PanelHeight() - 2 * this.yMargin;
return Math.min(childHeight, maxHeight, panelHeight);
- }
+ });
// This following three functions must be from the view Mehek showed
columnDividerDown = (e: React.PointerEvent) => {
@@ -531,6 +529,9 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
}
});
};
+ @computed get dontCenter() {
+ return this._props.dontCenter ?? (this._props.childIgnoreNativeSize ? 'xy' : (StrCast(this.layoutDoc.layout_dontCenter) as 'x' | 'y' | 'xy'));
+ }
headings = () => Array.from(this.Sections);
// what a section looks like if we're in stacking view
sectionStacking = (heading: SchemaHeaderField | undefined, docList: Doc[]) => {
@@ -565,6 +566,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection
type={type}
createDropTarget={this.createDashEventsTarget}
screenToLocalTransform={this.ScreenToLocalBoxXf}
+ dontCenter={this.dontCenter}
/>
);
};
diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
index 5ae08e535..ed0cabd0a 100644
--- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
+++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx
@@ -41,6 +41,7 @@ interface CSVFieldColumnProps {
columnWidth: number;
numGroupColumns: number;
gridGap: number;
+ dontCenter: 'x' | 'xy' | 'y';
type: 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | undefined;
headings: () => object[];
// I think that stacking view actually has a single column and then supposedly you can add more columns? Unsure
@@ -345,15 +346,6 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
<button type="button" className="collectionStackingView-sectionDelete" onClick={this.deleteColumn}>
<FontAwesomeIcon icon="trash" size="lg" />
</button>
- {/* {evContents === noValueHeader ? null : (
- <div className="collectionStackingView-sectionOptions">
- <Flyout anchorPoint={anchorPoints.TOP_RIGHT} content={this.renderMenu()}>
- <button className="collectionStackingView-sectionOptionButton">
- <FontAwesomeIcon icon="ellipsis-v" size="lg"></FontAwesomeIcon>
- </button>
- </Flyout>
- </div>
- )} */}
</div>
<div
className={'collectionStackingView-collapseBar' + (this._props.headingObject.collapsed === true ? ' active' : '')}
@@ -368,14 +360,20 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
<>
{this._props.Document._columnsHideIfEmpty ? null : headingView}
{this.collapsed ? null : (
- <div>
+ <div
+ style={{
+ margin: 'auto',
+ marginTop: this._props.dontCenter.includes('y') ? undefined : 'auto',
+ marginBottom: this._props.dontCenter.includes('y') ? undefined : 'auto',
+ width: this._props.columnWidth / (uniqueHeadings.length + (this._props.chromeHidden ? 0 : 1) || 1),
+ }}>
<div
key={`${heading}-stack`}
className="collectionStackingView-masonrySingle"
style={{
padding: `${columnYMargin}px ${0}px ${this._props.yMargin}px ${0}px`,
- margin: 'auto',
- width: 'max-content', // singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`,
+ margin: this._props.dontCenter.includes('x') ? undefined : 'auto',
+ // width: 'max-content', // singleColumn ? undefined : `${cols * (style.columnWidth + style.gridGap) + 2 * style.xMargin - style.gridGap}px`,
height: 'max-content',
position: 'relative',
gridGap: this._props.gridGap,
@@ -419,11 +417,11 @@ export class CollectionStackingViewFieldColumn extends ObservableReactComponent<
const uniqueHeadings = headings.map((i, idx) => headings.indexOf(i) === idx);
return (
<div
- className={'collectionStackingViewFieldColumn' + (SnappingManager.IsDragging ? 'Dragging' : '')}
+ className="collectionStackingViewFieldColumn"
key={heading}
style={{
width: `${100 / (uniqueHeadings.length + (this._props.chromeHidden ? 0 : 1) || 1)}%`,
- height: undefined, // DraggingManager.GetIsDragging() ? "100%" : undefined,
+ height: undefined,
background: this._background,
}}
ref={this.createColumnDropRef}
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index ab93abab6..c9e934448 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -34,6 +34,7 @@ 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>() {
@@ -91,7 +92,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
if (type === undefined) return null;
switch (type) {
case CollectionViewType.Schema: return <CollectionSchemaView key="collview" {...props} />;
- case CollectionViewType.Calendar: return <CollectionCalendarView key="collview" {...props} />;
+ case CollectionViewType.Calendar: return <CalendarBox key="collview" {...props} />;
case CollectionViewType.Docking: return <CollectionDockingView key="collview" {...props} />;
case CollectionViewType.Tree: return <CollectionTreeView key="collview" {...props} />;
case CollectionViewType.Multicolumn: return <CollectionMulticolumnView key="collview" {...props} />;
@@ -126,6 +127,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent<CollectionViewPr
{ description: 'Masonry', event: () => func(CollectionViewType.Masonry), icon: 'columns' },
{ 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: 'Map', event: () => func(CollectionViewType.Map), icon: 'globe-americas' },
{ description: 'Grid', event: () => func(CollectionViewType.Grid), icon: 'th-list' },
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index b10a521ca..d2514dfd1 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -474,7 +474,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
};
docTransform = () => this.refTransform(this._dref?.ContentDiv);
getTransform = () => this.refTransform(this._tref.current);
- embeddedPanelWidth = () => this._props.panelWidth() / (this.treeView._props.NativeDimScaling?.() || 1);
+ embeddedPanelWidth = () => this._props.panelWidth() / (this.treeView._props.NativeDimScaling?.() || 1) - 3 /* paddingRight for bullet */;
embeddedPanelHeight = () => {
const layoutDoc = (temp => temp && Doc.expandTemplateLayout(temp, this.Document))(this.treeView._props.childLayoutTemplate?.()) || this.layoutDoc;
return Math.min(
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index c4cf8dee7..dbf781e63 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -1811,7 +1811,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
const contentDiv = this.DocumentView?.().ContentDiv;
contentDiv &&
UpdateIcon(
- this.layoutDoc[Id] + '-icon' + new Date().getTime(),
+ this.layoutDoc[Id] + '_icon_' + new Date().getTime(),
contentDiv,
NumCast(this.layoutDoc._width),
NumCast(this.layoutDoc._height),
diff --git a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
index 717081666..f9f6c81ab 100644
--- a/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
+++ b/src/client/views/collections/collectionFreeForm/FaceCollectionBox.tsx
@@ -264,6 +264,7 @@ export class FaceCollectionBox extends ViewBoxBaseComponent<FieldViewProps>() {
isContentActive={returnTrue}
isAnyChildContentActive={returnTrue}
childHideDecorations={true}
+ dontCenter="y"
/>
</div>
);
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index 6cc75aa4b..ccb6bc9be 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -160,8 +160,8 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
pasteImageBitmap((data: any, error: any) => {
error && console.log(error);
data &&
- ClientUtils.convertDataUri(data, this._props.Document[Id] + '-thumb-frozen').then(returnedfilename => {
- this._props.Document['thumb-frozen'] = new ImageField(returnedfilename);
+ ClientUtils.convertDataUri(data, this._props.Document[Id] + '_icon_' + new Date().getTime()).then(returnedfilename => {
+ this._props.Document[DocData].icon = new ImageField(returnedfilename);
});
})
);
diff --git a/src/client/views/collections/collectionGrid/CollectionGridView.tsx b/src/client/views/collections/collectionGrid/CollectionGridView.tsx
index 61bd0241c..5c41fee37 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 { undoBatch } from '../../../util/UndoManager';
+import { undoable, undoBatch } from '../../../util/UndoManager';
import { ContextMenu } from '../../ContextMenu';
import { ContextMenuProps } from '../../ContextMenuItem';
import { DocumentView } from '../../nodes/DocumentView';
@@ -221,13 +221,13 @@ export class CollectionGridView extends CollectionSubView() {
});
if (this.Document.gridStartCompaction) {
- undoBatch(() => {
+ undoable(() => {
this.Document.gridCompaction = this.Document.gridStartCompaction;
this.setLayoutList(savedLayouts);
- })();
+ }, 'start grid compaction')();
this.Document.gridStartCompaction = undefined;
} else {
- undoBatch(() => this.setLayoutList(savedLayouts))();
+ undoable(() => this.setLayoutList(savedLayouts), 'start grid compaction')();
}
}
};
@@ -315,9 +315,9 @@ export class CollectionGridView extends CollectionSubView() {
e,
returnFalse,
action(() => {
- undoBatch(() => {
+ undoable(() => {
this.Document.gridRowHeight = this._rowHeight;
- })();
+ }, 'changing row height')();
this._rowHeight = undefined;
}),
emptyFunction,
@@ -360,13 +360,14 @@ export class CollectionGridView extends CollectionSubView() {
returnFalse,
(clickEv: PointerEvent, doubleTap?: boolean) => {
if (doubleTap && !clickEv.button) {
- undoBatch(
+ undoable(
action(() => {
const text = Docs.Create.TextDocument('', { _width: 150, _height: 50 });
Doc.SetSelectOnLoad(text); // track the new text box so we can give it a prop that tells it to focus itself when it's displayed
Doc.AddDocToList(this.Document, this._props.fieldKey, text);
this.setLayoutList(this.addLayoutItem(this.savedLayoutList, this.makeLayoutItem(text, this.screenToCell(clickEv.clientX, clickEv.clientY))));
- })
+ }),
+ 'create grid text'
)();
}
},
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
index 06d78c39e..9ed247d50 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.scss
@@ -1,5 +1,6 @@
.collectionMulticolumnView_drop {
height: 100%;
+ width: 100%;
top: 0;
left: 0;
position: absolute;
diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
index 0d49fabaa..91779065d 100644
--- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
+++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.scss
@@ -1,5 +1,6 @@
.collectionMultirowView_drop {
height: 100%;
+ width: 100%;
top: 0;
left: 0;
position: absolute;
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index f46b66840..25d4e9115 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -134,10 +134,10 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
});
// eslint-disable-next-line prefer-arrow-callback
-ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce', checkResult?: boolean, persist?: boolean) {
+ScriptingGlobals.add(function showFreeform(attr: 'hcenter' | 'vcenter' | 'grid' | 'snaplines' | 'clusters' | 'viewAll' | 'fitOnce', checkResult?: boolean, persist?: boolean) {
const selected = DocumentView.SelectedDocs().lastElement();
// prettier-ignore
- const map: Map<'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'links' | 'like' | 'star' | 'idea' | 'chat' | '1' | '2' | '3' | '4',
+ const map: Map<'flashcards' | 'hcenter' | 'vcenter' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'links' | 'like' | 'star' | 'idea' | 'chat' | '1' | '2' | '3' | '4',
{
waitForRender?: boolean;
checkResult: (doc: Doc) => boolean;
@@ -159,9 +159,13 @@ ScriptingGlobals.add(function showFreeform(attr: 'center' | 'grid' | 'snaplines'
else (dv.ComponentView as CollectionFreeFormView)?.fitContentOnce();
},
}],
- ['center', {
- checkResult: (doc:Doc) => BoolCast(doc?._stacking_alignCenter, false),
- setDoc: (doc:Doc) => { doc._stacking_alignCenter = !doc._stacking_alignCenter; },
+ ['vcenter', {
+ checkResult: (doc:Doc) => !StrCast(doc?._layout_dontCenter).includes('y'),
+ setDoc: (doc:Doc) => { doc._layout_dontCenter = StrCast(doc.layout_dontCenter).includes('y') ? StrCast(doc.layout_dontCenter).replace(/y/,"") : StrCast(doc.layout_dontCenter) + 'y'; },
+ }],
+ ['hcenter', {
+ checkResult: (doc:Doc) => !StrCast(doc?._layout_dontCenter).includes('x'),
+ setDoc: (doc:Doc) => { doc._layout_dontCenter = StrCast(doc.layout_dontCenter).includes('x') ? StrCast(doc.layout_dontCenter).replace(/x/,"") : 'x'+ StrCast(doc.layout_dontCenter); },
}],
['clusters', {
waitForRender: true, // flags that undo batch should terminate after a re-render giving the script the chance to fire
diff --git a/src/client/views/nodes/ComparisonBox.tsx b/src/client/views/nodes/ComparisonBox.tsx
index 1eae163df..39a2e3a31 100644
--- a/src/client/views/nodes/ComparisonBox.tsx
+++ b/src/client/views/nodes/ComparisonBox.tsx
@@ -3,7 +3,7 @@ import { Tooltip } from '@mui/material';
import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { returnFalse, returnNone, setupMoveUpEvents } from '../../../ClientUtils';
+import { returnFalse, returnNone, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
@@ -303,6 +303,9 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
<DocumentView
// eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
+ fitWidth={undefined}
+ NativeHeight={returnZero}
+ NativeWidth={returnZero}
ignoreUsePath={layoutString ? true : undefined}
renderDepth={this.props.renderDepth + 1}
LayoutTemplateString={layoutString}
@@ -310,8 +313,6 @@ export class ComparisonBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
containerViewPath={this.DocumentView?.().docViewPath}
moveDocument={whichSlot.endsWith('1') ? this.moveDoc1 : this.moveDoc2}
removeDocument={whichSlot.endsWith('1') ? this.remDoc1 : this.remDoc2}
- NativeWidth={this.layoutWidth}
- NativeHeight={this.layoutHeight}
isContentActive={emptyFunction}
isDocumentActive={returnFalse}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index dd71fd946..c269c7bcb 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -82,6 +82,7 @@ export interface FieldViewSharedProps {
// eslint-disable-next-line no-use-before-define
onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined;
fitWidth?: (doc: Doc) => boolean | undefined;
+ dontCenter?: 'x' | 'y' | 'xy' | undefined;
searchFilterDocs: () => Doc[];
showTitle?: () => string;
whenChildContentsActiveChanged: (isActive: boolean) => void;
diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
index f2f7f39bb..7a09ad9e2 100644
--- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx
+++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx
@@ -345,7 +345,7 @@ export class FontIconBox extends ViewBoxBaseComponent<ButtonProps>() {
@computed get editableText() {
const script = ScriptCast(this.Document.script);
- const checkResult = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result;
+ const checkResult = script?.script.run({ this: this.Document, value: '', _readOnly_: true }).result as string;
const setValue = (value: string) => script?.script.run({ this: this.Document, value, _readOnly_: false }).result as boolean;
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d0a7fc6ac..be3525544 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -11,7 +11,7 @@ import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { ObjectField } from '../../../fields/ObjectField';
-import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types';
+import { Cast, ImageCast, NumCast, RTFCast, StrCast } from '../../../fields/Types';
import { ImageField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { emptyFunction } from '../../../Utils';
@@ -188,8 +188,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@undoBatch
setNativeSize = action(() => {
+ const oldnativeWidth = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']);
const nscale = NumCast(this._props.PanelWidth()) * NumCast(this.layoutDoc._freeform_scale, 1);
- const nw = nscale / NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']);
+ const nw = nscale / oldnativeWidth;
this.dataDoc[this.fieldKey + '_nativeHeight'] = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']) * nw;
this.dataDoc[this.fieldKey + '_nativeWidth'] = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']) * nw;
this.dataDoc._freeform_panX = nw * NumCast(this.dataDoc._freeform_panX);
@@ -198,6 +199,15 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this.dataDoc._freeform_panX_min = this.dataDoc._freeform_panX_min ? nw * NumCast(this.dataDoc._freeform_panX_min) : undefined;
this.dataDoc._freeform_panY_max = this.dataDoc._freeform_panY_max ? nw * NumCast(this.dataDoc._freeform_panY_max) : undefined;
this.dataDoc._freeform_panY_min = this.dataDoc._freeform_panY_min ? nw * NumCast(this.dataDoc._freeform_panY_min) : undefined;
+ const newnativeWidth = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']);
+ DocListCast(this.dataDoc[this.annotationKey]).forEach(doc => {
+ doc.x = (NumCast(doc.x) / oldnativeWidth) * newnativeWidth;
+ doc.y = (NumCast(doc.y) / oldnativeWidth) * newnativeWidth;
+ if (!RTFCast(doc[Doc.LayoutFieldKey(doc)])) {
+ doc.width = (NumCast(doc.width) / oldnativeWidth) * newnativeWidth;
+ doc.height = (NumCast(doc.height) / oldnativeWidth) * newnativeWidth;
+ }
+ });
});
@undoBatch
rotate = action(() => {
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index 95e344004..3daacc9bb 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -84,7 +84,7 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
const onDelegate = rawvalue.startsWith('=');
rawvalue = onDelegate ? rawvalue.substring(1) : rawvalue;
const type: 'computed' | 'script' | false = rawvalue.startsWith(':=') ? 'computed' : rawvalue.startsWith('$=') ? 'script' : false;
- rawvalue = type ? rawvalue.substring(2) : rawvalue;
+ rawvalue = type ? rawvalue.substring(2) : rawvalue.replace(/^:/, '');
rawvalue = rawvalue.replace(/.*\(\((.*)\)\)/, 'dashCallChat(_setCacheResult_, this, `$1`)');
const value = ["'", '"', '`'].includes(rawvalue.length ? rawvalue[0] : '') || !isNaN(+rawvalue) ? rawvalue : '`' + rawvalue + '`';
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index b17275a1e..cb0b0d71f 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -56,10 +56,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@computed get pdfUrl() {
return Cast(this.dataDoc[this._props.fieldKey], PdfField);
}
- @computed get pdfThumb() {
- return ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url;
- }
-
constructor(props: FieldViewProps) {
super(props);
makeObservable(this);
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.scss b/src/client/views/nodes/calendarBox/CalendarBox.scss
new file mode 100644
index 000000000..f8ac4b2d1
--- /dev/null
+++ b/src/client/views/nodes/calendarBox/CalendarBox.scss
@@ -0,0 +1,25 @@
+.calendarBox {
+ display: flex;
+ width: 100%;
+ height: 100%;
+ transform-origin: top left;
+ .calendarBox-wrapper {
+ width: 100%;
+ height: 100%;
+ .fc-timegrid-body {
+ width: 100% !important;
+ table {
+ width: 100% !important;
+ }
+ }
+ .fc-col-header {
+ width: 100% !important;
+ }
+ .fc-daygrid-body {
+ width: 100% !important;
+ .fc-scrollgrid-sync-table {
+ width: 100% !important;
+ }
+ }
+ }
+}
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index bd66941c3..678b7dd0b 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -1,127 +1,207 @@
-import { Calendar, EventSourceInput } from '@fullcalendar/core';
+import { Calendar, DateInput, EventClickArg, EventSourceInput } from '@fullcalendar/core';
import dayGridPlugin from '@fullcalendar/daygrid';
import multiMonthPlugin from '@fullcalendar/multimonth';
-import { makeObservable } from 'mobx';
+import timeGrid from '@fullcalendar/timegrid';
+import interactionPlugin from '@fullcalendar/interaction';
+import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { dateRangeStrToDates } from '../../../../ClientUtils';
import { Doc } from '../../../../fields/Doc';
-import { StrCast } from '../../../../fields/Types';
-import { DocumentType } from '../../../documents/DocumentTypes';
-import { Docs } from '../../../documents/Documents';
-import { ViewBoxBaseComponent } from '../../DocComponent';
-import { FieldView, FieldViewProps } from '../FieldView';
-
-type CalendarView = 'month' | 'multi-month' | 'week';
+import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
+import { CollectionSubView, SubCollectionViewProps } from '../../collections/CollectionSubView';
+import './CalendarBox.scss';
+import { Id } from '../../../../fields/FieldSymbols';
+import { DocServer } from '../../../DocServer';
+import { DocumentView } from '../DocumentView';
+import { OpenWhere } from '../OpenWhere';
+import { DragManager } from '../../../util/DragManager';
+import { DocData } from '../../../../fields/DocSymbols';
+
+type CalendarView = 'multiMonth' | 'dayGridMonth' | 'timeGridWeek' | 'timeGridDay';
@observer
-export class CalendarBox extends ViewBoxBaseComponent<FieldViewProps>() {
- public static LayoutString(fieldKey: string = 'calendar') {
- return FieldView.LayoutString(CalendarBox, fieldKey);
- }
-
- constructor(props: FieldViewProps) {
+export class CalendarBox extends CollectionSubView() {
+ _calendarRef: HTMLDivElement | null = null;
+ _calendar: Calendar | undefined;
+ _oldWheel: HTMLElement | null = null;
+ _observer: ResizeObserver | undefined;
+ _eventsDisposer: IReactionDisposer | undefined;
+ _selectDisposer: IReactionDisposer | undefined;
+
+ constructor(props: SubCollectionViewProps) {
super(props);
makeObservable(this);
}
- componentDidMount(): void {}
-
- componentWillUnmount(): void {}
-
- _calendarRef = React.createRef<HTMLElement>();
+ @observable _multiMonth = 0;
+ isMultiMonth: boolean | undefined;
- get dateRangeStr() {
- return StrCast(this.Document.date_range);
+ componentDidMount(): void {
+ this._props.setContentViewBox?.(this);
+ this._eventsDisposer = reaction(
+ () => ({ events: this.calendarEvents }),
+ ({ events }) => this._calendar?.setOption('events', events),
+ { fireImmediately: true }
+ );
+ this._selectDisposer = reaction(
+ () => ({ initialDate: this.dateSelect }),
+ ({ initialDate }) => {
+ const state = this._calendar?.getCurrentData();
+ state &&
+ this._calendar?.dispatch({
+ type: 'CHANGE_DATE',
+ dateMarker: state.dateEnv.createMarker(initialDate.start),
+ });
+ setTimeout(() => (initialDate.start.toISOString() !== initialDate.end.toISOString() ? this._calendar?.select(initialDate.start, initialDate.end) : this._calendar?.select(initialDate.start)));
+ },
+ { fireImmediately: true }
+ );
}
-
- // Choose a calendar view based on the date range
- get calendarViewType(): CalendarView {
- const [fromDate, toDate] = dateRangeStrToDates(this.dateRangeStr);
-
- if (fromDate.getFullYear() !== toDate.getFullYear() || fromDate.getMonth() !== toDate.getMonth()) return 'multi-month';
-
- if (Math.abs(fromDate.getDay() - toDate.getDay()) > 7) return 'month';
- return 'week';
+ componentWillUnmount(): void {
+ this._eventsDisposer?.();
+ this._selectDisposer?.();
}
- get calendarStartDate() {
- return this.dateRangeStr.split('|')[0];
+ @computed get calendarEvents(): EventSourceInput | undefined {
+ return this.childDocs.map(doc => {
+ const { start, end } = dateRangeStrToDates(StrCast(doc.date_range));
+ return {
+ title: StrCast(doc.title),
+ start,
+ end,
+ groupId: doc[Id],
+ startEditable: true,
+ endEditable: true,
+ allDay: BoolCast(doc.allDay),
+ classNames: ['mother'], // will determine the style
+ editable: true, // subject to change in the future
+ backgroundColor: this.eventToColor(doc),
+ borderColor: this.eventToColor(doc),
+ color: 'white',
+ extendedProps: {
+ description: StrCast(doc.description),
+ },
+ };
+ });
}
- get calendarToDate() {
- return this.dateRangeStr.split('|')[1];
+ @computed get dateRangeStrDates() {
+ return dateRangeStrToDates(StrCast(this.Document.date_range));
}
-
- get childDocs(): Doc[] {
- return this.childDocs; // get all sub docs for a calendar
+ get dateSelect() {
+ return dateRangeStrToDates(StrCast(this.Document.date));
}
- docBackgroundColor(type: string): string {
- // TODO: Return a different color based on the event type
- console.log(type);
- return 'blue';
+ // Choose a calendar view based on the date range
+ @computed get calendarViewType(): CalendarView {
+ if (this.dataDoc[this.fieldKey + '_calendarType']) return StrCast(this.dataDoc[this.fieldKey + '_calendarType']) as CalendarView;
+ if (this.isMultiMonth) return 'multiMonth';
+ const { start, end } = this.dateRangeStrDates;
+ if (start.getFullYear() !== end.getFullYear() || start.getMonth() !== end.getMonth()) return 'multiMonth';
+ if (Math.abs(start.getDay() - end.getDay()) > 7) return 'dayGridMonth';
+ return 'timeGridWeek';
}
- get calendarEvents(): EventSourceInput | undefined {
- if (this.childDocs.length === 0) return undefined;
- return this.childDocs.map(doc => {
- const docTitle = StrCast(doc.title);
- const docDateRange = StrCast(doc.date_range);
- const [startDate, endDate] = dateRangeStrToDates(docDateRange);
- const docType = doc.type;
- const docDescription = doc.description ? StrCast(doc.description) : '';
+ // TODO: Return a different color based on the event type
+ eventToColor(event: Doc): string {
+ return 'red';
+ }
- return {
- title: docTitle,
- start: startDate,
- end: endDate,
- allDay: false,
- classNames: [StrCast(docType)], // will determine the style
- editable: false, // subject to change in the future
- backgroundColor: this.docBackgroundColor(StrCast(doc.type)),
- color: 'white',
- extendedProps: {
- description: docDescription,
- },
- };
+ internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData) {
+ if (!super.onInternalDrop(e, de)) return false;
+ de.complete.docDragData?.droppedDocuments.forEach(doc => {
+ const today = new Date().toISOString();
+ if (!doc.date_range) doc[DocData].date_range = `${today}|${today}`;
});
+ return true;
}
- handleEventClick = (/* arg: EventClickArg */) => {
- // TODO: open popover with event description, option to open CalendarManager and change event date, delete event, etc.
+ onInternalDrop = (e: Event, de: DragManager.DropEvent): boolean => {
+ if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData);
+ return false;
};
- calendarEl: HTMLElement = document.getElementById('calendar-box-v1')!;
+ handleEventClick = (arg: EventClickArg) => {
+ const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
+ DocumentView.DeselectAll();
+ if (doc) {
+ DocumentView.showDocument(doc, { openLocation: OpenWhere.lightboxAlways });
+ arg.jsEvent.stopPropagation();
+ }
+ };
// https://fullcalendar.io
- get calendar() {
- return new Calendar(this.calendarEl, {
- plugins: [this.calendarViewType === 'multi-month' ? multiMonthPlugin : dayGridPlugin],
- headerToolbar: {
- left: 'prev,next today',
- center: 'title',
- right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek',
- },
- initialDate: this.calendarStartDate,
- navLinks: true,
- editable: false,
- displayEventTime: false,
- displayEventEnd: false,
- events: this.calendarEvents,
- eventClick: this.handleEventClick,
- });
- }
+ renderCalendar = () => {
+ const cal = !this._calendarRef
+ ? null
+ : (this._calendar = new Calendar(this._calendarRef, {
+ plugins: [multiMonthPlugin, dayGridPlugin, timeGrid, interactionPlugin],
+ headerToolbar: {
+ left: 'prev,next today',
+ center: 'title',
+ right: 'multiMonth dayGridMonth timeGridWeek timeGridDay',
+ },
+ selectable: true,
+ initialView: this.calendarViewType === 'multiMonth' ? undefined : this.calendarViewType,
+ initialDate: this.dateSelect.start,
+ navLinks: true,
+ editable: false,
+ displayEventTime: false,
+ displayEventEnd: false,
+ select: info => {
+ const start = dateRangeStrToDates(info.startStr).start.toISOString();
+ const end = dateRangeStrToDates(info.endStr).start.toISOString();
+ this.dataDoc.date = start + '|' + end;
+ },
+ aspectRatio: NumCast(this.Document.width) / NumCast(this.Document.height),
+ events: this.calendarEvents,
+ eventClick: this.handleEventClick,
+ }));
+ cal?.render();
+ setTimeout(() => cal?.view.calendar.select(this.dateSelect.start, this.dateSelect.end));
+ };
+ onPassiveWheel = (e: WheelEvent) => e.stopPropagation();
render() {
return (
- <div className="calendar-box-conatiner">
- <div id="calendar-box-v1" />
+ <div
+ key={this.calendarViewType}
+ className="calendarBox"
+ onPointerDown={e => {
+ setTimeout(
+ action(() => {
+ const cname = (e.nativeEvent.target as HTMLButtonElement)?.className ?? '';
+ if (cname.includes('multiMonth')) this.dataDoc[this.fieldKey + '_calendarType'] = 'multiMonth';
+ if (cname.includes('dayGridMonth')) this.dataDoc[this.fieldKey + '_calendarType'] = 'dayGridMonth';
+ if (cname.includes('timeGridWeek')) this.dataDoc[this.fieldKey + '_calendarType'] = 'timeGridWeek';
+ if (cname.includes('timeGridDay')) this.dataDoc[this.fieldKey + '_calendarType'] = 'timeGridDay';
+ })
+ );
+ }}
+ style={{
+ width: this._props.PanelWidth() / this._props.ScreenToLocalTransform().Scale,
+ height: this._props.PanelHeight() / this._props.ScreenToLocalTransform().Scale,
+ transform: `scale(${this._props.ScreenToLocalTransform().Scale})`,
+ }}
+ ref={r => {
+ this.createDashEventsTarget(r);
+ this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
+ this._oldWheel = r;
+ // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
+ r?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
+
+ if (r) {
+ this._observer?.disconnect();
+ (this._observer = new ResizeObserver(() => {
+ this._calendar?.setOption('aspectRatio', NumCast(this.Document.width) / NumCast(this.Document.height));
+ this._calendar?.updateSize();
+ })).observe(r);
+ this.renderCalendar();
+ }
+ }}>
+ <div className="calendarBox-wrapper" ref={r => (this._calendarRef = r)} />
</div>
);
}
}
-Docs.Prototypes.TemplateMap.set(DocumentType.CALENDAR, {
- layout: { view: CalendarBox, dataField: 'data' },
- options: { acl: '' },
-});
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index a88bd8920..343e255dc 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -2035,7 +2035,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
style={{
cursor: this._props.isContentActive() ? 'text' : undefined,
height: this._props.height ? 'max-content' : undefined,
- overflow: this.layout_autoHeight ? 'hidden' : undefined,
pointerEvents: Doc.ActiveTool === InkTool.None && !SnappingManager.ExploreMode ? undefined : 'none',
}}
onContextMenu={this.specificContextMenu}
@@ -2054,7 +2053,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}}
style={{
width: this.noSidebar ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`,
- overflow: this.layoutDoc._createDocOnCR ? 'hidden' : this.layoutDoc._layout_autoHeight ? 'visible' : undefined,
+ overflow: this.layoutDoc._createDocOnCR || this.layoutDoc._layout_hideScroll ? 'hidden' : this.layout_autoHeight ? 'visible' : undefined,
}}
onScroll={this.onScroll}
onDrop={this.ondrop}>