aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes')
-rw-r--r--src/client/views/nodes/CollectionFreeFormDocumentView.tsx14
-rw-r--r--src/client/views/nodes/DocumentLinksButton.tsx1
-rw-r--r--src/client/views/nodes/DocumentView.scss3
-rw-r--r--src/client/views/nodes/DocumentView.tsx109
-rw-r--r--src/client/views/nodes/EquationBox.tsx9
-rw-r--r--src/client/views/nodes/FieldView.tsx4
-rw-r--r--src/client/views/nodes/IconTagBox.tsx24
-rw-r--r--src/client/views/nodes/ImageBox.scss2
-rw-r--r--src/client/views/nodes/ImageBox.tsx190
-rw-r--r--src/client/views/nodes/KeyValueBox.tsx8
-rw-r--r--src/client/views/nodes/LabelBox.tsx39
-rw-r--r--src/client/views/nodes/LinkDocPreview.tsx1
-rw-r--r--src/client/views/nodes/MapBox/MapBox.tsx59
-rw-r--r--src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx51
-rw-r--r--src/client/views/nodes/PDFBox.scss2
-rw-r--r--src/client/views/nodes/PDFBox.tsx59
-rw-r--r--src/client/views/nodes/ScreenshotBox.tsx4
-rw-r--r--src/client/views/nodes/VideoBox.tsx17
-rw-r--r--src/client/views/nodes/WebBox.tsx47
-rw-r--r--src/client/views/nodes/WebBoxRenderer.js2
-rw-r--r--src/client/views/nodes/calendarBox/CalendarBox.scss12
-rw-r--r--src/client/views/nodes/calendarBox/CalendarBox.tsx208
-rw-r--r--src/client/views/nodes/formattedText/DashFieldView.tsx3
-rw-r--r--src/client/views/nodes/formattedText/FormattedTextBox.tsx112
-rw-r--r--src/client/views/nodes/formattedText/marks_rts.ts14
-rw-r--r--src/client/views/nodes/imageEditor/ImageEditor.tsx5
-rw-r--r--src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts6
-rw-r--r--src/client/views/nodes/trails/PresBox.tsx2
-rw-r--r--src/client/views/nodes/trails/PresSlideBox.tsx8
29 files changed, 508 insertions, 507 deletions
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
index 940c4cb99..3805b0dca 100644
--- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
+++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx
@@ -148,7 +148,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static getValues(doc: Doc, time: number, fillIn: boolean = true) {
return CollectionFreeFormDocumentView.animFields.reduce(
(p, val) => {
- p[val.key] = Cast(doc[`${val.key}_indexed`], listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : []).reduce(
+ p[val.key] = Cast(doc[`${val.key}_indexed`], listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : [])!.reduce(
(prev, v, i) => ((i <= Math.round(time) && v !== undefined) || prev === undefined ? v : prev),
undefined as unknown as number
);
@@ -161,7 +161,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static getStringValues(doc: Doc, time: number) {
return CollectionFreeFormDocumentView.animStringFields.reduce(
(p, val) => {
- p[val] = Cast(doc[`${val}_indexed`], listSpec('string'), [StrCast(doc[val])]).reduce((prev, v, i) => ((i <= Math.round(time) && v !== undefined) || prev === undefined ? v : prev), undefined as unknown as string);
+ p[val] = Cast(doc[`${val}_indexed`], listSpec('string'), [StrCast(doc[val])])!.reduce((prev, v, i) => ((i <= Math.round(time) && v !== undefined) || prev === undefined ? v : prev), undefined as unknown as string);
return p;
},
{} as { [val: string]: Opt<string> }
@@ -171,7 +171,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static setStringValues(time: number, d: Doc, vals: { [val: string]: Opt<string> }) {
const timecode = Math.round(time);
Object.keys(vals).forEach(val => {
- const findexed = Cast(d[`${val}_indexed`], listSpec('string'), []).slice();
+ const findexed = Cast(d[`${val}_indexed`], listSpec('string'), [])!.slice();
findexed[timecode] = vals[val] || '';
d[`${val}_indexed`] = new List<string>(findexed);
});
@@ -180,7 +180,7 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
public static setValues(time: number, d: Doc, vals: { [val: string]: Opt<number> }) {
const timecode = Math.round(time);
Object.keys(vals).forEach(val => {
- const findexed = Cast(d[`${val}_indexed`], listSpec('number'), []).slice();
+ const findexed = Cast(d[`${val}_indexed`], listSpec('number'), [])!.slice();
findexed[timecode] = vals[val] as unknown as number;
d[`${val}_indexed`] = new List<number>(findexed);
});
@@ -204,15 +204,15 @@ export class CollectionFreeFormDocumentView extends DocComponent<CollectionFreeF
docs.forEach(doc => {
this.animFields.forEach(val => {
const findexed = Cast(doc[`${val.key}_indexed`], listSpec('number'), null);
- findexed?.length <= timecode + 1 && findexed.push(undefined as unknown as number);
+ (findexed?.length ?? 0) <= timecode + 1 && findexed?.push(undefined as unknown as number);
});
this.animStringFields.forEach(val => {
const findexed = Cast(doc[`${val}_indexed`], listSpec('string'), null);
- findexed?.length <= timecode + 1 && findexed.push(undefined as unknown as string);
+ (findexed?.length ?? 0) <= timecode + 1 && findexed?.push(undefined as unknown as string);
});
this.animDataFields(doc).forEach(val => {
const findexed = Cast(doc[`${val}_indexed`], listSpec(InkField), null);
- findexed?.length <= timecode + 1 && findexed.push(undefined as unknown as InkField);
+ (findexed?.length ?? 0) <= timecode + 1 && findexed?.push(undefined as unknown as InkField);
});
});
return newTimer;
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx
index c35a329c9..d30f00829 100644
--- a/src/client/views/nodes/DocumentLinksButton.tsx
+++ b/src/client/views/nodes/DocumentLinksButton.tsx
@@ -38,7 +38,6 @@ export class DocButtonState {
// eslint-disable-next-line no-use-before-define
public static _instance: DocButtonState | undefined;
public static get Instance() {
- // eslint-disable-next-line no-return-assign
return DocButtonState._instance ?? (DocButtonState._instance = new DocButtonState());
}
constructor() {
diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss
index 5ac66f2cd..c4351a200 100644
--- a/src/client/views/nodes/DocumentView.scss
+++ b/src/client/views/nodes/DocumentView.scss
@@ -119,6 +119,7 @@
display: flex;
justify-content: center;
align-items: center;
+ margin: auto;
position: relative; // allows contents to be positioned relative/below title
> .formattedTextBox {
position: absolute; // position a child text box
@@ -302,6 +303,6 @@
background: transparent;
.documentView-editorView-resizer {
- height: 5px;
+ height: 2px;
}
}
diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx
index 284014e54..9b73cc073 100644
--- a/src/client/views/nodes/DocumentView.tsx
+++ b/src/client/views/nodes/DocumentView.tsx
@@ -648,11 +648,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
rootSelected = () => this._rootSelected;
panelHeight = () => this._props.PanelHeight() - this.headerMargin - 2 * NumCast(this.Document.borderWidth);
- screenToLocalContent = () =>
- this._props
- .ScreenToLocalTransform()
- .translate(-NumCast(this.Document.borderWidth), -this.headerMargin - NumCast(this.Document.borderWidth))
- .scale(this.viewingAiEditor() ? (this._props.PanelHeight() || 1) / this.aiContentsHeight() : 1);
+ aiShift = () => (!this.viewingAiEditor() ? 0 : (this._props.PanelWidth() - this.aiContentsWidth()) / 2);
+ aiScale = () => (this.viewingAiEditor() ? (this._props.PanelHeight() || 1) / this.aiContentsHeight() : 1);
onClickFunc = () => (this.disableClickScriptFunc ? undefined : this.onClickHdlr);
setHeight = (height: number) => { !this._props.suppressSetHeight && (this.layoutDoc._height = Math.min(NumCast(this.layoutDoc._maxHeight, Number.MAX_SAFE_INTEGER), height + 2 * NumCast(this.Document.borderWidth))); } // prettier-ignore
setContentView = action((view: ViewBoxInterface<FieldViewProps>) => (this._componentView = view));
@@ -678,7 +675,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
return this._props.styleProvider?.(doc, props, property);
};
- @observable _aiWinHeight = 88;
+ @observable _aiWinHeight = 32;
TagsBtnHeight = 22;
@computed get currentScale() {
@@ -703,33 +700,21 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
@computed get uiBtnScaling() { return Math.max(this.maxWidgetSize / this.TagsBtnHeight, 1) * Math.min(1, this.viewScaling); } // prettier-ignore
aiContentsWidth = () => (this.aiContentsHeight() * (this._props.NativeWidth?.() || 1)) / (this._props.NativeHeight?.() || 1);
- aiContentsHeight = () => Math.max(10, this._props.PanelHeight() - this._aiWinHeight * this.uiBtnScaling);
+ aiContentsHeight = () => Math.max(10, this._props.PanelHeight() - (this._aiWinHeight + (this.tagsOverlayFunc() ? 22 : 0)) * this.uiBtnScaling);
@computed get aiEditor() {
return (
- <>
- <div
- className="documentView-editorView-history"
- ref={r => this.historyRef(this._oldAiWheel, (this._oldAiWheel = r))}
- style={{
- transform: `scale(${this.uiBtnScaling})`,
- height: this.aiContentsHeight() / this.uiBtnScaling,
- width: ((this._props.PanelWidth() - this.aiContentsWidth()) * 0.95) / this.uiBtnScaling,
- }}>
- {this._componentView?.componentAIViewHistory?.() ?? null}
- </div>
- <div
- className="documentView-editorView"
- style={{
- background: SnappingManager.userVariantColor,
- width: `${100 / this.uiBtnScaling}%`, //
- transform: `scale(${this.uiBtnScaling})`,
- }}
- ref={r => this.historyRef(this._oldHistoryWheel, (this._oldHistoryWheel = r))}>
- <div className="documentView-editorView-resizer" />
- {this._componentView?.componentAIView?.() ?? null}
- {this._props.DocumentView?.() ? <TagsView background={this.backgroundBoxColor} Views={[this._props.DocumentView?.()]} /> : null}
- </div>
- </>
+ <div
+ className="documentView-editorView"
+ style={{
+ background: SnappingManager.userVariantColor,
+ width: `${100 / this.uiBtnScaling}%`, //
+ transform: `scale(${this.uiBtnScaling})`,
+ }}
+ ref={r => this.historyRef(this._oldHistoryWheel, (this._oldHistoryWheel = r))}>
+ <div className="documentView-editorView-resizer" />
+ {this._componentView?.componentAIView?.() ?? null}
+ {this._props.DocumentView?.() ? <TagsView background={this.backgroundBoxColor} Views={[this._props.DocumentView?.()]} /> : null}
+ </div>
);
}
@computed get tagsOverlay() {
@@ -755,11 +740,11 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
}
widgetOverlayFunc = () => (this.widgetDecorations ? this.widgetOverlay : null);
viewingAiEditor = () => (this._props.showAIEditor && this._componentView?.componentAIView?.() !== undefined ? this.aiEditor : null);
- _contentsRef = React.createRef<DocumentContentsView>();
+ @observable _contentsRef: DocumentContentsView | undefined = undefined;
@computed get viewBoxContents() {
TraceMobx();
const isInk = this.layoutDoc._layout_isSvg && !this._props.LayoutTemplateString;
- const noBackground = this.Document.isGroup && !this._componentView?.isUnstyledView?.() && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent');
+ const noBackground = Doc.IsFreeformGroup(this.Document) && !this._componentView?.isUnstyledView?.() && (!this.layoutDoc.backgroundColor || this.layoutDoc.backgroundColor === 'transparent');
return (
<>
<div
@@ -771,7 +756,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
}}>
<DocumentContentsView
{...this._props}
- ref={this._contentsRef}
+ ref={action((r: DocumentContentsView) => (this._contentsRef = r))}
layoutFieldKey={StrCast(this.Document.layout_fieldKey, 'layout')}
pointerEvents={this.contentPointerEvents}
setContentViewBox={this.setContentView}
@@ -780,7 +765,6 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
PanelHeight={this.viewingAiEditor() ? this.aiContentsHeight : this.panelHeight}
setHeight={this.setHeight}
isContentActive={this.isContentActive}
- ScreenToLocalTransform={this.screenToLocalContent}
rootSelected={this.rootSelected}
onClickScript={this.onClickFunc}
setTitleFocus={this.setTitleFocus}
@@ -913,8 +897,8 @@ export class DocumentViewInternal extends DocComponent<DocumentViewProps & Field
}}>
<FormattedTextBox
{...this._props}
- yPadding={10}
- xPadding={10}
+ yMargin={10}
+ xMargin={10}
fieldKey={this.showCaption}
styleProvider={this.captionStyleProvider}
dontRegisterView
@@ -1184,7 +1168,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
* @returns boolean whether sub-component Doc is in synch with the layoutDoc that this view thinks its rendering
*/
IsInvalid = (renderDoc?: Doc): boolean => {
- const docContents = this._docViewInternal?._contentsRef.current;
+ const docContents = this._docViewInternal?._contentsRef;
return !(
(!renderDoc ||
(docContents?.layoutDoc === renderDoc && //
@@ -1291,7 +1275,7 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
if (this.ComponentView?.screenBounds?.()) {
return this.ComponentView.screenBounds();
}
- const xf = this.screenToContentsTransform().scale(this.nativeScaling).inverse();
+ const xf = this.screenToContentBoundsTransform().inverse();
const [[left, top], [right, bottom]] = [xf.transformPoint(0, 0), xf.transformPoint(this.panelWidth, this.panelHeight)];
// transition is returned so that the bounds will 'update' at the end of an animated transition. This is needed by xAnchor in LinkBox
@@ -1405,28 +1389,36 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
custom && DocUtils.makeCustomViewClicked(this.Document, Docs.Create.StackingDocument, layout, undefined);
}, 'set custom view');
- public static setDefaultTemplate(checkResult?: boolean) {
- if (checkResult) {
- return Doc.UserDoc().defaultTextLayout;
+ private static getTemplate(view: DocumentView | undefined) {
+ if (view) {
+ if (!view.layoutDoc.isTemplateDoc) {
+ MakeTemplate(view.Document);
+ Doc.AddDocToList(Doc.UserDoc(), 'template_user', view.Document);
+ Doc.AddDocToList(DocListCast(Doc.MyTools?.data)[1], 'data', makeUserTemplateButtonOrImage(view.Document));
+ DocCast(Doc.UserDoc().template_user) && view.Document && Doc.AddDocToList(DocCast(Doc.UserDoc().template_user)!, 'data', view.Document);
+ return view.Document;
+ }
+ return DocCast(Doc.LayoutField(view.Document)) ?? view.Document;
}
+ }
+ public static setDefaultTemplate(checkResult?: boolean) {
+ if (checkResult) return Doc.UserDoc().defaultTextLayout;
const view = DocumentView.Selected()[0]?._props.renderDepth > 0 ? DocumentView.Selected()[0] : undefined;
undoable(() => {
- let tempDoc: Opt<Doc>;
- if (view) {
- if (!view.layoutDoc.isTemplateDoc) {
- tempDoc = view.Document;
- MakeTemplate(tempDoc);
- Doc.AddDocToList(Doc.UserDoc(), 'template_user', tempDoc);
- Doc.AddDocToList(DocListCast(Doc.MyTools?.data)[1], 'data', makeUserTemplateButtonOrImage(tempDoc));
- DocCast(Doc.UserDoc().template_user) && tempDoc && Doc.AddDocToList(DocCast(Doc.UserDoc().template_user)!, 'data', tempDoc);
- } else {
- tempDoc = DocCast(Doc.LayoutField(view.Document));
- }
- }
+ const tempDoc = DocumentView.getTemplate(view);
Doc.UserDoc().defaultTextLayout = tempDoc ? new PrefetchProxy(tempDoc) : undefined;
}, 'set default template')();
return undefined;
}
+ public static setDefaultImageTemplate(checkResult?: boolean) {
+ if (checkResult) return Doc.UserDoc().defaultImageLayout;
+ const view = DocumentView.Selected()[0]?._props.renderDepth > 0 || DocumentView.Selected()[0]?.Document.isTemplateDoc ? DocumentView.Selected()[0] : undefined;
+ undoable(() => {
+ const tempDoc = DocumentView.getTemplate(view);
+ Doc.UserDoc().defaultImageLayout = tempDoc ? new PrefetchProxy(tempDoc) : undefined;
+ }, 'set default image template')();
+ return undefined;
+ }
/**
* This switches between the current view of a Doc and a specified alternate layout view.
@@ -1498,17 +1490,22 @@ export class DocumentView extends DocComponent<DocumentViewProps>() {
isHovering = () => this._isHovering;
selfView = () => this;
/**
- * @returns Transform to the document view (in the coordinate system of whatever contains the DocumentView)
+ * @returns Transform to the document view's available panel space (in the coordinate system of whatever contains the DocumentView)
*/
screenToViewTransform = () => this._props.ScreenToLocalTransform();
/**
+ * @returns Transform to the document view after centering in available panel space(in the coordinate system of whatever contains the DocumentView)
+ */
+ private screenToContentBoundsTransform = () => this.screenToViewTransform().translate(-this.centeringX, -this.centeringY);
+ /**
* @returns Transform to the coordinate system of the contents of the document view (includes native dimension scaling and centering)
*/
screenToContentsTransform = () =>
this._props
.ScreenToLocalTransform()
.translate(-this.centeringX, -this.centeringY)
- .scale(1 / this.nativeScaling);
+ .translate(-(this._docViewInternal?.aiShift() ?? 0), 0)
+ .scale((this._docViewInternal?.aiScale() ?? 1) / this.nativeScaling);
htmlOverlay = () => {
const effect = StrCast(this._htmlOverlayEffect?.presentation_effect, StrCast(this._htmlOverlayEffect?.followLinkAnimEffect));
diff --git a/src/client/views/nodes/EquationBox.tsx b/src/client/views/nodes/EquationBox.tsx
index 3cacb6692..8e48a0b54 100644
--- a/src/client/views/nodes/EquationBox.tsx
+++ b/src/client/views/nodes/EquationBox.tsx
@@ -6,7 +6,7 @@ import { TraceMobx } from '../../../fields/util';
import { DocUtils } from '../../documents/DocUtils';
import { DocumentType } from '../../documents/DocumentTypes';
import { Docs } from '../../documents/Documents';
-import { undoBatch } from '../../util/UndoManager';
+import { undoBatch, UndoManager } from '../../util/UndoManager';
import { ViewBoxBaseComponent } from '../DocComponent';
import { StyleProp } from '../StyleProp';
import { DocumentView } from './DocumentView';
@@ -21,6 +21,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
return FieldView.LayoutString(EquationBox, fieldKey);
}
_ref: React.RefObject<EquationEditor> = React.createRef();
+ _liveTextUndo: UndoManager.Batch | undefined; // captured undo batch when typing a new text note into a collection
constructor(props: FieldViewProps) {
super(props);
@@ -30,6 +31,8 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
componentDidMount() {
this._props.setContentViewBox?.(this);
if (DocumentView.SelectOnLoad === this.Document && (!DocumentView.LightboxDoc() || DocumentView.LightboxContains(this.DocumentView?.()))) {
+ this._liveTextUndo = FormattedTextBox.LiveTextUndo;
+ FormattedTextBox.LiveTextUndo = undefined;
this._props.select(false);
this._ref.current?.mathField.focus();
@@ -113,9 +116,7 @@ export class EquationBox extends ViewBoxBaseComponent<FieldViewProps>() {
className="equationBox-cont"
onKeyDown={e => e.stopPropagation()}
onPointerDown={e => !e.ctrlKey && e.stopPropagation()}
- onBlur={() => {
- FormattedTextBox.LiveTextUndo?.end();
- }}
+ onBlur={() => this._liveTextUndo?.end()}
style={{
transform: `scale(${scale})`,
minWidth: `${100 / scale}%`,
diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx
index a6872f8dc..f6b405a43 100644
--- a/src/client/views/nodes/FieldView.tsx
+++ b/src/client/views/nodes/FieldView.tsx
@@ -52,8 +52,8 @@ export interface FieldViewSharedProps {
renderDepth: number;
scriptContext?: unknown; // can be assigned anything and will be passed as 'scriptContext' to any OnClick script that executes on this document
screenXPadding?: (view: DocumentView | undefined) => number; // padding in screen space coordinates (used by text box to reflow around UI buttons in carouselView)
- xPadding?: number;
- yPadding?: number;
+ xMargin?: number;
+ yMargin?: number;
dontRegisterView?: boolean;
dropAction?: dropActionType;
dragAction?: dropActionType;
diff --git a/src/client/views/nodes/IconTagBox.tsx b/src/client/views/nodes/IconTagBox.tsx
index 0bbd6a0d3..d04ec3a10 100644
--- a/src/client/views/nodes/IconTagBox.tsx
+++ b/src/client/views/nodes/IconTagBox.tsx
@@ -58,16 +58,20 @@ export class IconTagBox extends ObservableReactComponent<IconTagProps> {
const tag = StrCast(key.toolType);
const color = dv._props.styleProvider?.(dv.layoutDoc, dv.ComponentView?._props, StyleProp.FontColor) as string;
return (
- <Toggle
- tooltip={`Click to add/remove the tag ${tag}`}
- toggleStatus={TagItem.docHasTag(dv.Document, tag)}
- toggleType={ToggleType.BUTTON}
- icon={<FontAwesomeIcon className={`fontIconBox-icon-${ToggleType.BUTTON}`} icon={icon} color={color} />}
- size={Size.XSMALL}
- type={Type.PRIM}
- onClick={() => this.setIconTag(tag, !TagItem.docHasTag(this.View.Document, tag))}
- color={color}
- />
+ <div>
+ {' '}
+ {/* tooltips require the wrapped item to be an element ref */}
+ <Toggle
+ tooltip={`Click to add/remove the tag ${tag}`}
+ toggleStatus={TagItem.docHasTag(dv.Document, tag)}
+ toggleType={ToggleType.BUTTON}
+ icon={<FontAwesomeIcon className={`fontIconBox-icon-${ToggleType.BUTTON}`} icon={icon} color={color} />}
+ size={Size.XSMALL}
+ type={Type.PRIM}
+ onClick={() => this.setIconTag(tag, !TagItem.docHasTag(this.View.Document, tag))}
+ color={color}
+ />
+ </div>
);
};
diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss
index 9f7a5d03f..5a6292fab 100644
--- a/src/client/views/nodes/ImageBox.scss
+++ b/src/client/views/nodes/ImageBox.scss
@@ -236,7 +236,7 @@
.imageBox-aiView-input {
overflow: hidden;
text-overflow: ellipsis;
- max-width: 65%;
+ max-width: 80%;
width: 100%;
color: black;
}
diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx
index d16baada6..30fc44f62 100644
--- a/src/client/views/nodes/ImageBox.tsx
+++ b/src/client/views/nodes/ImageBox.tsx
@@ -1,6 +1,6 @@
-import { Button, Colors, EditableText, IconButton, Size, Toggle, ToggleType, Type } from '@dash/components';
+import { Button, Colors, EditableText, IconButton, NumberDropdown, Size, Toggle, ToggleType, Type } from '@dash/components';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Slider, Tooltip } from '@mui/material';
+import { Tooltip } from '@mui/material';
import axios from 'axios';
import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
@@ -37,7 +37,6 @@ import { OverlayView } from '../OverlayView';
import { AnchorMenu } from '../pdf/AnchorMenu';
import { PinDocView, PinProps } from '../PinFuncs';
import { DrawingFillHandler } from '../smartdraw/DrawingFillHandler';
-import { FireflyImageData, isFireflyImageData } from '../smartdraw/FireflyConstants';
import { SmartDrawHandler } from '../smartdraw/SmartDrawHandler';
import { StickerPalette } from '../smartdraw/StickerPalette';
import { StyleProp } from '../StyleProp';
@@ -102,7 +101,6 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@observable private _regenInput = '';
@observable private _canInteract = true;
@observable private _regenerateLoading = false;
- @observable private _prevImgs: FireflyImageData[] = StrCast(this.Document.ai_firefly_history) ? JSON.parse(StrCast(this.Document.ai_firefly_history)) : [];
// Add these observable properties to the ImageBox class
@observable private _outpaintingInProgress = false;
@@ -114,6 +112,28 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._props.setContentViewBox?.(this);
}
+ @computed get oupaintOriginalSize(): { width: number; height: number } {
+ return {
+ width: NumCast(this.Document[this.fieldKey + '_outpaintOriginalWidth']),
+ height: NumCast(this.Document[this.fieldKey + '_outpaintOriginalHeight']),
+ };
+ }
+ set outpaintOriginalSize(prop: { width: number; height: number } | undefined) {
+ this.Document[this.fieldKey + '_outpaintOriginalWidth'] = prop?.width;
+ this.Document[this.fieldKey + '_outpaintOriginalHeight'] = prop?.height;
+ }
+
+ @computed get imgNativeSize() {
+ return {
+ nativeWidth: NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth'], 500)),
+ nativeHeight: NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight'], 500)),
+ };
+ }
+ set imgNativeSize(prop: { nativeWidth: number; nativeHeight: number }) {
+ this.dataDoc[this.fieldKey + '_nativeWidth'] = prop.nativeWidth;
+ this.dataDoc[this.fieldKey + '_nativeHeight'] = prop.nativeHeight;
+ }
+
protected createDropTarget = (ele: HTMLDivElement) => {
this._mainCont = ele;
this._dropDisposer?.();
@@ -154,7 +174,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this._disposers.path = reaction(
() => ({ nativeSize: this.nativeSize, width: NumCast(this.layoutDoc._width), height: this.layoutDoc._height }),
({ nativeSize, width, height }) => {
- if (!this.layoutDoc._layout_nativeDimEditable || !height) {
+ if (!this.layoutDoc._layout_nativeDimEditable || !height || this.layoutDoc.layout_resetNativeDim) {
+ this.layoutDoc.layout_resetNativeDim = undefined; // reset dimensions of templates rendered with content or if image changes. afterwards, remove this flag.
this.layoutDoc._height = (width * nativeSize.nativeHeight) / nativeSize.nativeWidth;
}
},
@@ -171,7 +192,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
{ fireImmediately: true }
);
this._disposers.outpaint = reaction(
- () => this.Document[this.fieldKey + '_outpaintOriginalWidth'] !== undefined && !SnappingManager.ShiftKey,
+ () => this.outpaintOriginalSize?.width && !SnappingManager.ShiftKey,
complete => complete && this.openOutpaintPrompt(),
{ fireImmediately: true }
);
@@ -227,19 +248,20 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const drag = de.complete.docDragData.draggedDocuments.lastElement();
const dragField = drag[Doc.LayoutDataKey(drag)];
const descText = RTFCast(dragField)?.Text || StrCast(dragField) || RTFCast(drag.text)?.Text || StrCast(drag.text) || StrCast(this.Document.title);
- const oldPrompt = StrCast(this.Document.ai_firefly_prompt, StrCast(this.Document.title));
+ const oldPrompt = StrCast(this.Document.ai_prompt, StrCast(this.Document.title));
const newPrompt = (text: string) => (oldPrompt ? `${oldPrompt} ~~~ ${text}` : text);
DrawingFillHandler.drawingToImage(this.Document, 90, newPrompt(descText), drag)?.then(action(() => (this._regenerateLoading = false)));
added = false;
} else if (de.altKey || !this.dataDoc[this.fieldKey]) {
- const layoutDoc = de.complete.docDragData?.draggedDocuments[0];
- const targetField = Doc.LayoutDataKey(layoutDoc);
- const targetDoc = layoutDoc[DocData];
- if (targetDoc[targetField] instanceof ImageField) {
+ const dropDoc = de.complete.docDragData?.draggedDocuments[0];
+ const dropDocFieldKey = Doc.LayoutDataKey(dropDoc);
+ const dropDataDoc = dropDoc[DocData];
+ if (dropDataDoc[dropDocFieldKey] instanceof ImageField) {
added = true;
- this.dataDoc[this.fieldKey] = ObjectField.MakeCopy(targetDoc[targetField] as ImageField);
- Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(targetDoc), this.fieldKey);
- Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(targetDoc), this.fieldKey);
+ this.dataDoc.layout_resetNativeDim = true;
+ this.dataDoc[this.fieldKey] = ObjectField.MakeCopy(dropDataDoc[dropDocFieldKey] as ImageField);
+ Doc.SetNativeWidth(this.dataDoc, Doc.NativeWidth(dropDataDoc), this.fieldKey);
+ Doc.SetNativeHeight(this.dataDoc, Doc.NativeHeight(dropDataDoc), this.fieldKey);
}
}
added === false && e.preventDefault();
@@ -258,18 +280,17 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@undoBatch
setNativeSize = action(() => {
- const oldnativeWidth = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']);
+ const oldnativeWidth = this.imgNativeSize.nativeWidth;
const nscale = NumCast(this._props.PanelWidth()) * NumCast(this.layoutDoc._freeform_scale, 1);
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.imgNativeSize = { nativeWidth: this.imgNativeSize.nativeWidth * nw, nativeHeight: this.imgNativeSize.nativeHeight * nw };
this.dataDoc.freeform_panX = nw * NumCast(this.dataDoc.freeform_panX);
this.dataDoc.freeform_panY = nw * NumCast(this.dataDoc.freeform_panY);
this.dataDoc.freeform_panX_max = this.dataDoc.freeform_panX_max ? nw * NumCast(this.dataDoc.freeform_panX_max) : undefined;
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']);
+ const newnativeWidth = this.imgNativeSize.nativeWidth;
DocListCast(this.dataDoc[this.annotationKey]).forEach(doc => {
doc.x = (NumCast(doc.x) / oldnativeWidth) * newnativeWidth;
doc.y = (NumCast(doc.y) / oldnativeWidth) * newnativeWidth;
@@ -281,13 +302,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
});
@undoBatch
rotate = action(() => {
- const nw = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']);
- const nh = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight']);
+ const nativeSize = this.imgNativeSize;
const w = this.layoutDoc._width;
const h = this.layoutDoc._height;
this.dataDoc[this.fieldKey + '_rotation'] = (NumCast(this.dataDoc[this.fieldKey + '_rotation']) + 90) % 360;
- this.dataDoc[this.fieldKey + '_nativeWidth'] = nh;
- this.dataDoc[this.fieldKey + '_nativeHeight'] = nw;
+ this.imgNativeSize = { nativeWidth: nativeSize.nativeHeight, nativeHeight: nativeSize.nativeWidth }; // swap width and height
this.layoutDoc._width = h;
this.layoutDoc._height = w;
});
@@ -303,7 +322,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const anchy = NumCast(cropping.y);
const anchw = NumCast(cropping._width);
const anchh = NumCast(cropping._height);
- const viewScale = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth']) / anchw;
+ const viewScale = this.nativeSize.nativeWidth / anchw;
cropping.x = NumCast(this.Document.x) + NumCast(this.layoutDoc._width);
cropping.y = NumCast(this.Document.y);
cropping.onClick = undefined;
@@ -365,11 +384,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@action
cancelOutpaintPrompt = () => {
- const origWidth = NumCast(this.Document[this.fieldKey + '_outpaintOriginalWidth']);
- const origHeight = NumCast(this.Document[this.fieldKey + '_outpaintOriginalHeight']);
- this.Document._width = origWidth;
- this.Document._height = origHeight;
+ [this.Document._width, this.Document._height] = [this.oupaintOriginalSize.width, this.oupaintOriginalSize.height];
this._outpaintingInProgress = false;
+ this.outpaintOriginalSize = undefined;
this.closeOutpaintPrompt();
};
@@ -417,8 +434,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
loadingOverlay.innerHTML = '<div style="color: white; font-size: 16px;">Generating outpainted image...</div>';
this._mainCont?.appendChild(loadingOverlay);
- const origWidth = NumCast(this.Document[this.fieldKey + '_outpaintOriginalWidth']);
- const origHeight = NumCast(this.Document[this.fieldKey + '_outpaintOriginalHeight']);
+ const { width: origWidth, height: origHeight } = this.oupaintOriginalSize;
const response = await Networking.PostToServer('/outpaintImage', {
imageUrl: currentPath,
prompt: customPrompt,
@@ -455,8 +471,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
this.Document.$ai = true;
this.Document.$ai_outpainted = true;
this.Document.$ai_outpaint_prompt = customPrompt;
- this.Document[this.fieldKey + '_outpaintOriginalWidth'] = undefined;
- this.Document[this.fieldKey + '_outpaintOriginalHeight'] = undefined;
+ this.outpaintOriginalSize = undefined;
} else {
this.cancelOutpaintPrompt();
alert('Failed to receive a valid image URL from server.');
@@ -669,8 +684,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@computed get nativeSize() {
TraceMobx();
if (this.paths.length && this.paths[0].includes(DefaultPath)) return { nativeWidth: NumCast(this.layoutDoc._width), nativeHeight: NumCast(this.layoutDoc._height), nativeOrientation: 0 };
- const nativeWidth = NumCast(this.dataDoc[this.fieldKey + '_nativeWidth'], NumCast(this.layoutDoc[this.fieldKey + '_nativeWidth'], 500));
- const nativeHeight = NumCast(this.dataDoc[this.fieldKey + '_nativeHeight'], NumCast(this.layoutDoc[this.fieldKey + '_nativeHeight'], 500));
+ const { nativeWidth, nativeHeight } = this.imgNativeSize;
const nativeOrientation = NumCast(this.dataDoc[this.fieldKey + '_nativeOrientation'], 1);
return { nativeWidth, nativeHeight, nativeOrientation };
}
@@ -690,7 +704,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@computed get overlayImageIcon() {
const usePath = this.layoutDoc[`_${this.fieldKey}_usePath`];
- return (
+ return this._regenerateLoading ? null : (
<Tooltip
title={
<div className="dash-tooltip">
@@ -732,14 +746,14 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
);
}
@computed get regenerateImageIcon() {
- return (
+ return this._regenerateLoading ? null : (
<Tooltip title={'click to show AI generations. Drop an image on to create a new generation'}>
<div
className="imageBox-regenerateDropTarget"
ref={this._regenerateIconRef}
- onClick={() => DocCast(this.Document.ai_firefly_generatedDocs) && DocumentView.showDocument(DocCast(this.Document.ai_firefly_generatedDocs)!, { openLocation: OpenWhere.addRight })}
+ onClick={() => DocCast(this.Document.ai_generatedDocs) && DocumentView.showDocument(DocCast(this.Document.ai_generatedDocs)!, { openLocation: OpenWhere.addRight })}
style={{
- display: (this._props.isContentActive() && (SnappingManager.CanEmbed || this.Document.ai_firefly_generatedDocs)) || this._regenerateLoading ? 'block' : 'none',
+ display: (this._props.isContentActive() && (SnappingManager.CanEmbed || this.Document.ai_generatedDocs)) || this._regenerateLoading ? 'block' : 'none',
transform: `scale(${this.uiBtnScaling})`,
width: this._sideBtnWidth,
height: this._sideBtnWidth,
@@ -821,7 +835,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
transform,
transformOrigin,
width: this._outpaintAlign ? 'max-content' : this._outpaintAlign ? '100%' : undefined,
- height: this._outpaintVAlign ? 'max-content' : this.Document[this.fieldKey + '_outpaintOriginalWidth'] !== undefined ? '100%' : undefined,
+ height: this._outpaintVAlign ? 'max-content' : this.outpaintOriginalSize?.width ? '100%' : undefined,
}}
onError={action(e => (this._error = e.toString()))}
draggable={false}
@@ -845,34 +859,10 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@observable _filterFunc: ((doc: Doc) => boolean) | undefined = undefined;
@observable private _fireflyRefStrength = 0;
- componentAIViewHistory = () => (
- <div className="imageBox-aiView-history">
- <Button text="Clear History" type={Type.SEC} size={Size.XSMALL} />
- {this._prevImgs.map(img => (
- <div key={img.pathname}>
- <img
- className="imageBox-aiView-img"
- src={ClientUtils.prepend(img.pathname.replace(extname(img.pathname), '_s' + extname(img.pathname)))}
- onClick={() => {
- this.dataDoc[this.fieldKey] = new ImageField(img.pathname);
- this.dataDoc.ai_firefly_prompt = img.prompt;
- this.dataDoc.ai_firefly_seed = img.seed;
- }}
- />
- <span>{img.prompt}</span>
- </div>
- ))}
- </div>
- );
-
componentAIView = () => {
- const field = this.dataDoc[this.fieldKey] instanceof ImageField ? Cast(this.dataDoc[this.fieldKey], ImageField, null) : new ImageField(String(this.dataDoc[this.fieldKey]));
return (
<div className="imageBox-aiView">
<div className="imageBox-aiView-regenerate">
- <span className="imageBox-aiView-firefly" style={{ color: SnappingManager.userColor }}>
- Firefly:
- </span>
<input
style={{ color: SnappingManager.userColor, background: SnappingManager.userBackgroundColor }}
className="imageBox-aiView-input"
@@ -886,57 +876,39 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
<Button
text="Create"
type={Type.TERT}
+ size={Size.XSMALL}
color={SnappingManager.userColor}
background={SnappingManager.userBackgroundColor}
// style={{ alignSelf: 'flex-end' }}
icon={this._regenerateLoading ? <ReactLoading type="spin" color={SettingsManager.userVariantColor} width={16} height={20} /> : <AiOutlineSend />}
iconPlacement="right"
- onClick={action(async () => {
+ onClick={action(() => {
this._regenerateLoading = true;
- if (this._fireflyRefStrength) {
- DrawingFillHandler.drawingToImage(this.Document, this._fireflyRefStrength, this._regenInput || StrCast(this.Document.title))?.then(action(() => (this._regenerateLoading = false)));
- } else {
- SmartDrawHandler.Instance.regenerate([this.Document], undefined, undefined, this._regenInput || StrCast(this.Document.title), true).then(
- action(newImgs => {
- const firstImg = newImgs[0];
- if (isFireflyImageData(firstImg)) {
- const url = firstImg.pathname;
- const imgField = new ImageField(url);
- this._prevImgs.length === 0 &&
- this._prevImgs.push({ prompt: StrCast(this.dataDoc.ai_firefly_prompt), seed: this.dataDoc.ai_firefly_seed as number, href: this.paths.lastElement(), pathname: field?.url.pathname ?? '' });
- this._prevImgs.unshift({ prompt: firstImg.prompt, seed: firstImg.seed, pathname: url });
- this.dataDoc.ai_firefly_history = JSON.stringify(this._prevImgs);
- this.dataDoc.ai_firefly_prompt = firstImg.prompt;
- this.dataDoc[this.fieldKey] = imgField;
- this._regenerateLoading = false;
- this._regenInput = '';
- }
- })
- );
- }
+ DrawingFillHandler.drawingToImage(this.Document, this._fireflyRefStrength, this._regenInput || StrCast(this.Document.title))?.then(action(() => (this._regenerateLoading = false)));
})}
/>
</div>
- </div>
- <div className="imageBox-aiView-strength">
- <span className="imageBox-aiView-similarity" style={{ color: SnappingManager.userColor }}>
- Similarity
- </span>
- <Slider
- className="imageBox-aiView-slider"
- sx={{
- '& .MuiSlider-track': { color: SettingsManager.userColor },
- '& .MuiSlider-rail': { color: SettingsManager.userBackgroundColor },
- '& .MuiSlider-thumb': { color: SettingsManager.userColor, '&.Mui-focusVisible, &:hover, &.Mui-active': { boxShadow: `0px 0px 0px 8px${SettingsManager.userColor.slice(0, 7)}10` } },
- }}
- min={0}
- max={100}
- step={1}
- size="small"
- value={this._fireflyRefStrength}
- onChange={action((e, val) => this._canInteract && (this._fireflyRefStrength = val as number))}
- valueLabelDisplay="auto"
- />
+ <div>
+ <NumberDropdown
+ color={SnappingManager.userColor}
+ background={SnappingManager.userBackgroundColor}
+ numberDropdownType="slider"
+ showPlusMinus={false}
+ formLabel="similarity"
+ tooltip="structure similarity of created images to current image"
+ type={Type.PRIM}
+ width={75}
+ min={0}
+ max={100}
+ number={this._fireflyRefStrength}
+ size={Size.XXSMALL}
+ setNumber={undoable(
+ action(val => this._canInteract && (this._fireflyRefStrength = val as number)),
+ `${this.Document.title} button set from list`
+ )}
+ fillWidth
+ />
+ </div>
</div>
</div>
);
@@ -986,6 +958,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return { width, height };
};
savedAnnotations = () => this._savedAnnotations;
+ rejectDrop = (de: DragManager.DropEvent, subView?: DocumentView | undefined) => (this.dataDoc[this.fieldKey] === undefined ? true : (this._props.rejectDrop?.(de, subView) ?? false));
render() {
TraceMobx();
const borderRad = this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BorderRounding) as string;
@@ -1029,7 +1002,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
ScreenToLocalTransform={this.screenToLocalTransform}
select={emptyFunction}
focus={this.focus}
- rejectDrop={this._props.rejectDrop}
+ rejectDrop={this.rejectDrop}
getScrollHeight={this.getScrollHeight}
NativeDimScaling={returnOne}
isAnyChildContentActive={returnFalse}
@@ -1091,8 +1064,11 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
if (result instanceof Error) {
alert('Error uploading files - possibly due to unsupported file types');
} else {
- this.dataDoc[this.fieldKey] = new ImageField(result.accessPaths.agnostic.client);
- !(result instanceof Error) && DocUtils.assignImageInfo(result, this.dataDoc);
+ runInAction(() => {
+ this.dataDoc.layout_resetNativeDim = true;
+ !(result instanceof Error) && DocUtils.assignUploadInfo(result, this.dataDoc, this.fieldKey);
+ this.dataDoc[this.fieldKey] = new ImageField(result.accessPaths.agnostic.client);
+ });
}
disposer();
} else {
diff --git a/src/client/views/nodes/KeyValueBox.tsx b/src/client/views/nodes/KeyValueBox.tsx
index aa66b5ba9..606f63d6d 100644
--- a/src/client/views/nodes/KeyValueBox.tsx
+++ b/src/client/views/nodes/KeyValueBox.tsx
@@ -273,15 +273,15 @@ export class KeyValueBox extends ViewBoxBaseComponent<FieldViewProps>() {
};
createFieldView = (templateDoc: Doc, row: KeyValuePair) => {
- const metaKey = row._props.keyName;
- const fieldTempDoc = Doc.IsDelegateField(templateDoc, metaKey) ? Doc.MakeDelegate(templateDoc) : Doc.MakeEmbedding(templateDoc);
- fieldTempDoc.title = metaKey;
+ const keyName = row._props.keyName;
+ const fieldTempDoc = Doc.IsDelegateField(templateDoc, keyName) ? Doc.MakeDelegate(templateDoc) : Doc.MakeEmbedding(templateDoc);
+ fieldTempDoc.title = keyName;
fieldTempDoc.layout_fitWidth = true;
fieldTempDoc._xMargin = 10;
fieldTempDoc._yMargin = 10;
fieldTempDoc._width = 100;
fieldTempDoc._height = 40;
- fieldTempDoc.layout = this.inferType(templateDoc[metaKey], metaKey);
+ fieldTempDoc.layout = this.inferType(templateDoc[keyName], keyName);
return fieldTempDoc;
};
diff --git a/src/client/views/nodes/LabelBox.tsx b/src/client/views/nodes/LabelBox.tsx
index b08ed84b7..e1ecc2018 100644
--- a/src/client/views/nodes/LabelBox.tsx
+++ b/src/client/views/nodes/LabelBox.tsx
@@ -9,7 +9,7 @@ import { TraceMobx } from '../../../fields/util';
import { DocumentType } from '../../documents/DocumentTypes';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
-import { undoable } from '../../util/UndoManager';
+import { undoable, UndoManager } from '../../util/UndoManager';
import { ViewBoxBaseComponent } from '../DocComponent';
import { PinDocView, PinProps } from '../PinFuncs';
import { StyleProp } from '../StyleProp';
@@ -27,7 +27,8 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
private dropDisposer?: DragManager.DragDropDisposer;
private _timeout: NodeJS.Timeout | undefined;
private _divRef: HTMLDivElement | null = null;
- private _reaction: IReactionDisposer | undefined;
+ private _disposers: { [key: string]: IReactionDisposer } = {};
+ private _liveTextUndo: UndoManager.Batch | undefined; // captured undo batch when typing a new text note into a collection
constructor(props: FieldViewProps) {
super(props);
@@ -43,7 +44,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
componentDidMount() {
this._props.setContentViewBox?.(this);
- this._reaction = reaction(
+ this._disposers.active = reaction(
() => this.Title,
() => document.activeElement !== this._divRef && this._forceRerender++
);
@@ -51,7 +52,7 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
componentWillUnMount() {
this._timeout && clearTimeout(this._timeout);
this.setText(this._divRef?.innerText ?? '');
- this._reaction?.();
+ Object.values(this._disposers).forEach(disposer => disposer());
}
@observable _forceRerender = 0;
@@ -171,20 +172,21 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
render() {
TraceMobx();
const boxParams = this.fitTextToBox(undefined); // this causes mobx to trigger re-render when data changes
+ const [xmargin, ymargin] = [NumCast(this.layoutDoc._xMargin), NumCast(this.layoutDoc._uMargin)];
return (
<div className="labelBox-outerDiv" ref={this.createDropTarget} style={{ boxShadow: this.boxShadow }}>
<div
className="labelBox-mainButton"
style={{
backgroundColor: this.backgroundColor,
- color: StrCast(this.layoutDoc._text_fontColor, StrCast(this.layoutDoc._color)),
- fontFamily: StrCast(this.layoutDoc._text_fontFamily, StrCast(Doc.UserDoc().fontFamily)) || 'inherit',
+ color: StrCast(this.layoutDoc[`${this.fieldKey}_fontColor`], StrCast(this.layoutDoc._color)),
+ fontFamily: StrCast(this.layoutDoc[`${this.fieldKey}_fontFamily`], StrCast(Doc.UserDoc().fontFamily)) || 'inherit',
letterSpacing: StrCast(this.layoutDoc.letterSpacing),
- textTransform: StrCast(this.layoutDoc[this.fieldKey + '_transform']) as Property.TextTransform,
- paddingLeft: NumCast(this.layoutDoc._xPadding),
- paddingRight: NumCast(this.layoutDoc._xPadding),
- paddingTop: NumCast(this.layoutDoc._yPadding),
- paddingBottom: NumCast(this.layoutDoc._yPadding),
+ textTransform: StrCast(this.layoutDoc[`${this.fieldKey}_transform`]) as Property.TextTransform,
+ paddingLeft: xmargin,
+ paddingRight: xmargin,
+ paddingTop: ymargin,
+ paddingBottom: ymargin,
width: this._props.PanelWidth(),
height: this._props.PanelHeight(),
whiteSpace: boxParams.multiLine ? 'pre-wrap' : 'pre',
@@ -192,8 +194,8 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
<div
key={this._forceRerender}
style={{
- width: this._props.PanelWidth() - 2 * NumCast(this.layoutDoc._xPadding),
- height: this._props.PanelHeight() - 2 * NumCast(this.layoutDoc._yPadding),
+ width: this._props.PanelWidth() - 2 * xmargin,
+ height: this._props.PanelHeight() - 2 * ymargin,
outline: 'unset !important',
}}
onKeyDown={e => {
@@ -214,12 +216,13 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
this._divRef?.removeEventListener('focusout', this.keepFocus);
this._divRef?.addEventListener('focusout', this.keepFocus);
}}
- onBlur={() => {
+ onBlur={e => {
this._divRef?.removeEventListener('focusout', this.keepFocus);
this.setText(this._divRef?.innerText ?? '');
- RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
- FormattedTextBox.LiveTextUndo?.end();
- FormattedTextBox.LiveTextUndo = undefined;
+ if (!FormattedTextBox.tryKeepingFocus(e.relatedTarget, () => this._divRef?.focus())) {
+ RichTextMenu.Instance?.updateMenu(undefined, undefined, undefined, undefined);
+ this._liveTextUndo?.end();
+ }
}}
dangerouslySetInnerHTML={{
__html: `<span class="textFitted textFitAlignVert" style="display: inline-block; text-align: center; font-size: 100px; height: 0px;">${this.Title?.startsWith('#') ? '' : (this.Title ?? '')}</span>`,
@@ -233,6 +236,8 @@ export class LabelBox extends ViewBoxBaseComponent<FieldViewProps>() {
if (DocumentView.SelectOnLoad === this.Document) {
DocumentView.SetSelectOnLoad(undefined);
+ this._liveTextUndo = FormattedTextBox.LiveTextUndo;
+ FormattedTextBox.LiveTextUndo = undefined;
this._divRef.focus();
}
this.fitTextToBox(this._divRef);
diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx
index 5026f52fb..5b07b303a 100644
--- a/src/client/views/nodes/LinkDocPreview.tsx
+++ b/src/client/views/nodes/LinkDocPreview.tsx
@@ -40,7 +40,6 @@ export class LinkInfo {
LinkInfo._instance = this;
makeObservable(this);
}
- // eslint-disable-next-line no-use-before-define
@observable public LinkInfo: Opt<LinkDocPreviewProps> = undefined;
public static get Instance() {
diff --git a/src/client/views/nodes/MapBox/MapBox.tsx b/src/client/views/nodes/MapBox/MapBox.tsx
index a563b7c1b..a279ccc48 100644
--- a/src/client/views/nodes/MapBox/MapBox.tsx
+++ b/src/client/views/nodes/MapBox/MapBox.tsx
@@ -13,7 +13,7 @@ import { CirclePicker, ColorResult } from 'react-color';
import { Layer, MapProvider, MapRef, Map as MapboxMap, Marker, Source, ViewState, ViewStateChangeEvent } from 'react-map-gl/mapbox';
import { ClientUtils, setupMoveUpEvents } from '../../../../ClientUtils';
import { emptyFunction } from '../../../../Utils';
-import { Doc, DocListCast, Field, LinkedTo, Opt, StrListCast } from '../../../../fields/Doc';
+import { Doc, DocListCast, Field, LinkedTo, StrListCast } from '../../../../fields/Doc';
import { List } from '../../../../fields/List';
import { RichTextField } from '../../../../fields/RichTextField';
import { DocCast, NumCast, StrCast, toList } from '../../../../fields/Types';
@@ -111,12 +111,12 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
// this list contains pushpins and configs
@computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); } // prettier-ignore
- @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.SidebarKey]); } // prettier-ignore
+ @computed get allSidebarDocs() { return DocListCast(this.dataDoc[this.sidebarKey]); } // prettier-ignore
@computed get allPushpins() { return this.allAnnotations.filter(anno => anno.type === DocumentType.PUSHPIN); } // prettier-ignore
@computed get allRoutes() { return this.allAnnotations.filter(anno => anno.type === DocumentType.MAPROUTE); } // prettier-ignore
@computed get SidebarShown() { return !!this.layoutDoc._layout_showSidebar; } // prettier-ignore
@computed get sidebarWidthPercent() { return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%'); } // prettier-ignore
- @computed get SidebarKey() { return this.fieldKey + '_sidebar'; } // prettier-ignore
+ @computed get sidebarKey() { return this.fieldKey + '_sidebar'; } // prettier-ignore
@computed get sidebarColor() {
return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this._props.fieldKey + '_backgroundColor'], '#e4e4e4'));
}
@@ -260,7 +260,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
removeMapDocument = (doc: Doc | Doc[], annotationKey?: string) => {
this.allAnnotations
- .filter(anno => toList(doc).includes(DocCast(anno.mapPin)))
+ .filter(anno => toList(doc).includes(DocCast(anno.mapPin)!))
.forEach(anno => {
anno.mapPin = undefined;
});
@@ -339,27 +339,19 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
startAnchorDrag = (e: PointerEvent, ele: HTMLElement) => {
e.preventDefault();
e.stopPropagation();
-
- const sourceAnchorCreator = action(() => {
- const note = this.getAnchor(true);
- if (note && this._selectedPinOrRoute) {
- note.latitude = this._selectedPinOrRoute.latitude;
- note.longitude = this._selectedPinOrRoute.longitude;
- note.map = this._selectedPinOrRoute.map;
- }
- return note as Doc;
- });
-
const targetCreator = (annotationOn: Doc | undefined) => {
const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
+ target.layout_fitWidth = true;
DocumentView.SetSelectOnLoad(target);
return target;
};
+
+ const sourceAnchorCreator = () => this.getAnchor(true);
const docView = this.DocumentView?.();
docView &&
DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
dragComplete: dragEv => {
- if (!dragEv.aborted && dragEv.annoDragData && dragEv.annoDragData.linkSourceDoc && dragEv.annoDragData.dropDocument && dragEv.linkDocument) {
+ if (!dragEv.aborted && dragEv.annoDragData?.linkSourceDoc && dragEv.annoDragData.dropDocument && dragEv.linkDocument) {
dragEv.annoDragData.linkSourceDoc.followLinkToggle = dragEv.annoDragData.dropDocument.annotationOn === this.Document;
dragEv.annoDragData.linkSourceDoc.followLinkZoom = false;
}
@@ -368,17 +360,7 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
createNoteAnnotation = () => {
- const createFunc = undoable(
- action(() => {
- const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(true), ['latitude', 'longitude', LinkedTo]);
- if (note && this._selectedPinOrRoute) {
- note.latitude = this._selectedPinOrRoute.latitude;
- note.longitude = this._selectedPinOrRoute.longitude;
- note.map = this._selectedPinOrRoute.map;
- }
- }),
- 'create note annotation'
- );
+ const createFunc = undoable(() => this._sidebarRef.current?.anchorMenuClick(this.getAnchor(true), ['latitude', 'longitude', LinkedTo]), 'create note annotation');
if (!this.layoutDoc.layout_showSidebar) {
this.toggleSidebar();
setTimeout(createFunc);
@@ -428,14 +410,11 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
};
- getView = (doc: Doc, options: FocusViewOptions) => {
- if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) {
- this.toggleSidebar();
- options.didMove = true;
+ getView = async (doc: Doc, options: FocusViewOptions) => {
+ if (DocListCast(this.dataDoc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
+ SidebarAnnos.getView(this._sidebarRef.current, this.SidebarShown, this.toggleSidebar, doc, options);
}
- return new Promise<Opt<DocumentView>>(res => {
- DocumentView.addViewRenderedCb(doc, dv => res(dv));
- });
+ return undefined;
};
/*
@@ -476,13 +455,14 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@action
deleteSelectedPinOrRoute = undoable(() => {
- if (this._selectedPinOrRoute) {
+ const selPin = DocCast(this._selectedPinOrRoute);
+ if (selPin) {
// Removes filter
- Doc.setDocFilter(this.Document, 'latitude', NumCast(this._selectedPinOrRoute.latitude), 'remove');
- Doc.setDocFilter(this.Document, 'longitude', NumCast(this._selectedPinOrRoute.longitude), 'remove');
- Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this._selectedPinOrRoute))}`, 'remove');
+ Doc.setDocFilter(this.Document, 'latitude', NumCast(selPin.latitude), 'remove');
+ Doc.setDocFilter(this.Document, 'longitude', NumCast(selPin.longitude), 'remove');
+ Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(selPin)}`, 'remove');
- this.removePushpinOrRoute(this._selectedPinOrRoute);
+ this.removePushpinOrRoute(selPin);
}
MapAnchorMenu.Instance.fadeOut(true);
document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
@@ -1299,7 +1279,6 @@ export class MapBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
<SidebarAnnos
ref={this._sidebarRef}
{...this._props}
- fieldKey={this.fieldKey}
Doc={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index e0efab576..0beefcb67 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -145,7 +145,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
removeMapDocument = (docsIn: Doc | Doc[], annotationKey?: string) => {
const docs = toList(docsIn);
this.allAnnotations
- .filter(anno => docs.includes(DocCast(anno.mapPin)))
+ .filter(anno => docs.includes(DocCast(anno.mapPin)!))
.forEach(anno => {
anno.mapPin = undefined;
});
@@ -224,6 +224,12 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
startAnchorDrag = (e: PointerEvent, ele: HTMLElement) => {
e.preventDefault();
e.stopPropagation();
+ const targetCreator = (annotationOn: Doc | undefined) => {
+ const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
+ target.layout_fitWidth = true;
+ DocumentView.SetSelectOnLoad(target);
+ return target;
+ };
const sourceAnchorCreator = action(() => {
const note = this.getAnchor(true);
@@ -235,11 +241,6 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
return note as Doc;
});
- const targetCreator = (annotationOn: Doc | undefined) => {
- const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
- DocumentView.SetSelectOnLoad(target);
- return target;
- };
const docView = this.DocumentView?.();
docView &&
DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY, {
@@ -362,22 +363,22 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
@action
deselectPin = () => {
- if (this.selectedPin) {
+ const selPin = DocCast(this.selectedPin);
+ if (selPin) {
// Removes filter
- Doc.setDocFilter(this.Document, 'latitude', NumCast(this.selectedPin.latitude), 'remove');
- Doc.setDocFilter(this.Document, 'longitude', NumCast(this.selectedPin.longitude), 'remove');
- Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
+ Doc.setDocFilter(this.Document, 'latitude', NumCast(selPin.latitude), 'remove');
+ Doc.setDocFilter(this.Document, 'longitude', NumCast(selPin.longitude), 'remove');
+ Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(selPin)}`, 'remove');
- const temp = this.selectedPin;
if (!this._unmounting) {
- this._bingMap.current.entities.remove(this.map_docToPinMap.get(temp));
+ this._bingMap.current.entities.remove(this.map_docToPinMap.get(selPin));
}
- const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(temp.latitude, temp.longitude));
- this.MicrosoftMaps.Events.addHandler(newpin, 'click', () => this.pushpinClicked(temp as Doc));
+ const newpin = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(selPin.latitude, selPin.longitude));
+ this.MicrosoftMaps.Events.addHandler(newpin, 'click', () => this.pushpinClicked(selPin));
if (!this._unmounting) {
this._bingMap.current.entities.push(newpin);
}
- this.map_docToPinMap.set(temp, newpin);
+ this.map_docToPinMap.set(selPin, newpin);
this.selectedPin = undefined;
this.bingSearchBarContents = this.Document.map;
}
@@ -388,9 +389,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
this.toggleSidebar();
options.didMove = true;
}
- return new Promise<Opt<DocumentView>>(res => {
- DocumentView.addViewRenderedCb(doc, dv => res(dv));
- });
+ return new Promise<Opt<DocumentView>>(res => DocumentView.addViewRenderedCb(doc, res));
};
/*
* Pushpin onclick
@@ -535,13 +534,14 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
@action
deleteSelectedPin = undoable(() => {
- if (this.selectedPin) {
+ const selPin = this.selectedPin;
+ if (selPin) {
// Removes filter
- Doc.setDocFilter(this.Document, 'latitude', NumCast(this.selectedPin.latitude), 'remove');
- Doc.setDocFilter(this.Document, 'longitude', NumCast(this.selectedPin.longitude), 'remove');
- Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
+ Doc.setDocFilter(this.Document, 'latitude', NumCast(selPin.latitude), 'remove');
+ Doc.setDocFilter(this.Document, 'longitude', NumCast(selPin.longitude), 'remove');
+ Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(selPin)}`, 'remove');
- this.removePushpin(this.selectedPin);
+ this.removePushpin(selPin);
}
MapAnchorMenu.Instance.fadeOut(true);
document.removeEventListener('pointerdown', this.tryHideMapAnchorMenu, true);
@@ -638,7 +638,10 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<FieldViewProps>
this._disposers.highlight = reaction(
() => this.allAnnotations.map(doc => doc[Highlight]),
() => {
- const allConfigPins = this.allAnnotations.map(doc => ({ doc, pushpin: DocCast(doc.mapPin) })).filter(pair => pair.pushpin);
+ const allConfigPins = this.allAnnotations
+ .map(doc => ({ doc, pushpin: DocCast(doc.mapPin) }))
+ .filter(pair => pair.pushpin)
+ .map(pair => ({ doc: pair.doc, pushpin: pair.pushpin! }));
allConfigPins.forEach(({ pushpin }) => {
if (!pushpin[Highlight] && this.map_pinHighlighted.get(pushpin)) {
this.recolorPin(pushpin);
diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss
index eaea272dc..f09a2630a 100644
--- a/src/client/views/nodes/PDFBox.scss
+++ b/src/client/views/nodes/PDFBox.scss
@@ -198,7 +198,7 @@
pointer-events: all;
.pdfBox-searchBar {
- width: 70%;
+ width: calc(100% - 120px); // less size of search buttons
font-size: 14px;
}
}
diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx
index 83c44c80f..45fa5cc12 100644
--- a/src/client/views/nodes/PDFBox.tsx
+++ b/src/client/views/nodes/PDFBox.tsx
@@ -10,7 +10,7 @@ import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { InkTool } from '../../../fields/InkField';
import { ComputedField } from '../../../fields/ScriptField';
-import { Cast, FieldValue, NumCast, StrCast, toList } from '../../../fields/Types';
+import { Cast, DocCast, FieldValue, NumCast, StrCast, toList } from '../../../fields/Types';
import { ImageField, PdfField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { emptyFunction } from '../../../Utils';
@@ -27,7 +27,6 @@ import { Colors } from '../global/globalEnums';
import { PDFViewer } from '../pdf/PDFViewer';
import { PinDocView, PinProps } from '../PinFuncs';
import { SidebarAnnos } from '../SidebarAnnos';
-import { DocumentView } from './DocumentView';
import { FieldView, FieldViewProps } from './FieldView';
import { FocusViewOptions } from './FocusViewOptions';
import { ImageBox } from './ImageBox';
@@ -56,6 +55,9 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@observable private _pdf: Opt<Pdfjs.PDFDocumentProxy> = undefined;
@observable private _pageControls = false;
+ @computed get sidebarKey() {
+ return this.fieldKey + '_sidebar';
+ }
@computed get pdfUrl() {
return Cast(this.dataDoc[this._props.fieldKey], PdfField);
}
@@ -232,14 +234,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
return this._pdfViewer?.scrollFocus(anchor, NumCast(anchor.y, NumCast(anchor.config_scrollTop)), options);
};
- getView = (doc: Doc, options: FocusViewOptions) => {
- if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) {
- options.didMove = true;
- this.toggleSidebar(false);
+ getView = async (doc: Doc, options: FocusViewOptions) => {
+ if (DocListCast(this.dataDoc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
+ SidebarAnnos.getView(this._sidebarRef.current, this.SidebarShown, () => this.toggleSidebar(false), doc, options);
}
- return new Promise<Opt<DocumentView>>(res => {
- DocumentView.addViewRenderedCb(doc, dv => res(dv));
- });
+ return undefined;
};
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
@@ -397,7 +396,15 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
onKeyDown={e => ([KeyCodes.BACKSPACE, KeyCodes.DELETE].includes(e.keyCode) ? e.stopPropagation() : true)}
onPointerDown={e => e.stopPropagation()}
style={{ display: this._props.isContentActive() ? 'flex' : 'none' }}>
- <div className="pdfBox-overlayCont" onPointerDown={e => e.stopPropagation()} style={{ left: `${this._searching ? 0 : 100}%` }}>
+ <div
+ className="pdfBox-overlayCont"
+ onPointerDown={e => e.stopPropagation()}
+ style={{
+ transformOrigin: 'bottom left',
+ transform: `scale(${this._props.DocumentView?.().UIBtnScaling || 1})`,
+ width: `${100 / (this._props.DocumentView?.().UIBtnScaling || 1)}%`,
+ left: `${this._searching ? 0 : 100}%`,
+ }}>
<button type="button" className="pdfBox-overlayButton" title={searchTitle} />
<input
className="pdfBox-searchBar"
@@ -423,17 +430,25 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
type="button"
className="pdfBox-overlayButton"
title={searchTitle}
+ style={{
+ transformOrigin: 'bottom right',
+ transform: `scale(${this._props.DocumentView?.().UIBtnScaling || 1})`,
+ }}
onClick={action(() => {
this._searching = !this._searching;
this.search('', true, true);
})}>
- <div className="pdfBox-overlayButton-arrow" onPointerDown={e => e.stopPropagation()} />
<div className="pdfBox-overlayButton-iconCont" onPointerDown={e => e.stopPropagation()}>
<FontAwesomeIcon icon={this._searching ? 'times' : 'search'} size="lg" />
</div>
</button>
- <div className="pdfBox-pageNums">
+ <div
+ className="pdfBox-pageNums"
+ style={{
+ transformOrigin: 'top left',
+ transform: `scale(${this._props.DocumentView?.().UIBtnScaling || 1})`,
+ }}>
<input
value={curPage}
style={{ width: `${curPage > 99 ? 4 : 3}ch`, pointerEvents: 'all' }}
@@ -557,8 +572,8 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
NativeHeight={this.sidebarNativeHeightFunc}
PanelHeight={this._props.PanelHeight}
PanelWidth={this.sidebarWidth}
- xPadding={0}
- yPadding={0}
+ xMargin={0}
+ yMargin={0}
viewField={this.SidebarKey}
isAnnotationOverlay={false}
originTopLeft
@@ -637,19 +652,11 @@ export class PDFBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const pdfView = !this._pdf ? null : this.renderPdfView;
const href = this.pdfUrl?.url.href;
if (!pdfView && href) {
- if (PDFBox.pdfcache.get(href))
- setTimeout(
- action(() => {
- this._pdf = PDFBox.pdfcache.get(href);
- })
- );
+ if (PDFBox.pdfcache.get(href)) setTimeout(action(() => (this._pdf = PDFBox.pdfcache.get(href))));
else {
- if (!PDFBox.pdfpromise.get(href)) PDFBox.pdfpromise.set(href, Pdfjs.getDocument(href).promise);
- PDFBox.pdfpromise.get(href)?.then(
- action(pdf => {
- PDFBox.pdfcache.set(href, (this._pdf = pdf));
- })
- );
+ const pdfPromise = PDFBox.pdfpromise.get(href) ?? Pdfjs.getDocument(href).promise;
+ PDFBox.pdfpromise.set(href, pdfPromise);
+ pdfPromise.then(action(pdf => PDFBox.pdfcache.set(href, (this._pdf = pdf))));
}
}
return pdfView ?? this.renderTitleBox;
diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx
index 4f02d68d6..603dcad5c 100644
--- a/src/client/views/nodes/ScreenshotBox.tsx
+++ b/src/client/views/nodes/ScreenshotBox.tsx
@@ -335,8 +335,8 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<FieldViewProps>()
select={emptyFunction}
isContentActive={emptyFunction}
NativeDimScaling={returnOne}
- xPadding={25}
- yPadding={10}
+ xMargin={25}
+ yMargin={10}
whenChildContentsActiveChanged={emptyFunction}
removeDocument={returnFalse}
moveDocument={returnFalse}
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index fa099178c..b3cb0e1db 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -338,12 +338,17 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
const timecode = Cast(this.layoutDoc._layout_currentTimecode, 'number', null);
const marquee = AnchorMenu.Instance.GetAnchor?.(undefined, addAsAnnotation);
+ const docAnchor = () =>
+ Docs.Create.ConfigDocument({
+ title: '#' + timecode,
+ _timecodeToShow: timecode,
+ annotationOn: this.Document,
+ });
if (!addAsAnnotation && marquee) marquee.backgroundColor = 'transparent';
- const anchor =
- addAsAnnotation && marquee
- ? CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.annotationKey, timecode || undefined, undefined, marquee, addAsAnnotation) || this.Document
- : Docs.Create.ConfigDocument({ title: '#' + timecode, _timecodeToShow: timecode, annotationOn: this.Document });
+ const visibleAnchor = () => addAsAnnotation && marquee && (CollectionStackedTimeline.createAnchor(this.Document, this.dataDoc, this.annotationKey, timecode || undefined, undefined, marquee, addAsAnnotation) || this.Document);
+ const anchor = visibleAnchor() || docAnchor();
PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), temporal: true, pannable: true } }, this.Document);
+ addAsAnnotation && this.addDocument(anchor);
return anchor;
};
@@ -376,9 +381,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
return this._stackedTimeline.getView(doc, options);
}
- return new Promise<Opt<DocumentView>>(res => {
- DocumentView.addViewRenderedCb(doc, dv => res(dv));
- });
+ return new Promise<Opt<DocumentView>>(res => DocumentView.addViewRenderedCb(doc, res));
};
// extracts video thumbnails and saves them as field of doc
diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx
index 5603786f0..aeabc6752 100644
--- a/src/client/views/nodes/WebBox.tsx
+++ b/src/client/views/nodes/WebBox.tsx
@@ -13,7 +13,7 @@ import { InkTool } from '../../../fields/InkField';
import { List } from '../../../fields/List';
import { RefField } from '../../../fields/RefField';
import { listSpec } from '../../../fields/Schema';
-import { Cast, NumCast, StrCast, toList, WebCast } from '../../../fields/Types';
+import { Cast, DocCast, NumCast, StrCast, toList, WebCast } from '../../../fields/Types';
import { ImageField, WebField } from '../../../fields/URLField';
import { TraceMobx } from '../../../fields/util';
import { emptyFunction, stringHash } from '../../../Utils';
@@ -104,6 +104,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
@computed get webField() {
return Cast(this.Document[this._props.fieldKey], WebField)?.url;
}
+ @computed get sidebarKey() {
+ return this.fieldKey + '_sidebar';
+ }
constructor(props: FieldViewProps) {
super(props);
@@ -308,18 +311,17 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
@action
- getView = (doc: Doc /* , options: FocusViewOptions */) => {
- if (Doc.AreProtosEqual(doc, this.Document))
- return new Promise<Opt<DocumentView>>(res => {
- res(this.DocumentView?.());
- });
+ getView = async (doc: Doc, options: FocusViewOptions) => {
+ if (Doc.AreProtosEqual(doc, this.Document)) return new Promise<Opt<DocumentView>>(res => res(this.DocumentView?.()));
+
if (this.Document.layout_fieldKey === 'layout_icon') this.DocumentView?.().iconify();
const webUrl = WebCast(doc.config_data)?.url;
if (this._url && webUrl && webUrl.href !== this._url) this.setData(webUrl.href);
- if (this._sidebarRef?.current?.makeDocUnfiltered(doc) && !this.SidebarShown) this.toggleSidebar(false);
- return new Promise<Opt<DocumentView>>(res => {
- DocumentView.addViewRenderedCb(doc, dv => res(dv));
- });
+
+ if (DocListCast(this.dataDoc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
+ return SidebarAnnos.getView(this._sidebarRef.current, this.SidebarShown, () => this.toggleSidebar(false), doc, options);
+ }
+ return undefined;
};
sidebarAddDocTab = (doc: Doc, where: OpenWhere) => {
@@ -393,7 +395,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
.transformPoint(e.clientX, e.clientY - NumCast(this.layoutDoc.layout_scrollTop));
if (!this._marqueeref.current?.isEmpty) this._marqueeref.current?.onEnd(theclick[0], theclick[1]);
else {
- if (!(e.target as HTMLElement)?.tagName?.includes('INPUT')) this.finishMarquee(theclick[0], theclick[1]);
+ if (!(e.target as HTMLElement)?.tagName?.includes('INPUT') && !(e.target as HTMLElement)?.tagName?.includes('TEXTAREA')) this.finishMarquee(theclick[0], theclick[1]);
this._getAnchor = AnchorMenu.Instance?.GetAnchor;
this.marqueeing = undefined;
}
@@ -454,7 +456,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
iframeDown = (e: PointerEvent) => {
this._textAnnotationCreator = undefined;
const sel = this._url ? this._iframe?.contentDocument?.getSelection() : window.document.getSelection();
- if (sel?.empty)
+ if (sel?.empty && !(e.target as HTMLElement).textContent)
sel.empty(); // Chrome
else if (sel?.removeAllRanges) sel.removeAllRanges(); // Firefox
@@ -465,6 +467,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
.transformPoint(e.clientX, e.clientY - NumCast(this.layoutDoc.layout_scrollTop));
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
const target = e.target as HTMLElement;
+ if ((target as HTMLElement)?.tagName?.includes('INPUT') || (target as HTMLElement)?.tagName?.includes('TEXTAREA')) e.stopPropagation();
const word = target && getWordAtPoint(target, e.clientX, e.clientY);
if (!word && !target?.className?.includes('rangeslider') && !target?.onclick && !target?.parentElement?.onclick) {
this.marqueeing = theclick;
@@ -509,10 +512,10 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
try {
href = iframe?.contentWindow?.location.href;
} catch {
- runInAction(() => this._warning++);
+ // runInAction(() => this._warning++);
href = undefined;
}
- let requrlraw = decodeURIComponent(href?.replace(ClientUtils.prepend('') + '/corsProxy/', '') ?? this._url.toString());
+ let requrlraw = decodeURIComponent(href?.replace(ClientUtils.prepend('') + '/corsproxy/', '') ?? this._url.toString());
if (requrlraw !== this._url.toString()) {
if (requrlraw.match(/q=.*&/)?.length && this._url.toString().match(/q=.*&/)?.length) {
const matches = requrlraw.match(/[^a-zA-z]q=[^&]*/g);
@@ -565,9 +568,9 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
'click',
undoable(
action((e: MouseEvent) => {
- let eleHref = '';
+ let eleHref = (e.target as HTMLElement)?.outerHTML?.split('"="')[1]?.split('"')[0];
for (let ele = e.target as HTMLElement | Element | null; ele; ele = ele.parentElement) {
- if (ele instanceof HTMLAnchorElement) {
+ if ('href' in ele) {
eleHref = (typeof ele.href === 'string' ? ele.href : eleHref) || (ele.parentElement && 'href' in ele.parentElement ? (ele.parentElement.href as string) : eleHref);
}
}
@@ -576,7 +579,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
const batch = UndoManager.StartBatch('webclick');
e.stopPropagation();
setTimeout(() => {
- this.setData(eleHref.replace(ClientUtils.prepend(''), origin));
+ const url = eleHref.replace(ClientUtils.prepend(''), origin);
+ this.setData(url);
batch.end();
});
if (this._outerRef.current) {
@@ -858,7 +862,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
);
}
if (field instanceof WebField) {
- const url = this.layoutDoc[this.fieldKey + '_useCors'] ? ClientUtils.CorsProxy(this._webUrl) : this._webUrl;
+ const url = this.layoutDoc[this.fieldKey + '_useCors'] ? '/corsproxy/' + this._webUrl : this._webUrl;
const scripts = this.dataDoc[this.fieldKey + '_allowScripts'] || this._webUrl.includes('wikipedia.org') || this._webUrl.includes('google.com') || this._webUrl.startsWith('https://bing');
// if (!scripts) console.log('No scripts for: ' + url);
return (
@@ -1074,15 +1078,15 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
childPointerEvents = () => (this._props.isContentActive() ? 'all' : undefined);
@computed get webpage() {
TraceMobx();
- const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
+ // const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1;
const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this._props.pointerEvents?.() as Property.PointerEvents | undefined);
- const scale = previewScale * (this._props.NativeDimScaling?.() || 1);
+ // const scale = previewScale * (this._props.NativeDimScaling?.() || 1);
return (
<div
className="webBox-outerContent"
ref={this._outerRef}
style={{
- height: `${100 / scale}%`,
+ height: '100%', //`${100 / scale}%`,
pointerEvents,
}}
// when active, block wheel events from propagating since they're handled by the iframe
@@ -1175,6 +1179,7 @@ export class WebBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
className="webBox-container"
style={{
width: `calc(${100 / scale}% - ${!this.SidebarShown ? 0 : ((this.sidebarWidth() - WebBox.sidebarResizerWidth) / scale) * (this._previewWidth ? scale : 1)}px)`,
+ height: `${100 / scale}%`,
transform: `scale(${scale})`,
pointerEvents,
}}
diff --git a/src/client/views/nodes/WebBoxRenderer.js b/src/client/views/nodes/WebBoxRenderer.js
index b727107a9..ef465c453 100644
--- a/src/client/views/nodes/WebBoxRenderer.js
+++ b/src/client/views/nodes/WebBoxRenderer.js
@@ -21,7 +21,7 @@ const ForeignHtmlRenderer = function (styleSheets) {
return window.location.origin + extension;
}
function CorsProxy(url) {
- return prepend('/corsProxy/') + encodeURIComponent(url);
+ return prepend('/corsproxy/') + encodeURIComponent(url);
}
/**
*
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.scss b/src/client/views/nodes/calendarBox/CalendarBox.scss
index f5d3613e3..891db9d90 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.scss
+++ b/src/client/views/nodes/calendarBox/CalendarBox.scss
@@ -1,9 +1,12 @@
+.calendarBox-interactive,
.calendarBox {
display: flex;
width: 100%;
height: 100%;
transform-origin: top left;
- .calendarBox-wrapper {
+ overflow: auto;
+ > div {
+ pointer-events: none;
width: 100%;
height: 100%;
.fc-timegrid-body {
@@ -41,4 +44,9 @@
text-decoration: line-through;
color: #ffffff;
}
- \ No newline at end of file
+
+.calendarBox-interactive {
+ > div {
+ pointer-events: unset;
+ }
+}
diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx
index df505eb16..40064ad4d 100644
--- a/src/client/views/nodes/calendarBox/CalendarBox.tsx
+++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx
@@ -4,13 +4,14 @@ import dayGridPlugin from '@fullcalendar/daygrid';
import interactionPlugin from '@fullcalendar/interaction';
import multiMonthPlugin from '@fullcalendar/multimonth';
import timeGrid from '@fullcalendar/timegrid';
-import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
+import FullCalendar from '@fullcalendar/react';
+import { IReactionDisposer, action, computed, makeObservable, observable, reaction, untracked } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { dateRangeStrToDates } from '../../../../ClientUtils';
import { Doc } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
-import { BoolCast, NumCast, StrCast } from '../../../../fields/Types';
+import { BoolCast, StrCast } from '../../../../fields/Types';
import { DocServer } from '../../../DocServer';
import { DragManager } from '../../../util/DragManager';
import { CollectionSubView, SubCollectionViewProps } from '../../collections/CollectionSubView';
@@ -19,25 +20,26 @@ import { DocumentView } from '../DocumentView';
import { OpenWhere } from '../OpenWhere';
import './CalendarBox.scss';
import { DateField } from '../../../../fields/DateField';
+import { undoable } from '../../../util/UndoManager';
type CalendarView = 'multiMonth' | 'dayGridMonth' | 'timeGridWeek' | 'timeGridDay';
@observer
export class CalendarBox extends CollectionSubView() {
- _calendarRef: HTMLDivElement | null = null;
+ _calendarRef: FullCalendar | null = null;
_calendar: Calendar | undefined;
_observer: ResizeObserver | undefined;
_eventsDisposer: IReactionDisposer | undefined;
_selectDisposer: IReactionDisposer | undefined;
+ _isMultiMonth: boolean | undefined;
+
+ @observable _multiMonth = 0;
constructor(props: SubCollectionViewProps) {
super(props);
makeObservable(this);
}
- @observable _multiMonth = 0;
- isMultiMonth: boolean | undefined;
-
componentDidMount(): void {
this._props.setContentViewBox?.(this);
this._eventsDisposer = reaction(
@@ -54,7 +56,7 @@ export class CalendarBox extends CollectionSubView() {
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)));
+ setTimeout(() => initialDate.start.toISOString() !== initialDate.end.toISOString() && this._calendar?.select(initialDate.start, initialDate.end));
},
{ fireImmediately: true }
);
@@ -98,7 +100,7 @@ export class CalendarBox extends CollectionSubView() {
// 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';
+ 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';
@@ -129,7 +131,7 @@ export class CalendarBox extends CollectionSubView() {
return false;
};
- handleEventDrop = (arg: EventDropArg | EventResizeDoneArg) => {
+ handleEventDrop = undoable((arg: EventDropArg | EventResizeDoneArg ) => {
const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
// doc && arg.event.start && (doc.date_range = arg.event.start?.toString() + '|' + (arg.event.end ?? arg.event.start).toString());
if (!doc || !arg.event.start) return;
@@ -153,7 +155,7 @@ export class CalendarBox extends CollectionSubView() {
doc.$startTime = new DateField(startDate);
doc.$endTime = new DateField(endDate);
}
- };
+ }, 'change event date');
handleEventClick = (arg: EventClickArg) => {
const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
@@ -172,89 +174,114 @@ export class CalendarBox extends CollectionSubView() {
};
// https://fullcalendar.io
- 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,
- eventDrop: this.handleEventDrop,
- eventResize: this.handleEventDrop,
- eventDidMount: arg => {
- const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
- if (!doc) return;
-
- if (doc.type === 'task') {
- const checkButton = document.createElement('button');
- checkButton.innerText = doc.$completed ? '✅' : '⬜';
- checkButton.style.position = 'absolute';
- checkButton.style.right = '5px';
- checkButton.style.top = '50%';
- checkButton.style.transform = 'translateY(-50%)';
- checkButton.style.background = 'transparent';
- checkButton.style.border = 'none';
- checkButton.style.cursor = 'pointer';
- checkButton.style.fontSize = '18px';
- checkButton.style.zIndex = '1000';
- checkButton.style.padding = '0';
- checkButton.style.margin = '0';
+ @computed get renderCalendar() {
+ const availableWidth = this._props.PanelWidth() / (this._props.DocumentView?.().UIBtnScaling ?? 1);
+ const btn = (text: string, view: string | (() => void), hint: string) => ({ text, hint, click: typeof view === 'string' ? () => this._calendarRef?.getApi().changeView(view) : view });
+ return (
+ <FullCalendar
+ ref={(r:any) => (this._calendarRef = r)}
+ customButtons={{
+ nowBtn: btn('Now', () => this._calendarRef?.getApi().gotoDate(new Date()), 'Go to Today'),
+ multiBtn: btn('M+', 'multiMonth', 'Multiple Month View'),
+ monthBtn: btn('M', 'dayGridMonth', 'Month View'),
+ weekBtn: btn('W', 'timeGridWeek', 'Week View'),
+ dayBtn: btn('D', 'timeGridDay', 'Day View'),
+ }}
+ headerToolbar={
+ availableWidth > 450
+ ? {
+ left: 'prev,next nowBtn',
+ center: 'title',
+ right: 'multiBtn monthBtn weekBtn dayBtn',
+ }
+ : availableWidth > 300
+ ? {
+ left: 'prev,next',
+ center: 'title',
+ right: '',
+ }
+ : {
+ left: '',
+ center: 'title',
+ right: '',
+ }
+ }
+ selectable={true}
+ initialView={this.calendarViewType === 'multiMonth' ? undefined : this.calendarViewType}
+ initialDate={untracked(() => this.dateSelect.start)}
+ navLinks={true}
+ editable={false}
+ // expandRows={true}
+ // handleWindowResize={true}
+ displayEventTime={false}
+ displayEventEnd={false}
+ plugins={[multiMonthPlugin, dayGridPlugin, timeGrid, interactionPlugin]}
+ aspectRatio={this._props.PanelWidth() / this._props.PanelHeight()}
+ weekends={false}
+ events={this.calendarEvents}
+ eventClick={this.handleEventClick}
+ eventDrop={this.handleEventDrop}
+ unselectAuto={false}
+ // unselect={() => {}}
+ select={(info:any) => {
+ const start = dateRangeStrToDates(info.startStr).start.toISOString();
+ const end = info.allDay ? start : dateRangeStrToDates(info.endStr).start.toISOString();
+ this.dataDoc.date = start + '|' + end;
+ }}
+ // eventContent={() => {
+ // return null;
+ // }}
+ eventDidMount={(arg:any) => { const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? '');
+ if (!doc) return;
- checkButton.onclick = ev => {
- ev.stopPropagation();
- doc.$completed = !doc.$completed;
- this._calendar?.refetchEvents();
- };
+ if (doc.type === 'task') {
+ const checkButton = document.createElement('button');
+ checkButton.innerText = doc.$completed ? '✅' : '⬜';
+ checkButton.style.position = 'absolute';
+ checkButton.style.right = '5px';
+ checkButton.style.top = '50%';
+ checkButton.style.transform = 'translateY(-50%)';
+ checkButton.style.background = 'transparent';
+ checkButton.style.border = 'none';
+ checkButton.style.cursor = 'pointer';
+ checkButton.style.fontSize = '18px';
+ checkButton.style.zIndex = '1000';
+ checkButton.style.padding = '0';
+ checkButton.style.margin = '0';
- arg.el.style.position = 'relative';
- arg.el.appendChild(checkButton);
- }
+ checkButton.onclick = ev => {
+ ev.stopPropagation();
+ doc.$completed = !doc.$completed;
+ this._calendar?.refetchEvents();
+ };
- arg.el.addEventListener('pointerdown', ev => {
- ev.button && ev.stopPropagation();
- });
- if (navigator.userAgent.includes('Macintosh')) {
- arg.el.addEventListener('pointerup', ev => {
- ev.button && ev.stopPropagation();
- ev.button && this.handleEventContextMenu(ev.pageX, ev.pageY, arg.event._def.groupId);
- });
- }
- arg.el.addEventListener('contextmenu', ev => {
- if (!navigator.userAgent.includes('Macintosh')) {
- this.handleEventContextMenu(ev.pageX, ev.pageY, arg.event._def.groupId);
- }
- ev.stopPropagation();
- ev.preventDefault();
- });
- },
- }));
- cal?.render();
- setTimeout(() => cal?.view.calendar.select(this.dateSelect.start, this.dateSelect.end));
- };
+ arg.el.style.position = 'relative';
+ arg.el.appendChild(checkButton);
+ }
+ arg.el.addEventListener('pointerdown', (ev:any) => ev.button && ev.stopPropagation());
+ if (navigator.userAgent.includes('Macintosh')) {
+ arg.el.addEventListener('pointerup', (ev:any) => {
+ ev.button && ev.stopPropagation();
+ ev.button && this.handleEventContextMenu(ev.pageX, ev.pageY, arg.event._def.groupId);
+ });
+ }
+ arg.el.addEventListener('contextmenu', (ev:any) => {
+ if (!navigator.userAgent.includes('Macintosh')) {
+ this.handleEventContextMenu(ev.pageX, ev.pageY, arg.event._def.groupId);
+ }
+ ev.stopPropagation();
+ ev.preventDefault();
+ });
+ }}
+ />
+ );
+ }
render() {
return (
<div
key={this.calendarViewType}
- className="calendarBox"
+ className={`calendarBox${this._props.isContentActive() ? '-interactive' : ''}`}
onPointerDown={e => {
setTimeout(
action(() => {
@@ -274,17 +301,8 @@ export class CalendarBox extends CollectionSubView() {
ref={r => {
this.createDashEventsTarget(r);
this.fixWheelEvents(r, this._props.isContentActive);
-
- 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)} />
+ {this.renderCalendar}
</div>
);
}
diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx
index 7ea5d1fcf..d700a705a 100644
--- a/src/client/views/nodes/formattedText/DashFieldView.tsx
+++ b/src/client/views/nodes/formattedText/DashFieldView.tsx
@@ -262,7 +262,8 @@ export class DashFieldViewInternal extends ObservableReactComponent<IDashFieldVi
render() {
return (
<div
- className={`dashFieldView${this.isRowActive() ? '-active' : ''}`}
+ // eslint-disable-next-line no-use-before-define
+ className={`${DashFieldView.name}${this.isRowActive() ? '-active' : ''}`}
ref={this._fieldRef}
style={{
// width: this._props.width,
diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
index c51f6c38b..c8df6e50f 100644
--- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx
+++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx
@@ -98,7 +98,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
public static PasteOnLoad: ClipboardEvent | undefined;
public static SelectOnLoadChar = '';
- public static LiveTextUndo: UndoManager.Batch | undefined; // undo batch when typing a new text note into a collection
+ public static LiveTextUndo: UndoManager.Batch | undefined; // undo batch request when typing a new text note into a collection
+ private _liveTextUndo: UndoManager.Batch | undefined; // captured undo batch when typing a new text note into a collection
private static _nodeViews: (self: FormattedTextBox) => { [key: string]: NodeViewConstructor };
private _curHighlights = new ObservableSet<string>(['Audio Tags']);
@@ -270,13 +271,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
e.preventDefault();
e.stopPropagation();
const targetCreator = (annotationOn?: Doc) => {
- const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn);
+ const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, annotationOn, 'yellow');
+ target.layout_fitWidth = true;
DocumentView.SetSelectOnLoad(target);
return target;
};
+ const sourceAnchorCreator = () => this.getAnchor(true);
+
const docView = this.DocumentView?.();
- docView && DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, () => this.getAnchor(true), targetCreator), e.pageX, e.pageY);
+ docView && DragManager.StartAnchorAnnoDrag([ele], new DragManager.AnchorAnnoDragData(docView, sourceAnchorCreator, targetCreator), e.pageX, e.pageY);
});
AnchorMenu.Instance.AddDrawingAnnotation = (drawing: Doc) => {
@@ -425,10 +429,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const newAutoLinks = new Set<Doc>();
const oldAutoLinks = Doc.Links(this.Document).filter(
link =>
- ((!Doc.isTemplateForField(this.Document) &&
- ((DocCast(link.link_anchor_1) && !Doc.isTemplateForField(DocCast(link.link_anchor_1)!)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_1), this.Document)) &&
- ((DocCast(link.link_anchor_2) && !Doc.isTemplateForField(DocCast(link.link_anchor_2)!)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_2), this.Document))) ||
- (Doc.isTemplateForField(this.Document) && (link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document))) &&
+ ((!Doc.IsTemplateForField(this.Document) &&
+ ((DocCast(link.link_anchor_1) && !Doc.IsTemplateForField(DocCast(link.link_anchor_1)!)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_1), this.Document)) &&
+ ((DocCast(link.link_anchor_2) && !Doc.IsTemplateForField(DocCast(link.link_anchor_2)!)) || !Doc.AreProtosEqual(DocCast(link.link_anchor_2), this.Document))) ||
+ (Doc.IsTemplateForField(this.Document) && (link.link_anchor_1 === this.Document || link.link_anchor_2 === this.Document))) &&
link.link_relationship === LinkManager.AutoKeywords
); // prettier-ignore
if (this.EditorView?.state.doc.textContent) {
@@ -1070,15 +1074,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
getView = (doc: Doc, options: FocusViewOptions) => {
if (DocListCast(this.dataDoc[this.sidebarKey]).find(anno => Doc.AreProtosEqual(doc.layout_unrendered ? DocCast(doc.annotationOn) : doc, anno))) {
- if (!this.SidebarShown) {
- this.toggleSidebar(false);
- options.didMove = true;
- }
- setTimeout(() => this._sidebarRef?.current?.makeDocUnfiltered(doc));
+ return SidebarAnnos.getView(this._sidebarRef.current, this.SidebarShown, () => this.toggleSidebar(false), doc, options);
}
- return new Promise<Opt<DocumentView>>(res => {
- DocumentView.addViewRenderedCb(doc, dv => res(dv));
- });
+ return new Promise<Opt<DocumentView>>(res => DocumentView.addViewRenderedCb(doc, res));
};
focus = (textAnchor: Doc, options: FocusViewOptions) => {
const focusSpeed = options.zoomTime ?? 500;
@@ -1145,13 +1143,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
return undefined;
};
- // if the scroll height has changed and we're in layout_autoHeight mode, then we need to update the textHeight component of the doc.
- // Since we also monitor all component height changes, this will update the document's height.
- resetNativeHeight = action((scrollHeight: number) => {
- this.layoutDoc['_' + this.fieldKey + '_height'] = scrollHeight;
- if (!this.layoutDoc.isTemplateForField && NumCast(this.layoutDoc._nativeHeight)) this.layoutDoc._nativeHeight = scrollHeight;
- });
-
addPlugin = (plugin: Plugin) => {
const editorView = this.EditorView;
if (editorView) {
@@ -1164,7 +1155,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
};
@computed get tagsHeight() {
- return this.DocumentView?.().showTags ? Math.max(0, 20 - Math.max(this._props.yPadding ?? 0, NumCast(this.layoutDoc._yMargin))) * this.ScreenToLocalBoxXf().Scale : 0;
+ return this.DocumentView?.().showTags ? Math.max(0, 20 - Math.max(this._props.yMargin ?? 0, NumCast(this.layoutDoc._yMargin))) * this.ScreenToLocalBoxXf().Scale : 0;
}
@computed get contentScaling() {
@@ -1182,21 +1173,31 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this._disposers.width = reaction(this._props.PanelWidth, this.tryUpdateScrollHeight);
this._disposers.scrollHeight = reaction(
() => ({ scrollHeight: this.scrollHeight, layoutAutoHeight: this.layout_autoHeight, width: NumCast(this.layoutDoc._width) }),
- ({ width, scrollHeight, layoutAutoHeight }) => width && layoutAutoHeight && this.resetNativeHeight(scrollHeight),
+ ({ width, scrollHeight, layoutAutoHeight }) => width && layoutAutoHeight && (this.layoutDoc['_' + this.fieldKey + '_height'] = scrollHeight),
{ fireImmediately: true }
);
this._disposers.componentHeights = reaction(
// set the document height when one of the component heights changes and layout_autoHeight is on
- () => ({ border: this._props.PanelHeight(), sidebarHeight: this.sidebarHeight, textHeight: this.textHeight, layoutAutoHeight: this.layout_autoHeight, marginsHeight: this.layout_autoHeightMargins }),
- ({ border, sidebarHeight, textHeight, layoutAutoHeight, marginsHeight }) => {
+ () => ({
+ border: this._props.PanelHeight(),
+ scrollHeight: NumCast(this.layoutDoc['_' + this.fieldKey + '_height']),
+ sidebarHeight: this.sidebarHeight,
+ textHeight: this.textHeight,
+ layoutAutoHeight: this.layout_autoHeight,
+ marginsHeight: this.layout_autoHeightMargins,
+ }),
+ ({ border, sidebarHeight, scrollHeight, textHeight, layoutAutoHeight, marginsHeight }) => {
const newHeight = this.contentScaling * (marginsHeight + Math.max(sidebarHeight, textHeight));
if (
(!Array.from(this._curHighlights).includes('Bold Text') || this._props.isSelected()) && //
layoutAutoHeight &&
newHeight &&
- (newHeight !== this.layoutDoc.height || border < NumCast(this.layoutDoc.height)) &&
+ (newHeight !== this.layoutDoc.height || border < NumCast(this.layoutDoc.height) || this.layoutDoc._nativeHeight !== scrollHeight) &&
!this._props.dontRegisterView
) {
+ if (NumCast(this.layoutDoc.nativeHeight)) {
+ this.layoutDoc._nativeHeight = scrollHeight;
+ }
this._props.setHeight?.(newHeight);
}
},
@@ -1219,18 +1220,19 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const protoTime = protoData && this.dataDoc[this.fieldKey + '_autoUpdate'] ? (DateCast(DocCast(this.dataDoc.proto)?.[this.fieldKey + '_modificationDate'])?.date.getTime() ?? 0) : 0;
const recentData = dataTime >= layoutTime ? (protoTime >= dataTime ? protoData : dataData) : layoutTime >= protoTime ? layoutData : protoData;
const whichData = recentData ?? (this.layoutDoc.isTemplateDoc ? layoutData : protoData) ?? protoData;
- return !whichData ? undefined : { data: RTFCast(whichData), str: Field.toString(DocCast(whichData) ?? StrCast(whichData)) };
+ return !whichData ? undefined : { data: RTFCast(whichData), str: Field.toString(DocCast(whichData) ?? NumCast(whichData)?.toString() ?? StrCast(whichData)) };
},
incomingValue => {
if (this.EditorView && this.ApplyingChange !== this.fieldKey) {
if (incomingValue?.data) {
- const updatedState = JSON.parse(incomingValue.data.Data);
+ const updatedState = JSON.parse(incomingValue.data.Data.replace(/\n/g, ''));
if (JSON.stringify(this.EditorView.state.toJSON()) !== JSON.stringify(updatedState)) {
this.EditorView.updateState(EditorState.fromJSON(this.config, updatedState));
this.tryUpdateScrollHeight();
}
} else if (this.EditorView.state.doc.textContent !== (incomingValue?.str ?? '')) {
selectAll(this.EditorView.state, tx => this.EditorView?.dispatch(tx.insertText(incomingValue?.str ?? '')));
+ this.tryUpdateScrollHeight();
}
}
},
@@ -1524,7 +1526,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
if (this.EditorView && selectOnLoad && !this._props.dontRegisterView && !this._props.dontSelectOnLoad && this.isActiveTab(this.ProseRef)) {
const { $from } = this.EditorView.state.selection;
- const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) });
+ const mark = schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail() });
const curMarks = this.EditorView.state.storedMarks ?? $from?.marksAcross(this.EditorView.state.selection.$head) ?? [];
const storedMarks = [...curMarks.filter(m => m.type !== mark.type), mark];
if (selLoadChar === 'Enter') {
@@ -1535,6 +1537,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this.tryUpdateDoc(true); // calling select() above will make isContentActive() true only after a render .. which means the selectAll() above won't write to the Document and the incomingValue will overwrite the selection with the non-updated data
}
if (selectOnLoad) {
+ this._liveTextUndo = FormattedTextBox.LiveTextUndo;
+ FormattedTextBox.LiveTextUndo = undefined;
this.EditorView!.focus();
}
if (this._props.isContentActive()) this.prepareForTyping();
@@ -1551,7 +1555,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const { text, paragraph } = schema.nodes;
const selNode = this.EditorView.state.selection.$anchor.node();
if (this.EditorView.state.selection.from === 1 && this.EditorView.state.selection.empty && [undefined, text, paragraph].includes(selNode?.type)) {
- const docDefaultMarks = [schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) })];
+ const docDefaultMarks = [schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail() })];
this.EditorView.state.selection.empty && this.EditorView.state.selection.from === 1 && this.EditorView?.dispatch(this.EditorView?.state.tr.setStoredMarks(docDefaultMarks).removeStoredMark(schema.marks.pFontColor));
}
}
@@ -1564,8 +1568,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
removeStyleSheet(this._userStyleSheetElement);
Object.values(this._disposers).forEach(disposer => disposer?.());
this.endUndoTypingBatch();
- FormattedTextBox.LiveTextUndo?.end();
- FormattedTextBox.LiveTextUndo = undefined;
+ this._liveTextUndo?.end();
this.unhighlightSearchTerms();
this.EditorView?.destroy();
RichTextMenu.Instance?.TextView === this && RichTextMenu.Instance.updateMenu(undefined, undefined, undefined, undefined);
@@ -1687,7 +1690,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
if (e.clientX > boundsRect.left && e.clientX < boundsRect.right && e.clientY > boundsRect.bottom) {
// if we clicked below the last prosemirror div, then set the selection to be the end of the document
editorView.focus();
- editorView.dispatch(editorView.state.tr.setSelection(TextSelection.create(editorView.state.doc, editorView.state.doc.content.size)));
+ // editorView.dispatch(editorView.state.tr.setSelection(TextSelection.create(editorView.state.doc, editorView.state.doc.content.size)));
}
} else if (node && [editorView.state.schema.nodes.ordered_list, editorView.state.schema.nodes.listItem].includes(node.type) && node !== (editorView.state.selection as NodeSelection)?.node && pcords) {
editorView.dispatch(editorView.state.tr.setSelection(NodeSelection.create(editorView.state.doc, pcords.pos)));
@@ -1750,23 +1753,26 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
* When a text box loses focus, it might be because a text button was clicked (eg, bold, italics) or color picker.
* In these cases, force focus back onto the text box.
* @param target
+ * @returns true if focus was kept on the text box, false otherwise
*/
- tryKeepingFocus = (target: Element | null) => {
+ public static tryKeepingFocus(target: Element | null, refocusFunc?: () => void) {
for (let newFocusEle = target instanceof HTMLElement ? target : null; newFocusEle; newFocusEle = newFocusEle?.parentElement) {
// bcz: HACK!! test if parent of new focused element is a UI button (should be more specific than testing className)
if (['fonticonbox', 'antimodeMenu-cont', 'popup-container'].includes(newFocusEle?.className ?? '')) {
- return this.EditorView?.focus(); // keep focus on text box
+ refocusFunc?.(); // keep focus on text box
+ return true;
}
}
- };
+ return false;
+ }
@action
onBlur = (e: React.FocusEvent) => {
- this.tryKeepingFocus(e.relatedTarget);
+ FormattedTextBox.tryKeepingFocus(e.relatedTarget, () => this.EditorView?.focus());
if (this.ProseRef?.children[0] !== e.nativeEvent.target) return;
if (!(this.EditorView?.state.selection instanceof NodeSelection) || this.EditorView.state.selection.node.type !== this.EditorView.state.schema.nodes.footnote) {
const stordMarks = this.EditorView?.state.storedMarks?.slice();
- if (!(this.EditorView?.state.selection instanceof NodeSelection)) {
+ if (!(this.EditorView?.state.selection instanceof NodeSelection) && typeof this.dataDoc[this.fieldKey] !== 'number') {
this.autoLink();
if (this.EditorView?.state.tr) {
const tr = stordMarks?.reduce((tr2, m) => {
@@ -1791,8 +1797,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
this.endUndoTypingBatch();
- FormattedTextBox.LiveTextUndo?.end();
- FormattedTextBox.LiveTextUndo = undefined;
+ this._liveTextUndo?.end();
// if the text box blurs and none of its contents are focused(), then pass the blur along
setTimeout(() => !this.ProseRef?.contains(document.activeElement) && this._props.onBlur?.());
@@ -1813,7 +1818,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
return;
}
if (this._enteringStyle && 'tix!'.includes(e.key)) {
- const tag = e.key === 't' ? 'todo' : e.key === 'i' ? 'ignore' : e.key === 'x' ? 'disagree' : e.key === '!' ? 'important' : '??';
const node = state.selection.$from.nodeAfter;
const start = state.selection.from;
const end = state.selection.to;
@@ -1822,9 +1826,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
StopEvent(e);
_editorView.dispatch(
state.tr
- .removeMark(start, end, schema.marks.user_mark)
- .addMark(start, end, schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified: Math.floor(Date.now() / 1000) }))
- .addMark(start, end, schema.marks.user_tag.create({ userid: ClientUtils.CurrentUserEmail(), tag, modified: Math.round(Date.now() / 1000 / 60) }))
+ .removeMark(start, end, schema.marks.user_mark) //
+ .addMark(start, end, schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail() }))
);
return;
}
@@ -1857,9 +1860,12 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
break;
default:
if ([AclEdit, AclAugment, AclAdmin].includes(GetEffectiveAcl(this.Document))) {
- const modified = Math.floor(Date.now() / 1000);
- const mark = state.selection.$to.marks().find(m => m.type === schema.marks.user_mark && m.attrs.modified === modified);
- _editorView.dispatch(state.tr.removeStoredMark(schema.marks.user_mark).addStoredMark(mark ?? schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail(), modified })));
+ const mark = state.selection.$to.marks().find(m => m.type === schema.marks.user_mark);
+ _editorView.dispatch(
+ state.tr
+ .removeStoredMark(schema.marks.user_mark) //
+ .addStoredMark(mark ?? schema.marks.user_mark.create({ userid: ClientUtils.CurrentUserEmail() }))
+ );
}
break;
}
@@ -1880,17 +1886,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
}
};
tryUpdateScrollHeight = () => {
- const margins = 2 * NumCast(this.layoutDoc._yMargin, this._props.yPadding || 0);
+ const margins = 2 * NumCast(this.layoutDoc._yMargin, this._props.yMargin || 0);
const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined;
if (this.EditorView && children && !SnappingManager.IsDragging) {
- const getChildrenHeights = (kids: Element[] | undefined) => kids?.reduce((p, child) => p + toHgt(child), margins) ?? 0;
+ const getChildrenHeights = (kids: Element[] | undefined) => kids?.reduce((p, child) => p + toHgt(child), 0) ?? 0;
const toNum = (val: string) => Number(val.replace('px', ''));
const toHgt = (node: Element): number => {
const { height, marginTop, marginBottom } = getComputedStyle(node);
const childHeight = height === 'auto' ? getChildrenHeights(Array.from(node.children)) : toNum(height);
return childHeight + Math.max(0, toNum(marginTop)) + Math.max(0, toNum(marginBottom));
};
- const proseHeight = !this.ProseRef ? 0 : getChildrenHeights(children);
+ const proseHeight = !this.ProseRef ? 0 : getChildrenHeights(children) + margins;
const scrollHeight = this.ProseRef && proseHeight;
if (this._props.setHeight && !this._props.suppressSetHeight && scrollHeight && !this._props.dontRegisterView) {
// if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation
@@ -2112,8 +2118,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FormattedTextB
const rounded = StrCast(this.layoutDoc._layout_borderRounding) === '100%' ? '-rounded' : '';
setTimeout(() => !this._props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide);
- const paddingX = Math.max(NumCast(this.layoutDoc._xMargin), 0, this._props.xPadding ?? 0, this._props.screenXPadding?.(this._props.DocumentView?.()) ?? 0);
- const paddingY = Math.max(NumCast(this.layoutDoc._yMargin), 0, this._props.yPadding ?? 0); // prettier-ignore
+ const paddingX = Math.max(NumCast(this.layoutDoc._xMargin), 0, this._props.xMargin ?? 0, this._props.screenXPadding?.(this._props.DocumentView?.()) ?? 0);
+ const paddingY = Math.max(NumCast(this.layoutDoc._yMargin), 0, this._props.yMargin ?? 0); // prettier-ignore
const styleFromLayout = styleFromLayoutString(this.Document, this._props, scale); // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._header_height}px' >
return this.isLabel ? (
<LabelBox {...this._props} />
diff --git a/src/client/views/nodes/formattedText/marks_rts.ts b/src/client/views/nodes/formattedText/marks_rts.ts
index b7dae1ca3..333ee6be8 100644
--- a/src/client/views/nodes/formattedText/marks_rts.ts
+++ b/src/client/views/nodes/formattedText/marks_rts.ts
@@ -330,20 +330,6 @@ export const marks: { [index: string]: MarkSpec } = {
return ['span', { class: 'UM-' + uid + remote + ' UM-min-' + min + ' UM-hr-' + hr + ' UM-day-' + day }, 0];
},
},
- // the id of the user who entered the text
- user_tag: {
- attrs: {
- userid: { default: '' },
- modified: { default: 'when?' }, // 1 second intervals since 1970
- tag: { default: '' },
- },
- group: 'inline',
- inclusive: false,
- toDOM: node => {
- const uid = node.attrs.userid.replace('.', '').replace('@', '');
- return ['span', { class: 'UT-' + uid + ' UT-' + node.attrs.tag }, 0];
- },
- },
// :: MarkSpec Code font mark. Represented as a `<code>` element.
code: {
diff --git a/src/client/views/nodes/imageEditor/ImageEditor.tsx b/src/client/views/nodes/imageEditor/ImageEditor.tsx
index 85bd95d15..198b8e713 100644
--- a/src/client/views/nodes/imageEditor/ImageEditor.tsx
+++ b/src/client/views/nodes/imageEditor/ImageEditor.tsx
@@ -281,11 +281,14 @@ const ImageEditor = ({ imageEditorOpen, imageEditorSource, imageRootDoc, addDoc
try {
const canvasOriginalImg = ImageUtility.getCanvasImg(img);
if (!canvasOriginalImg) return;
- const canvasMask = ImageUtility.getCanvasMask(canvas, canvasOriginalImg);
+ const canvasMask = ImageUtility.getCanvasMask(canvas, canvas);
if (!canvasMask) return;
const maskBlob = await ImageUtility.canvasToBlob(canvasMask);
const imgBlob = await ImageUtility.canvasToBlob(canvasOriginalImg);
const res = await ImageUtility.getEdit(imgBlob, maskBlob, input || 'Fill in the image in the same style', 2);
+ if ((res as any).status == 'error') {
+ alert((res as any).message);
+ }
// create first image
if (!newCollectionRef.current) {
diff --git a/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts b/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts
index 1c6a38a24..d6093c6eb 100644
--- a/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts
+++ b/src/client/views/nodes/imageEditor/imageEditorUtils/ImageHandler.ts
@@ -75,7 +75,7 @@ export class ImageUtility {
fd.append('mask', maskBlob, 'mask.png');
fd.append('prompt', prompt);
fd.append('size', '1024x1024');
- fd.append('n', n ? JSON.stringify(n) : '1');
+ fd.append('n', n ? n + '' : '1');
fd.append('response_format', 'b64_json');
try {
@@ -268,14 +268,14 @@ export class ImageUtility {
ctx.drawImage(img, xOffset, 0, width, height);
// draw reflected image padding
- this.drawHorizontalReflection(ctx, canvas, xOffset);
+ // this.drawHorizontalReflection(ctx, canvas, xOffset);
} else {
// vertical padding, y offset
const yOffset = Math.floor((canvasSize - height) / 2);
ctx.drawImage(img, 0, yOffset, width, height);
// draw reflected image padding
- this.drawVerticalReflection(ctx, canvas, yOffset);
+ // this.drawVerticalReflection(ctx, canvas, yOffset);
}
return canvas;
};
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx
index 11f35b8ef..cb2a1f13f 100644
--- a/src/client/views/nodes/trails/PresBox.tsx
+++ b/src/client/views/nodes/trails/PresBox.tsx
@@ -702,7 +702,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps>() {
transTime + 10
);
}
- if ((pinDataTypes?.pannable || (!pinDataTypes && (activeItem.config_viewBounds !== undefined || activeItem.config_panX !== undefined || activeItem.config_viewScale !== undefined))) && !bestTarget.isGroup) {
+ if ((pinDataTypes?.pannable || (!pinDataTypes && (activeItem.config_viewBounds !== undefined || activeItem.config_panX !== undefined || activeItem.config_viewScale !== undefined))) && !Doc.IsFreeformGroup(bestTarget)) {
const contentBounds = Cast(activeItem.config_viewBounds, listSpec('number'));
if (contentBounds) {
const viewport = { panX: (contentBounds[0] + contentBounds[2]) / 2, panY: (contentBounds[1] + contentBounds[3]) / 2, width: contentBounds[2] - contentBounds[0], height: contentBounds[3] - contentBounds[1] };
diff --git a/src/client/views/nodes/trails/PresSlideBox.tsx b/src/client/views/nodes/trails/PresSlideBox.tsx
index 3dbb3da88..55a655c7a 100644
--- a/src/client/views/nodes/trails/PresSlideBox.tsx
+++ b/src/client/views/nodes/trails/PresSlideBox.tsx
@@ -559,10 +559,10 @@ export class PresSlideBox extends ViewBoxBaseComponent<FieldViewProps>() {
style={{
backgroundColor: presColorBool ? (isSelected ? 'rgba(250,250,250,0.3)' : 'transparent') : isSelected ? Colors.LIGHT_BLUE : 'transparent',
opacity: this._dragging ? 0.3 : 1,
- paddingLeft: NumCast(this.layoutDoc._xPadding, this._props.xPadding),
- paddingRight: NumCast(this.layoutDoc._xPadding, this._props.xPadding),
- paddingTop: NumCast(this.layoutDoc._yPadding, this._props.yPadding),
- paddingBottom: NumCast(this.layoutDoc._yPadding, this._props.yPadding),
+ paddingLeft: NumCast(this.layoutDoc._xMargin, this._props.xMargin),
+ paddingRight: NumCast(this.layoutDoc._xMargin, this._props.xMargin),
+ paddingTop: NumCast(this.layoutDoc._yPadding, this._props.yMargin),
+ paddingBottom: NumCast(this.layoutDoc._yPadding, this._props.yMargin),
}}
onDoubleClick={action(() => {
this.toggleProperties();