aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/collections/CollectionCardDeckView.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2025-03-10 16:13:04 -0400
committerbobzel <zzzman@gmail.com>2025-03-10 16:13:04 -0400
commitb7989dded8bb001876de6cbca59bf77935f0daf7 (patch)
tree0dba0665674db7bb84770833df0a4100d0520701 /src/client/views/collections/CollectionCardDeckView.tsx
parent4979415d4604d280e81a162bf9a9d39c731d3738 (diff)
parent5bf944035c0ba94ad15245416f51ca0329a51bde (diff)
Merge branch 'master' into alyssa-starter
Diffstat (limited to 'src/client/views/collections/CollectionCardDeckView.tsx')
-rw-r--r--src/client/views/collections/CollectionCardDeckView.tsx348
1 files changed, 95 insertions, 253 deletions
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index 0ba6b679c..756b37f99 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -1,42 +1,33 @@
-import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction } from 'mobx';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import * as CSS from 'csstype';
+import { action, computed, IReactionDisposer, makeObservable, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import { computedFn } from 'mobx-utils';
import * as React from 'react';
-import { ClientUtils, DashColor, imageUrlToBase64, returnFalse, returnNever, returnZero } from '../../../ClientUtils';
+import { ClientUtils, returnFalse, returnNever, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
import { Doc, DocListCast, Opt } from '../../../fields/Doc';
-import { Animation, DocData } from '../../../fields/DocSymbols';
+import { Animation } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
import { List } from '../../../fields/List';
import { ScriptField } from '../../../fields/ScriptField';
-import { BoolCast, DateCast, DocCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
-import { URLField } from '../../../fields/URLField';
-import { gptImageLabel } from '../../apis/gpt/GPT';
+import { BoolCast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types';
import { DocumentType } from '../../documents/DocumentTypes';
import { Docs } from '../../documents/Documents';
import { DragManager } from '../../util/DragManager';
import { dropActionType } from '../../util/DropActionTypes';
+import { SettingsManager } from '../../util/SettingsManager';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
-import { undoable } from '../../util/UndoManager';
-import { PinDocView } from '../PinFuncs';
+import { undoable, UndoManager } from '../../util/UndoManager';
+import { PinDocView, PinProps } from '../PinFuncs';
import { StyleProp } from '../StyleProp';
import { TagItem } from '../TagsView';
import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
import { FocusViewOptions } from '../nodes/FocusViewOptions';
-import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
import './CollectionCardDeckView.scss';
import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView';
-enum cardSortings {
- Time = 'time',
- Type = 'type',
- Color = 'color',
- Chat = 'chat',
- Tag = 'tag',
- None = '',
-}
-
/**
* New view type specifically for studying more dynamically. Allows you to reorder docs however you see fit, easily
* sort and filter using presets, and customize your experience with chat gpt.
@@ -48,21 +39,19 @@ enum cardSortings {
export class CollectionCardView extends CollectionSubView() {
private _dropDisposer?: DragManager.DragDropDisposer;
private _disposers: { [key: string]: IReactionDisposer } = {};
- private _textToDoc = new Map<string, Doc>();
private _oldWheel: HTMLElement | null = null;
private _dropped = false; // set when a card doc has just moved and the drop method has been called - prevents the pointerUp method from hiding doc decorations (which needs to be done when clicking on a card to animate it to front/center)
private _setCurDocScript = () => ScriptField.MakeScript('scriptContext.layoutDoc._card_curDoc=this', { scriptContext: 'any' })!;
+ private _draggerRef = React.createRef<HTMLDivElement>();
@observable _forceChildXf = 0;
@observable _hoveredNodeIndex = -1;
@observable _docRefs = new ObservableMap<Doc, DocumentView>();
- @observable _maxRowCount = 10;
- @observable _docDraggedIndex: number = -1;
+ @observable _cursor: CSS.Property.Cursor = 'ew-resize';
constructor(props: SubCollectionViewProps) {
super(props);
makeObservable(this);
- this.setRegenerateCallback();
}
protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
this._dropDisposer?.();
@@ -74,36 +63,17 @@ export class CollectionCardView extends CollectionSubView() {
// prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling
ele?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
};
- /**
- * Callback to ensure gpt's text versions of the child docs are updated
- */
- setRegenerateCallback = () => GPTPopup.Instance.setRegenerateCallback(this.childPairStringListAndUpdateSortDesc);
-
- /**
- * update's gpt's doc-text list and initializes callbacks
- */
- @action
- childPairStringListAndUpdateSortDesc = async () => {
- const sortDesc = await this.childPairStringList(); // Await the promise to get the string result
- GPTPopup.Instance.setSortDesc(sortDesc.join());
- GPTPopup.Instance.onSortComplete = (sortResult: string, questionType: string, tag?: string) => this.processGptOutput(sortResult, questionType, tag);
- GPTPopup.Instance.onQuizRandom = () => this.quizMode();
- };
+ @computed get cardWidth() {
+ return NumCast(this.layoutDoc._cardWidth, 50);
+ }
+ @computed get _maxRowCount() {
+ return Math.ceil(this.cardDeckWidth / this.cardWidth);
+ }
componentDidMount() {
this._props.setContentViewBox?.(this);
- this._disposers.sort = reaction(
- () => GPTPopup.Instance.visible,
- isVis => {
- if (isVis) {
- this.openChatPopup();
- } else {
- this.Document.card_sort = this.cardSort === cardSortings.Chat ? '' : this.Document.card_sort;
- }
- }
- );
// if card deck moves, then the child doc views are hidden so their screen to local transforms will return empty rectangles
- // when inquired from the dom (below in childScreenToLocal). When the doc is actually renders, we need to act like the
+ // when inquired from the dom (below in childScreenToLocal). When the doc is actually rendered, we need to act like the
// dash data just changed and trigger a React involidation with the correct data (read from the dom).
this._disposers.child = reaction(
() => [this.Document.x, this.Document.y],
@@ -113,6 +83,12 @@ export class CollectionCardView extends CollectionSubView() {
}
}
);
+ this._disposers.select = reaction(
+ () => this.childDocs.find(d => this._docRefs.get(d)?.IsSelected),
+ selected => {
+ selected && (this.layoutDoc._card_curDoc = selected);
+ }
+ );
}
componentWillUnmount() {
@@ -120,20 +96,17 @@ export class CollectionCardView extends CollectionSubView() {
this._dropDisposer?.();
}
- @computed get cardSort() {
- return StrCast(this.Document.card_sort) as cardSortings;
- }
/**
* Number of rows of cards to be rendered
*/
@computed get numRows() {
- return Math.ceil(this.sortedDocs.length / this._maxRowCount);
+ return Math.ceil(this.childDocs.length / this._maxRowCount);
}
/**
* Circle arc size, in radians, to layout cards
*/
@computed get archAngle() {
- return NumCast(this.layoutDoc.card_arch, 90) * (Math.PI / 180) * (this.childCards.length < this._maxRowCount ? this.childCards.length / this._maxRowCount : 1);
+ return NumCast(this.layoutDoc.card_arch, 90) * (Math.PI / 180) * (this.childDocsNoInk.length < this._maxRowCount ? this.childDocsNoInk.length / this._maxRowCount : 1);
}
/**
* Spacing card rows as a percent of Doc size. 100 means rows spread out to fill 100% of the Doc vertically. Default is 60%
@@ -145,7 +118,7 @@ export class CollectionCardView extends CollectionSubView() {
/**
* The child documents to be rendered-- everything other than ink/link docs (which are marks as being svg's)
*/
- @computed get childCards() {
+ @computed get childDocsNoInk() {
return this.childLayoutPairs.filter(pair => !pair.layout.layout_isSvg);
}
@@ -153,28 +126,32 @@ export class CollectionCardView extends CollectionSubView() {
* how much to scale down the contents of the view so that everything will fit
*/
@computed get fitContentScale() {
- const length = Math.min(this.childCards.length, this._maxRowCount);
- return (this.childPanelWidth() * length) / this._props.PanelWidth();
+ const length = Math.min(this.childDocsNoInk.length, this._maxRowCount);
+ return (this.childPanelWidth() * length) / (this._props.PanelWidth() - 2 * this.xMargin);
}
@computed get nativeScaling() {
return this._props.NativeDimScaling?.() || 1;
}
- /**
- * When in quiz mode, randomly selects a document
- */
- quizMode = () => {
- const randomIndex = Math.floor(Math.random() * this.childDocs.length);
- this.layoutDoc._card_curDoc = this.childDocs[randomIndex];
- };
+ @computed get xMargin() {
+ return NumCast(this.layoutDoc._xMargin, Math.max(3, 0.05 * this._props.PanelWidth()));
+ }
+
+ @computed get yMargin() {
+ return this._props.yPadding || NumCast(this.layoutDoc._yMargin, Math.min(5, 0.05 * this._props.PanelWidth()));
+ }
+
+ @computed get cardDeckWidth() {
+ return this._props.PanelWidth() - 2 * this.xMargin;
+ }
setHoveredNodeIndex = action((index: number) => {
if (!SnappingManager.IsDragging) this._hoveredNodeIndex = index;
});
isSelected = (doc: Doc) => this._docRefs.get(doc)?.IsSelected;
- childPanelWidth = () => NumCast(this.layoutDoc.childPanelWidth, Math.max(150, this._props.PanelWidth() / (this.childCards.length > this._maxRowCount ? this._maxRowCount : this.childCards.length) / this.nativeScaling));
+ childPanelWidth = () => NumCast(this.layoutDoc.childPanelWidth, Math.max(150, this._props.PanelWidth() / (this.childDocsNoInk.length > this._maxRowCount ? this._maxRowCount : this.childDocsNoInk.length) / this.nativeScaling));
childPanelHeight = () => this._props.PanelHeight() * this.fitContentScale;
onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this.isAnyChildContentActive();
@@ -187,7 +164,7 @@ export class CollectionCardView extends CollectionSubView() {
* @returns the card's new index
*/
findCardDropIndex = (mouseX: number, mouseY: number) => {
- const cardCount = this.sortedDocs.length;
+ const cardCount = this.childDocs.length;
let index = 0;
const cardWidth = cardCount < this._maxRowCount ? this._props.PanelWidth() / cardCount : this._props.PanelWidth() / this._maxRowCount;
@@ -221,8 +198,8 @@ export class CollectionCardView extends CollectionSubView() {
*/
@action
onPointerMove = (x: number, y: number) => {
- if (DragManager.docsBeingDragged.some(doc => this.sortedDocs.includes(doc)) || SnappingManager.CanEmbed) {
- this._docDraggedIndex = this.findCardDropIndex(x, y);
+ if (DragManager.docsBeingDragged.some(doc => this.childDocs.includes(doc)) || SnappingManager.CanEmbed) {
+ this.docDraggedIndex = this.findCardDropIndex(x, y);
}
};
@@ -235,14 +212,14 @@ export class CollectionCardView extends CollectionSubView() {
onInternalDrop = undoable(
action((e: Event, de: DragManager.DropEvent) => {
if (de.complete.docDragData) {
- const dragIndex = this._docDraggedIndex;
+ const dragIndex = this.docDraggedIndex;
const draggedDoc = DragManager.docsBeingDragged[0];
if (dragIndex > -1 && draggedDoc) {
- this._docDraggedIndex = -1;
- const sorted = this.sortedDocs;
+ this.docDraggedIndex = -1;
+ const sorted = this.childDocs;
const originalIndex = sorted.findIndex(doc => doc === draggedDoc);
- this.Document.card_sort = '';
+ this.Document[this._props.fieldKey + '_sort'] = '';
originalIndex !== -1 && sorted.splice(originalIndex, 1);
sorted.splice(dragIndex, 0, draggedDoc);
if (de.complete.docDragData.removeDocument?.(draggedDoc)) {
@@ -271,49 +248,6 @@ export class CollectionCardView extends CollectionSubView() {
.map(({ i }) => i)
.join('.');
- /**
- * Called in the sortedDocsType method. Compares the cards' value in regards to the desired sort type-- earlier cards are move to the
- * front, latter cards to the back
- * @param docs
- * @param sortType
- * @param isDesc
- * @returns
- */
- sort = (docsIn: Doc[], sortType: cardSortings, isDesc: boolean, dragIndex: number) => {
- const docs = docsIn.slice(); // need make new object list since sort() modifies the incoming list which confuses mobx caching
- sortType &&
- docs.sort((docA, docB) => {
- const [typeA, typeB] = (() => {
- switch (sortType) {
- 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()];
- }
- })(); //prettier-ignore
- return (typeA < typeB ? -1 : typeA > typeB ? 1 : 0) * (isDesc ? 1 : -1);
- });
- if (dragIndex !== -1) {
- const draggedDoc = DragManager.docsBeingDragged[0];
- const originalIndex = docs.findIndex(doc => doc === draggedDoc);
-
- originalIndex !== -1 && docs.splice(originalIndex, 1);
- draggedDoc && docs.splice(dragIndex, 0, draggedDoc);
- }
-
- return docs;
- };
-
- @computed get sortedDocs() {
- return this.sort(
- this.childCards.map(card => card.layout),
- this.cardSort,
- BoolCast(this.Document.card_sort_isDesc),
- this._docDraggedIndex
- );
- }
-
isChildContentActive = computedFn(
(doc: Doc) => () =>
this._props.isContentActive?.() === false
@@ -349,7 +283,7 @@ export class CollectionCardView extends CollectionSubView() {
onClickScript={this.curDoc() === doc ? undefined : this._setCurDocScript}
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={BoolCast(this.layoutDoc.showChildTags)}
+ showTags={BoolCast(this.layoutDoc.showChildTags) || BoolCast(this.Document._layout_showTags)}
whenChildContentsActiveChanged={this._props.whenChildContentsActiveChanged}
dontHideOnDrag
/>
@@ -361,10 +295,10 @@ export class CollectionCardView extends CollectionSubView() {
* @returns number of cards in row that contains index
*/
cardsInRowThatIncludesCardIndex = (index: number) => {
- if (this.childCards.length < this._maxRowCount) {
- return this.childCards.length;
+ if (this.childDocsNoInk.length < this._maxRowCount) {
+ return this.childDocsNoInk.length;
}
- const totalCards = this.childCards.length;
+ const totalCards = this.childDocsNoInk.length;
if (index < totalCards - (totalCards % this._maxRowCount)) {
return this._maxRowCount;
}
@@ -428,126 +362,6 @@ export class CollectionCardView extends CollectionSubView() {
: this.translateY(index);
};
- /**
- * A list of the text content of all the child docs. RTF documents will have just their text and pdf documents will have the first 50 words.
- * Image documents are converted to bse64 and gpt generates a description for them. all other documents use their title. This string is
- * inputted into the gpt prompt to sort everything together
- * @returns
- */
- childPairStringList = () => {
- const docToText = (doc: Doc) => {
- switch (doc.type) {
- case DocumentType.PDF: return StrCast(doc.text).split(/\s+/).slice(0, 50).join(' '); // first 50 words of pdf text
- case DocumentType.IMG: return this.getImageDesc(doc);
- case DocumentType.RTF: return StrCast(RTFCast(doc.text).Text);
- default: return StrCast(doc.title);
- } // prettier-ignore
- };
- const docTextPromises = this.childCards
- .map(pair => pair.layout)
- .map(async doc => {
- const docText = (await docToText(doc)) ?? '';
- doc.gptInputText = docText;
- this._textToDoc.set(docText.replace(/\n/g, ' ').trim(), doc);
- return `======${docText.replace(/\n/g, ' ').trim()}======`;
- });
- return Promise.all<string>(docTextPromises);
- };
-
- /**
- * Calls the gpt API to generate descriptions for the images in the view
- * @param image
- * @returns
- */
- getImageDesc = async (image: Doc) => {
- if (StrCast(image.description)) return StrCast(image.description); // Return existing description
- const { href } = (image.data as URLField).url;
- const hrefParts = href.split('.');
- const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
- try {
- const hrefBase64 = await imageUrlToBase64(hrefComplete);
- const response = await gptImageLabel(hrefBase64, 'Give three to five labels to describe this image.');
- image[DocData].description = response.trim();
- return response; // Return the response from gptImageLabel
- } catch (error) {
- console.log(error);
- }
- return '';
- };
-
- /**
- * Processes gpt's output depending on the type of question the user asked. Converts gpt's string output to
- * usable code
- * @param gptOutput
- */
- @action
- processGptOutput = undoable((gptOutput: string, questionType: string, tag?: string) => {
- // Split the string into individual list items
- const listItems = gptOutput.split('======').filter(item => item.trim() !== '');
-
- if (questionType === '2' || questionType === '4') {
- this.childDocs.forEach(d => {
- d.chatFilter = false;
- });
- }
-
- if (questionType === '6') {
- this.Document.card_sort = 'chat';
- }
-
- listItems.forEach((item, index) => {
- const normalizedItem = item.trim();
- // find the corresponding Doc in the textToDoc map
- const doc = this._textToDoc.get(normalizedItem);
-
- if (doc) {
- switch (questionType) {
- case '6':
- doc.chatIndex = index;
- break;
- case '1': {
- const allHotKeys = Doc.MyFilterHotKeys;
-
- let myTag = '';
-
- if (tag) {
- for (let i = 0; i < allHotKeys.length; i++) {
- // bcz: CHECK THIS CODE OUT -- SOMETHING CHANGED
- const keyTag = StrCast(allHotKeys[i].toolType);
- if (tag.includes(keyTag)) {
- myTag = keyTag;
- break;
- }
- }
-
- if (myTag != '') {
- doc[myTag] = true;
- }
- }
- break;
- }
- case '2':
- case '4':
- doc.chatFilter = true;
- Doc.setDocFilter(DocCast(this.Document.embedContainer), 'chatFilter', true, 'match');
- break;
- }
- } else {
- console.warn(`No matching document found for item: ${normalizedItem}`);
- }
- });
- }, '');
-
- /**
- * Opens up the chat popup and starts the process for smart sorting.
- */
- openChatPopup = async () => {
- GPTPopup.Instance.setVisible(true);
- GPTPopup.Instance.setMode(GPTPopupMode.CARD);
- GPTPopup.Instance.setCardsDoneLoading(true); // Set dataDoneLoading to true after data is loaded
- await this.childPairStringListAndUpdateSortDesc();
- };
-
childScreenToLocal = computedFn((doc: Doc, index: number, isSelected: boolean) => () => {
// need to explicitly trigger an invalidation since we're reading everything from the Dom
this._forceChildXf;
@@ -555,7 +369,7 @@ export class CollectionCardView extends CollectionSubView() {
const dref = this._docRefs.get(doc);
const { translateX, translateY, scale } = ClientUtils.GetScreenTransform(dref?.ContentDiv);
- if (!scale) return new Transform(0, 0, 0);
+ if (!scale) return new Transform(0, 0, 1);
return new Transform(-translateX + (dref?.centeringX || 0) * scale,
-translateY + (dref?.centeringY || 0) * scale, 1)
@@ -585,6 +399,26 @@ export class CollectionCardView extends CollectionSubView() {
}
});
+ cardSizerDown = (e: React.PointerEvent) => {
+ runInAction(() => {
+ this._cursor = 'grabbing';
+ });
+ const batch = UndoManager.StartBatch('card view size');
+ setupMoveUpEvents(
+ this,
+ e,
+ (emove: PointerEvent) => {
+ this.layoutDoc._cardWidth = Math.max(10, this.ScreenToLocalBoxXf().transformPoint(emove.clientX, 0)[0] - this.xMargin);
+ return false;
+ },
+ action(() => {
+ this._cursor = 'ew-resize';
+ batch.end();
+ }),
+ emptyFunction
+ );
+ };
+
/**
* turns off the _dropped flag at the end of a drag/drop, or releases the focused Doc if a different Doc is clicked
*/
@@ -608,9 +442,9 @@ export class CollectionCardView extends CollectionSubView() {
}
return undefined;
});
- getAnchor = (addAsAnnotation: boolean) => {
+ getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
const anchor = Docs.Create.ConfigDocument({ annotationOn: this.Document, config_card_curDoc: this.curDoc() });
- PinDocView(anchor, { pinData: { collectionType: true, filters: true } }, this.Document);
+ PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { collectionType: true, filters: true } }, this.Document);
addAsAnnotation && Doc.AddDocToList(this.dataDoc, this.fieldKey + '_annotations', anchor); // when added as an annotation, links to anchors can be found as links to the document even if the anchors are not rendered
return anchor;
};
@@ -621,7 +455,7 @@ export class CollectionCardView extends CollectionSubView() {
*/
@computed get renderCards() {
// Map sorted documents to their rendered components
- return this.sortedDocs.map((doc, index) => {
+ return this.childDocs.map((doc, index) => {
const cardsInRow = this.cardsInRowThatIncludesCardIndex(index);
const childScreenToLocal = this.childScreenToLocal(doc, index, doc === this.curDoc());
@@ -631,7 +465,7 @@ export class CollectionCardView extends CollectionSubView() {
const aspect = NumCast(doc.height) / NumCast(doc.width, 1);
const vscale = Math.max(1,Math.min((this._props.PanelHeight() * 0.95 * this.fitContentScale * this.nativeScaling) / (aspect * this.childPanelWidth()),
(this._props.PanelHeight() - 80) / (aspect * (this._props.PanelWidth() / 10)))); // prettier-ignore
- const hscale = Math.min(this.sortedDocs.length, this._maxRowCount) / 2; // bcz: hack - the grid is divided evenly into maxRowCount cells, so the max scaling would be maxRowCount -- but making things that wide is ugly, so cap it off at half the window size
+ const hscale = Math.min(this.childDocs.length, this._maxRowCount) / 2; // bcz: hack - the grid is divided evenly into maxRowCount cells, so the max scaling would be maxRowCount -- but making things that wide is ugly, so cap it off at half the window size
return (
<div
key={doc[Id]}
@@ -667,21 +501,20 @@ export class CollectionCardView extends CollectionSubView() {
curDoc = () => DocCast(this.layoutDoc._card_curDoc);
render() {
- const fitContentScale = this.childCards.length === 0 ? 1 : this.fitContentScale;
+ const fitContentScale = this.childDocsNoInk.length === 0 ? 1 : this.fitContentScale;
return (
<div
className="collectionCardView-outer"
ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)}
- onPointerDown={action(e => {
- if (e.button === 2 || e.ctrlKey) return;
- this.releaseCurDoc();
- })}
- onPointerLeave={action(() => (this._docDraggedIndex = -1))}
+ onPointerDown={e => e.button !== 2 && !e.ctrlKey && this.releaseCurDoc()}
+ onPointerLeave={action(() => (this.docDraggedIndex = -1))}
onPointerMove={e => this.onPointerMove(...this._props.ScreenToLocalTransform().transformPoint(e.clientX, e.clientY))}
onDrop={this.onExternalDrop.bind(this)}
style={{
background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor) as string,
color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color) as string,
+ paddingLeft: this.xMargin,
+ paddingRight: this.xMargin,
}}>
<div
className="collectionCardView-inner"
@@ -689,10 +522,12 @@ export class CollectionCardView extends CollectionSubView() {
transform: `scale(${1 / fitContentScale})`,
height: `${100 * fitContentScale}%`,
width: `${100 * fitContentScale}%`,
+ top: this.yMargin,
}}>
<div
className="collectionCardView-cardwrapper"
style={{
+ gridTemplateColumns: `repeat(${this._maxRowCount}, 1fr)`,
gridAutoRows: `${100 / this.numRows}%`,
height: `${this.cardSpacing}%`,
}}>
@@ -701,13 +536,20 @@ export class CollectionCardView extends CollectionSubView() {
<div
className="collectionCardView-flashcardUI"
style={{
- pointerEvents: this.childCards.length === 0 ? undefined : 'none',
+ pointerEvents: this.childDocsNoInk.length === 0 ? undefined : 'none',
height: `${100 / this.nativeScaling / fitContentScale}%`,
width: `${100 / this.nativeScaling / fitContentScale}%`,
transform: `scale(${this.nativeScaling * fitContentScale})`,
- }}>
- {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
- </div>
+ }}></div>
+ </div>
+
+ {this.flashCardUI(this.curDoc, this.docViewProps, this.answered)}
+ <div
+ className="collectionCardView-cardSizeDragger"
+ onPointerDown={this.cardSizerDown}
+ ref={this._draggerRef}
+ style={{ display: this._props.isContentActive() ? undefined : 'none', cursor: this._cursor, color: SettingsManager.userColor, left: `${this.cardWidth + this.xMargin}px` }}>
+ <FontAwesomeIcon icon="arrows-alt-h" />
</div>
</div>
);