aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/collections')
-rw-r--r--src/client/views/collections/CollectionCalendarView.tsx99
-rw-r--r--src/client/views/collections/CollectionCardDeckView.tsx220
-rw-r--r--src/client/views/collections/CollectionCarousel3DView.tsx5
-rw-r--r--src/client/views/collections/CollectionCarouselView.tsx9
-rw-r--r--src/client/views/collections/CollectionDockingView.tsx17
-rw-r--r--src/client/views/collections/CollectionMenu.scss2
-rw-r--r--src/client/views/collections/CollectionSubView.tsx2
-rw-r--r--src/client/views/collections/CollectionView.tsx2
-rw-r--r--src/client/views/collections/TreeView.tsx2
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx1
-rw-r--r--src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx390
-rw-r--r--src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss14
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx3
-rw-r--r--src/client/views/collections/collectionFreeForm/MarqueeView.tsx43
-rw-r--r--src/client/views/collections/collectionSchema/CollectionSchemaView.tsx1
15 files changed, 382 insertions, 428 deletions
diff --git a/src/client/views/collections/CollectionCalendarView.tsx b/src/client/views/collections/CollectionCalendarView.tsx
deleted file mode 100644
index 0ea9f8ebc..000000000
--- a/src/client/views/collections/CollectionCalendarView.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-import { computed, makeObservable } from 'mobx';
-import { observer } from 'mobx-react';
-import * as React from 'react';
-import { emptyFunction } from '../../../Utils';
-import { dateRangeStrToDates, returnTrue } from '../../../ClientUtils';
-import { Doc, DocListCast } from '../../../fields/Doc';
-import { StrCast } from '../../../fields/Types';
-import { CollectionStackingView } from './CollectionStackingView';
-import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-
-@observer
-export class CollectionCalendarView extends CollectionSubView() {
- constructor(props: SubCollectionViewProps) {
- super(props);
- makeObservable(this);
- }
-
- componentDidMount(): void {}
-
- componentWillUnmount(): void {}
-
- @computed get allCalendars() {
- return this.childDocs; // returns a list of docs (i.e. calendars)
- }
-
- removeCalendar = () => {};
-
- addCalendar = (/* doc: Doc | Doc[], annotationKey?: string | undefined */): boolean =>
- // bring up calendar modal with option to create a calendar
- true;
-
- _stackRef = React.createRef<CollectionStackingView>();
-
- panelHeight = () => this._props.PanelHeight() - 40; // this should be the height of the stacking view. For now, it's the hieight of the calendar view minus 40 to allow for a title
-
- // most recent calendar should come first
- sortByMostRecentDate = (calendarA: Doc, calendarB: Doc) => {
- const aDateRangeStr = StrCast(DocListCast(calendarA.data).lastElement()?.date_range);
- const bDateRangeStr = StrCast(DocListCast(calendarB.data).lastElement()?.date_range);
-
- const { start: aFromDate, end: aToDate } = dateRangeStrToDates(aDateRangeStr);
- const { start: bFromDate, end: bToDate } = dateRangeStrToDates(bDateRangeStr);
-
- if (aFromDate > bFromDate) {
- return -1; // a comes first
- }
- if (aFromDate < bFromDate) {
- return 1; // b comes first
- }
- // start dates are the same
- if (aToDate > bToDate) {
- return -1; // a comes first
- }
- if (aToDate < bToDate) {
- return 1; // b comes first
- }
- return 0; // same start and end dates
- };
-
- screenToLocalTransform = () =>
- this._props
- .ScreenToLocalTransform()
- .translate(Doc.NativeWidth(this.Document), 0)
- .scale(this._props.NativeDimScaling?.() || 1);
-
- get calendarsKey() {
- return this._props.fieldKey;
- }
-
- render() {
- return (
- <div className="collectionCalendarView">
- <CollectionStackingView
- // eslint-disable-next-line react/jsx-props-no-spreading
- {...this._props}
- setContentViewBox={emptyFunction}
- ref={this._stackRef}
- PanelHeight={this.panelHeight}
- PanelWidth={this._props.PanelWidth}
- sortFunc={this.sortByMostRecentDate}
- setHeight={undefined}
- isAnnotationOverlay={false}
- // select={emptyFunction} What does this mean?
- isAnyChildContentActive={returnTrue} // ??
- dontCenter="y"
- // childDocumentsActive={}
- // whenChildContentsActiveChanged={}
- childHideDecorationTitle={false}
- removeDocument={this.removeDocument} // will calendar automatically be removed from myCalendars
- moveDocument={this.moveDocument}
- addDocument={this.addCalendar}
- ScreenToLocalTransform={this.screenToLocalTransform}
- renderDepth={this._props.renderDepth + 1}
- fieldKey={this.calendarsKey}
- />
- </div>
- );
- }
-}
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index e5a6ebc7f..92c69c3cf 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -1,8 +1,7 @@
-import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, trace } from 'mobx';
+import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ClientUtils, DashColor, returnFalse, returnZero } from '../../../ClientUtils';
-import { emptyFunction } from '../../../Utils';
import { Doc } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
@@ -13,7 +12,6 @@ import { gptImageLabel } from '../../apis/gpt/GPT';
import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
import { dropActionType } from '../../util/DropActionTypes';
-import { SelectionManager } from '../../util/SelectionManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { undoable } from '../../util/UndoManager';
@@ -23,12 +21,13 @@ import { DocumentView } from '../nodes/DocumentView';
import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
import './CollectionCardDeckView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
+import { computedFn } from 'mobx-utils';
+import { DocumentDecorations } from '../DocumentDecorations';
enum cardSortings {
Time = 'time',
Type = 'type',
Color = 'color',
- Custom = 'custom',
Chat = 'chat',
Tag = 'tag',
None = '',
@@ -46,14 +45,13 @@ export class CollectionCardView extends CollectionSubView() {
private _dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [key: string]: IReactionDisposer } = {};
private _textToDoc = new Map<string, Doc>();
- private _dropped = false; // indicate when a card doc has just moved;
+ private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
@observable _forceChildXf = 0;
@observable _hoveredNodeIndex = -1;
@observable _docRefs = new ObservableMap<Doc, DocumentView>();
@observable _maxRowCount = 10;
@observable _docDraggedIndex: number = -1;
- @observable overIndex: number = -1;
static imageUrlToBase64 = async (imageUrl: string): Promise<string> => {
try {
@@ -101,7 +99,6 @@ export class CollectionCardView extends CollectionSubView() {
componentDidMount() {
this._props.setContentViewBox?.(this);
- // Reaction to cardSort changes
this._disposers.sort = reaction(
() => GPTPopup.Instance.visible,
isVis => {
@@ -135,11 +132,10 @@ export class CollectionCardView extends CollectionSubView() {
}
/**
- * The child documents to be rendered-- either all of them except the Links or the docs in the currently active
- * custom group
+ * The child documents to be rendered-- everything other than ink/link docs (which are marks as being svg's)
*/
@computed get childDocsWithoutLinks() {
- return this.childDocs.filter(l => l.type !== DocumentType.LINK);
+ return this.childDocs.filter(l => !l.layout_isSvg);
}
/**
@@ -155,8 +151,7 @@ export class CollectionCardView extends CollectionSubView() {
*/
quizMode = () => {
const randomIndex = Math.floor(Math.random() * this.childDocs.length);
- SelectionManager.DeselectAll();
- DocumentView.SelectView(DocumentView.getDocumentView(this.childDocs[randomIndex]), false);
+ DocumentView.getDocumentView(this.childDocs[randomIndex])?.select(false);
};
/**
@@ -168,29 +163,15 @@ export class CollectionCardView extends CollectionSubView() {
@action
setHoveredNodeIndex = (index: number) => {
- if (!DocumentView.SelectedDocs().includes(this.childDocs[index])) {
- this._hoveredNodeIndex = index;
- }
+ if (!SnappingManager.IsDragging) this._hoveredNodeIndex = index;
};
- /**
- * Translates the hovered node to the center of the screen
- * @param index
- * @returns
- */
- translateHover = (index: number) => (this._hoveredNodeIndex === index && !DocumentView.SelectedDocs().includes(this.childDocs[index]) ? -50 : 0);
-
- isSelected = (index: number) => DocumentView.SelectedDocs().includes(this.childDocs[index]);
-
- /**
- * Returns all the documents except the one that's currently selected
- */
- inactiveDocs = () => this.childDocsWithoutLinks.filter(d => !DocumentView.SelectedDocs().includes(d));
+ isSelected = (doc: Doc) => this._docRefs.get(doc)?.IsSelected;
childPanelWidth = () => NumCast(this.layoutDoc.childPanelWidth, this._props.PanelWidth() / 2);
childPanelHeight = () => this._props.PanelHeight() * this.fitContentScale;
onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
- isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive();
- isChildContentActive = () => !!this.isContentActive();
+ isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this.isAnyChildContentActive();
+ isAnyChildContentActive = this._props.isAnyChildContentActive;
/**
* Returns the degree to rotate a card dependind on the amount of cards in their row and their index in said row
@@ -202,13 +183,14 @@ export class CollectionCardView extends CollectionSubView() {
if (amCards == 1) return 0;
const possRotate = -30 + index * (30 / ((amCards - (amCards % 2)) / 2));
- const stepMag = Math.abs(-30 + (amCards / 2 - 1) * (30 / ((amCards - (amCards % 2)) / 2)));
-
- if (amCards % 2 === 0 && possRotate === 0) {
- return possRotate + Math.abs(-30 + (index - 1) * (30 / (amCards / 2)));
- }
- if (amCards % 2 === 0 && index > (amCards + 1) / 2) {
- return possRotate + stepMag;
+ if (amCards % 2 === 0) {
+ if (possRotate === 0) {
+ return possRotate + Math.abs(-30 + (index - 1) * (30 / (amCards / 2)));
+ }
+ if (index > (amCards + 1) / 2) {
+ const stepMag = Math.abs(-30 + (amCards / 2 - 1) * (30 / ((amCards - (amCards % 2)) / 2)));
+ return possRotate + stepMag;
+ }
}
return possRotate;
@@ -274,22 +256,13 @@ export class CollectionCardView extends CollectionSubView() {
/**
* Checks to see if a card is being dragged and calls the appropriate methods if so
- * @param e the current pointer event
*/
-
@action
onPointerMove = (x: number, y: number) => {
this._docDraggedIndex = DragManager.docsBeingDragged.length ? this.findCardDropIndex(x, y) : -1;
};
/**
- * Handles external drop of images/PDFs etc from outside Dash.
- */
- onExternalDrop = async (e: React.DragEvent): Promise<void> => {
- super.onExternalDrop(e, {});
- };
-
- /**
* Resets all the doc dragging vairables once a card is dropped
* @param e
* @param de drop event
@@ -326,7 +299,7 @@ export class CollectionCardView extends CollectionSubView() {
}
/**
- * Used to determine how to sort cards based on tags. The lestmost tags are given lower values while cards to the right are
+ * Used to determine how to sort cards based on tags. The leftmost tags are given lower values while cards to the right are
* given higher values. Decimals are used to determine placement for cards with multiple tags
* @param doc the doc whose value is being determined
* @returns its value based on its tags
@@ -352,24 +325,14 @@ export class CollectionCardView extends CollectionSubView() {
docs.sort((docA, docB) => {
const [typeA, typeB] = (() => {
switch (sortType) {
- case cardSortings.Time:
- return [DateCast(docA.author_date)?.date ?? Date.now(), DateCast(docB.author_date)?.date ?? Date.now()];
- case cardSortings.Color: {
- const d1 = DashColor(StrCast(docA.backgroundColor));
- const d2 = DashColor(StrCast(docB.backgroundColor));
- return [d1.hsv().hue(), d2.hsv().hue()];
- }
- case cardSortings.Tag:
- return [this.tagValue(docA) ?? 9999, this.tagValue(docB) ?? 9999];
- case cardSortings.Chat:
- return [NumCast(docA.chatIndex) ?? 9999, NumCast(docB.chatIndex) ?? 9999];
- default:
- return [StrCast(docA.type), StrCast(docB.type)];
+ default:
+ case cardSortings.Type: return [StrCast(docA.type), StrCast(docB.type)];
+ case cardSortings.Chat: return [NumCast(docA.chatIndex, 9999), NumCast(docB.chatIndex,9999)];
+ case cardSortings.Time: return [DateCast(docA.author_date)?.date ?? Date.now(), DateCast(docB.author_date)?.date ?? Date.now()];
+ case cardSortings.Color:return [DashColor(StrCast(docA.backgroundColor)).hsv().hue(), DashColor(StrCast(docB.backgroundColor)).hsv().hue()];
}
- })();
-
- const out = typeA < typeB ? -1 : typeA > typeB ? 1 : 0;
- return isDesc ? out : -out;
+ })(); //prettier-ignore
+ return (typeA < typeB ? -1 : typeA > typeB ? 1 : 0) * (isDesc ? 1 : -1);
});
if (dragIndex !== -1) {
const draggedDoc = DragManager.docsBeingDragged[0];
@@ -382,6 +345,15 @@ export class CollectionCardView extends CollectionSubView() {
return docs;
};
+ isChildContentActive = () =>
+ this._props.isContentActive?.() === false
+ ? false
+ : this._props.isDocumentActive?.() && (this._props.childDocumentsActive?.() || BoolCast(this.Document.childDocumentsActive))
+ ? true
+ : this._props.childDocumentsActive?.() === false || this.Document.childDocumentsActive === false
+ ? false
+ : undefined;
+
displayDoc = (doc: Doc, screenToLocalTransform: () => Transform) => (
<DocumentView
{...this._props}
@@ -396,13 +368,14 @@ export class CollectionCardView extends CollectionSubView() {
LayoutTemplateString={this._props.childLayoutString}
containerViewPath={this.childContainerViewPath}
ScreenToLocalTransform={screenToLocalTransform} // makes sure the box wrapper thing is in the right spot
- isContentActive={emptyFunction}
isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
PanelWidth={this.childPanelWidth}
PanelHeight={this.childPanelHeight}
dontCenter="y" // Don't center it vertically, because the grid it's in is already doing that and we don't want to do it twice.
dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType}
- showTags={true}
+ showTags={BoolCast(this.layoutDoc.showChildTags)}
+ whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
+ isContentActive={this.isChildContentActive}
dontHideOnDrag
/>
);
@@ -416,13 +389,11 @@ export class CollectionCardView extends CollectionSubView() {
if (this.sortedDocs.length < this._maxRowCount) {
return this.sortedDocs.length;
}
- // 13 - 3 = 10
const totalCards = this.sortedDocs.length;
// if 9 or less
if (index < totalCards - (totalCards % this._maxRowCount)) {
return this._maxRowCount;
}
- // (3)
return totalCards % this._maxRowCount;
};
/**
@@ -441,19 +412,18 @@ export class CollectionCardView extends CollectionSubView() {
translateOverflowX = (realIndex: number, calcRowCards: number) => (realIndex < this._maxRowCount ? 0 : (this._maxRowCount - calcRowCards) * (this.childPanelWidth() / 2));
/**
- * Determines how far to translate a card in the y direction depending on its index, whether or not its being hovered, or if it's selected
- * @param isHovered
- * @param isSelected
- * @param realIndex
- * @param amCards
- * @param calcRowIndex
- * @returns
+ * Determines how far to translate a card in the y direction depending on its index and if it's selected
+ * @param isActive whether the card is focused for interaction
+ * @param realIndex index of card from start of deck
+ * @param amCards ??
+ * @param calcRowIndex index of card from start of row
+ * @returns Y translation of card
*/
- calculateTranslateY = (isHovered: boolean, isSelected: boolean, realIndex: number, amCards: number, calcRowIndex: number) => {
+ calculateTranslateY = (isActive: boolean, realIndex: number, amCards: number, calcRowIndex: number) => {
const rowHeight = (this._props.PanelHeight() * this.fitContentScale) / this.numRows;
const rowIndex = Math.trunc(realIndex / this._maxRowCount);
const rowToCenterShift = this.numRows / 2 - rowIndex;
- if (isSelected) return rowToCenterShift * rowHeight - rowHeight / 2;
+ if (isActive) return rowToCenterShift * rowHeight - rowHeight / 2;
if (amCards == 1) return 50 * this.fitContentScale;
return this.translateY(amCards, calcRowIndex, realIndex);
};
@@ -576,15 +546,43 @@ export class CollectionCardView extends CollectionSubView() {
await this.childPairStringListAndUpdateSortDesc();
};
+ childScreenToLocal = computedFn((doc: Doc, index: number, calcRowIndex: number, isSelected: boolean, amCards: number) => () => {
+ // need to explicitly trigger an invalidation since we're reading everything from the Dom
+ this._forceChildXf;
+ this._props.ScreenToLocalTransform();
+
+ const dref = this._docRefs.get(doc);
+ const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(dref?.ContentDiv);
+ if (!scale) return new Transform(0, 0, 0);
+
+ return new Transform(-translateX + (dref?.centeringX || 0) * scale,
+ -translateY + (dref?.centeringY || 0) * scale, 1)
+ .scale(1 / scale).rotate(!isSelected ? -this.rotate(amCards, calcRowIndex) : 0); // prettier-ignore
+ });
+
+ cardPointerUp = action((doc: Doc) => {
+ // if a card doc has just moved, or a card is selected and in front, then ignore this event
+ if (this.isSelected(doc) || this._dropped) {
+ this._dropped = false;
+ } else {
+ // otherwise, turn off documentDecorations becase we're in a selection transition and want to avoid artifacts.
+ // Turn them back on when the animation has completed and the render and backend structures are in synch
+ SnappingManager.SetHideDecorations(true);
+ setTimeout(
+ action(() => {
+ SnappingManager.SetHideDecorations(false);
+ this._forceChildXf++;
+ }),
+ 1000
+ );
+ }
+ });
+
/**
* Actually renders all the cards
*/
@computed get renderCards() {
- const sortedDocs = this.sortedDocs;
- const anySelected = this.childDocs.some(doc => DocumentView.SelectedDocs().includes(doc));
- const isEmpty = this.childDocsWithoutLinks.length === 0;
-
- if (isEmpty) {
+ if (!this.childDocsWithoutLinks.length) {
return (
<span className="no-card-span" style={{ width: ` ${this._props.PanelWidth()}px`, height: ` ${this._props.PanelHeight()}px` }}>
Sorry ! There are no cards in this group
@@ -593,29 +591,17 @@ export class CollectionCardView extends CollectionSubView() {
}
// Map sorted documents to their rendered components
- return sortedDocs.map((doc, index) => {
- const realIndex = sortedDocs.indexOf(doc);
- const calcRowIndex = this.overflowIndexCalc(realIndex);
- const amCards = this.overflowAmCardsCalc(realIndex);
+ return this.sortedDocs.map((doc, index) => {
+ const calcRowIndex = this.overflowIndexCalc(index);
+ const amCards = this.overflowAmCardsCalc(index);
const view = DocumentView.getDocumentView(doc, this.DocumentView?.());
- const isSelected = view?.ComponentView?.isAnyChildContentActive?.() || view?.IsSelected ? true : false;
-
- const childScreenToLocal = () => {
- // need to explicitly trigger an invalidation since we're reading everything from the Dom
- this._forceChildXf;
- this._props.ScreenToLocalTransform();
-
- const dref = this._docRefs.get(doc);
- const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(dref?.ContentDiv);
- return new Transform(-translateX + (dref?.centeringX || 0) * scale,
- -translateY + (dref?.centeringY || 0) * scale, 1)
- .scale(1 / scale).rotate(!isSelected ? -this.rotate(amCards, calcRowIndex) : 0); // prettier-ignore
- };
+
+ const childScreenToLocal = this.childScreenToLocal(doc, index, calcRowIndex, !!view?.IsContentActive, amCards);
const translateIfSelected = () => {
const indexInRow = index % this._maxRowCount;
const rowIndex = Math.trunc(index / this._maxRowCount);
- const rowCenterIndex = Math.min(this._maxRowCount, sortedDocs.length - rowIndex * this._maxRowCount) / 2;
+ const rowCenterIndex = Math.min(this._maxRowCount, this.sortedDocs.length - rowIndex * this._maxRowCount) / 2;
return (rowCenterIndex - indexInRow) * 100 - 50;
};
const aspect = NumCast(doc.height) / NumCast(doc.width, 1);
@@ -625,33 +611,18 @@ export class CollectionCardView extends CollectionSubView() {
return (
<div
key={doc[Id]}
- className={`card-item${isSelected ? '-active' : anySelected ? '-inactive' : ''}`}
- onPointerUp={action(() => {
- // if a card doc has just moved, or a card is selected and in front, then ignore this event
- if (DocumentView.SelectedDocs().includes(doc) || this._dropped) {
- this._dropped = false;
- } else {
- // otherwise, turn off documentDecorations becase we're in a selection transition and want to avoid artifacts.
- // Turn them back on when the animation has completed and the render and backend structures are in synch
- SnappingManager.SetIsResizing(doc[Id]);
- setTimeout(
- action(() => {
- SnappingManager.SetIsResizing(undefined);
- this._forceChildXf++;
- }),
- 600
- );
- }
- })}
+ className={`card-item${view?.IsContentActive ? '-active' : this.isAnyChildContentActive() ? '-inactive' : ''}`}
+ onPointerUp={() => this.cardPointerUp(doc)}
style={{
width: this.childPanelWidth(),
height: 'max-content',
- transform: `translateY(${this.calculateTranslateY(this._hoveredNodeIndex === index, isSelected, realIndex, amCards, calcRowIndex)}px)
- translateX(calc(${isSelected ? translateIfSelected() : 0}% + ${this.translateOverflowX(realIndex, amCards)}px))
- rotate(${!isSelected ? this.rotate(amCards, calcRowIndex) : 0}deg)
- scale(${isSelected ? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.05 : 1})`,
+ transform: `translateY(${this.calculateTranslateY(!!view?.IsContentActive, index, amCards, calcRowIndex)}px)
+ translateX(calc(${view?.IsContentActive ? translateIfSelected() : 0}% + ${this.translateOverflowX(index, amCards)}px))
+ rotate(${!view?.IsContentActive ? this.rotate(amCards, calcRowIndex) : 0}deg)
+ scale(${view?.IsContentActive ? `${Math.min(hscale, vscale) * 100}%` : this._hoveredNodeIndex === index ? 1.1 : 1})`,
}} // prettier-ignore
- onPointerEnter={() => !SnappingManager.IsDragging && this.setHoveredNodeIndex(index)}>
+ onPointerEnter={() => this.setHoveredNodeIndex(index)}
+ onPointerLeave={() => this.setHoveredNodeIndex(-1)}>
{this.displayDoc(doc, childScreenToLocal)}
</div>
);
@@ -678,8 +649,7 @@ export class CollectionCardView extends CollectionSubView() {
...(!isEmpty && { transform: `scale(${1 / this.fitContentScale})` }),
...(!isEmpty && { height: `${100 * this.fitContentScale}%` }),
gridAutoRows: `${100 / this.numRows}%`,
- }}
- onMouseLeave={() => this.setHoveredNodeIndex(-1)}>
+ }}>
{this.renderCards}
</div>
</div>
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx
index 139aebb02..c5da8e037 100644
--- a/src/client/views/collections/CollectionCarousel3DView.tsx
+++ b/src/client/views/collections/CollectionCarousel3DView.tsx
@@ -16,7 +16,7 @@ import './CollectionCarousel3DView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
import { Transform } from '../../util/Transform';
-// eslint-disable-next-line @typescript-eslint/no-var-requires
+// eslint-disable-next-line @typescript-eslint/no-require-imports
const { CAROUSEL3D_CENTER_SCALE, CAROUSEL3D_SIDE_SCALE, CAROUSEL3D_TOP } = require('../global/globalCssVariables.module.scss');
@observer
@@ -89,14 +89,13 @@ export class CollectionCarousel3DView extends CollectionSubView() {
const currentIndex = NumCast(this.layoutDoc._carousel_index);
const displayDoc = (childPair: { layout: Doc; data: Doc }, dxf: () => Transform) => (
<DocumentView
- // eslint-disable-next-line react/jsx-props-no-spreading
{...this._props}
Document={childPair.layout}
TemplateDataDocument={childPair.data}
// suppressSetHeight={true}
NativeWidth={returnZero}
NativeHeight={returnZero}
- fitWidth={undefined}
+ fitWidth={this._props.childLayoutFitWidth}
containerViewPath={this.childContainerViewPath}
onDoubleClickScript={this.onChildDoubleClick}
renderDepth={this._props.renderDepth + 1}
diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx
index 6ea263215..8b3a699ed 100644
--- a/src/client/views/collections/CollectionCarouselView.tsx
+++ b/src/client/views/collections/CollectionCarouselView.tsx
@@ -3,18 +3,18 @@ import { IReactionDisposer, action, computed, makeObservable, observable, reacti
import { observer } from 'mobx-react';
import * as React from 'react';
import { StopEvent, returnOne, returnZero } from '../../../ClientUtils';
-import { Doc, DocListCast, Opt } from '../../../fields/Doc';
+import { Doc, Opt } from '../../../fields/Doc';
import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
import { ContextMenu } from '../ContextMenu';
import { StyleProp } from '../StyleProp';
+import { TagItem } from '../TagsView';
import { DocumentView } from '../nodes/DocumentView';
import { FieldViewProps } from '../nodes/FieldView';
import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox';
import './CollectionCarouselView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { TagItem } from '../TagsView';
enum cardMode {
PRACTICE = 'practice',
@@ -164,8 +164,8 @@ export class CollectionCarouselView extends CollectionSubView() {
Document={doc}
NativeWidth={returnZero}
NativeHeight={returnZero}
- fitWidth={undefined}
- showTags={true}
+ fitWidth={this._props.childLayoutFitWidth}
+ showTags={BoolCast(this.layoutDoc.showChildTags)}
containerViewPath={this.childContainerViewPath}
setContentViewBox={undefined}
ScreenToLocalTransform={this.childScreenToLocalXf}
@@ -178,6 +178,7 @@ export class CollectionCarouselView extends CollectionSubView() {
LayoutTemplate={this._props.childLayoutTemplate}
LayoutTemplateString={this._props.childLayoutString}
TemplateDataDocument={DocCast(Doc.Layout(doc).resolvedDataDoc)}
+ xPadding={35}
PanelHeight={this.panelHeight}
/>
);
diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx
index 028133a6e..e1786d2c9 100644
--- a/src/client/views/collections/CollectionDockingView.tsx
+++ b/src/client/views/collections/CollectionDockingView.tsx
@@ -31,17 +31,17 @@ import { ScriptingRepl } from '../ScriptingRepl';
import { UndoStack } from '../UndoStack';
import './CollectionDockingView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-import { TabHTMLElement } from './TabDocView';
+import { TabDocView, TabHTMLElement } from './TabDocView';
@observer
export class CollectionDockingView extends CollectionSubView() {
- static tabClass: unknown = null;
+ static tabClass?: typeof TabDocView;
/**
* Initialize by assigning the add split method to DocumentView and by
* configuring golden layout to render its documents using the specified React component
* @param ele - typically would be set to TabDocView
*/
- public static Init(ele: unknown) {
+ public static Init(ele: typeof TabDocView) {
this.tabClass = ele;
DocumentView.addSplit = CollectionDockingView.AddSplit;
}
@@ -203,7 +203,6 @@ export class CollectionDockingView extends CollectionSubView() {
} else if (instance._goldenLayout.root.contentItems[0].isRow) {
// if row
switch (pullSide) {
- // eslint-disable-next-line default-case-last
default:
case OpenWhereMod.none:
case OpenWhereMod.right:
@@ -299,7 +298,7 @@ export class CollectionDockingView extends CollectionSubView() {
this._goldenLayout.unbind('tabCreated', this.tabCreated);
this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
this._goldenLayout.unbind('stackCreated', this.stackCreated);
- } catch (e) {
+ } catch {
/* empty */
}
this.tabMap.clear();
@@ -380,7 +379,7 @@ export class CollectionDockingView extends CollectionSubView() {
try {
this._goldenLayout.unbind('stackCreated', this.stackCreated);
this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed);
- } catch (e) {
+ } catch {
/* empty */
}
this._goldenLayout?.destroy();
@@ -507,8 +506,8 @@ export class CollectionDockingView extends CollectionSubView() {
dashboardDoc.myOverlayDocs = new List<Doc>();
dashboardDoc.myPublishedDocs = new List<Doc>();
- DashboardView.SetupDashboardTrails(dashboardDoc);
- DashboardView.SetupDashboardCalendars(dashboardDoc); // Zaul TODO: needed?
+ DashboardView.SetupDashboardTrails();
+ DashboardView.SetupDashboardCalendars(); // Zaul TODO: needed?
return DashboardView.openDashboard(dashboardDoc);
}
@@ -545,7 +544,7 @@ export class CollectionDockingView extends CollectionSubView() {
tabCreated = (tab: { contentItem: { element: HTMLElement[] } }) => {
this.tabMap.add(tab);
// InitTab is added to the tab's HTMLElement in TabDocView
- const tabdocviewContent = tab.contentItem.element[0]?.firstChild?.firstChild as unknown as TabHTMLElement;
+ const tabdocviewContent = tab.contentItem.element[0]?.firstChild?.firstChild as TabHTMLElement;
tabdocviewContent?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content)
};
diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss
index 3ec875df4..45d9394ed 100644
--- a/src/client/views/collections/CollectionMenu.scss
+++ b/src/client/views/collections/CollectionMenu.scss
@@ -6,7 +6,7 @@
align-content: center;
justify-content: space-between;
background-color: $dark-gray;
- height: 35px;
+ height: 40px;
border-bottom: $standard-border;
padding: 0 10px;
align-items: center;
diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx
index 5d32482c3..581201a20 100644
--- a/src/client/views/collections/CollectionSubView.tsx
+++ b/src/client/views/collections/CollectionSubView.tsx
@@ -296,7 +296,7 @@ export function CollectionSubView<X>() {
return false;
}
- protected async onExternalDrop(e: React.DragEvent, options: DocumentOptions, completed?: (docs: Doc[]) => void) {
+ protected async onExternalDrop(e: React.DragEvent, options: DocumentOptions = {}, completed?: (docs: Doc[]) => void) {
if (e.ctrlKey) {
e.stopPropagation(); // bcz: this is a hack to stop propagation when dropping an image on a text document with shift+ctrl
return;
diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx
index c9e934448..7418d4360 100644
--- a/src/client/views/collections/CollectionView.tsx
+++ b/src/client/views/collections/CollectionView.tsx
@@ -1,4 +1,3 @@
-/* eslint-disable react/jsx-props-no-spreading */
import { IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
@@ -16,7 +15,6 @@ import { ContextMenuProps } from '../ContextMenuItem';
import { ViewBoxAnnotatableComponent } from '../DocComponent';
import { FieldView } from '../nodes/FieldView';
import { OpenWhere } from '../nodes/OpenWhere';
-import { CollectionCalendarView } from './CollectionCalendarView';
import { CollectionCardView } from './CollectionCardDeckView';
import { CollectionCarousel3DView } from './CollectionCarousel3DView';
import { CollectionCarouselView } from './CollectionCarouselView';
diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx
index d2514dfd1..5444a7a57 100644
--- a/src/client/views/collections/TreeView.tsx
+++ b/src/client/views/collections/TreeView.tsx
@@ -467,7 +467,7 @@ export class TreeView extends ObservableReactComponent<TreeViewProps> {
return false;
}
- refTransform = (ref: HTMLElement | undefined | null) => {
+ refTransform = (ref: HTMLDivElement | undefined | null) => {
if (!ref) return this.ScreenToLocalTransform();
const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(ref);
return new Transform(-translateX, -translateY, 1).scale(1 / scale);
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
index c17371151..51add85a8 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormInfoState.tsx
@@ -46,7 +46,6 @@ export function InfoState(
gif?: string,
entryFunc?: () => unknown
) {
- // eslint-disable-next-line new-cap
return new infoState(msg, arcs, gif, entryFunc);
}
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
index f106eba26..d8678eebc 100644
--- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
+++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx
@@ -30,7 +30,7 @@ import { CompileScript } from '../../../util/Scripting';
import { ScriptingGlobals } from '../../../util/ScriptingGlobals';
import { freeformScrollMode, SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
-import { undoable, UndoManager } from '../../../util/UndoManager';
+import { undoable, undoBatch, UndoManager } from '../../../util/UndoManager';
import { Timeline } from '../../animationtimeline/Timeline';
import { ContextMenu } from '../../ContextMenu';
import { InkingStroke } from '../../InkingStroke';
@@ -42,6 +42,8 @@ import { FocusViewOptions } from '../../nodes/FocusViewOptions';
import { FormattedTextBox } from '../../nodes/formattedText/FormattedTextBox';
import { OpenWhere, OpenWhereMod } from '../../nodes/OpenWhere';
import { PinDocView, PinProps } from '../../PinFuncs';
+import { AnnotationPalette } from '../../smartdraw/AnnotationPalette';
+import { DrawingOptions, SmartDrawHandler } from '../../smartdraw/SmartDrawHandler';
import { StyleProp } from '../../StyleProp';
import { CollectionSubView, SubCollectionViewProps } from '../CollectionSubView';
import { TreeViewType } from '../CollectionTreeViewType';
@@ -115,6 +117,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
@observable _panZoomTransition: number = 0; // sets the pan/zoom transform ease time- used by nudge(), focus() etc to smoothly zoom/pan. set to 0 to use document's transition time or default of 0
@observable _firstRender = false; // this turns off rendering of the collection's content so that there's instant feedback when a tab is switched of what content will be shown. could be used for performance improvement
@observable _showAnimTimeline = false;
+ @observable _showDrawingEditor = false;
@observable _deleteList: DocumentView[] = [];
@observable _timelineRef = React.createRef<Timeline>();
@observable _marqueeViewRef = React.createRef<MarqueeView>();
@@ -135,7 +138,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine);
}
@computed get childPointerEvents() {
- return SnappingManager.IsResizing
+ return falseSnappingManager.IsResizing
? 'none'
: (this._props.childPointerEvents?.() ??
(this._props.viewDefDivClick || //
@@ -492,28 +495,31 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (!this.Document.isGroup) {
// group freeforms don't pan when dragged -- instead let the event go through to allow the group itself to drag
// prettier-ignore
+ const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
switch (Doc.ActiveTool) {
- case InkTool.Highlighter: break;
- case InkTool.Write: break;
- case InkTool.Pen: break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
+ case InkTool.Highlighter:
+ case InkTool.Write:
+ case InkTool.Pen:
+ break; // the GestureOverlay handles ink stroke input -- either as gestures, or drying as ink strokes that are added to document views
case InkTool.StrokeEraser:
case InkTool.SegmentEraser:
- this._batch = UndoManager.StartBatch('collectionErase');
- this._eraserPts.length = 0;
- setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, emptyFunction);
- break;
case InkTool.RadiusEraser:
this._batch = UndoManager.StartBatch('collectionErase');
this._eraserPts.length = 0;
- setupMoveUpEvents(this, e, this.onRadiusEraserMove, this.onEraserUp, emptyFunction);
+ setupMoveUpEvents(this, e, this.onEraserMove, this.onEraserUp, this.onEraserClick, hit !== -1);
+ e.stopPropagation();
+ break;
+ case InkTool.SmartDraw:
+ setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, () => this.showSmartDraw(e.pageX, e.pageY), hit !== -1);
+ e.stopPropagation();
break;
case InkTool.None:
if (!(this._props.layoutEngine?.() || StrCast(this.layoutDoc._layoutEngine))) {
- const hit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
- setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, hit !== -1, false);
+ const ahit = this._clusters.handlePointerDown(this.screenToFreeformContentsXf.transformPoint(e.clientX, e.clientY));
+ setupMoveUpEvents(this, e, this.onPointerMove, emptyFunction, emptyFunction, ahit !== -1, false);
}
break;
- default:
+ default:
}
}
}
@@ -536,24 +542,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
default: {
const { points } = ge;
const B = this.screenToFreeformContentsXf.transformBounds(ge.bounds.left, ge.bounds.top, ge.bounds.width, ge.bounds.height);
- const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
- const inkDoc = Docs.Create.InkDocument(
- points,
- { title: ge.gesture.toString(),
- x: B.x - inkWidth / 2,
- y: B.y - inkWidth / 2,
- _width: B.width + inkWidth,
- _height: B.height + inkWidth,
- stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
- inkWidth,
- ActiveInkColor(),
- ActiveInkBezierApprox(),
- ActiveFillColor(),
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveDash(),
- ActiveIsInkMask()
- );
+ const inkDoc = this.createInkDoc(points, B);
if (Doc.ActiveTool === InkTool.Write) {
this.unprocessedDocs.push(inkDoc);
CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
@@ -605,96 +594,76 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
/**
* Erases strokes by intersecting them with an invisible "eraser stroke".
- * By default this iterates through all intersected ink strokes, determines their segmentation, draws back the non-intersected segments,
- * and deletes the original stroke.
+ * By default this iterates through all intersected ink strokes, determines which parts of a stroke need to be erased based on the type
+ * of eraser, draws back the ink segments to keep, and deletes the original stroke.
+ *
+ * Radius eraser: erases strokes by intersecting them with a circle of variable radius. Essentially creates an InkField for the
+ * eraser circle, then determines its intersections with other ink strokes. Each stroke's DocumentView and its
+ * intersection t-values are put into a map, which gets looped through to take out the erased parts.
*/
- @action
- onEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
+ erase = (e: PointerEvent, delta: number[]) => {
+ e.stopImmediatePropagation();
const currPoint = { X: e.clientX, Y: e.clientY };
this._eraserPts.push([currPoint.X, currPoint.Y]);
this._eraserPts = this._eraserPts.slice(Math.max(0, this._eraserPts.length - 5));
- // if (this._eraserLock) return false; // leaving this commented out in case the idea is revisited in the future
- this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => {
- if (!this._deleteList.includes(intersect.inkView)) {
- this._deleteList.push(intersect.inkView);
- SetActiveInkWidth(StrCast(intersect.inkView.Document.stroke_width?.toString()) || '1');
- SetActiveInkColor(StrCast(intersect.inkView.Document.color?.toString()) || 'black');
- // create a new curve by appending all curves of the current segment together in order to render a single new stroke.
- if (Doc.ActiveTool !== InkTool.StrokeEraser) {
- // this._eraserLock++;
- const segments = this.segmentErase(intersect.inkView, intersect.t); // intersect.t is where the eraser intersected the ink stroke - want to remove the segment that starts at the intersection just before this t value and goes to the one just after it
- const newStrokes = segments?.map(segment => {
- const points = segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]);
- const bounds = InkField.getBounds(points);
- const B = this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
- const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
- return Docs.Create.InkDocument(
- points,
- { title: 'stroke',
- x: B.x - inkWidth / 2,
- y: B.y - inkWidth / 2,
- _width: B.width + inkWidth,
- _height: B.height + inkWidth,
- stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
- inkWidth,
- ActiveInkColor(),
- ActiveInkBezierApprox(),
- ActiveFillColor(),
- ActiveArrowStart(),
- ActiveArrowEnd(),
- ActiveDash(),
- ActiveIsInkMask()
- );
- });
- newStrokes && this.addDocument?.(newStrokes);
- // setTimeout(() => this._eraserLock--);
+ if (Doc.ActiveTool === InkTool.RadiusEraser) {
+ const strokeMap: Map<DocumentView, number[]> = this.getRadiusEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint);
+ strokeMap.forEach((intersects, stroke) => {
+ if (!this._deleteList.includes(stroke)) {
+ this._deleteList.push(stroke);
+ SetActiveInkWidth(StrCast(stroke.Document.stroke_width?.toString()) || '1');
+ SetActiveInkColor(StrCast(stroke.Document.color?.toString()) || 'black');
+ const segments = this.radiusErase(stroke, intersects.sort());
+ segments?.forEach(segment =>
+ this.forceStrokeGesture(
+ e,
+ Gestures.Stroke,
+ segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
+ )
+ );
}
- // Lower ink opacity to give the user a visual indicator of deletion.
- intersect.inkView.layoutDoc.opacity = 0;
- intersect.inkView.layoutDoc.dontIntersect = true;
- }
- });
+ stroke.layoutDoc.opacity = 0;
+ stroke.layoutDoc.dontIntersect = true;
+ });
+ } else {
+ this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => {
+ if (!this._deleteList.includes(intersect.inkView)) {
+ this._deleteList.push(intersect.inkView);
+ SetActiveInkWidth(StrCast(intersect.inkView.Document.stroke_width?.toString()) || '1');
+ SetActiveInkColor(StrCast(intersect.inkView.Document.color?.toString()) || 'black');
+ // create a new curve by appending all curves of the current segment together in order to render a single new stroke.
+ if (Doc.ActiveTool !== InkTool.StrokeEraser) {
+ // this._eraserLock++;
+ const segments = this.segmentErase(intersect.inkView, intersect.t); // intersect.t is where the eraser intersected the ink stroke - want to remove the segment that starts at the intersection just before this t value and goes to the one just after it
+ const newStrokes = segments?.map(segment => {
+ const points = segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]);
+ return this.createInkDoc(points);
+ });
+ newStrokes && this.addDocument?.(newStrokes);
+ // setTimeout(() => this._eraserLock--);
+ }
+ }
+ });
+ }
return false;
};
- /**
- * Erases strokes by intersecting them with a circle of variable radius. Essentially creates an InkField for the
- * eraser circle, then determines its intersections with other ink strokes. Each stroke's DocumentView and its
- * intersection t-values are put into a map, which gets looped through to take out the erased parts.
- * @param e
- * @param down
- * @param delta
- * @returns
- */
@action
- onRadiusEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
- const currPoint = { X: e.clientX, Y: e.clientY };
- this._eraserPts.push([currPoint.X, currPoint.Y]);
- this._eraserPts = this._eraserPts.slice(Math.max(0, this._eraserPts.length - 5));
- const strokeMap: Map<DocumentView, number[]> = this.getRadiusEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint);
-
- strokeMap.forEach((intersects, stroke) => {
- if (!this._deleteList.includes(stroke)) {
- this._deleteList.push(stroke);
- SetActiveInkWidth(StrCast(stroke.Document.stroke_width?.toString()) || '1');
- SetActiveInkColor(StrCast(stroke.Document.color?.toString()) || 'black');
- const segments = this.radiusErase(stroke, intersects.sort());
- segments?.forEach(segment =>
- this.forceStrokeGesture(
- e,
- Gestures.Stroke,
- segment.reduce((data, curve) => [...data, ...curve.points.map(p => stroke.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[])
- )
- );
- }
- stroke.layoutDoc.opacity = 0;
- stroke.layoutDoc.dontIntersect = true;
- });
+ onEraserMove = (e: PointerEvent, down: number[], delta: number[]) => {
+ this.erase(e, delta);
+ // if (this._eraserLock) return false; // leaving this commented out in case the idea is revisited in the future
return false;
};
- forceStrokeGesture = (e: PointerEvent, gesture: Gestures, points: InkData) => {
- this.onGesture(e, new GestureUtils.GestureEvent(gesture, points, InkField.getBounds(points)));
+ @action
+ onEraserClick = (e: PointerEvent) => {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ this.erase(e, [0, 0]);
+ };
+
+ forceStrokeGesture = (e: PointerEvent, gesture: Gestures, points: InkData, text?: string) => {
+ this.onGesture(e, new GestureUtils.GestureEvent(gesture, points, InkField.getBounds(points), text));
};
onPointerMove = (e: PointerEvent) => {
@@ -722,7 +691,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
// increase radius slightly based on the erased stroke's width, added to make eraser look more realistic
const radius = ActiveEraserWidth() + 5 + inkStrokeWidth * 0.1; // add 5 to prevent eraser from being too small
const c = 0.551915024494; // circle tangent length to side ratio
- const movement = { x: endInkCoordsIn.X - startInkCoordsIn.X, y: endInkCoordsIn.Y - startInkCoordsIn.Y };
+ const movement = { x: Math.max(endInkCoordsIn.X - startInkCoordsIn.X, 1), y: Math.max(endInkCoordsIn.Y - startInkCoordsIn.Y, 1) };
const moveLen = Math.sqrt(movement.x ** 2 + movement.y ** 2);
const direction = { x: (movement.x / moveLen) * radius, y: (movement.y / moveLen) * radius };
const normal = { x: -direction.y, y: direction.x }; // prettier-ignore
@@ -889,13 +858,12 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
strokeToTVals.set(inkView, [Math.floor(inkData.length / 4) + 1]);
}
}
-
for (let i = 0; i < inkData.length - 3; i += 4) {
// iterate over each segment of bezier curve
for (let j = 0; j < eraserInkData.length - 3; j += 4) {
const intersectCurve: Bezier = InkField.Segment(inkData, i); // other curve
const eraserCurve: Bezier = InkField.Segment(eraserInkData, j); // eraser curve
- this.bintersects(intersectCurve, eraserCurve).forEach((val: string | number, k: number) => {
+ InkField.bintersects(intersectCurve, eraserCurve).forEach((val: string | number, k: number) => {
// Converting the Bezier.js Split type to a t-value number.
const t = +val.toString().split('/')[0];
if (k % 2 === 0) {
@@ -1169,26 +1137,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return this.getClosestTs(tVals, excludeT, startIndex, mid - 1);
};
- // for some reason bezier.js doesn't handle the case of intersecting a linear curve, so we wrap the intersection
- // call in a test for linearity
- bintersects = (curve: Bezier, otherCurve: Bezier) => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- if ((curve as any)._linear) {
- // bezier.js doesn't intersect properly if the curve is actually a line -- so get intersect other curve against this line, then figure out the t coordinates of the intersection on this line
- const intersections = otherCurve.lineIntersects({ p1: curve.points[0], p2: curve.points[3] });
- if (intersections.length) {
- const intPt = otherCurve.get(intersections[0]);
- const intT = curve.project(intPt).t;
- return intT ? [intT] : [];
- }
- }
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- if ((otherCurve as any)._linear) {
- return curve.lineIntersects({ p1: otherCurve.points[0], p2: otherCurve.points[3] });
- }
- return curve.intersects(otherCurve);
- };
-
/**
* Determines all possible intersections of the current curve of the intersected ink stroke with all other curves of all
* ink strokes in the current collection.
@@ -1220,7 +1168,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
if (apt.d !== undefined && apt.d < 1 && apt.t !== undefined && !tVals.includes(apt.t)) {
tVals.push(apt.t);
}
- this.bintersects(curve, otherCurve).forEach((val: string | number, ival: number) => {
+ InkField.bintersects(curve, otherCurve).forEach((val: string | number, ival: number) => {
// Converting the Bezier.js Split type to a t-value number.
const t = +val.toString().split('/')[0];
if (ival % 2 === 0 && !tVals.includes(t)) tVals.push(t); // bcz: Hack! don't know why but intersection points are doubled from bezier.js (but not identical).
@@ -1233,6 +1181,108 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
return tVals;
};
+ /**
+ * Creates an ink document to add to the freeform canvas.
+ */
+ createInkDoc = (points: InkData, transformedBounds?: { x: number; y: number; width: number; height: number }) => {
+ const bounds = InkField.getBounds(points);
+ const B = transformedBounds || this.screenToFreeformContentsXf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
+ const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
+ return Docs.Create.InkDocument(
+ points,
+ { title: 'stroke',
+ x: B.x - inkWidth / 2,
+ y: B.y - inkWidth / 2,
+ _width: B.width + inkWidth,
+ _height: B.height + inkWidth,
+ stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
+ inkWidth,
+ ActiveInkColor(),
+ ActiveInkBezierApprox(),
+ ActiveFillColor(),
+ ActiveArrowStart(),
+ ActiveArrowEnd(),
+ ActiveDash(),
+ ActiveIsInkMask()
+ );
+ };
+
+ @action
+ showSmartDraw = (x: number, y: number) => {
+ SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc;
+ SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing;
+ SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
+ SmartDrawHandler.Instance.displaySmartDrawHandler(x, y);
+ };
+
+ _drawing: Doc[] = [];
+ _drawingContainer: Doc | undefined = undefined;
+ /**
+ * Function that creates a drawing--a group of ink strokes--to go with the smart draw function.
+ */
+ @undoBatch
+ createDrawingDoc = (strokeData: [InkData, string, string][], opts: DrawingOptions, gptRes: string) => {
+ this._drawing = [];
+ const xf = this.screenToFreeformContentsXf;
+ strokeData.forEach((stroke: [InkData, string, string]) => {
+ const bounds = InkField.getBounds(stroke[0]);
+ const B = xf.transformBounds(bounds.left, bounds.top, bounds.width, bounds.height);
+ const inkWidth = ActiveInkWidth() * this.ScreenToLocalBoxXf().Scale;
+ const inkDoc = Docs.Create.InkDocument(
+ stroke[0],
+ { title: 'stroke',
+ x: B.x - inkWidth / 2,
+ y: B.y - inkWidth / 2,
+ _width: B.width + inkWidth,
+ _height: B.height + inkWidth,
+ stroke_showLabel: BoolCast(Doc.UserDoc().activeInkHideTextLabels)}, // prettier-ignore
+ inkWidth,
+ opts.autoColor ? stroke[1] : ActiveInkColor(),
+ ActiveInkBezierApprox(),
+ stroke[2] === 'none' ? ActiveFillColor() : stroke[2],
+ ActiveArrowStart(),
+ ActiveArrowEnd(),
+ ActiveDash(),
+ ActiveIsInkMask()
+ );
+ this._drawing.push(inkDoc);
+ });
+ return MarqueeView.getCollection(this._drawing, undefined, true, { left: opts.x, top: opts.y, width: 1, height: 1 });
+ };
+
+ /**
+ * Part of regenerating a drawing--deletes the old drawing.
+ */
+ removeDrawing = (useLastContainer: boolean, doc?: Doc) => {
+ this._batch = UndoManager.StartBatch('regenerateDrawing');
+ if (useLastContainer && this._drawingContainer) {
+ this._props.removeDocument?.(this._drawingContainer);
+ } else if (doc) {
+ const docData = doc[DocData];
+ const children = DocListCast(docData.data);
+ this._props.removeDocument?.(doc);
+ this._props.removeDocument?.(children);
+ }
+ this._drawing = [];
+ };
+
+ /**
+ * Adds the created drawing to the freeform canvas and sets the metadata.
+ */
+ addDrawing = (doc: Doc, opts: DrawingOptions, gptRes: string) => {
+ const docData = doc[DocData];
+ docData.title = opts.text.match(/^(.*?)~~~.*$/)?.[1] || opts.text;
+ docData.width = opts.size;
+ docData.drawingInput = opts.text;
+ docData.drawingComplexity = opts.complexity;
+ docData.drawingColored = opts.autoColor;
+ docData.drawingSize = opts.size;
+ docData.drawingData = gptRes;
+ this._drawingContainer = doc;
+ this.addDocument(doc);
+ this._batch?.end();
+ };
+
@action
zoom = (pointX: number, pointY: number, deltaY: number): void => {
if (this.Document.isGroup || this.Document[(this._props.viewField ?? '_') + 'freeform_noZoom']) return;
@@ -1803,33 +1853,34 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
Object.values(this._disposers).forEach(disposer => disposer?.());
}
- updateIcon = () => {
+ updateIcon = (usePanelDimensions?: boolean) => {
const contentDiv = this.DocumentView?.().ContentDiv;
- contentDiv &&
- UpdateIcon(
- this.layoutDoc[Id] + '_icon_' + new Date().getTime(),
- contentDiv,
- NumCast(this.layoutDoc._width),
- NumCast(this.layoutDoc._height),
- this._props.PanelWidth(),
- this._props.PanelHeight(),
- 0,
- 1,
- false,
- '',
- (iconFile, nativeWidth, nativeHeight) => {
- this.dataDoc.icon = new ImageField(iconFile);
- this.dataDoc.icon_nativeWidth = nativeWidth;
- this.dataDoc.icon_nativeHeight = nativeHeight;
- }
- );
+ return !contentDiv
+ ? new Promise<void>(res => res())
+ : UpdateIcon(
+ this.layoutDoc[Id] + '_icon_' + new Date().getTime(),
+ contentDiv,
+ usePanelDimensions ? this._props.PanelWidth() : NumCast(this.layoutDoc._width),
+ usePanelDimensions ? this._props.PanelHeight() : NumCast(this.layoutDoc._height),
+ this._props.PanelWidth(),
+ this._props.PanelHeight(),
+ 0,
+ 1,
+ false,
+ '',
+ (iconFile, nativeWidth, nativeHeight) => {
+ this.dataDoc.icon = new ImageField(iconFile);
+ this.dataDoc.icon_nativeWidth = nativeWidth;
+ this.dataDoc.icon_nativeHeight = nativeHeight;
+ }
+ );
};
@action
onCursorMove = (e: React.PointerEvent) => {
- this._eraserX = e.clientX;
- this._eraserY = e.clientY;
- // super.setCursorPosition(this.getTransform().transformPoint(e.clientX, e.clientY));
+ const locPt = this.ScreenToLocalBoxXf().transformPoint(e.clientX, e.clientY);
+ this._eraserX = locPt[0];
+ this._eraserY = locPt[1];
};
@action
@@ -1910,7 +1961,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
icon: 'eye',
});
appearanceItems.push({ description: `Pin View`, event: () => this._props.pinToPres(this.Document, { pinViewport: MarqueeView.CurViewBounds(this.dataDoc, this._props.PanelWidth(), this._props.PanelHeight()) }), icon: 'map-pin' });
- !Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: this.updateIcon, icon: 'compress-arrows-alt' });
+ !Doc.noviceMode && appearanceItems.push({ description: `update icon`, event: () => this.updateIcon(), icon: 'compress-arrows-alt' });
this._props.renderDepth && appearanceItems.push({ description: 'Ungroup collection', event: this.promoteCollection, icon: 'table' });
this.Document.isGroup && this.Document.transcription && appearanceItems.push({ description: 'Ink to text', event: this.transcribeStrokes, icon: 'font' });
@@ -1930,6 +1981,21 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
}),
icon: 'eye',
});
+ optionItems.push({
+ description: 'Show Drawing Editor',
+ event: action(() => {
+ SmartDrawHandler.Instance.CreateDrawingDoc = this.createDrawingDoc;
+ SmartDrawHandler.Instance.AddDrawing = this.addDrawing;
+ SmartDrawHandler.Instance.RemoveDrawing = this.removeDrawing;
+ !SmartDrawHandler.Instance.ShowRegenerate ? SmartDrawHandler.Instance.displayRegenerate(this._downX, this._downY - 10) : SmartDrawHandler.Instance.hideRegenerate();
+ }),
+ icon: 'pen-to-square',
+ });
+ optionItems.push({
+ description: this.Document.savedAsAnno ? 'Saved as Annotation!' : 'Save to Annotation Palette',
+ event: action(undoable(async () => await AnnotationPalette.addToPalette(this.Document), 'save to palette')),
+ icon: this.Document.savedAsAnno ? 'clipboard-check' : 'file-arrow-down',
+ });
this._props.renderDepth &&
optionItems.push({
description: 'Use Background Color as Default',
@@ -1945,6 +2011,14 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
!options && ContextMenu.Instance.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
const mores = ContextMenu.Instance.findByDescription('More...');
const moreItems = mores?.subitems ?? [];
+ moreItems.push({
+ description: 'recognize all ink',
+ event: () => {
+ this.unprocessedDocs.push(...this.childDocs.filter(doc => doc.type === DocumentType.INK));
+ CollectionFreeFormView.collectionsWithUnprocessedInk.add(this);
+ },
+ icon: 'pen',
+ });
!mores && ContextMenu.Instance.addItem({ description: 'More...', subitems: moreItems, icon: 'eye' });
};
@@ -2133,8 +2207,8 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection
onPointerMove={this.onCursorMove}
style={{
position: 'fixed',
- left: this._eraserX - 60,
- top: this._eraserY - 100,
+ left: this._eraserX,
+ top: this._eraserY,
width: (ActiveEraserWidth() + 5) * 2,
height: (ActiveEraserWidth() + 5) * 2,
borderRadius: '50%',
diff --git a/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss b/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss
index e7413bf8e..9b8727e1a 100644
--- a/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss
+++ b/src/client/views/collections/collectionFreeForm/ImageLabelHandler.scss
@@ -42,3 +42,17 @@
}
}
}
+
+.complexity-slider {
+ width: 50%; /* Full-width */
+ height: 25px; /* Specified height */
+ background: #d3d3d3; /* Grey background */
+ outline: none; /* Remove outline */
+ opacity: 0.7; /* Set transparency (for mouse-over effects on hover) */
+ -webkit-transition: 0.2s; /* 0.2 seconds transition on hover */
+ transition: opacity 0.2s;
+
+ :hover {
+ opacity: 1; /* Fully shown on mouse-over */
+ }
+}
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
index 44c916ab9..de65b240f 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeOptionsMenu.tsx
@@ -3,6 +3,7 @@ import { IconButton } from 'browndash-components';
import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
+import { Doc } from '../../../../fields/Doc';
import { unimplementedFunction } from '../../../../Utils';
import { SettingsManager } from '../../../util/SettingsManager';
import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
@@ -12,7 +13,7 @@ export class MarqueeOptionsMenu extends AntimodeMenu<AntimodeMenuProps> {
// eslint-disable-next-line no-use-before-define
static Instance: MarqueeOptionsMenu;
- public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean) => void = unimplementedFunction;
+ public createCollection: (e: KeyboardEvent | React.PointerEvent | undefined, group?: boolean, selection?: Doc[]) => Doc | void = unimplementedFunction;
public delete: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
public summarize: (e: KeyboardEvent | React.PointerEvent | undefined) => void = unimplementedFunction;
public showMarquee: () => void = unimplementedFunction;
diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
index ccb6bc9be..10709cc00 100644
--- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
+++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx
@@ -2,11 +2,11 @@ import { action, computed, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ClientUtils, lightOrDark, returnFalse } from '../../../../ClientUtils';
-import { intersectRect } from '../../../../Utils';
+import { intersectRect, unimplementedFunction } from '../../../../Utils';
import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
import { AclAdmin, AclAugment, AclEdit, DocData } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
-import { InkTool } from '../../../../fields/InkField';
+import { InkData, InkTool } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
import { Cast, NumCast, StrCast } from '../../../../fields/Types';
import { ImageField } from '../../../../fields/URLField';
@@ -57,6 +57,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
return { left: NumCast(pinDoc._freeform_panX) - panelWidth / 2 / ps, top: NumCast(pinDoc._freeform_panY) - panelHeight / 2 / ps, width: panelWidth / ps, height: panelHeight / ps };
}
+ // eslint-disable-next-line no-use-before-define
static Instance: MarqueeView;
constructor(props: SubCollectionViewProps & MarqueeViewProps) {
@@ -88,6 +89,8 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
return bounds;
}
+ public AddInkDoc: (points: InkData) => Doc | void = unimplementedFunction;
+
componentDidMount() {
this._props.setPreviewCursor?.(this.setPreviewCursor);
}
@@ -363,7 +366,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
this.hideMarquee();
});
- getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, makeGroup: Opt<boolean>) => {
+ public static getCollection = action((selected: Doc[], creator: Opt<(documents: Array<Doc>, options: DocumentOptions, id?: string) => Doc>, makeGroup: Opt<boolean>, bounds: MarqueeViewBounds) => {
const newCollection = creator
? creator(selected, { title: 'nested stack' })
: ((doc: Doc) => {
@@ -375,14 +378,13 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
return doc;
})(Doc.MakeCopy(Doc.UserDoc().emptyCollection as Doc, true));
newCollection.isSystem = undefined;
- newCollection._width = this.Bounds.width;
- newCollection._height = this.Bounds.height;
+ newCollection._width = bounds.width || 1; // if width/height are unset/0, then groups won't autoexpand to contain their children
+ newCollection._height = bounds.height || 1;
newCollection._dragWhenActive = makeGroup;
- newCollection.x = this.Bounds.left;
- newCollection.y = this.Bounds.top;
+ newCollection.x = bounds.left;
+ newCollection.y = bounds.top;
newCollection.layout_fitWidth = true;
selected.forEach(d => Doc.SetContainer(d, newCollection));
- this.hideMarquee();
return newCollection;
});
@@ -419,7 +421,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
this._props.removeDocument?.(selected);
}
- const newCollection = this.getCollection(selected, (e as KeyboardEvent)?.key === 't' ? Docs.Create.StackingDocument : undefined, group);
+ const newCollection = MarqueeView.getCollection(selected, (e as KeyboardEvent)?.key === 't' ? Docs.Create.StackingDocument : undefined, group, this.Bounds);
newCollection._freeform_panX = this.Bounds.left + this.Bounds.width / 2;
newCollection._freeform_panY = this.Bounds.top + this.Bounds.height / 2;
newCollection._currentFrame = activeFrame;
@@ -427,6 +429,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
this._props.selectDocuments([newCollection]);
MarqueeOptionsMenu.Instance.fadeOut(true);
this.hideMarquee();
+ return newCollection;
});
/**
@@ -455,20 +458,19 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
let x_offset = 0;
let y_offset = 0;
let row_count = 0;
+ const newColDim = 900;
for (const label of labelGroups) {
- const newCollection = this.getCollection([], undefined, false);
- newCollection._width = 900;
- newCollection._height = 900;
- newCollection._x = this.Bounds.left;
- newCollection._y = this.Bounds.top;
+ const newCollection = MarqueeView.getCollection([], undefined, false, this.Bounds);
+ newCollection._x = this.Bounds.left + x_offset;
+ newCollection._y = this.Bounds.top + y_offset;
+ newCollection._width = newColDim;
+ newCollection._height = newColDim;
newCollection._freeform_panX = this.Bounds.left + this.Bounds.width / 2;
newCollection._freeform_panY = this.Bounds.top + this.Bounds.height / 2;
- newCollection._x = (newCollection._x as number) + x_offset;
- newCollection._y = (newCollection._y as number) + y_offset;
- x_offset += (newCollection._width as number) + 40;
+ x_offset += newColDim + 40;
row_count += 1;
if (row_count == 3) {
- y_offset += (newCollection._height as number) + 40;
+ y_offset += newColDim + 40;
x_offset = 0;
row_count = 0;
}
@@ -547,7 +549,6 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
};
touchesLine(r1: { left: number; top: number; width: number; height: number }) {
- // eslint-disable-next-line no-restricted-syntax
for (const lassoPt of this._lassoPts) {
const topLeft = this.Transform.transformPoint(lassoPt[0], lassoPt[1]);
if (r1.left < topLeft[0] && topLeft[0] < r1.left + r1.width && r1.top < topLeft[1] && topLeft[1] < r1.top + r1.height) {
@@ -568,7 +569,6 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
let hasLeft = false;
let hasBottom = false;
let hasRight = false;
- // eslint-disable-next-line no-restricted-syntax
for (const lassoPt of this._lassoPts) {
const truePoint = this.Transform.transformPoint(lassoPt[0], lassoPt[1]);
hasLeft = hasLeft || (truePoint[0] > tl[0] && truePoint[0] < r1.left && truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height);
@@ -685,8 +685,7 @@ export class MarqueeView extends ObservableReactComponent<SubCollectionViewProps
<div
className="marqueeView"
ref={r => {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- r?.addEventListener('dashDragMovePause', this.onDragMovePause as any);
+ r?.addEventListener('dashDragMovePause', this.onDragMovePause as EventListenerOrEventListenerObject);
this.MarqueeRef = r;
}}
style={{
diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
index 8b0639b3b..325628d53 100644
--- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
+++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx
@@ -32,7 +32,6 @@ import './CollectionSchemaView.scss';
import { SchemaColumnHeader } from './SchemaColumnHeader';
import { SchemaRowBox } from './SchemaRowBox';
-// eslint-disable-next-line @typescript-eslint/no-var-requires
const { SCHEMA_NEW_NODE_HEIGHT } = require('../../global/globalCssVariables.module.scss'); // prettier-ignore
export const FInfotoColType: { [key: string]: ColumnType } = {