aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2024-05-18 22:57:22 -0400
committerbobzel <zzzman@gmail.com>2024-05-18 22:57:22 -0400
commit38d2d361aa723917b5721e2635933d2d8b9f483a (patch)
tree979046937c01e9c751016f03dc2895dd15b056a3
parent57f0d29c0a783bfa797b8e5584bbf800a1f076de (diff)
updated cardDeckView code
-rw-r--r--src/client/util/CurrentUserUtils.ts59
-rw-r--r--src/client/views/collections/CollectionCardDeckView.scss25
-rw-r--r--src/client/views/collections/CollectionCardDeckView.tsx859
-rw-r--r--src/client/views/global/globalScripts.ts105
4 files changed, 373 insertions, 675 deletions
diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts
index 89f1375bc..f2a7f953d 100644
--- a/src/client/util/CurrentUserUtils.ts
+++ b/src/client/util/CurrentUserUtils.ts
@@ -715,48 +715,35 @@ pie title Minerals in my tap water
]
}
static cardTools(): Button[] {
-
-
- // if (view != undefined){
- // for (let i=0; i< NumCast(view.layoutDoc.customSortCount); i++){
- // customs.push(
- // { title: "Custom " + i, icon:"robot", toolTip:"Custom sort option " + i, btnType: ButtonType.ToggleButton, expertMode: false, toolType:"custom", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}
- // )
-
- // }
- // }
return [
- { title: "Time", icon:"hourglass-half", toolTip:"Sort by most recent document creation", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"time", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "Type", icon:"eye", toolTip:"Sort by document type", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"docType", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "Color", icon:"palette", toolTip:"Sort by document color", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"color", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "Smart Sort", icon:"robot", toolTip:"Have ChatGPT sort your text-based nodes !", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"chat", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "Time", icon:"hourglass-half", toolTip:"Sort by most recent document creation", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"time", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "Type", icon:"eye", toolTip:"Sort by document type", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"docType",funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "Color", icon:"palette", toolTip:"Sort by document color", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"color", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ ]
+ }
+ static labelTools(): Button[] {
+ return [
+ { title: "AI", icon:"robot", toolTip:"Add AI labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"chat", funcs: {hidden:`showFreeform ("chat", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "AIs", icon:"AI Sort", toolTip:"Filter AI labels", subMenu: this.cardGroupTools("chat"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("chat", true)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} },
+ { title: "Like", icon:"heart", toolTip:"Add Like labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"like", funcs: {hidden:`showFreeform ("like", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "Likes", icon:"Likes", toolTip:"Filter likes", width: 10, subMenu: this.cardGroupTools("heart"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("like", true)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} },
+ { title: "Star", icon:"star", toolTip:"Add Star labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"star", funcs: {hidden:`showFreeform ("star", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "Stars", icon:"Stars", toolTip:"Filter stars", width: 80, subMenu: this.cardGroupTools("star"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("star", true)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} },
+ { title: "Idea", icon:"satellite", toolTip:"Add Idea labels", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"idea", funcs: {hidden:`showFreeform ("idea", true)`},scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "Ideas", icon:"Ideas", toolTip:"Filter ideas", width: 80, subMenu: this.cardGroupTools("satellite"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden:`!showFreeform("idea", true)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} },
]
}
-
- static customCardTools(): Button[] {
- return [
- { title: "Create", icon:"heart", toolTip:"Create your first custom grouping!", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"custom1", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "Create", icon:"star", toolTip:"Create your second custom grouping!", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"custom2", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "Create", icon:"satellite", toolTip:"Create your third custom grouping!", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"custom3", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "Custom 1", icon: "Custom", width: 10, toolTip: "Set visibilty!", subMenu: CurrentUserUtils.cardGroupTools("heart"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden: `!showFreeform("custom1", true)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Always available
- { title: "Custom 2", icon: "Custom", width: 80, toolTip: "Set visibilty!", subMenu: CurrentUserUtils.cardGroupTools("star"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden: `!showFreeform("custom2", true)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Always available
- { title: "Custom 3", icon: "Custom", width: 80, toolTip: "Set visibilty!", subMenu: CurrentUserUtils.cardGroupTools("satellite"), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden: `!showFreeform("custom3", true)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Always available
-
- ]
- }
-
static cardGroupTools(icon: string): Button[] {
return [
- { title: "", icon:icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"1", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "", icon:icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"2", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "", icon:icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"3", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
- { title: "", icon:icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"4", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
-
+ { title: "1", icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"1", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "2", icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"2", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "3", icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"3", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "4", icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"4", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "5", icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"5", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "6", icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"6", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
+ { title: "7", icon, toolTip:"Click to toggle visibility", btnType: ButtonType.ToggleButton, expertMode: false, toolType:"7", funcs: {hidden:`!cardHasLabel(this.toolType)`}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}},
]
}
-
-
-
static viewTools(): Button[] {
return [
{ title: "Snap", icon: "th", toolTip: "Show Snap Lines", btnType: ButtonType.ToggleButton, ignoreClick: true, expertMode: false, toolType:"snaplines", funcs: {}, scripts: { onClick: '{ return showFreeform(this.toolType, _readOnly_);}'}}, // Only when floating document is selected in freeform
@@ -858,7 +845,7 @@ pie title Minerals in my tap water
{ title: "View", icon: "View", toolTip: "View tools", subMenu: CurrentUserUtils.viewTools(), expertMode: false, toolType:CollectionViewType.Freeform, funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Always available
{ title: "Stack", icon: "View", toolTip: "Stacking tools", subMenu: CurrentUserUtils.stackTools(), expertMode: false, toolType:CollectionViewType.Stacking, funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Always available
{ title: "Card", icon: "Sort", toolTip: "Card sort", subMenu: CurrentUserUtils.cardTools(), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Always available
- { title: "CustomCard", icon: "Create", toolTip: "Create custom groupings!", subMenu: CurrentUserUtils.customCardTools(), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Always available
+ { title: "Label", icon: "Label", toolTip: "Assign card labels", subMenu: CurrentUserUtils.labelTools(), expertMode: false, toolType:CollectionViewType.Card, funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Always available
{ title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), expertMode: false, toolType:DocumentType.WEB, funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Only when Web is selected
{ title: "Video", icon: "Video", toolTip: "Video functions", subMenu: CurrentUserUtils.videoTools(), expertMode: false, toolType:DocumentType.VID, funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Only when video is selected
{ title: "Image", icon: "Image", toolTip: "Image functions", subMenu: CurrentUserUtils.imageTools(), expertMode: false, toolType:DocumentType.IMG, funcs: {hidden: `!SelectionManager_selectedDocType(this.toolType, this.expertMode)`, linearView_IsOpen: `SelectionManager_selectedDocType(this.toolType, this.expertMode)`} }, // Only when image is selected
diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss
index babc604b5..a089b248d 100644
--- a/src/client/views/collections/CollectionCardDeckView.scss
+++ b/src/client/views/collections/CollectionCardDeckView.scss
@@ -12,6 +12,7 @@
display: grid;
grid-template-columns: repeat(10, 1fr);
// width: 100%;
+ transform-origin: top left;
position: absolute;
align-items: center;
@@ -22,15 +23,15 @@
}
.card-button-container {
- display: flex;
+ display: flex;
padding: 3px;
- // width: 300px;
+ // width: 300px;
background-color: rgb(218, 218, 218); /* Background color of the container */
- border-radius: 50px; /* Rounds the corners of the container */
- transform: translateY(-50px);
+ border-radius: 50px; /* Rounds the corners of the container */
+ transform: translateY(75px);
// box-shadow: 0 4px 8px rgba(0,0,0,0.1); /* Optional: Adds shadow for depth */
- align-items: center; /* Centers buttons vertically */
- justify-content: start;/* Centers buttons horizontally */
+ align-items: center; /* Centers buttons vertically */
+ justify-content: start; /* Centers buttons horizontally */
}
button {
@@ -39,11 +40,11 @@ button {
border-radius: 50%;
background-color: $dark-gray;
// border-color: $medium-blue;
- margin: 5px; // transform: translateY(-50px);
+ margin: 5px; // transform: translateY(-50px);
}
// button:hover {
-// transform: translateY(-50px);
+// transform: translateY(-50px);
// }
// .card-wrapper::after {
@@ -62,13 +63,19 @@ button {
// align-items: center;
// transition: transform 0.3s cubic-bezier(0.455, 0.03, 0.515, 0.955);
-
// }
+.card-item-inactive,
.card-item-active,
.card-item {
position: relative;
transition: transform 0.5s ease-in-out;
+ display: flex;
+ flex-direction: column;
+}
+
+.card-item-inactive {
+ opacity: 0.5;
}
.card-item-active {
diff --git a/src/client/views/collections/CollectionCardDeckView.tsx b/src/client/views/collections/CollectionCardDeckView.tsx
index b2a94c3c6..9e5668ffa 100644
--- a/src/client/views/collections/CollectionCardDeckView.tsx
+++ b/src/client/views/collections/CollectionCardDeckView.tsx
@@ -1,182 +1,173 @@
-import { ObservableMap, action, computed, makeObservable, observable } from 'mobx';
+import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Utils, returnFalse, returnTrue, returnZero } from '../../../Utils';
-import { Doc, DocListCast, Field, StrListCast } from '../../../fields/Doc';
+import { DashColor, Utils, numberRange, returnFalse, returnZero } from '../../../Utils';
+import { Doc, NumListCast } from '../../../fields/Doc';
+import { DocData } from '../../../fields/DocSymbols';
import { Id } from '../../../fields/FieldSymbols';
-import { NumCast, ScriptCast, StrCast, BoolCast, DocCast, RTFCast, Cast} from '../../../fields/Types';
+import { BoolCast, Cast, DateCast, NumCast, RTFCast, ScriptCast, StrCast } from '../../../fields/Types';
+import { URLField } from '../../../fields/URLField';
+import { gptImageLabel } from '../../apis/gpt/GPT';
+import { DocumentType } from '../../documents/DocumentTypes';
import { DragManager } from '../../util/DragManager';
import { SelectionManager } from '../../util/SelectionManager';
+import { SnappingManager } from '../../util/SnappingManager';
+import { Transform } from '../../util/Transform';
+import { undoable } from '../../util/UndoManager';
import { StyleProp } from '../StyleProvider';
import { DocumentView } from '../nodes/DocumentView';
+import { GPTPopup, GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
import './CollectionCardDeckView.scss';
import { CollectionSubView } from './CollectionSubView';
-import { Transform } from '../../util/Transform';
-import { LinkManager } from '../../util/LinkManager';
-// import Card from 'react-bootstrap/Card';
-import { DocumentType } from '../../documents/DocumentTypes';
-import { forEach } from 'lodash';
-import { SnappingManager } from '../../util/SnappingManager';
-import { List } from '../../../fields/List';
-import { gptAPICall, gptImageLabel } from '../../apis/gpt/GPT';
-import { GPTCallType } from '../../apis/gpt/GPT';
-import { ImageField, PdfField, URLField } from '../../../fields/URLField';
-import { GPTPopup } from '../pdf/GPTPopup/GPTPopup';
-import { GPTPopupMode } from '../pdf/GPTPopup/GPTPopup';
-import { reaction } from 'mobx';
-import { NumListCast } from '../../../fields/Doc';
-import { undoBatch } from '../../util/UndoManager';
-import { CalendarContainer } from 'react-datepicker';
+
+enum cardSortings {
+ Time = 'time',
+ Type = 'type',
+ Color = 'color',
+ Custom = 'custom',
+ None = '',
+}
@observer
export class CollectionCardView extends CollectionSubView() {
+ private _dropDisposer?: DragManager.DragDropDisposer;
+ private _childDocumentWidth = 600; // target width of a Doc...
+ private _disposers: { [key: string]: IReactionDisposer } = {};
+ private _textToDoc = new Map<string, Doc>();
- @observable hoveredNodeIndex = -1;
-
- //key is the index in the child pair list, value is the id# for the group its in
- @observable customGroupDictionary: Map<number, number>[] = [new Map<number, number>(), new Map<number, number>(), new Map<number, number>()];
-
- /**
- * The child documents to be rendered-- either all of them except the Links or the docs in the currently active
- * custom group
- */
- @computed get myChildLayoutPairs() {
- let activeGroups = NumListCast(this._props.Document.visibleGroupNumbers);
- let currCustom = NumCast(this._props.Document.customSortNumber);
-
- if (activeGroups.length <= 0) {
- return this.childLayoutPairs.filter(l => l.layout.type != DocumentType.LINK);
- }
-
- if (StrCast(this._props.Document.cardSort).includes("custom")) {
- return this.childLayoutPairs.filter((l, index) => {
- if (l.layout.type === DocumentType.LINK) {
- return false;
- }
- // Get the group number for the current index from the customGroupDictionary
- const groupNumber = this.getButtonGroup(currCustom, l.layout)
- // Check if the group number is in the active groups
- return groupNumber !== undefined && activeGroups.includes(groupNumber);
- });
- }
+ @observable _forceChildXf = false;
+ @observable _isLoading = false;
+ @observable _hoveredNodeIndex = -1;
+ @observable _docRefs = new ObservableMap<Doc, DocumentView>();
+ @observable _maxRowCount = 10;
- // Default return for non-custom cardSort or other cases, filtering out links
- return this.childLayoutPairs.filter(l => l.layout.type != DocumentType.LINK);
+ static getButtonGroup(groupFieldKey: 'chat' | 'star' | 'idea' | 'like', doc: Doc): number | undefined {
+ return Cast(doc[groupFieldKey], 'number', null);
}
+ static imageUrlToBase64 = async (imageUrl: string): Promise<string> => {
+ try {
+ const response = await fetch(imageUrl);
+ const blob = await response.blob();
- @action
- setHoveredNodeIndex = (index: number) => {
- if (!this.isSelected(index)) {
- this.hoveredNodeIndex = index;
- }
- };
- /**
- * Translates the hovered node to the center of the screen
- * @param index
- * @returns
- */
- translateHover = (index: number): number => {
- if (this.hoveredNodeIndex == index && !this.isSelected(index)) {
- return -50;
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsDataURL(blob);
+ reader.onloadend = () => resolve(reader.result as string);
+ reader.onerror = error => reject(error);
+ });
+ } catch (error) {
+ console.error('Error:', error);
+ throw error;
}
- return 0;
};
- @action
- setSelectedNodeIndex = (index: number) => {
- const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
- if (SelectionManager.IsSelected(docs[index])) {
- this.setSelectedNodeIndex(index);
+ protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
+ this._dropDisposer?.();
+ if (ele) {
+ this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
}
};
- isSelected = (index: number): boolean => {
- const docs = DocListCast(this.Document[this.fieldKey ?? Doc.LayoutFieldKey(this.Document)]);
- return SelectionManager.IsSelected(docs[index]);
- };
-
- /**
- * Returns all the documents except the one that's currently selected
- */
- inactiveDocs = () => {
- const docs = this.myChildLayoutPairs;
- return docs.filter(d => !SelectionManager.IsSelected(d.layout));
- };
-
- middleIndex = Math.floor(this.inactiveDocs().length / 2);
-
constructor(props: any) {
super(props);
makeObservable(this);
- const pairs = this.childLayoutPairs.filter(d => d.layout.type != DocumentType.LINK);
+ }
- //gets the values from the last time the view was loaded and puts them into their respective places in the hashmaps(s)
- for (let i=0; i< pairs.length; i++){
- if (pairs[i].layout.custom1Group != undefined){
- this.customGroupDictionary[0].set(i, NumCast(pairs[i].layout.custom1Group))
- }
+ componentDidMount(): void {
+ this._disposers.sort = reaction(
+ () => ({ cardSort: this.cardSort, field: this.customSortField }),
+ ({ cardSort, field }) => (cardSort === cardSortings.Custom && field === 'chat' ? this.openChatPopup() : GPTPopup.Instance.setVisible(false))
+ );
+ }
- if (pairs[i].layout.custom2Group != undefined){
- this.customGroupDictionary[1].set(i, NumCast(pairs[i].layout.custom2Group))
- }
+ componentWillUnmount() {
+ Object.keys(this._disposers).forEach(key => this._disposers[key]?.());
+ this._dropDisposer?.();
+ }
- if (pairs[i].layout.custom3Group != undefined){
- this.customGroupDictionary[2].set(i, NumCast(pairs[i].layout.custom3Group))
- }
+ @computed get customSortField() {
+ return StrCast(this.Document.customSortField) as any as 'chat' | 'star' | 'idea' | 'like';
+ }
- if (pairs[i].layout.chatGroup != undefined){
- this.gptGroups.set(pairs[i].layout, NumCast(pairs[i].layout.chatGroup))
- this.amGPTGroups = NumCast(this._props.Document.chatAmGroups)
+ @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
+ */
+ @computed get fitContentScale() {
+ const length = Math.min(this.childDocsWithoutLinks.length, this._maxRowCount);
+ 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;
}
- reaction(
- () => this._props.Document.cardSort,
- (cardSort) => {
- if (cardSort === 'chat') {
- this.openChatPopup();
- }
- }
- );
+ return translate;
}
- private _dropDisposer?: DragManager.DragDropDisposer;
+ /**
+ * 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.visibleGroupNumbers);
+
+ if (activeGroups.length > 0 && this.cardSort === cardSortings.Custom) {
+ return regularDocs.filter(doc => {
+ // Get the group number for the current index
+ const groupNumber = CollectionCardView.getButtonGroup(this.customSortField, doc);
+ // Check if the group number is in the active groups
+ return groupNumber !== undefined && activeGroups.includes(groupNumber);
+ });
+ }
- componentWillUnmount() {
- this._dropDisposer?.();
+ // Default return for non-custom cardSort or other cases, filtering out links
+ return regularDocs;
}
- protected createDashEventsTarget = (ele: HTMLDivElement | null) => {
- this._dropDisposer?.();
- if (ele) {
- this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc);
+ /**
+ * Determines the order in which the cards will be rendered depending on the current sort type
+ */
+ @computed get sortedDocs() {
+ return this.sort(this.childDocsWithoutLinks, this.cardSort, BoolCast(this.layoutDoc.sortDesc));
+ }
+
+ @action
+ setHoveredNodeIndex = (index: number) => {
+ if (!SelectionManager.IsSelected(this.childDocs[index])) {
+ this._hoveredNodeIndex = index;
}
};
+ /**
+ * Translates the hovered node to the center of the screen
+ * @param index
+ * @returns
+ */
+ translateHover = (index: number) => (this._hoveredNodeIndex === index && !SelectionManager.IsSelected(this.childDocs[index]) ? -50 : 0);
+
+ isSelected = (index: number) => SelectionManager.IsSelected(this.childDocs[index]);
- childDocumentWidth = 600; // target width of a Doc...
/**
- * how much to scale down the contents of the view so that everything will fit
+ * Returns all the documents except the one that's currently selected
*/
- @computed get fitContentScale() {
- if (this.myChildLayoutPairs.length < this.maxRowCount) {
- length = this.myChildLayoutPairs.length;
- } else {
- length = this.maxRowCount;
- }
- return (this.childDocumentWidth * length) / this._props.PanelWidth();
- }
+ inactiveDocs = () => this.childDocsWithoutLinks.filter(d => !SelectionManager.IsSelected(d));
- panelWidth = () => this.childDocumentWidth;
- panelHeight = (layout: Doc) => () => (2 * (this.panelWidth() * NumCast(layout._height))) / NumCast(layout._width);
+ panelWidth = () => this._childDocumentWidth;
+ panelHeight = (layout: Doc) => () => (this.panelWidth() * NumCast(layout._height)) / NumCast(layout._width);
onChildDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick);
isContentActive = () => this._props.isSelected() || this._props.isContentActive() || this._props.isAnyChildContentActive();
isChildContentActive = () => (this.isContentActive() ? true : false);
/**
* Returns the degree to rotate a card dependind on the amount of cards in their row and their index in said row
- * @param amCards
- * @param index
- * @returns
+ * @param amCards
+ * @param index
+ * @returns
*/
rotate = (amCards: number, index: number) => {
const possRotate = -30 + index * (30 / ((amCards - (amCards % 2)) / 2));
@@ -184,7 +175,8 @@ export class CollectionCardView extends CollectionSubView() {
if (amCards % 2 == 0 && possRotate == 0) {
return possRotate + Math.abs(-30 + (index - 1) * (30 / (amCards / 2)));
- } else if (amCards % 2 == 0 && index > (amCards + 1) / 2) {
+ }
+ if (amCards % 2 == 0 && index > (amCards + 1) / 2) {
return possRotate + stepMag;
}
@@ -197,26 +189,25 @@ export class CollectionCardView extends CollectionSubView() {
const evenOdd = amCards % 2;
const apex = (amCards - evenOdd) / 2;
const stepMag = 200 / ((amCards - evenOdd) / 2) + Math.abs((apex - index) * 25);
- let rowOffset = 0;
- if (realIndex > this.maxRowCount - 1) {
- rowOffset = 400 * ((realIndex - (realIndex % this.maxRowCount)) / this.maxRowCount);
+ let rowOffset = 0;
+ if (realIndex > this._maxRowCount - 1) {
+ rowOffset = 400 * ((realIndex - (realIndex % this._maxRowCount)) / this._maxRowCount);
}
if (evenOdd == 1 || index < apex - 1) {
return Math.abs(stepMag * (apex - index)) - rowOffset;
- } else {
- if (index == apex || index == apex - 1) {
- return 0 - rowOffset;
- }
-
- return Math.abs(stepMag * (apex - index - 1)) - rowOffset;
}
+ if (index == apex || index == apex - 1) {
+ return 0 - rowOffset;
+ }
+
+ return Math.abs(stepMag * (apex - index - 1)) - rowOffset;
};
/**
* Translates the selected node to the middle fo the screen
- * @param index
- * @returns
+ * @param index
+ * @returns
*/
translateSelected = (index: number): number => {
// if (this.isSelected(index)) {
@@ -235,125 +226,45 @@ export class CollectionCardView extends CollectionSubView() {
};
/**
- * Determines the order in which the cards will be rendered depending on the current sort type
- */
- @computed get sortedDocsType() {
- const desc = BoolCast(this.layoutDoc.sortDesc);
- let sorted = [];
- let docs = [];
-
- for (let i = 0; i < this.myChildLayoutPairs.length; i++) {
- //copying everything in childlayout pairs to sorted so that i can use the sort function without altering the original list
- sorted[i] = this.myChildLayoutPairs[i];
- }
-
- switch (this._props.Document.cardSort) {
- case 'type':
- // Copy and sort documents by type
- return this.sort(sorted, 'type', desc);
- case 'color':
- return this.sort(sorted, 'color', desc);
- case 'custom':
- return this.sort(sorted, 'custom', desc);
- case 'chat':
- return this.sort(this.myChildLayoutPairs, 'gpt', BoolCast(this.layoutDoc.sortDesc));
-
- default:
- docs = this.myChildLayoutPairs;
- return { docs };
- }
- }
- /**
- * Converts a hex # to its hsv color value
- * @param hex
- * @returns
- */
- hexToHsv = (hex: string): [number, number, number] => {
- if (!hex) return [0, 0, 0]; // Default to black if hex is not defined
- const r = parseInt(hex.slice(1, 3), 16) / 255;
- const g = parseInt(hex.slice(3, 5), 16) / 255;
- const b = parseInt(hex.slice(5, 7), 16) / 255;
- const max = Math.max(r, g, b),
- min = Math.min(r, g, b);
- const d = max - min;
- let h: number;
- const s = max === 0 ? 0 : d / max;
- const v = max;
-
- switch (max) {
- case min:
- h = 0;
- break;
- case r:
- h = (g - b) / d + (g < b ? 6 : 0);
- break;
- case g:
- h = (b - r) / d + 2;
- break;
- case b:
- h = (r - g) / d + 4;
- break;
- default:
- h = 0;
- break;
- }
- h /= 6;
- return [h, s, v];
- };
- /**
* 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
+ * @param docs
+ * @param sortType
+ * @param isDesc
+ * @returns
*/
- sort = (docs: { layout: Doc; data: Doc }[], sortType: string, isDesc: boolean) => {
+ sort = (docs: Doc[], sortType: cardSortings, isDesc: boolean) => {
+ if (sortType === cardSortings.None) return docs;
docs.sort((docA, docB) => {
- let typeA;
- let typeB;
-
- switch (sortType) {
- case 'color':
- typeA = this.hexToHsv(StrCast(docA.layout.backgroundColor)) ?? ''; // If docA.type is undefined, use an empty string
- typeB = this.hexToHsv(StrCast(docB.layout.backgroundColor)) ?? ''; // If docB.type is undefined, use an empty string
- break;
-
- case 'custom':
- typeA = this.getButtonGroup(NumCast(this._props.Document.customSortNumber), docA.layout) ?? '9999'
- typeB = this.getButtonGroup(NumCast(this._props.Document.customSortNumber), docB.layout) ?? '9999'
- break;
-
- case 'gpt':
- typeA = this.getButtonGroup(99999, docA.layout, true) ?? '9999';
- typeB = this.getButtonGroup(99999, docB.layout, true) ?? '9999';
- break;
-
- default:
- typeA = docA.layout.type ?? ''; // If docA.type is undefined, use an empty string
- typeB = docB.layout.type ?? ''; // If docB.type is undefined, use an empty string
- break;
- }
-
- // Perform a basic string comparison if types are strings
- let out = 0;
- if (typeA < typeB) out = -1;
- if (typeA > typeB) out = 1;
- if (isDesc) out *= -1; // Reverse the sort order if descending is true
- return out;
+ 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:
+ 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
+ case cardSortings.Custom:
+ return [CollectionCardView.getButtonGroup(this.customSortField, docA)??0,
+ CollectionCardView.getButtonGroup(this.customSortField, 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
+ })();
+
+ const out = typeA < typeB ? -1 : typeA > typeB ? 1 : 0;
+ return isDesc ? -out : out; // Reverse the sort order if descending is true
});
- return { docs };
+ return docs;
};
-
- displayDoc = (childPair: { layout: Doc; data: Doc }, screenToLocalTransform: () => Transform) => {
+ displayDoc = (doc: Doc, screenToLocalTransform: () => Transform) => {
return (
<DocumentView
{...this._props}
- ref={action((r: DocumentView) => r?.ContentDiv && this.docRefs.set(childPair.layout, r))}
- Document={childPair.layout}
- TemplateDataDocument={childPair.data}
+ ref={action((r: DocumentView) => r?.ContentDiv && this._docRefs.set(doc, r))}
+ Document={doc}
NativeWidth={returnZero}
NativeHeight={returnZero}
layout_fitWidth={returnFalse}
@@ -365,382 +276,219 @@ export class CollectionCardView extends CollectionSubView() {
isContentActive={this.isChildContentActive}
isDocumentActive={this._props.childDocumentsActive?.() || this.Document._childDocumentsActive ? this._props.isDocumentActive : this.isContentActive}
PanelWidth={this.panelWidth}
- PanelHeight={this.panelHeight(childPair.layout)}
+ PanelHeight={this.panelHeight(doc)}
/>
);
};
- @observable docRefs = new ObservableMap<Doc, DocumentView>();
-
- @observable maxRowCount = 10;
-
/**
* Determines how many cards are in the row of a card at a specific index
- * @param index
- * @returns
+ * @param index
+ * @returns
*/
- overflowAmCardsCalc(index: number) {
- if (this.inactiveDocs().length < this.maxRowCount) {
+ overflowAmCardsCalc = (index: number) => {
+ if (this.inactiveDocs().length < this._maxRowCount) {
return this.inactiveDocs().length;
}
// 13 - 3 = 10
const totalCards = this.inactiveDocs().length;
// if 9 or less
if (index < totalCards - (totalCards % 10)) {
- return this.maxRowCount;
+ return this._maxRowCount;
}
//(3)
return totalCards % 10;
- }
+ };
/**
* Determines the index a card is in in a row
- * @param realIndex
- * @returns
+ * @param realIndex
+ * @returns
*/
- overflowIndexCalc(realIndex: number) {
- if (realIndex < 10) {
- return realIndex;
- }
-
- return realIndex % 10;
- }
+ overflowIndexCalc = (realIndex: number) => realIndex % 10;
/**
* Translates the cards in the second rows and beyond over to the right
- * @param realIndex
- * @param calcIndex
- * @param calcRowCards
- * @returns
+ * @param realIndex
+ * @param calcIndex
+ * @param calcRowCards
+ * @returns
*/
- translateOverflowX(realIndex: number, calcIndex: number, calcRowCards: number) {
-
- if (realIndex < this.maxRowCount) {
- return 0;
- }
+ translateOverflowX = (realIndex: number, calcRowCards: number) => (realIndex < this._maxRowCount ? 0 : (10 - calcRowCards) * (this.panelWidth() / 2));
- return (10 - calcRowCards) * (this.panelWidth() / 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
+ * @param isHovered
+ * @param isSelected
+ * @param realIndex
+ * @param amCards
+ * @param calcRowIndex
+ * @returns
*/
- calculateTranslateY(isHovered: boolean, isSelected: boolean, realIndex: number, amCards: number, calcRowIndex: number) {
- let trans = 0;
- if (isHovered) {
- trans += this.translateHover(realIndex);
- }
-
- trans += this.translateY(amCards, calcRowIndex, realIndex);
- if (isSelected) {
- trans = 50 * this.fitContentScale;
- }
- return trans;
- }
-
- @observable _forceChildXf = false;
+ calculateTranslateY = (isHovered: boolean, isSelected: boolean, realIndex: number, amCards: number, calcRowIndex: number) => {
+ if (isSelected) return 50 * this.fitContentScale;
+ const trans = isHovered ? this.translateHover(realIndex) : 0;
+ return trans + this.translateY(amCards, calcRowIndex, realIndex);
+ };
/**
- * Actually renders all the cards
+ * Toggles the buttons between on and off when creating custom sort groupings/changing those created by gpt
+ * @param childPairIndex
+ * @param buttonID
+ * @param doc
*/
- @computed get contentSorted() {
- const sortedDocs = this.sortedDocsType.docs;
- // Map sorted documents to their rendered components
- return sortedDocs.map((childPair, index) => {
-
- const isHovered = this.hoveredNodeIndex === index;
- const childPairIndex = this.childLayoutPairs.filter(d => d.layout.type != DocumentType.LINK).indexOf(childPair)
- const realIndex = this.sortedDocsType.docs.filter(d => !SelectionManager.IsSelected(d.layout)).indexOf(childPair);
- const calcRowIndex = this.overflowIndexCalc(realIndex);
-
- const amCards = this.overflowAmCardsCalc(realIndex);
-
- const isSelected = SelectionManager.IsSelected(childPair.layout);
-
- const childScreenToLocal = () => {
- this._forceChildXf;
- const dref = this.docRefs.get(childPair.layout);
- const { translateX, translateY, scale } = Utils.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);
- };
-
- return (
- <div
- key={childPair.layout[Id]}
- className={`card-item${isSelected ? '-active' : ''}`}
- onPointerUp={e => {
- SnappingManager.SetIsResizing(this.Document);
- setTimeout(
- action(() => {
- SnappingManager.SetIsResizing(undefined);
- this._forceChildXf = !this._forceChildXf;
- }),
- 700
- );
- }}
- style={{
- width: this.panelWidth(),
- height: this.panelHeight(childPair.layout)(),
- transform: `
- translateY(${this.calculateTranslateY(isHovered, isSelected, realIndex, amCards, calcRowIndex)}px)
- translateX(${isSelected ? this.translateSelected(calcRowIndex) : this.translateOverflowX(realIndex, calcRowIndex, amCards)}px)
- rotate(${!isSelected ? this.rotate(amCards, calcRowIndex) : 0}deg)
- scale(${isSelected ? 1.25 : 1})
- `,
- }}
- onMouseEnter={() => this.setHoveredNodeIndex(index)}>
- {this.displayDoc(childPair, childScreenToLocal)}
-
- {this._props.Document.cardSort == 'custom' ? this.renderButtons(childPairIndex, childPair.layout, false) : ''}
- {this._props.Document.cardSort == 'chat' ? this.renderButtons(childPairIndex, childPair.layout, true) : ''}
-
- </div>
- );
- });
- }
+ toggleButton = undoable((buttonID: number, doc: Doc) => this.customSortField && (doc[this.customSortField] = buttonID), 'toggle custom button');
/**
- * Toggles the buttons between on and off when creating custom sort groupings/changing those created by gpt
- * @param childPairIndex
- * @param buttonID
- * @param isChat
- * @param doc
- */
-
- @undoBatch
- @action toggleButton(childPairIndex: number, buttonID: number, isChat = false, doc: Doc) {
- if (!isChat) {
- const sortNumber = NumCast(this._props.Document.customSortNumber)
- this.customGroupDictionary[sortNumber].set(childPairIndex, buttonID)
- switch (sortNumber){
- case 0:
- doc.custom1Group = buttonID
- break
- case 1:
- doc.custom2Group = buttonID
- break
- case 2:
- doc.custom3Group = buttonID
- break
- default:
- break
- }
- }
-
- if (isChat && doc) {
- this.gptGroups.set(doc, buttonID);
- doc.chatGroup = buttonID
- }
- }
-
- /**
- * 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.
+ * 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
+ * inputted into the gpt prompt to sort everything together
+ * @returns
*/
- async childPairStringList(): Promise<string> {
- let string = "";
- for (let i = 0; i < this.childDocs.length; i++) {
- switch (this.childDocs[i].type) {
- case DocumentType.IMG:
- string += `======${await this.getImageDesc(this.childDocs[i])}======`;
- break;
-
- case DocumentType.PDF:
- let pdfText = StrCast(this.childDocs[i].text);
- let words = pdfText.split(/\s+/);
- let first50Words = words.slice(0, 50);
- string += `======${first50Words.join(' ')}======`;
-
- this.textToDoc.set(first50Words.join(' ').trim(), this.childDocs[i]);
- break;
-
- case DocumentType.RTF:
- let rtfText = StrCast((RTFCast(this.childDocs[i].text)).Text);
- string += `======${StrCast((RTFCast(this.childDocs[i].text)).Text)}======`;
- this.textToDoc.set(rtfText.trim(), this.childDocs[i]);
- break;
-
- default:
- string += `======${StrCast(this.childDocs[i].title)}======`;
- this.textToDoc.set(StrCast(this.childDocs[i].title).trim(), this.childDocs[i]);
- }
- }
- return string;
- }
-
- @observable isLoading = false
-
-
- imageUrlToBase64 = async (imageUrl: string): Promise<string> => {
- try {
- const response = await fetch(imageUrl);
- const blob = await response.blob();
-
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.readAsDataURL(blob);
- reader.onloadend = () => resolve(reader.result as string);
- reader.onerror = error => reject(error);
- });
- } catch (error) {
- console.error('Error:', error);
- throw error;
- }
+ childPairStringList = () => {
+ const docToText = (doc: Doc) => {
+ switch (doc.type) {
+ case DocumentType.PDF: const words = StrCast(doc.text).split(/\s+/);
+ return words.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.childDocsWithoutLinks.map(async doc => {
+ const docText = (await docToText(doc)) ?? '';
+ this._textToDoc.set(docText.trim(), doc);
+ return `======${docText.replace(/\n/g, ' ').trim()}======`;
+ });
+ return Promise.all<string>(docTextPromises);
};
- textToDoc = new Map<string, Doc>()
- gptProccessedImages = new Set<Doc>()
-
/**
* Calls the gpt API to generate descriptions for the images in the view
- * @param image
- * @returns
+ * @param image
+ * @returns
*/
- @action async getImageDesc(image: Doc) {
- if (this.gptProccessedImages.has(image)) {
- // Return the already processed description
- return Array.from(this.textToDoc.keys()).find(key => this.textToDoc.get(key) === image) || '';
- }
-
- let href = (image['data'] as URLField).url.href;
- let hrefParts = href.split('.');
- let hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
+ getImageDesc = async (image: Doc) => {
+ if (StrCast(image.description)) return StrCast(image.description); // Return existing description
+ const href = (image.data as URLField).url.href;
+ const hrefParts = href.split('.');
+ const hrefComplete = `${hrefParts[0]}_o.${hrefParts[1]}`;
try {
- let hrefBase64 = await this.imageUrlToBase64(hrefComplete);
- let response = await gptImageLabel(hrefBase64);
- this.textToDoc.set(response.trim(), image);
- this.gptProccessedImages.add(image);
- console.log(response);
+ const hrefBase64 = await CollectionCardView.imageUrlToBase64(hrefComplete);
+ const response = await gptImageLabel(hrefBase64);
+ image[DocData].description = response.trim();
return response; // Return the response from gptImageLabel
} catch (error) {
- console.log("bad things have happened");
+ console.log('bad things have happened');
}
- }
+ return '';
+ };
- //child doc to its group determind by gpt
- gptGroups = new ObservableMap<Doc, number>
- @observable amGPTGroups = 0
-
/**
* 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
+ * @param gptOutput
*/
- processGptOutput(gptOutput: string) {
-
+ processGptOutput = (gptOutput: string) => {
// Split the string into individual list items
const listItems = gptOutput.split('======').filter(item => item.trim() !== '');
- this.amGPTGroups = listItems.length
- this._props.Document.chatAmGroups = this.amGPTGroups
-
listItems.forEach((item, index) => {
// Split the item by '~~~~~~' to get all descriptors
const parts = item.split('~~~~~~').map(part => part.trim());
parts.forEach(part => {
- console.log(part + "part")
// Find the corresponding Doc in the textToDoc map
- if (this.textToDoc.has(part)) {
- const doc = this.textToDoc.get(part);
- if (doc) {
- this.gptGroups.set(doc, index);
- doc.chatGroup = index
- }
+ const doc = this._textToDoc.get(part);
+ if (doc) {
+ doc.chat = index;
}
});
});
- }
-
- @observable isChatPopupOpen = false;
+ };
/**
- * Opens up the chat popup and starts the process for smart sorting.
+ * Opens up the chat popup and starts the process for smart sorting.
*/
- @action openChatPopup = async () => {
- this.isChatPopupOpen = true;
+ openChatPopup = async () => {
GPTPopup.Instance.setVisible(true);
GPTPopup.Instance.setMode(GPTPopupMode.SORT);
-
- // Await the promise to get the string result
- const sortDesc = await this.childPairStringList();
+ const sortDesc = await this.childPairStringList(); // Await the promise to get the string result
GPTPopup.Instance.setCardsDoneLoading(true); // Set dataDoneLoading to true after data is loaded
- GPTPopup.Instance.setSortDesc(sortDesc);
- GPTPopup.Instance.onSortComplete = this.handleGptSortResult;
-};
- @action handleGptSortResult = (sortResult: string) => {
- this.processGptOutput(sortResult);
+ GPTPopup.Instance.setSortDesc(sortDesc.join());
+ GPTPopup.Instance.onSortComplete = (sortResult: string) => this.processGptOutput(sortResult);
};
/**
- * 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 isChat
- * @returns
+ * Renders the buttons to customize sorting depending on which group the card belongs to and the amount of total groups
+ * @param childPairIndex
+ * @param doc
+ * @returns
*/
- renderButtons(childPairIndex: number, doc: Doc, isChat = false) {
- const buttons = [];
- const groupNumber = NumCast(this._props.Document.customSortNumber);
-
- let amButtons = 4;
- let activeButtonIndex = this.getButtonGroup(groupNumber, doc)
-
- if (isChat && doc) {
- if (this.amGPTGroups > 4){
- amButtons = this.amGPTGroups;
- }
- activeButtonIndex = this.getButtonGroup(9999, doc, true);
- }
-
- for (let i = 0; i < amButtons; i++) {
- const isActive = activeButtonIndex === i;
- buttons.push(
- <button
- key={i}
- style={{ backgroundColor: isActive ? '#4476f7' : '#323232' }}
- onClick={() => this.toggleButton(childPairIndex, i, isChat, doc)}
- ></button>
- );
- }
-
+ renderButtons = (doc: Doc, cardSort: cardSortings) => {
+ if (cardSort !== cardSortings.Custom) return '';
+ const amButtons = Math.max(4, this.childDocs?.reduce((set, doc) => this.customSortField && set.add(NumCast(doc[this.customSortField])), new Set<number>()).size ?? 0);
+ const activeButtonIndex = CollectionCardView.getButtonGroup(this.customSortField, doc);
const totalWidth = amButtons * 35 + amButtons * 2 * 5 + 6;
return (
<div className="card-button-container" style={{ width: `${totalWidth}px` }}>
- {buttons}
+ {numberRange(amButtons).map(i => (
+ <button
+ key={i}
+ type="button"
+ style={{ backgroundColor: activeButtonIndex === i ? '#4476f7' : '#323232' }} //
+ onClick={() => this.toggleButton(i, doc)}
+ />
+ ))}
</div>
);
- }
-
- getButtonGroup(groupNumber: number, doc: Doc, isChat?: boolean) {
- if (isChat){
- return NumCast(doc.chatGroup)
- }
- switch (groupNumber){
- case 0:
- return NumCast(doc.custom1Group)
- case 1:
- return NumCast(doc.custom2Group)
- case 2:
- return NumCast(doc.custom3Group)
- default:
- break
- }
-
-
- }
-
+ };
+ /**
+ * Actually renders all the cards
+ */
+ renderCards = () => {
+ const anySelected = this.childDocs.some(doc => SelectionManager.IsSelected(doc));
+ // Map sorted documents to their rendered components
+ return this.sortedDocs.map((doc, index) => {
+ const realIndex = this.sortedDocs.filter(sortDoc => !SelectionManager.IsSelected(sortDoc)).indexOf(doc);
+ const calcRowIndex = this.overflowIndexCalc(realIndex);
+ const amCards = this.overflowAmCardsCalc(realIndex);
+ const isSelected = SelectionManager.IsSelected(doc);
- @computed get translateWrapperX() {
- let translate = 0;
+ const childScreenToLocal = () => {
+ this._forceChildXf;
+ const dref = this._docRefs.get(doc);
+ const { translateX, translateY, scale } = Utils.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
+ };
- if (this.inactiveDocs().length != this.myChildLayoutPairs.length && this.inactiveDocs().length < 10) {
- translate += this.panelWidth() / 2;
- }
- return translate;
- }
+ return (
+ <div
+ key={doc[Id]}
+ className={`card-item${isSelected ? '-active' : anySelected ? '-inactive' : ''}`}
+ onPointerUp={() => {
+ // this turns off documentDecorations during a transition, then turns them back on afterward.
+ SnappingManager.SetIsResizing(this.Document);
+ setTimeout(
+ action(() => {
+ SnappingManager.SetIsResizing(undefined);
+ this._forceChildXf = !this._forceChildXf;
+ }),
+ 700
+ );
+ }}
+ style={{
+ width: this.panelWidth(),
+ height: 'max-content', // this.panelHeight(childPair.layout)(),
+ transform: `translateY(${this.calculateTranslateY(this._hoveredNodeIndex === index, isSelected, realIndex, amCards, calcRowIndex)}px)
+ translateX(${isSelected ? this.translateSelected(calcRowIndex) : this.translateOverflowX(realIndex, amCards)}px)
+ rotate(${!isSelected ? this.rotate(amCards, calcRowIndex) : 0}deg)
+ scale(${isSelected ? 1.25 : 1})`,
+ }}
+ onMouseEnter={() => this.setHoveredNodeIndex(index)}>
+ {this.displayDoc(doc, childScreenToLocal)}
+ {this.renderButtons(doc, this.cardSort)}
+ </div>
+ );
+ });
+ };
render() {
return (
<div
@@ -754,11 +502,10 @@ export class CollectionCardView extends CollectionSubView() {
className="card-wrapper"
style={{
transform: ` scale(${1 / this.fitContentScale}) translateX(${this.translateWrapperX}px)`,
- transformOrigin: 'top left',
height: `${100 * this.fitContentScale}%`,
}}
onMouseLeave={() => this.setHoveredNodeIndex(-1)}>
- {this.contentSorted}
+ {this.renderCards()}
</div>
</div>
);
diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts
index 496d7482c..35a3a2e31 100644
--- a/src/client/views/global/globalScripts.ts
+++ b/src/client/views/global/globalScripts.ts
@@ -1,7 +1,7 @@
import { Colors } from 'browndash-components';
import { action, runInAction } from 'mobx';
import { aggregateBounds } from '../../../Utils';
-import { Doc, Opt } from '../../../fields/Doc';
+import { Doc, DocListCast, Opt } from '../../../fields/Doc';
import { InkTool } from '../../../fields/InkField';
import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types';
import { WebField } from '../../../fields/URLField';
@@ -106,17 +106,15 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) {
selected ? selected.CollectionFreeFormDocumentView?.float() : console.log('[FontIconBox.tsx] toggleOverlay failed');
});
-
-
ScriptingGlobals.add(function showFreeform(
- attr: 'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'links' | 'custom1' | 'custom2' | 'custom3' | 'chat' | '1' | '2' | '3' | '4',
+ attr: 'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'links' | 'like' | 'star' | 'idea' | 'chat' | '1' | '2' | '3' | '4',
checkResult?: boolean,
persist?: boolean,
isDoubleClick?: boolean
) {
const selected = SelectionManager.Docs.lastElement();
// prettier-ignore
- const map: Map<'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'links' | 'custom1' | 'custom2' | 'custom3' | 'chat' | '1' | '2' | '3' | '4',
+ const map: Map<'flashcards' | 'center' | 'grid' | 'snaplines' | 'clusters' | 'arrange' | 'viewAll' | 'fitOnce' | 'time' | 'docType' | 'color' | 'links' | 'like' | 'star' | 'idea' | 'chat' | '1' | '2' | '3' | '4',
{
waitForRender?: boolean;
checkResult: (doc: Doc) => any;
@@ -167,87 +165,48 @@ ScriptingGlobals.add(function showFreeform(
checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "links",
setDoc: (doc: Doc, dv: DocumentView) => doc.cardSort = "links",
}],
- ['custom1', {
- checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "custom" && NumCast(doc?.customSortNumber) === 0,
+ ['like', {
+ checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "custom" && StrCast(doc?.customSortField) === "like",
setDoc: (doc: Doc, dv: DocumentView) => {
doc.cardSort = "custom";
- doc.customSortNumber = 0;
+ doc.customSortField = "like";
doc.visibleGroupNumbers = new List<number>();
}
}],
- ['custom2', {
- checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "custom" && NumCast(doc?.customSortNumber) === 1,
+ ['star', {
+ checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "custom" && StrCast(doc?.customSortField) === "star",
setDoc: (doc: Doc, dv: DocumentView) => {
doc.cardSort = "custom";
- doc.customSortNumber = 1;
+ doc.customSortField = "star";
doc.visibleGroupNumbers = new List<number>();
}
}],
- ['custom3', {
- checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "custom" && NumCast(doc?.customSortNumber) === 2,
+ ['idea', {
+ checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "custom" && StrCast(doc?.customSortField) === "idea",
setDoc: (doc: Doc, dv: DocumentView) => {
doc.cardSort = "custom";
- doc.customSortNumber = 2;
+ doc.customSortField = "idea";
doc.visibleGroupNumbers = new List<number>();
}
}],
['chat', {
- checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "chat",
- setDoc: (doc: Doc, dv: DocumentView) => doc.cardSort = "chat",
- }],
- ['1', {
- checkResult: (doc: Doc) => NumListCast(doc?.visibleGroupNumbers).includes(0),
- setDoc: (doc: Doc, dv: DocumentView) => {
- let list = NumListCast(doc.visibleGroupNumbers);
- if (list.includes(0)) {
- let newList = new List<number>(list.filter(d => d !== 0));
- doc.visibleGroupNumbers = newList;
- } else {
- list.push(0);
- doc.visibleGroupNumbers = new List<number>(list);
- }
- }
- }],
- ['2', {
- checkResult: (doc: Doc) => NumListCast(doc?.visibleGroupNumbers).includes(1),
- setDoc: (doc: Doc, dv: DocumentView) => {
- let list = NumListCast(doc.visibleGroupNumbers);
- if (list.includes(1)) {
- let newList = new List<number>(list.filter(d => d !== 1));
- doc.visibleGroupNumbers = newList;
- } else {
- list.push(1);
- doc.visibleGroupNumbers = new List<number>(list);
- }
- }
- }],
- ['3', {
- checkResult: (doc: Doc) => NumListCast(doc?.visibleGroupNumbers).includes(2),
- setDoc: (doc: Doc, dv: DocumentView) => {
- let list = NumListCast(doc.visibleGroupNumbers);
- if (list.includes(2)) {
- let newList = new List<number>(list.filter(d => d !== 2));
- doc.visibleGroupNumbers = newList;
- } else {
- list.push(2);
- doc.visibleGroupNumbers = new List<number>(list);
- }
- }
- }],
- ['4', {
- checkResult: (doc: Doc) => NumListCast(doc?.visibleGroupNumbers).includes(3),
+ checkResult: (doc: Doc) => StrCast(doc?.cardSort) === "custom" && StrCast(doc?.customSortField) === "chat",
setDoc: (doc: Doc, dv: DocumentView) => {
- let list = NumListCast(doc.visibleGroupNumbers);
- if (list.includes(3)) {
- let newList = new List<number>(list.filter(d => d !== 3));
- doc.visibleGroupNumbers = newList;
- } else {
- list.push(3);
- doc.visibleGroupNumbers = new List<number>(list);
- }
- }
+ doc.cardSort = "custom";
+ doc.customSortField = "chat";
+ doc.visibleGroupNumbers = new List<number>();
+ },
}],
]);
+ for (let i = 0; i < 8; i++) {
+ map.set((i + 1 + '') as any, {
+ checkResult: (doc: Doc) => NumListCast(doc?.visibleGroupNumbers).includes(i),
+ setDoc: (doc: Doc, dv: DocumentView) => {
+ const list = NumListCast(doc.visibleGroupNumbers);
+ doc.visibleGroupNumbers = new List<number>(list.includes(i) ? list.filter(d => d !== i) : [...list, i]);
+ },
+ });
+ }
if (checkResult) {
return map.get(attr)?.checkResult(selected);
@@ -257,13 +216,11 @@ ScriptingGlobals.add(function showFreeform(
setTimeout(() => batch.end(), 100);
});
-
-ScriptingGlobals.add(function isThatCardGroup(n: number){
- const canvas = SelectionManager.Docs.lastElement();
- return canvas.customSortNumber == n
-
-
-})
+ScriptingGlobals.add(function cardHasLabel(label: string) {
+ const selected = SelectionManager.Docs.lastElement();
+ const labelNum = Number(label) - 1;
+ return labelNum < 4 || (selected && DocListCast(selected[Doc.LayoutFieldKey(selected)]).some(doc => doc[StrCast(selected.customSortField)] == labelNum));
+}, '');
// ScriptingGlobals.add(function setCardSortAttr(attr: 'time' | 'docType' | 'color', value: any, checkResult?: boolean) {
// // const editorView = RichTextMenu.Instance?.TextView?.EditorView;