diff options
Diffstat (limited to 'src/client/views/collections/CollectionCardDeckView.tsx')
-rw-r--r-- | src/client/views/collections/CollectionCardDeckView.tsx | 424 |
1 files changed, 309 insertions, 115 deletions
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx index de46180e6..907a5a343 100644 --- a/src/client/views/collections/CollectionCardDeckView.tsx +++ b/src/client/views/collections/CollectionCardDeckView.tsx @@ -2,8 +2,8 @@ import { IReactionDisposer, ObservableMap, action, computed, makeObservable, obs import { observer } from 'mobx-react'; import * as React from 'react'; import { ClientUtils, DashColor, returnFalse, returnZero } from '../../../ClientUtils'; -import { numberRange } from '../../../Utils'; -import { Doc, NumListCast } from '../../../fields/Doc'; +import { emptyFunction, numberRange } from '../../../Utils'; +import { Doc, NumListCast, StrListCast } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; import { Id } from '../../../fields/FieldSymbols'; import { BoolCast, Cast, DateCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types'; @@ -19,12 +19,22 @@ import { DocumentView } from '../nodes/DocumentView'; import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup'; import './CollectionCardDeckView.scss'; import { CollectionSubView } from './CollectionSubView'; +import { FieldsDropdown } from '../FieldsDropdown'; +import { Button, IconButton } from 'browndash-components'; +import { faStar } from '@fortawesome/free-solid-svg-icons'; +import { FaStar, FaHeart, FaRobot, FaCloud } from 'react-icons/fa'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { SettingsManager } from '../../util/SettingsManager'; +import { Tooltip } from '@mui/material'; +import { dropActionType } from '../../util/DropActionTypes'; +import { List } from '../../../fields/List'; enum cardSortings { Time = 'time', Type = 'type', Color = 'color', Custom = 'custom', + Chat = 'chat', None = '', } @observer @@ -35,10 +45,15 @@ export class CollectionCardView extends CollectionSubView() { private _textToDoc = new Map<string, Doc>(); @observable _forceChildXf = false; - @observable _isLoading = false; + // @observable _isLoading = false; @observable _hoveredNodeIndex = -1; @observable _docRefs = new ObservableMap<Doc, DocumentView>(); + _draggerRef = React.createRef<HTMLDivElement>(); @observable _maxRowCount = 10; + @observable _docDraggedIndex: number = -1; + @observable _isACardBeingDragged: boolean = false; + @observable overIndex: number = -1; + static getButtonGroup(groupFieldKey: 'chat' | 'star' | 'idea' | 'like', doc: Doc): number | undefined { return Cast(doc[groupFieldKey], 'number', null); @@ -61,23 +76,32 @@ export class CollectionCardView extends CollectionSubView() { } }; - protected createDashEventsTarget = (ele: HTMLDivElement | null) => { - this._dropDisposer?.(); - if (ele) { - this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); - } - }; constructor(props: any) { super(props); makeObservable(this); + this.setRegenerateCallback(); } + setRegenerateCallback() { + GPTPopup.Instance.setRegenerateCallback(this.childPairStringListAndUpdateSortDesc); + } + + @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) => this.processGptOutput(sortResult); + }; + componentDidMount(): void { + this.Document.childFilters_boolean = 'OR' this._disposers.sort = reaction( - () => ({ cardSort: this.cardSort, field: this.cardSort_customField }), - ({ cardSort, field }) => (cardSort === cardSortings.Custom && field === 'chat' ? this.openChatPopup() : GPTPopup.Instance.setVisible(false)) + () => ({ cardSort: this.cardSort }), + ({ cardSort}) => (cardSort === cardSortings.Chat ? this.openChatPopup() : GPTPopup.Instance.setVisible(false)) ); + + } componentWillUnmount() { @@ -92,6 +116,7 @@ export class CollectionCardView extends CollectionSubView() { @computed get cardSort() { return StrCast(this.Document.cardSort) as any as cardSortings; } + /** * how much to scale down the contents of the view so that everything will fit */ @@ -100,42 +125,31 @@ export class CollectionCardView extends CollectionSubView() { return (this._childDocumentWidth * length) / this._props.PanelWidth(); } - @computed get translateWrapperX() { - let translate = 0; - - if (this.inactiveDocs().length !== this.childDocsWithoutLinks.length && this.inactiveDocs().length < 10) { - translate += this.panelWidth() / 2; - } - return translate; - } - /** * The child documents to be rendered-- either all of them except the Links or the docs in the currently active * custom group */ @computed get childDocsWithoutLinks() { const regularDocs = this.childDocs.filter(l => l.type !== DocumentType.LINK); - const activeGroups = NumListCast(this.Document.cardSort_visibleSortGroups); + const activeGroups = StrListCast(this.Document.cardSort_visibleSortGroups); - if (activeGroups.length > 0 && this.cardSort === cardSortings.Custom) { + if (activeGroups.length > 0) { return regularDocs.filter(doc => { - // Get the group number for the current index - const groupNumber = CollectionCardView.getButtonGroup(this.cardSort_customField, doc); - // Check if the group number is in the active groups - return groupNumber !== undefined && activeGroups.includes(groupNumber); - }); + const activeTags = StrListCast(doc.cardSort_activeIcons) + return activeTags !== undefined && activeTags.some(tag => activeGroups.includes(tag)); + }) } // Default return for non-custom cardSort or other cases, filtering out links return regularDocs; } + + /** - * Determines the order in which the cards will be rendered depending on the current sort type + * Number of rows of cards to be rendered */ - @computed get sortedDocs() { - return this.sort(this.childDocsWithoutLinks, this.cardSort, BoolCast(this.layoutDoc.sortDesc)); - } + @computed get numRows() { return Math.ceil(this.sortedDocs.length / 10); } @action setHoveredNodeIndex = (index: number) => { @@ -170,6 +184,8 @@ export class CollectionCardView extends CollectionSubView() { * @returns */ rotate = (amCards: number, index: number) => { + 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))); @@ -204,27 +220,76 @@ export class CollectionCardView extends CollectionSubView() { return Math.abs(stepMag * (apex - index - 1)) - rowOffset; }; - /** - * Translates the selected node to the middle fo the screen - * @param index - * @returns - */ - translateSelected = (index: number): number => { - // if (this.isSelected(index)) { - const middleOfPanel = this._props.PanelWidth() / 2; - const scaledNodeWidth = this.panelWidth() * 1.25; + findCardDropIndex = (mouseX: number, mouseY: number, direction: 'left' | 'right') => { + + const amCardsTotal = this.sortedDocs.length + let index = 0; + const cardWidth = amCardsTotal < this._maxRowCount ? this._props.PanelWidth() / amCardsTotal : this._props.PanelWidth() / this._maxRowCount; + + // Calculate the adjusted X position accounting for the initial offset + let adjustedX = mouseX - // Calculate the position of the node's left edge before scaling - const nodeLeftEdge = index * this.panelWidth(); - // Find the center of the node after scaling - const scaledNodeCenter = nodeLeftEdge + scaledNodeWidth / 2; + const amRows = Math.ceil(amCardsTotal / this._maxRowCount); + const rowHeight = this._props.PanelHeight( ) / amRows + const currRow = Math.floor((mouseY - 100) / rowHeight) //rows start at 0 - // Calculate the translation needed to align the scaled node's center with the panel's center - const translation = middleOfPanel - scaledNodeCenter - scaledNodeWidth - scaledNodeWidth / 4; + if (adjustedX < 0) { + return 0; // Before the first column + } + + if (amCardsTotal < this._maxRowCount) { + index = Math.floor(adjustedX / cardWidth); + } - return translation; + else if (currRow != amRows -1 ){ + index = Math.floor(adjustedX / cardWidth) + (currRow * this._maxRowCount) + } + + else { + // console.log(amRows + "am rows") + const rowAmCards = amCardsTotal - (currRow * this._maxRowCount) + const offset = ((this._maxRowCount - rowAmCards ) / 2) * cardWidth + adjustedX = mouseX - offset + + index = Math.floor(adjustedX / cardWidth) + (currRow * this._maxRowCount); + } + return index; }; + @action + onPointerMove = (e: React.PointerEvent<HTMLDivElement>) => { + if (DragManager.docsBeingDragged.length != 0 ) { + this._isACardBeingDragged = true + + const direction = e.movementX > 0 ? 'right' : 'left'; + const newIndex = this.findCardDropIndex(e.clientX, e.clientY, direction); + + if (newIndex !== this._docDraggedIndex && newIndex != -1) { + this._docDraggedIndex = newIndex; + } + } + }; + + + onInternalDrop = (e: Event, de: DragManager.DropEvent) => { + if (de.complete.docDragData) { + this._isACardBeingDragged = false; + this._docDraggedIndex = -1; + e.stopPropagation() + const draggedDocs = de.complete.docDragData?.draggedDocuments; + return true; + } + return false; + }; + + + @computed get sortedDocs() { + // console.log("hi hi hi") + // console.log(this.layoutDoc.cardSort_isDesc + "layoutdoc desc") + console.log(this.cardSort + "card sort") + + return this.sort(this.childDocsWithoutLinks, this.cardSort, BoolCast(this.layoutDoc.cardSort_isDesc), this._docDraggedIndex); + } /** * 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 @@ -233,38 +298,65 @@ export class CollectionCardView extends CollectionSubView() { * @param isDesc * @returns */ - sort = (docs: Doc[], sortType: cardSortings, isDesc: boolean) => { - if (sortType === cardSortings.None) return docs; - docs.sort((docA, docB) => { - const [typeA, typeB] = (() => { + sort = (docs: Doc[], sortType: cardSortings, isDesc: boolean, dragIndex: number) => { + // if (sortType === cardSortings.None) return docs; + + // if(sortType !== cardSortings.None){ + 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()]; + return [DateCast(docA.author_date)?.date ?? Date.now(), + DateCast(docB.author_date)?.date ?? Date.now()]; case cardSortings.Color: - return [DashColor(StrCast(docA.backgroundColor)).hsv().toString(), // If docA.type is undefined, use an empty string - DashColor(StrCast(docB.backgroundColor)).hsv().toString()]; // If docB.type is undefined, use an empty string + return [ClientUtils.hexToHsv(StrCast(docA.backgroundColor)), + ClientUtils.hexToHsv(StrCast(docB.backgroundColor))]; case cardSortings.Custom: - return [CollectionCardView.getButtonGroup(this.cardSort_customField, docA)??0, - CollectionCardView.getButtonGroup(this.cardSort_customField, docB)??0]; - default: return [StrCast(docA.type), // If docA.type is undefined, use an empty string - StrCast(docB.type)]; // If docB.type is undefined, use an empty string - } // prettier-ignore + return [CollectionCardView.getButtonGroup(this.cardSort_customField, docA) ?? 9999, + CollectionCardView.getButtonGroup(this.cardSort_customField, docB) ?? 9999]; + case cardSortings.Chat: + return [NumCast(docA.chat) ?? 9999, + NumCast(docB.chat) ?? 9999] + + default: + return [StrCast(docA.type), StrCast(docB.type)] + } })(); - + + // console.log(`Sorting ${sortType}: ${typeA} vs ${typeB}`); const out = typeA < typeB ? -1 : typeA > typeB ? 1 : 0; - return isDesc ? -out : out; // Reverse the sort order if descending is true - }); + // console.log(`Comparison result: ${out} (isDesc: ${isDesc})`); + + if (isDesc){ + return out + } + return -out + + }); + // } + + if (dragIndex != -1) { + const draggedDoc = DragManager.docsBeingDragged[0]; + const originalIndex = docs.findIndex(doc => doc === draggedDoc); + + docs.splice(originalIndex, 1); + docs.splice(dragIndex, 0, draggedDoc); + } + return docs; }; + + + displayDoc = (doc: Doc, screenToLocalTransform: () => Transform) => ( <DocumentView // eslint-disable-next-line react/jsx-props-no-spreading {...this._props} ref={action((r: DocumentView) => r?.ContentDiv && this._docRefs.set(doc, r))} - Document={doc} + Document={doc} NativeWidth={returnZero} NativeHeight={returnZero} fitWidth={returnFalse} @@ -273,10 +365,13 @@ export class CollectionCardView extends CollectionSubView() { LayoutTemplate={this._props.childLayoutTemplate} LayoutTemplateString={this._props.childLayoutString} ScreenToLocalTransform={screenToLocalTransform} // makes sure the box wrapper thing is in the right spot - isContentActive={this.isChildContentActive} + isContentActive={emptyFunction} isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive} PanelWidth={this.panelWidth} PanelHeight={this.panelHeight(doc)} + dragAction={(this.Document.childDragAction ?? this._props.childDragAction) as dropActionType} + dontHideOnDrag + // pointerEvents={this.blockPointerEventsWhenDragging(doc)} /> ); @@ -286,11 +381,11 @@ export class CollectionCardView extends CollectionSubView() { * @returns */ overflowAmCardsCalc = (index: number) => { - if (this.inactiveDocs().length < this._maxRowCount) { - return this.inactiveDocs().length; + if (this.sortedDocs.length < this._maxRowCount) { + return this.sortedDocs.length; } // 13 - 3 = 10 - const totalCards = this.inactiveDocs().length; + const totalCards = this.sortedDocs.length; // if 9 or less if (index < totalCards - (totalCards % 10)) { return this._maxRowCount; @@ -323,21 +418,17 @@ export class CollectionCardView extends CollectionSubView() { * @returns */ calculateTranslateY = (isHovered: boolean, isSelected: boolean, realIndex: number, amCards: number, calcRowIndex: number) => { - if (isSelected) return 50 * this.fitContentScale; - const trans = isHovered ? this.translateHover(realIndex) : 0; + 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 (amCards == 1) return 50 * this.fitContentScale; + // const trans = isHovered ? this.translateHover(realIndex) : 0; + const trans = 0; return trans + this.translateY(amCards, calcRowIndex, realIndex); }; - /** - * Toggles the buttons between on and off when creating custom sort groupings/changing those created by gpt - * @param childPairIndex - * @param buttonID - * @param doc - */ - toggleButton = undoable((buttonID: number, doc: Doc) => { - this.cardSort_customField && (doc[this.cardSort_customField] = buttonID); - }, 'toggle custom button'); - + /** * 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 @@ -378,6 +469,8 @@ export class CollectionCardView extends CollectionSubView() { return response; // Return the response from gptImageLabel } catch (error) { console.log('bad things have happened'); + + console.log(error); } return ''; }; @@ -386,22 +479,38 @@ export class CollectionCardView extends CollectionSubView() { * Converts the gpt output into a hashmap that can be used for sorting. lists are seperated by ==== while elements within the list are seperated by ~~~~~~ * @param gptOutput */ - processGptOutput = (gptOutput: string) => { + @action processGptOutput = (gptOutput: string) => { + + console.log("HIIII") + console.log(StrCast(this.Document.cardSort) + "cardSort") // Split the string into individual list items const listItems = gptOutput.split('======').filter(item => item.trim() !== ''); + // console.log(listItems + " LISTT"); + + // Debug: print the map contents + // console.log("Map contents:", Array.from(this._textToDoc.entries())); + listItems.forEach((item, index) => { - // Split the item by '~~~~~~' to get all descriptors - const parts = item.split('~~~~~~').map(part => part.trim()); - - parts.forEach(part => { - // Find the corresponding Doc in the textToDoc map - const doc = this._textToDoc.get(part); - if (doc) { - doc.chat = index; - } - }); + // Normalize the item (trim whitespace) + const normalizedItem = item.trim(); + // console.log("Normalized item:", normalizedItem); + + // Find the corresponding Doc in the textToDoc map + const doc = this._textToDoc.get(normalizedItem); + // console.log("DOC:", doc); + // console.log("ITEM:", normalizedItem); + + if (doc) { + doc.chat = index; + } else { + console.warn(`No matching document found for item: ${normalizedItem}`); + } }); - }; + + } + + // ); + // }; /** * Opens up the chat popup and starts the process for smart sorting. */ @@ -416,40 +525,111 @@ export class CollectionCardView extends CollectionSubView() { /** * Renders the buttons to customize sorting depending on which group the card belongs to and the amount of total groups - * @param childPairIndex * @param doc + * @param cardSort * @returns */ - renderButtons = (doc: Doc, cardSort: cardSortings) => { - if (cardSort !== cardSortings.Custom) return ''; - const amButtons = Math.max(4, this.childDocs?.reduce((set, d) => this.cardSort_customField && set.add(NumCast(d[this.cardSort_customField])), new Set<number>()).size ?? 0); - const activeButtonIndex = CollectionCardView.getButtonGroup(this.cardSort_customField, doc); - const totalWidth = amButtons * 35 + amButtons * 2 * 5 + 6; + renderButtons = (doc: Doc, cardSort: cardSortings): JSX.Element | null => { + // if (cardSort !== cardSortings.Custom) return null; + + const amButtons = 4 + + // const amButtons = Math.max( + // 4, + // this.childDocs?.reduce((set, d) => { + // if (this.cardSort_customField) { + // set.add(NumCast(d[this.cardSort_customField])); + // } + // return set; + // }, new Set<number>()).size ?? 0 + // ); + + // const activeButtonIndex = CollectionCardView.getButtonGroup(this.cardSort_customField, doc); + + const totalWidth = amButtons * 72 + amButtons * 2 * 5 + 6; + + const iconMap: { [key: number]: any } = { + 0: 'star', + 1: 'heart', + 2: 'cloud', + 3: 'bolt' + }; + return ( - <div className="card-button-container" style={{ width: `${totalWidth}px` }}> + <div className="card-button-container" style={{ width: `${totalWidth}px`, fontSize: '50px' }}> {numberRange(amButtons).map(i => ( - // eslint-disable-next-line jsx-a11y/control-has-associated-label - <button - key={i} - type="button" - style={{ backgroundColor: activeButtonIndex === i ? '#4476f7' : '#323232' }} // - onClick={() => this.toggleButton(i, doc)} - /> + <Tooltip key={i} title={<div className="dash-tooltip">Click to add/remove this card from the {iconMap[i]} group</div>}> + <button type="button" onClick={() => this.toggleButton(doc, iconMap[i] )}> + {this.getButtonIcon(doc, iconMap[i])} + </button> + </Tooltip> ))} </div> ); }; + + /** + * Toggles the buttons between on and off when creating custom sort groupings/changing those created by gpt + * @param childPairIndex + * @param buttonID + * @param doc + */ + toggleButton = undoable((doc: Doc, icon: string) => { + + + + // this.cardSort_customField && (doc[this.cardSort_customField] = buttonID); + + // doc.cardSort_activeIcons = new List<string>() + + + // const list = StrListCast(doc.cardSort_activeIcons); + // doc.cardSort_activeIcons = new List<string>(list.includes(icon) ? list.filter(d => d !== icon) : [...list, icon]); + + BoolCast(doc[icon]) ? doc[icon] = false : doc[icon] = true + + + + // StrListCast(doc.cardSort_activeIcons).push(iconMap[buttonID]) + }, 'toggle card tag'); + + + getButtonIcon = (doc: Doc, icon: any): JSX.Element => { + + // const isActive = StrListCast(doc.cardSort_activeIcons).includes(icon) + const isActive = doc[icon] + + // console.log(StrListCast(doc.cardSort_activeIcons)) + const color = isActive ? '#4476f7' : '#323232'; + + return <FontAwesomeIcon icon={icon} size="lg" style={{ color }} />; + }; + /** * Actually renders all the cards */ renderCards = () => { const anySelected = this.childDocs.some(doc => DocumentView.SelectedDocs().includes(doc)); + const isEmpty = this.childDocsWithoutLinks.length === 0; + const isDesc = BoolCast(this.Document.cardSort_isDesc) + + console.log(this.childDocsWithoutLinks.length + "length") + + if (isEmpty) { + 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 + </span> + ); + } + // Map sorted documents to their rendered components return this.sortedDocs.map((doc, index) => { - const realIndex = this.sortedDocs.filter(sortDoc => !DocumentView.SelectedDocs().includes(sortDoc)).indexOf(doc); + const realIndex = this.sortedDocs.indexOf(doc); const calcRowIndex = this.overflowIndexCalc(realIndex); const amCards = this.overflowAmCardsCalc(realIndex); const isSelected = DocumentView.SelectedDocs().includes(doc); + const isDragging = DragManager.docsBeingDragged.includes(doc); const childScreenToLocal = () => { this._forceChildXf; @@ -460,6 +640,12 @@ export class CollectionCardView extends CollectionSubView() { .scale(1 / scale).rotate(!isSelected ? -this.rotate(amCards, calcRowIndex) : 0); // prettier-ignore }; + const translateIfSelected = () => { + const indexInRow = index % this._maxRowCount; + const rowIndex = Math.trunc(index / this._maxRowCount); + const rowCenterIndex = Math.min(this._maxRowCount, this.sortedDocs.length - rowIndex * this._maxRowCount)/2; + return (rowCenterIndex - indexInRow) * 100 - 50; + } return ( <div key={doc[Id]} @@ -472,17 +658,17 @@ export class CollectionCardView extends CollectionSubView() { SnappingManager.SetIsResizing(undefined); this._forceChildXf = !this._forceChildXf; }), - 700 + 900 ); }} style={{ width: this.panelWidth(), - height: 'max-content', // this.panelHeight(childPair.layout)(), + height: 'max-content', transform: `translateY(${this.calculateTranslateY(this._hoveredNodeIndex === index, isSelected, realIndex, amCards, calcRowIndex)}px) - translateX(${isSelected ? this.translateSelected(calcRowIndex) : this.translateOverflowX(realIndex, amCards)}px) + translateX(calc(${(isSelected ? translateIfSelected() : 0) + "% + " + this.translateOverflowX(realIndex, amCards)+"px"})) rotate(${!isSelected ? this.rotate(amCards, calcRowIndex) : 0}deg) - scale(${isSelected ? 1.25 : 1})`, - }} + scale(${isSelected ? 2 : this._hoveredNodeIndex === index ? 1.05 : 1})`, + }} onMouseEnter={() => this.setHoveredNodeIndex(index)}> {this.displayDoc(doc, childScreenToLocal)} {this.renderButtons(doc, this.cardSort)} @@ -490,11 +676,17 @@ export class CollectionCardView extends CollectionSubView() { ); }); }; + render() { + const isEmpty = this.childDocsWithoutLinks.length === 0; + const transformValue = `scale(${1 / this.fitContentScale})`; + const heightValue = `${100 * this.fitContentScale}%`; + return ( <div + onPointerMove={e => this.onPointerMove(e)} className="collectionCardView-outer" - ref={this.createDashEventsTarget} + ref={(ele: HTMLDivElement | null) => this.createDashEventsTarget(ele)} style={{ background: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.BackgroundColor), color: this._props.styleProvider?.(this.layoutDoc, this._props, StyleProp.Color), @@ -502,8 +694,9 @@ export class CollectionCardView extends CollectionSubView() { <div className="card-wrapper" style={{ - transform: ` scale(${1 / this.fitContentScale}) translateX(${this.translateWrapperX}px)`, - height: `${100 * this.fitContentScale}%`, + ...(!isEmpty && { transform: transformValue }), + ...(!isEmpty && { height: heightValue }), + gridAutoRows: `${100 / this.numRows}%` }} onMouseLeave={() => this.setHoveredNodeIndex(-1)}> {this.renderCards()} @@ -511,4 +704,5 @@ export class CollectionCardView extends CollectionSubView() { </div> ); } + } |