diff options
Diffstat (limited to 'src/client/views')
35 files changed, 672 insertions, 690 deletions
diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 1a5781df0..ae55c8ebf 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -199,7 +199,7 @@ export class DashboardView extends React.Component { <div className="dashboard-container" key={dashboard[Id]} - style={{ background: this.isUnviewedSharedDashboard(dashboard) && this.selectedDashboardGroup === DashboardGroup.SharedDashboards ? 'green' : shared ? 'blue' : '' }} + style={{ background: this.isUnviewedSharedDashboard(dashboard) && this.selectedDashboardGroup === DashboardGroup.SharedDashboards ? '#6CB982' : shared ? variant : '' }} onContextMenu={e => this.onContextMenu(dashboard, e)} onClick={e => this.clickDashboard(e, dashboard)}> <img diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index a41fc8ded..8fe5c2e01 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -145,7 +145,7 @@ export function ViewBoxAnnotatableComponent<P extends ViewBoxAnnotatableProps>() const toRemove = value.filter(v => docs.includes(v)); if (toRemove.length !== 0) { - const recent = Doc.MyRecentlyClosed; + const recent = this.rootDoc !== Doc.MyRecentlyClosed ? Doc.MyRecentlyClosed : undefined; toRemove.forEach(doc => { leavePushpin && DocUtils.LeavePushpin(doc, annotationKey ?? this.annotationKey); Doc.RemoveDocFromList(targetDataDoc, annotationKey ?? this.annotationKey, doc); diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 16f5ad168..6f5e9f5c0 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -589,7 +589,6 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV <div style={{ position: 'absolute', zIndex: 1000 }}> <LinkPopup key="popup" - showPopup={this._showLinkPopup} linkCreated={link => (link.link_displayLine = !IsFollowLinkScript(this.props.views().lastElement()?.rootDoc.onClick))} linkCreateAnchor={() => this.props.views().lastElement()?.ComponentView?.getAnchor?.(true)} linkFrom={() => this.props.views().lastElement()?.rootDoc} diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3522830e5..956dac555 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -831,7 +831,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P {shareSymbolIcon + ' ' + shareMode} - {!Doc.noviceMode ? ( + {/* {!Doc.noviceMode ? ( <div className="checkbox"> <div className="checkbox-box"> <input type="checkbox" checked={this.showLayoutAcl} onChange={action(() => (this.showLayoutAcl = !this.showLayoutAcl))} /> @@ -839,7 +839,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P <div className="checkbox-text"> Layout </div> </div> ) : null} - + */} </div> </div> ) : ( @@ -911,7 +911,13 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P width: bounds.r - bounds.x + this._resizeBorderWidth + 'px', height: bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight + 'px', }}> - <div className="documentDecorations-topbar" style={{ display: hideDeleteButton && hideTitle && hideOpenButton ? 'none' : undefined }} onPointerDown={this.onContainerDown}> + <div + className="documentDecorations-topbar" + style={{ + color: 'black', + display: hideDeleteButton && hideTitle && hideOpenButton ? 'none' : undefined, + }} + onPointerDown={this.onContainerDown}> {hideDeleteButton ? null : topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')} {hideResizers || hideDeleteButton ? null : topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')} {titleArea} diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx index c39fa79da..6c213f40f 100644 --- a/src/client/views/InkTranscription.tsx +++ b/src/client/views/InkTranscription.tsx @@ -105,7 +105,7 @@ export class InkTranscription extends React.Component { : null; } - r.addEventListener('exported', (e: any) => this.exportInk(e, this._textRef)); + r?.addEventListener('exported', (e: any) => this.exportInk(e, this._textRef)); return (this._textRef = r); }; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 86e8bab13..cbaa763f5 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -21,7 +21,7 @@ import { DocumentManager } from '../util/DocumentManager'; import { GroupManager } from '../util/GroupManager'; import { HistoryUtil } from '../util/History'; import { Hypothesis } from '../util/HypothesisUtils'; -import { ReportManager } from '../util/ReportManager'; +import { ReportManager } from '../util/reportManager/ReportManager'; import { RTFMarkup } from '../util/RTFMarkup'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; import { SelectionManager } from '../util/SelectionManager'; @@ -49,7 +49,6 @@ import { InkTranscription } from './InkTranscription'; import { LightboxView } from './LightboxView'; import { LinkMenu } from './linking/LinkMenu'; import './MainView.scss'; -import { NewLightboxView } from './newlightbox/NewLightboxView'; import { AudioBox } from './nodes/AudioBox'; import { DocumentLinksButton } from './nodes/DocumentLinksButton'; import { DocumentView, DocumentViewInternal, OpenWhere, OpenWhereMod } from './nodes/DocumentView'; @@ -215,7 +214,7 @@ export class MainView extends React.Component { window.removeEventListener('keydown', KeyManager.Instance.handle); window.removeEventListener('pointerdown', this.globalPointerDown, true); window.removeEventListener('pointermove', this.globalPointerMove, true); - window.removeEventListener('mouseclick', this.globalPointerClick, true); + window.removeEventListener('pointerup', this.globalPointerClick, true); window.removeEventListener('paste', KeyManager.Instance.paste as any); document.removeEventListener('linkAnnotationToDash', Hypothesis.linkListener); } @@ -534,7 +533,7 @@ export class MainView extends React.Component { // document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined)); document.addEventListener('pointerdown', this.globalPointerDown, true); document.addEventListener('pointermove', this.globalPointerMove, true); - document.addEventListener('mouseclick', this.globalPointerClick, true); + document.addEventListener('pointerup', this.globalPointerClick, true); document.addEventListener( 'click', (e: MouseEvent) => { diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 2e3668268..b74eabcc3 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -244,10 +244,10 @@ export class PropertiesButtons extends React.Component<{}, {}> { @computed get layout_autoHeightButton() { // store previous dimensions to store old values return this.propertyToggleBtn( - on => 'Auto\xA0Size', + on => (on ? 'AUTO\xA0SIZE' : 'FIXED SIZE'), '_layout_autoHeight', on => `Automatical vertical sizing to show all content`, - on => 'arrows-alt-v' + on => <FontAwesomeIcon icon="arrows-alt-v" size="lg" /> ); } diff --git a/src/client/views/PropertiesView.scss b/src/client/views/PropertiesView.scss index 23bf31b95..f7dc1dc68 100644 --- a/src/client/views/PropertiesView.scss +++ b/src/client/views/PropertiesView.scss @@ -53,16 +53,6 @@ padding: 0; margin-top: -5; } - - .propertiesView-acls-checkbox { - margin-top: -15px; - margin-bottom: -10px; - - .propertiesView-acls-checkbox-text { - display: inline; - font-size: 9px; - } - } } .change-buttons { @@ -79,14 +69,19 @@ } } + .propertiesView-acls-checkbox { + float: right; + margin-left: 50px; + } + .propertiesView-shareDropDown{ margin-right: 10px; min-width: 65px; & .propertiesView-shareDropDownNone{ - height: 16px; padding: 0px; padding-left: 3px; + padding-right: 3px; background: grey; color: rgb(71, 71, 71); border-radius: 6px; @@ -94,18 +89,18 @@ } & .propertiesView-shareDropDownEdit, .propertiesView-shareDropDownAdmin{ - height: 16px; padding: 0px; padding-left: 3px; + padding-right: 3px; background: rgb(254, 254, 199); color: rgb(75, 75, 5); border-radius: 6px; border: 1px solid rgb(75, 75, 5); } & .propertiesView-shareDropDownAugment{ - height: 16px; padding: 0px; padding-left: 3px; + padding-right: 3px; background: rgb(208, 255, 208); color:rgb(19, 80, 19); border-radius: 6px; @@ -113,18 +108,18 @@ } & .propertiesView-shareDropDownView{ - height: 16px; padding: 0px; padding-left: 3px; + padding-right: 3px; background: rgb(213, 213, 255); color: rgb(25, 25, 101); border-radius: 6px; border: 1px solid rgb(25, 25, 101); } & .propertiesView-shareDropDownNot-Shared{ - height: 16px; padding: 0px; padding-left: 3px; + padding-right: 3px; background: rgb(255, 207, 207); color: rgb(138, 47, 47); border-radius: 6px; @@ -188,19 +183,7 @@ } .expansion-button { - margin-left: -15px; - margin-right: 20px; - - .expansion-button-icon { - width: 11px; - height: 11px; - color: black; - - &:hover { - color: rgb(131, 131, 131); - cursor: pointer; - } - } + margin-right: 10px; } .propertiesView-sharingTable { @@ -249,6 +232,11 @@ } } + .propertiesView-permissions-select { + background: inherit; + border: none; + } + .propertiesView-field { display: flex; font-size: 7px; diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 3ad60865d..35ca5dea4 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -3,7 +3,7 @@ import { IconLookup } from '@fortawesome/fontawesome-svg-core'; import { faAnchor, faArrowRight, faWindowMaximize } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Checkbox, Tooltip } from '@material-ui/core'; -import { Button, Colors, EditableText, NumberInput, Size, Slider, Type } from 'browndash-components'; +import { Button, Colors, EditableText, IconButton, NumberInput, Size, Slider, Type } from 'browndash-components'; import { concat } from 'lodash'; import { Lambda, action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; @@ -367,17 +367,18 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { */ @computed get expansionIcon() { return ( - <Tooltip title={<div className="dash-tooltip">Show more permissions</div>}> - <div - className="expansion-button" - onPointerDown={() => { + <div className="expansion-button" > + <IconButton + icon={<FontAwesomeIcon icon={'ellipsis-h'} />} + size={Size.XSMALL} + color={StrCast(Doc.UserDoc().userColor)} + onClick={action(() => { if (this.selectedDocumentView || this.selectedDoc) { SharingManager.Instance.open(this.selectedDocumentView?.props.Document === this.selectedDoc ? this.selectedDocumentView : undefined, this.selectedDoc); } - }}> - <FontAwesomeIcon className="expansion-button-icon" icon="ellipsis-h" color="black" size="sm" /> - </div> - </Tooltip> + })} + /> + </div> ); } @@ -417,7 +418,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { <div> <div className={'propertiesView-shareDropDown'}> <div className={`propertiesView-shareDropDown${permission}`}> - <div className="propertiesView-shareDropDown">{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission, showGuestOptions) : concat(shareImage, ' ', permission)}</div> + <div >{admin && permission !== 'Owner' ? this.getPermissionsSelect(name, permission, showGuestOptions) : concat(shareImage, ' ', permission)}</div> </div> </div> </div> @@ -449,6 +450,7 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { const target = docs[0]; const showAdmin = GetEffectiveAcl(target) == AclAdmin; + console.log(GetEffectiveAcl(target), Doc.GetProto(target)[`acl-${normalizeEmail(Doc.CurrentUserEmail)}`]) const individualTableEntries = []; const usersAdded: string[] = []; // all shared users being added - organized by denormalized email @@ -512,28 +514,29 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { } }); - // public permission - const publicPermission = StrCast((this.layoutDocAcls ? target : Doc.GetProto(target))['acl-Guest']); + // guest permission + const guestPermission = StrCast((this.layoutDocAcls ? target : Doc.GetProto(target))['acl-Guest']); return ( <div> - <br /> - <div className="propertiesView-sharingTable">{<div> {individualTableEntries}</div>}</div> + <div> + <br></br> Individuals with Access to this Document + </div> + <div className="propertiesView-sharingTable" style={{background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor)}}> + {<div> {individualTableEntries}</div>} + </div> {groupTableEntries.length > 0 ? ( <div> <div> - {' '} - <br></br> Groups with Access to this Document{' '} + <br></br> Groups with Access to this Document + </div> + <div className="propertiesView-sharingTable" style={{background: StrCast(Doc.UserDoc().userBackgroundColor), color: StrCast(Doc.UserDoc().userColor)}}> + {<div> {groupTableEntries}</div>} </div> - <div className="propertiesView-sharingTable">{<div> {groupTableEntries}</div>}</div> </div> ) : null} - Guest - <div>{this.colorACLDropDown('Guest', true, publicPermission!, true)}</div> - <div> - {' '} - <br></br> Individual Users with Access to this Document{' '} - </div> + <br></br> Guest + <div>{this.colorACLDropDown('Guest', showAdmin, guestPermission!, true)}</div> </div> ); } @@ -1146,26 +1149,17 @@ export class PropertiesView extends React.Component<PropertiesViewProps> { return <PropertiesSection title="Sharing & Permissions" content={<> - <div className="propertiesView-buttonContainer"> - {!Doc.noviceMode ? ( - <div className="propertiesView-buttonContainer"> - <div className="propertiesView-acls-checkbox"> - <div className="propertiesView-acls-checkbox-text"> Show / Contol Layout Permissions </div> - <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> - </div> - {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}> - <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}> - <FontAwesomeIcon icon="redo-alt" color="white" size="1x" /> - </button> - </Tooltip> */} - </div> - ) : null} + {/* <div className="propertiesView-buttonContainer"> */} + <div className="propertiesView-acls-checkbox"> + Layout Permissions + <Checkbox color="primary" onChange={action(() => (this.layoutDocAcls = !this.layoutDocAcls))} checked={this.layoutDocAcls} /> + </div> {/* <Tooltip title={<><div className="dash-tooltip">{"Re-distribute sharing settings"}</div></>}> <button onPointerDown={() => SharingManager.Instance.distributeOverCollection(this.selectedDoc!)}> <FontAwesomeIcon icon="redo-alt" size="1x" /> </button> </Tooltip> */} - </div> + {/* </div> */} {this.sharingTable} </>} isOpen={this.openSharing} diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss index a4c5229aa..d93015506 100644 --- a/src/client/views/collections/CollectionDockingView.scss +++ b/src/client/views/collections/CollectionDockingView.scss @@ -176,6 +176,7 @@ display: flex; align-content: center; justify-content: center; + background: $dark-gray; } .lm_controls > li { diff --git a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx index 6f88f6727..22beb19de 100644 --- a/src/client/views/collections/CollectionMasonryViewFieldRow.tsx +++ b/src/client/views/collections/CollectionMasonryViewFieldRow.tsx @@ -8,7 +8,7 @@ import { Id } from '../../../fields/FieldSymbols'; import { PastelSchemaPalette, SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { ScriptField } from '../../../fields/ScriptField'; import { NumCast, StrCast } from '../../../fields/Types'; -import { emptyFunction, numberRange, returnEmptyString, setupMoveUpEvents } from '../../../Utils'; +import { emptyFunction, numberRange, returnEmptyString, returnFalse, setupMoveUpEvents } from '../../../Utils'; import { Docs } from '../../documents/Documents'; import { DragManager } from '../../util/DragManager'; import { CompileScript } from '../../util/Scripting'; @@ -252,10 +252,7 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr return ( <div className="collectionStackingView-optionPicker"> <div className="optionOptions"> - <div className={'optionPicker' + (selected === true ? ' active' : '')} onClick={this.toggleEmbedding}> - Create Embedding - </div> - <div className={'optionPicker' + (selected === true ? ' active' : '')} onClick={this.deleteRow}> + <div className={'optionPicker' + (selected === true ? ' active' : '')} onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.deleteRow)}> Delete </div> </div> @@ -273,13 +270,16 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr const stackPad = showChrome ? `0px ${this.props.parent.xMargin}px` : `${this.props.parent.yMargin}px ${this.props.parent.xMargin}px 0px ${this.props.parent.xMargin}px `; return this.collapsed ? null : ( <div style={{ position: 'relative' }}> + {this.props.showHandle && this.props.parent.props.isContentActive() ? this.props.parent.columnDragger : null} {showChrome ? ( <div className="collectionStackingView-addDocumentButton" - style={{ - //width: style.columnWidth / style.numGroupColumns, - padding: `${NumCast(this.props.parent.layoutDoc._yPadding, this.props.parent.yMargin)}px 0px 0px 0px`, - }}> + style={ + { + //width: style.columnWidth / style.numGroupColumns, + //padding: `${NumCast(this.props.parent.layoutDoc._yPadding, this.props.parent.yMargin)}px 0px 0px 0px`, + } + }> <EditableView GetValue={returnEmptyString} SetValue={this.addDocument} textCallback={this.textCallback} contents={'+ NEW'} /> </div> ) : null} @@ -288,12 +288,12 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr ref={this._contRef} style={{ padding: stackPad, + minHeight: this.props.showHandle && this.props.parent.props.isContentActive() ? '10px' : undefined, width: this.props.parent.NodeWidth, gridGap: this.props.parent.gridGap, gridTemplateColumns: numberRange(rows).reduce((list: string, i: any) => list + ` ${this.props.parent.columnWidth}px`, ''), }}> {this.props.parent.children(this.props.docList)} - {this.props.showHandle && this.props.parent.props.isContentActive() ? this.props.parent.columnDragger : null} </div> </div> ); @@ -313,23 +313,33 @@ export class CollectionMasonryViewFieldRow extends React.Component<CMVFieldRowPr onPointerDown={this.headerDown} title={evContents === `NO ${key.toUpperCase()} VALUE` ? `Documents that don't have a ${key} value will go here. This column cannot be removed.` : ''} style={{ background: evContents !== `NO ${key.toUpperCase()} VALUE` ? this.color : 'lightgrey' }}> - {noChrome ? evContents : editableHeaderView} + {noChrome ? evContents : <div>{editableHeaderView}</div>} {noChrome || evContents === `NO ${key.toUpperCase()} VALUE` ? null : ( <div className="collectionStackingView-sectionColor"> - <button className="collectionStackingView-sectionColorButton" onClick={action(e => (this._paletteOn = !this._paletteOn))}> + <button + className="collectionStackingView-sectionColorButton" + onPointerDown={e => + setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + action(e => (this._paletteOn = !this._paletteOn)) + ) + }> <FontAwesomeIcon icon="palette" size="lg" /> </button> {this._paletteOn ? this.renderColorPicker() : null} </div> )} {noChrome ? null : ( - <button className="collectionStackingView-sectionDelete" onClick={noChrome ? undefined : this.collapseSection}> + <button className="collectionStackingView-sectionDelete" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, noChrome ? emptyFunction : this.collapseSection)}> <FontAwesomeIcon icon={this.collapsed ? 'chevron-down' : 'chevron-up'} size="lg" /> </button> )} {noChrome || evContents === `NO ${key.toUpperCase()} VALUE` ? null : ( - <div className="collectionStackingView-sectionOptions"> - <Flyout anchorPoint={anchorPoints.TOP_CENTER} content={this.renderMenu()}> + <div className="collectionStackingView-sectionOptions" onPointerDown={e => e.stopPropagation()}> + <Flyout anchorPoint={anchorPoints.RIGHT_TOP} content={this.renderMenu()}> <button className="collectionStackingView-sectionOptionButton"> <FontAwesomeIcon icon="ellipsis-v" size="lg" /> </button> diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 9eb716763..5135cfb57 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -33,7 +33,7 @@ import { LightboxView } from '../LightboxView'; import { MainView } from '../MainView'; import { DefaultStyleProvider } from '../StyleProvider'; import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView'; -import { DocumentView, OpenWhereMod } from '../nodes/DocumentView'; +import { DocumentView, DocumentViewInternal, OpenWhereMod } from '../nodes/DocumentView'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; import { CollectionDockingView } from './CollectionDockingView'; import './CollectionMenu.scss'; @@ -129,7 +129,7 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> { docViewPath={returnEmptyDoclist} moveDocument={returnFalse} addDocument={returnFalse} - addDocTab={returnFalse} + addDocTab={DocumentViewInternal.addDocTabFunc} pinToPres={emptyFunction} removeDocument={returnFalse} ScreenToLocalTransform={this.buttonBarXf} @@ -154,21 +154,21 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> { const hardCodedButtons = ( <div className={`hardCodedButtons`}> - <Toggle - toggleType={ToggleType.BUTTON} - type={Type.PRIM} - color={StrCast(Doc.UserDoc().userColor)} - onClick={this.toggleTopBar} - toggleStatus={SettingsManager.headerBarHeight > 0} + <Toggle + toggleType={ToggleType.BUTTON} + type={Type.PRIM} + color={StrCast(Doc.UserDoc().userColor)} + onClick={this.toggleTopBar} + toggleStatus={SettingsManager.headerBarHeight > 0} icon={<FontAwesomeIcon icon={headerIcon} size="lg" />} tooltip={headerTitle} /> - <Toggle - toggleType={ToggleType.BUTTON} + <Toggle + toggleType={ToggleType.BUTTON} type={Type.PRIM} - color={StrCast(Doc.UserDoc().userColor)} - onClick={this.toggleProperties} - toggleStatus={SettingsManager.propertiesWidth > 0} + color={StrCast(Doc.UserDoc().userColor)} + onClick={this.toggleProperties} + toggleStatus={SettingsManager.propertiesWidth > 0} icon={<FontAwesomeIcon icon={propIcon} size="lg" />} tooltip={propTitle} /> @@ -178,11 +178,12 @@ export class CollectionMenu extends AntimodeMenu<CollectionMenuProps> { // NEW BUTTONS //dash col linear view buttons const contMenuButtons = ( - <div className="collectionMenu-container" - style={{ - background: StrCast(Doc.UserDoc().userBackgroundColor), - // borderColor: StrCast(Doc.UserDoc().userColor) - }} > + <div + className="collectionMenu-container" + style={{ + background: StrCast(Doc.UserDoc().userBackgroundColor), + // borderColor: StrCast(Doc.UserDoc().userColor) + }}> {this.contMenuButtons} {hardCodedButtons} </div> diff --git a/src/client/views/collections/CollectionStackingView.scss b/src/client/views/collections/CollectionStackingView.scss index 99a68e94b..255bc3889 100644 --- a/src/client/views/collections/CollectionStackingView.scss +++ b/src/client/views/collections/CollectionStackingView.scss @@ -128,6 +128,7 @@ height: 15; position: absolute; margin-left: -5; + z-index: 10; } // Documents in stacking view diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 805002452..a5c276125 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -1,7 +1,7 @@ import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { CursorProperty } from 'csstype'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, Opt } from '../../../fields/Doc'; import { DocData, Height, Width } from '../../../fields/DocSymbols'; @@ -11,7 +11,7 @@ import { listSpec } from '../../../fields/Schema'; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, returnEmptyDoclist, returnFalse, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils'; +import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnNone, returnZero, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { CollectionViewType } from '../../documents/DocumentTypes'; import { DragManager, dropActionType } from '../../util/DragManager'; @@ -31,7 +31,6 @@ import { CollectionMasonryViewFieldRow } from './CollectionMasonryViewFieldRow'; import './CollectionStackingView.scss'; import { CollectionStackingViewFieldColumn } from './CollectionStackingViewFieldColumn'; import { CollectionSubView } from './CollectionSubView'; -import { Colors } from '../global/globalEnums'; const _global = (window /* browser */ || global) /* node */ as any; export type collectionStackingViewProps = { @@ -59,7 +58,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection // map of node headers to their heights. Used in Masonry @observable _heightMap = new Map<string, number>(); // Assuming that this is the current css cursor style - @observable _cursor: CursorProperty = 'grab'; + @observable _cursor: CursorProperty = 'ew-resize'; // gets reset whenever we scroll. Not sure what it is @observable _scroll = 0; // used to force the document decoration to update when scrolling // does this mean whether the browser is hidden? Or is chrome something else entirely? @@ -308,19 +307,18 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection ? false : undefined; isChildButtonContentActive = () => (this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined); + @observable docRefs = new ObservableMap<Doc, DocumentView>(); // this is what renders the document that you see on the screen // called in Children: this actually adds a document to our children list getDisplayDoc(doc: Doc, width: () => number, count: number) { const dataDoc = !doc.isTemplateDoc && !doc.isTemplateForField ? undefined : this.props.DataDoc; const height = () => this.getDocHeight(doc); - let dref: Opt<DocumentView>; - const stackedDocTransform = () => this.getDocTransform(doc, dref); + const stackedDocTransform = () => this.getDocTransform(doc); this._docXfs.push({ stackedDocTransform, width, height }); - //DocumentView is how the node will be rendered return count > this._renderCount ? null : ( <DocumentView - ref={r => (dref = r || undefined)} + ref={action((r: DocumentView) => r?.ContentDiv && this.docRefs.set(doc, r))} Document={doc} DataDoc={dataDoc ?? (!Doc.AreProtosEqual(doc[DocData], doc) ? doc[DocData] : undefined)} renderDepth={this.props.renderDepth + 1} @@ -368,9 +366,10 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection ); } - getDocTransform(doc: Doc, dref?: DocumentView) { - const y = this._scroll; // required for document decorations to update when the text box container is scrolled - const { scale, translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv || undefined); + getDocTransform(doc: Doc) { + const dref = this.docRefs.get(doc); + this._scroll; // must be referenced for document decorations to update when the text box container is scrolled + const { translateX, translateY } = Utils.GetScreenTransform(dref?.ContentDiv); // the document view may center its contents and if so, will prepend that onto the screenToLocalTansform. so we have to subtract that off return new Transform(-translateX + (dref?.centeringX || 0), -translateY + (dref?.centeringY || 0), 1).scale(this.props.ScreenToLocalTransform().Scale); } @@ -409,7 +408,7 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection e, this.onDividerMove, action(() => { - this._cursor = 'grab'; + this._cursor = 'ew-resize'; batch.end(); }), emptyFunction @@ -423,7 +422,11 @@ export class CollectionStackingView extends CollectionSubView<Partial<collection @computed get columnDragger() { return ( - <div className="collectionStackingView-columnDragger" onPointerDown={this.columnDividerDown} ref={this._draggerRef} style={{ cursor: this._cursor, left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }}> + <div + className="collectionStackingView-columnDragger" + onPointerDown={this.columnDividerDown} + ref={this._draggerRef} + style={{ cursor: this._cursor, color: StrCast(Doc.UserDoc().userColor), left: `${this.columnWidth + this.xMargin}px`, top: `${Math.max(0, this.yMargin - 9)}px` }}> <FontAwesomeIcon icon={'arrows-alt-h'} /> </div> ); diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index d904749b1..fb23fc7f1 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -11,7 +11,7 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, DocCast, FieldValue, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, return18, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils'; +import { emptyFunction, lightOrDark, return18, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnTrue, returnZero, simulateMouseClick, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; @@ -222,10 +222,8 @@ export class TreeView extends React.Component<TreeViewProps> { this.treeViewOpen = !this.treeViewOpen; } else { // choose an appropriate embedding or make one. --- choose the first embedding that (1) user owns, (2) has no context field ... otherwise make a new embedding - const bestEmbedding = docView.rootDoc.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.props.Document) - ? docView.rootDoc - : Doc.BestEmbedding(docView.rootDoc); - this.props.addDocTab(bestEmbedding, OpenWhere.lightbox); + const bestEmbedding = docView.rootDoc.author === Doc.CurrentUserEmail && !Doc.IsDataProto(docView.props.Document) ? docView.rootDoc : Doc.BestEmbedding(docView.rootDoc); + this.props.addDocTab(bestEmbedding, OpenWhere.lightbox); } }; @@ -344,7 +342,6 @@ export class TreeView extends React.Component<TreeViewProps> { TreeView._editTitleOnLoad = { id: folder[Id], parent: this.props.parentTreeView }; return this.props.addDocument(folder); }; - deleteItem = () => this.props.removeDoc?.(this.doc); preTreeDrop = (e: Event, de: DragManager.DropEvent) => { const dragData = de.complete.docDragData; @@ -540,8 +537,6 @@ export class TreeView extends React.Component<TreeViewProps> { TraceMobx(); const expandKey = this.treeViewExpandedView; const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; icon: JSX.Element | string } }) ?? {}; - const color = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.Color) - console.log("tree view", color, this.doc.title, Doc.IsSystem(this.doc)) if (['links', 'annotations', 'embeddings', this.fieldKey].includes(expandKey)) { const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None); const sortKeys = Object.keys(sortings); @@ -581,9 +576,7 @@ export class TreeView extends React.Component<TreeViewProps> { ); } return ( - <div style={{ - color: color - }}> + <div> {!docs?.length || this.props.AddToMap /* hack to identify pres box trees */ ? null : ( <div className={'treeView-sorting'}> <IconButton @@ -662,14 +655,14 @@ export class TreeView extends React.Component<TreeViewProps> { ); } else if (this.treeViewExpandedView === 'fields') { return ( - <ul key={this.doc[Id] + this.doc.title} style={{ cursor: 'inherit', color: color }}> + <ul key={this.doc[Id] + this.doc.title} style={{ cursor: 'inherit' }}> <div>{this.expandedField}</div> </ul> ); } return ( <ul - style={{ color: color }} + style={{}} onPointerDown={e => { e.preventDefault(); e.stopPropagation(); @@ -725,26 +718,19 @@ export class TreeView extends React.Component<TreeViewProps> { }> {this.props.treeView.outlineMode ? ( !(this.doc.text as RichTextField)?.Text ? null : ( - <IconButton - color={color} - icon={<FontAwesomeIcon - icon={[this.childDocs?.length && !this.treeViewOpen ? 'fas' : 'far', 'circle']} />} - size={Size.XSMALL} - /> + <IconButton color={color} icon={<FontAwesomeIcon icon={[this.childDocs?.length && !this.treeViewOpen ? 'fas' : 'far', 'circle']} />} size={Size.XSMALL} /> ) ) : ( <div className="treeView-bulletIcons" style={{ color: Doc.IsSystem(DocCast(this.doc.proto)) ? 'red' : undefined }}> - {this.onCheckedClick ? <IconButton - color={color} - icon={<FontAwesomeIcon size="sm" icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'} />} - size={Size.XSMALL} - /> : + {this.onCheckedClick ? ( <IconButton - color={color} - icon={<FontAwesomeIcon icon={iconType as IconProp} />} + color={color} + icon={<FontAwesomeIcon size="sm" icon={checked === 'check' ? 'check' : checked === 'x' ? 'times' : checked === 'unchecked' ? 'square' : !this.treeViewOpen ? 'caret-right' : 'caret-down'} />} size={Size.XSMALL} /> - } + ) : ( + <IconButton color={color} icon={<FontAwesomeIcon icon={iconType as IconProp} />} size={Size.XSMALL} /> + )} </div> )} </div> @@ -776,17 +762,15 @@ export class TreeView extends React.Component<TreeViewProps> { return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? null : ( <> {customHeaderButtons} {/* e.g.,. hide button is set by dashboardStyleProvider */} - {this.doc._layout_hideContextMenu ? null : ( - <IconButton - color={color} - icon={<FontAwesomeIcon icon="bars" />} - size={Size.XSMALL} - onClick={e => { - this.showContextMenu(e); - e.stopPropagation(); - }} - /> - )} + <IconButton + color={color} + icon={<FontAwesomeIcon icon="bars" />} + size={Size.XSMALL} + onClick={e => { + this.showContextMenu(e); + e.stopPropagation(); + }} + /> {Doc.noviceMode ? null : this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? null : ( <span className="collectionTreeView-keyHeader" title="type of expanded data" key={this.treeViewExpandedView} onPointerDown={this.expandNextviewType}> {this.treeViewExpandedView} @@ -803,10 +787,10 @@ export class TreeView extends React.Component<TreeViewProps> { }; contextMenuItems = () => { const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'New Folder' }; - const deleteItem = { script: ScriptField.MakeFunction(`scriptContext.deleteItem()`, { scriptContext: 'any' })!, icon: 'folder-plus', label: 'Delete' }; const folderOp = this.childDocs?.length ? [makeFolder] : []; const openEmbedding = { script: ScriptField.MakeFunction(`openDoc(getEmbedding(self), "${OpenWhere.addRight}")`)!, icon: 'copy', label: 'Open New Embedding' }; const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Focus or Open' }; + const reopenDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, icon: 'eye', label: 'Reopen' }; return [ ...(this.props.contextMenuItems ?? []).filter(mi => (!mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result)), ...(this.doc.isFolder @@ -817,7 +801,9 @@ export class TreeView extends React.Component<TreeViewProps> { ? [openEmbedding, makeFolder] : this.doc._type_collection === CollectionViewType.Docking ? [] - : [deleteItem, openEmbedding, focusDoc]), + : this.props.treeView.rootDoc === Doc.MyRecentlyClosed + ? [reopenDoc] + : [openEmbedding, focusDoc]), ]; }; childContextMenuItems = () => { @@ -989,6 +975,8 @@ export class TreeView extends React.Component<TreeViewProps> { ref={this._tref} title="click to edit title. Double Click or Drag to Open" style={{ + backgroundColor: Doc.IsSystem(this.props.document) || this.props.document.isFolder ? StrCast(Doc.UserDoc().userVariantColor) : undefined, + color: Doc.IsSystem(this.props.document) || this.props.document.isFolder ? lightOrDark(StrCast(Doc.UserDoc().userVariantColor)) : undefined, fontWeight: Doc.IsSearchMatch(this.doc) !== undefined ? 'bold' : undefined, textDecoration: Doc.GetT(this.doc, 'title', 'string', true) ? 'underline' : undefined, outline: this.doc === Doc.ActiveDashboard ? 'dashed 1px #06123232' : undefined, @@ -1014,12 +1002,13 @@ export class TreeView extends React.Component<TreeViewProps> { onPointerDown={this.ignoreEvent} onPointerEnter={this.onPointerEnter} onPointerLeave={this.onPointerLeave}> - {contents} - <div className={`treeView-background`} + <div + className="treeView-background" style={{ - background: StrCast(Doc.UserDoc().userColor) + background: StrCast(Doc.UserDoc().userColor), }} /> + {contents} </div> {this.renderBorder} </> diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index f1d98d22a..fb8ec93b2 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -235,6 +235,9 @@ export class CollectionFreeFormLinkView extends React.Component<CollectionFreeFo bActive, textX, textY, + // pt1, + // pt2, + // this code adds space between links pt1: [pt1[0] + pt1normalized[0] * 13, pt1[1] + pt1normalized[1] * 13], pt2: [pt2[0] + pt2normalized[0] * 13, pt2[1] + pt2normalized[1] * 13], }; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss index cb5cef29c..4ada1731f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinksView.scss @@ -1,11 +1,13 @@ -.collectionfreeformlinksview-svgCanvas{ +// TODO: change z-index to -1 when a modal is active? + +.collectionfreeformlinksview-svgCanvas { position: absolute; top: 0; left: 0; - width: 100%; + width: 100%; height: 100%; pointer-events: none; - } - .collectionfreeformlinksview-container { +} +.collectionfreeformlinksview-container { pointer-events: none; - }
\ No newline at end of file +} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index ba31916a7..b5e9994dd 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1264,13 +1264,15 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection return this.addDocument?.(newDoc); } }; - pointerEvents = () => { + @computed get _pointerEvents() { const engine = this.props.layoutEngine?.() || StrCast(this.props.Document._layoutEngine); const pointerEvents = DocumentDecorations.Instance.Interacting ? 'none' : this.props.childPointerEvents ?? (this.props.viewDefDivClick || (engine === computePassLayout.name && !this.props.isSelected(true)) || this.isContentActive() === false ? 'none' : this.props.pointerEvents?.()); return pointerEvents; - }; + } + pointerEvents = () => this._pointerEvents; + childContentsActive = () => (this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction)(); getChildDocView(entry: PoolData) { const childLayout = entry.pair.layout; const childData = entry.pair.data; @@ -1299,7 +1301,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection childFiltersByRanges={this.childDocRangeFilters} searchFilterDocs={this.searchFilterDocs} isDocumentActive={this.props.childDocumentsActive?.() || this.rootDoc._isGroup ? this.props.isDocumentActive : this.isContentActive} - isContentActive={this.props.childContentsActive ?? this.isContentActive() === false ? returnFalse : emptyFunction} + isContentActive={this.childContentsActive} focus={this.Document._isGroup ? this.groupFocus : this.isAnnotationOverlay ? this.props.focus : this.focus} addDocTab={this.addDocTab} addDocument={this.props.addDocument} @@ -1435,21 +1437,17 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection }.bind(this) ); - childPositionProviderUnmemoized = (doc: Doc, replica: string) => { - return this._layoutPoolData.get(doc[Id] + (replica || '')); - }; + childPositionProviderUnmemoized = (doc: Doc, replica: string) => this._layoutPoolData.get(doc[Id] + (replica || '')); childDataProvider = computedFn( function childDataProvider(this: any, doc: Doc, replica: string) { - return this._layoutPoolData.get(doc[Id] + (replica || '')); + return this.childPositionProviderUnmemoized(doc, replica); }.bind(this) ); - childSizeProviderUnmemoized = (doc: Doc, replica: string) => { - return this._layoutSizeData.get(doc[Id] + (replica || '')); - }; + childSizeProviderUnmemoized = (doc: Doc, replica: string) => this._layoutSizeData.get(doc[Id] + (replica || '')); childSizeProvider = computedFn( function childSizeProvider(this: any, doc: Doc, replica: string) { - return this._layoutSizeData.get(doc[Id] + (replica || '')); + return this.childSizeProviderUnmemoized(doc, replica); }.bind(this) ); diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index 56b8366d0..2254b2e5f 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -204,7 +204,7 @@ export class CollectionLinearView extends CollectionSubView() { const menuOpener = ( <Toggle - text={Cast(this.props.Document.icon, 'string', null)} + text={Cast(this.props.Document.icon, 'string', null)} icon={Cast(this.props.Document.icon, 'string', null) ? undefined : <FontAwesomeIcon icon={isExpanded ? 'minus' : 'plus'} />} type={Type.TERT} color={StrCast(Doc.UserDoc().userVariantColor, Colors.MEDIUM_BLUE)} @@ -212,7 +212,7 @@ export class CollectionLinearView extends CollectionSubView() { toggleType={ToggleType.BUTTON} toggleStatus={BoolCast(this.layoutDoc.linearView_IsOpen)} onClick={() => { - this.layoutDoc.linearView_IsOpen = !isExpanded; + this.layoutDoc.linearView_IsOpen = !isExpanded; }} tooltip={isExpanded ? 'Close' : 'Open'} fillWidth={true} @@ -223,25 +223,22 @@ export class CollectionLinearView extends CollectionSubView() { return ( <div className={`collectionLinearView-outer ${this.layoutDoc.linearView_SubMenu}`} style={{ backgroundColor: this.layoutDoc.linearView_IsOpen ? undefined : 'transparent' }}> <div className="collectionLinearView" ref={this.createDashEventsTarget} onContextMenu={this.myContextMenu} style={{ minHeight: this.dimension(), pointerEvents: 'all' }}> - {this.props.Document.linearView_Dropdown ? - <div>Hello World!</div> - : + { <> - {!this.props.Document.linearView_Expandable ? null : menuOpener} - {!this.layoutDoc.linearView_IsOpen ? null : ( - <div - className="collectionLinearView-content" - style={{ - height: this.dimension(), - flexDirection: flexDir as any, - gap: flexGap, - }}> - {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))} - </div> - )} + {menuOpener} + {!this.layoutDoc.linearView_IsOpen ? null : ( + <div + className="collectionLinearView-content" + style={{ + height: this.dimension(), + flexDirection: flexDir as any, + gap: flexGap, + }}> + {this.childLayoutPairs.map(pair => this.getDisplayDoc(pair.layout))} + </div> + )} </> } - </div> </div> ); diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index ee5bf82ed..babe5c810 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -810,7 +810,6 @@ export class CollectionSchemaView extends CollectionSubView() { } @computed get sortedDocs() { - trace(); const field = StrCast(this.layoutDoc.sortField); const desc = BoolCast(this.layoutDoc.sortDesc); const docs = !field diff --git a/src/client/views/global/globalScripts.ts b/src/client/views/global/globalScripts.ts index b906065a0..61920cdef 100644 --- a/src/client/views/global/globalScripts.ts +++ b/src/client/views/global/globalScripts.ts @@ -1,26 +1,28 @@ -import { Colors } from "browndash-components"; -import { runInAction, action } from "mobx"; -import { aggregateBounds } from "../../../Utils"; -import { Doc } from "../../../fields/Doc"; -import { Width, Height } from "../../../fields/DocSymbols"; -import { InkTool } from "../../../fields/InkField"; -import { Cast, StrCast, NumCast, BoolCast } from "../../../fields/Types"; -import { WebField } from "../../../fields/URLField"; -import { GestureUtils } from "../../../pen-gestures/GestureUtils"; -import { LinkManager } from "../../util/LinkManager"; -import { ScriptingGlobals } from "../../util/ScriptingGlobals"; -import { SelectionManager } from "../../util/SelectionManager"; -import { UndoManager } from "../../util/UndoManager"; -import { GestureOverlay } from "../GestureOverlay"; -import { InkTranscription } from "../InkTranscription"; -import { ActiveFillColor, SetActiveFillColor, ActiveIsInkMask, SetActiveIsInkMask, ActiveInkWidth, SetActiveInkWidth, ActiveInkColor, SetActiveInkColor } from "../InkingStroke"; -import { CollectionFreeFormView } from "../collections/collectionFreeForm"; -import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; -import { WebBox } from "../nodes/WebBox"; -import { RichTextMenu } from "../nodes/formattedText/RichTextMenu"; -import { DocumentType } from "../../documents/DocumentTypes"; +import { Colors } from 'browndash-components'; +import { runInAction, action } from 'mobx'; +import { aggregateBounds } from '../../../Utils'; +import { Doc } from '../../../fields/Doc'; +import { Width, Height } from '../../../fields/DocSymbols'; +import { InkTool } from '../../../fields/InkField'; +import { Cast, StrCast, NumCast, BoolCast } from '../../../fields/Types'; +import { WebField } from '../../../fields/URLField'; +import { GestureUtils } from '../../../pen-gestures/GestureUtils'; +import { LinkManager } from '../../util/LinkManager'; +import { ScriptingGlobals } from '../../util/ScriptingGlobals'; +import { SelectionManager } from '../../util/SelectionManager'; +import { undoable, UndoManager } from '../../util/UndoManager'; +import { GestureOverlay } from '../GestureOverlay'; +import { InkTranscription } from '../InkTranscription'; +import { ActiveFillColor, SetActiveFillColor, ActiveIsInkMask, SetActiveIsInkMask, ActiveInkWidth, SetActiveInkWidth, ActiveInkColor, SetActiveInkColor } from '../InkingStroke'; +import { CollectionFreeFormView } from '../collections/collectionFreeForm'; +import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView'; +import { WebBox } from '../nodes/WebBox'; +import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; +import { DocumentType } from '../../documents/DocumentTypes'; -ScriptingGlobals.add(function IsNoneSelected() { return SelectionManager.Views().length <= 0; }, "are no document selected"); +ScriptingGlobals.add(function IsNoneSelected() { + return SelectionManager.Views().length <= 0; +}, 'are no document selected'); // toggle: Set overlay status of selected document ScriptingGlobals.add(function setView(view: string) { @@ -52,7 +54,7 @@ ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: b if (contentFrameNumber !== undefined) { CollectionFreeFormDocumentView.setStringValues(contentFrameNumber, dv.rootDoc, { fieldKey: color }); } else { - console.log('setting color to: ', color) + console.log('setting color to: ', color); dv.rootDoc['_' + fieldKey] = color; } }); @@ -119,7 +121,6 @@ ScriptingGlobals.add(function showFreeform(attr: 'flashcards' | 'grid' | 'snapli ]); if (checkResult) { - console.log(attr, map.get(attr)?.checkResult(selected)) return map.get(attr)?.checkResult(selected); } const batch = map.get(attr)?.waitForRender ? UndoManager.StartBatch('set freeform attribute') : { end: () => {} }; @@ -193,10 +194,9 @@ ScriptingGlobals.add(function toggleCharStyle(charStyle: attrname, checkResult?: const map = new Map(attrs.concat(alignments).concat(listings)); if (checkResult) { - console.log(charStyle, checkResult, map.get(charStyle)?.checkResult()); return map.get(charStyle)?.checkResult(); } - map.get(charStyle)?.toggle(); + undoable(() => map.get(charStyle)?.toggle(), 'toggle ' + charStyle)(); }); export function checkInksToGroup() { diff --git a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx index 0ede75407..ff17e5c12 100644 --- a/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx +++ b/src/client/views/newlightbox/ButtonMenu/ButtonMenu.tsx @@ -1,6 +1,6 @@ import './ButtonMenu.scss'; import * as React from 'react'; -import { IButtonMenu } from "./utils"; +import { IButtonMenu } from './utils'; import { NewLightboxView } from '../NewLightboxView'; import { SelectionManager } from '../../../util/SelectionManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; @@ -11,43 +11,40 @@ import { MainView } from '../../MainView'; import { action } from 'mobx'; export const ButtonMenu = (props: IButtonMenu) => { - - return <div className={`newLightboxButtonMenu-container`}> - <div - className="newLightboxView-navBtn" - title="toggle fit width" - onClick={e => { - e.stopPropagation(); - NewLightboxView.NewLightboxDoc!._fitWidth = !NewLightboxView.NewLightboxDoc!._fitWidth; - }}> - </div> - <div - className="newLightboxView-tabBtn" - title="open in tab" - onClick={e => { - e.stopPropagation(); - CollectionDockingView.AddSplit(NewLightboxView.NewLightboxDoc || NewLightboxView.NewLightboxDoc!, OpenWhereMod.none); - SelectionManager.DeselectAll(); - NewLightboxView.SetNewLightboxDoc(undefined); - }}> - </div> - <div - className="newLightboxView-penBtn" - title="toggle pen annotation" - style={{ background: Doc.ActiveTool === InkTool.Pen ? 'white' : undefined }} - onClick={e => { - e.stopPropagation(); - Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen; - }}> - </div> - <div - className="newLightboxView-exploreBtn" - title="toggle explore mode to navigate among documents only" - style={{ background: MainView.Instance._exploreMode ? 'white' : undefined }} - onClick={action(e => { - e.stopPropagation(); - MainView.Instance._exploreMode = !MainView.Instance._exploreMode; - })}> - </div> - </div> -}
\ No newline at end of file + return ( + <div className={`newLightboxButtonMenu-container`}> + <div + className="newLightboxView-navBtn" + title="toggle fit width" + onClick={e => { + e.stopPropagation(); + NewLightboxView.LightboxDoc!._fitWidth = !NewLightboxView.LightboxDoc!._fitWidth; + }}></div> + <div + className="newLightboxView-tabBtn" + title="open in tab" + onClick={e => { + e.stopPropagation(); + CollectionDockingView.AddSplit(NewLightboxView.LightboxDoc || NewLightboxView.LightboxDoc!, OpenWhereMod.none); + SelectionManager.DeselectAll(); + NewLightboxView.SetNewLightboxDoc(undefined); + }}></div> + <div + className="newLightboxView-penBtn" + title="toggle pen annotation" + style={{ background: Doc.ActiveTool === InkTool.Pen ? 'white' : undefined }} + onClick={e => { + e.stopPropagation(); + Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen; + }}></div> + <div + className="newLightboxView-exploreBtn" + title="toggle explore mode to navigate among documents only" + style={{ background: MainView.Instance._exploreMode ? 'white' : undefined }} + onClick={action(e => { + e.stopPropagation(); + MainView.Instance._exploreMode = !MainView.Instance._exploreMode; + })}></div> + </div> + ); +}; diff --git a/src/client/views/newlightbox/ExploreView/ExploreView.tsx b/src/client/views/newlightbox/ExploreView/ExploreView.tsx index 855bfd9e2..44a0bcd5f 100644 --- a/src/client/views/newlightbox/ExploreView/ExploreView.tsx +++ b/src/client/views/newlightbox/ExploreView/ExploreView.tsx @@ -1,30 +1,35 @@ import './ExploreView.scss'; -import { IBounds, IExploreView, emptyBounds } from "./utils"; -import { IRecommendation } from "../components"; +import { IBounds, IExploreView, emptyBounds } from './utils'; +import { IRecommendation } from '../components'; import * as React from 'react'; import { NewLightboxView } from '../NewLightboxView'; import { StrCast } from '../../../../fields/Types'; - - export const ExploreView = (props: IExploreView) => { - const { recs, bounds=emptyBounds } = props - - return <div className={`exploreView-container`}> - {recs && recs.map((rec) => { - console.log(rec.embedding, bounds) - const x_bound: number = Math.max(Math.abs(bounds.max_x), Math.abs(bounds.min_x)) - const y_bound: number = Math.max(Math.abs(bounds.max_y), Math.abs(bounds.min_y)) - console.log(x_bound, y_bound) - if (rec.embedding) { - const x = (rec.embedding.x / x_bound) * 50; - const y = (rec.embedding.y / y_bound) * 50; - console.log(x, y) - return <div className={`exploreView-doc`} onClick={() => {}} style={{top: `calc(50% + ${y}%)`, left: `calc(50% + ${x}%)`}}> - {rec.title} - </div> - } else return (null) - })} - <div className={`exploreView-doc`} style={{top: `calc(50% + ${0}%)`, left: `calc(50% + ${0}%)`, background: '#073763', color: 'white'}}>{StrCast(NewLightboxView.NewLightboxDoc?.title)}</div> - </div> -}
\ No newline at end of file + const { recs, bounds = emptyBounds } = props; + + return ( + <div className={`exploreView-container`}> + {recs && + recs.map(rec => { + console.log(rec.embedding, bounds); + const x_bound: number = Math.max(Math.abs(bounds.max_x), Math.abs(bounds.min_x)); + const y_bound: number = Math.max(Math.abs(bounds.max_y), Math.abs(bounds.min_y)); + console.log(x_bound, y_bound); + if (rec.embedding) { + const x = (rec.embedding.x / x_bound) * 50; + const y = (rec.embedding.y / y_bound) * 50; + console.log(x, y); + return ( + <div className={`exploreView-doc`} onClick={() => {}} style={{ top: `calc(50% + ${y}%)`, left: `calc(50% + ${x}%)` }}> + {rec.title} + </div> + ); + } else return null; + })} + <div className={`exploreView-doc`} style={{ top: `calc(50% + ${0}%)`, left: `calc(50% + ${0}%)`, background: '#073763', color: 'white' }}> + {StrCast(NewLightboxView.LightboxDoc?.title)} + </div> + </div> + ); +}; diff --git a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx index c0d357ad5..b9d05c531 100644 --- a/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx +++ b/src/client/views/newlightbox/components/Recommendation/Recommendation.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { IRecommendation } from "./utils"; +import { IRecommendation } from './utils'; import './Recommendation.scss'; import { getType } from '../../utils'; import { FaEyeSlash } from 'react-icons/fa'; @@ -9,82 +9,95 @@ import { Doc } from '../../../../../fields/Doc'; import { Docs } from '../../../../documents/Documents'; export const Recommendation = (props: IRecommendation) => { - const {title, data, type, text, transcript, loading, source, previewUrl, related_concepts, distance, docId} = props - - return <div className={`recommendation-container ${loading && 'loading'} ${previewUrl && 'previewUrl'}`} onClick={() => { - let doc: Doc | null = null; - if (source == "Dash" && docId) { - const docView = DocumentManager.Instance.getDocumentViewById(docId) - if (docView) { - doc = docView.rootDoc; - } - } else if (data) { - console.log(data, type) - switch(type) { - case "YouTube": - console.log('create ', type, 'document') - doc = Docs.Create.VideoDocument(data, { title: title, _width: 400, _height: 315, transcript: transcript }) - break; - case "Video": - console.log('create ', type, 'document') - doc = Docs.Create.VideoDocument(data, { title: title, _width: 400, _height: 315, transcript: transcript }) - break; - case "Webpage": - console.log('create ', type, 'document') - doc = Docs.Create.WebDocument(data, { title: title, text: text }) - break; - case "HTML": - console.log('create ', type, 'document') - doc = Docs.Create.WebDocument(data, { title: title, text: text }) - break; - case "Text": - console.log('create ', type, 'document') - doc = Docs.Create.TextDocument(data, { title: title, text: text }) - break; - case "PDF": - console.log('create ', type, 'document') - doc = Docs.Create.PdfDocument(data, { title: title, text: text }) - break; - } - } - if (doc !== null) NewLightboxView.SetNewLightboxDoc(doc) - }}> - {loading ? - <div className={`image-container`}> - </div> - : - previewUrl ? <div className={`image-container`}> - {<img className={`image`} src={previewUrl}></img>} - </div> - : null - } - <div className={`title`}>{title}</div> - <div className={`info`}> - {!loading && <div className={`type-container`}> - <div className={`lb-label`}>Type</div><div className={`lb-type`}>{getType(type!)}</div> - </div>} - {!loading && <div className={`distance-container`}> - <div className={`lb-label`}>Distance</div><div className={`lb-distance`}>{distance}</div> - </div>} - </div> - <div className={`source`}> - {!loading && <div className={`source-container`}> - <div className={`lb-label`}>Source</div><div className={`lb-source`}>{source}</div> - </div>} - </div> - <div className={`explainer`}> - {!loading && - <div> - You are seeing this recommendation because this document also explores - <div className={`concepts-container`}> - {related_concepts?.map((val) => { - return <div className={'concept'}>{val}</div> - })} - </div> - </div>} - </div> - <div className={`hide-rec`}> - {!loading && <><div>Hide Recommendation</div><div style={{fontSize: 15, paddingRight: 5}}><FaEyeSlash/></div></>} + const { title, data, type, text, transcript, loading, source, previewUrl, related_concepts, distance, docId } = props; + + return ( + <div + className={`recommendation-container ${loading && 'loading'} ${previewUrl && 'previewUrl'}`} + onClick={() => { + let doc: Doc | null = null; + if (source == 'Dash' && docId) { + const docView = DocumentManager.Instance.getDocumentViewsById(docId).lastElement(); + if (docView) { + doc = docView.rootDoc; + } + } else if (data) { + console.log(data, type); + switch (type) { + case 'YouTube': + console.log('create ', type, 'document'); + doc = Docs.Create.VideoDocument(data, { title: title, _width: 400, _height: 315, transcript: transcript }); + break; + case 'Video': + console.log('create ', type, 'document'); + doc = Docs.Create.VideoDocument(data, { title: title, _width: 400, _height: 315, transcript: transcript }); + break; + case 'Webpage': + console.log('create ', type, 'document'); + doc = Docs.Create.WebDocument(data, { title: title, text: text }); + break; + case 'HTML': + console.log('create ', type, 'document'); + doc = Docs.Create.WebDocument(data, { title: title, text: text }); + break; + case 'Text': + console.log('create ', type, 'document'); + doc = Docs.Create.TextDocument(data, { title: title, text: text }); + break; + case 'PDF': + console.log('create ', type, 'document'); + doc = Docs.Create.PdfDocument(data, { title: title, text: text }); + break; + } + } + if (doc !== null) NewLightboxView.SetNewLightboxDoc(doc); + }}> + {loading ? <div className={`image-container`}></div> : previewUrl ? <div className={`image-container`}>{<img className={`image`} src={previewUrl}></img>}</div> : null} + <div className={`title`}>{title}</div> + <div className={`info`}> + {!loading && ( + <div className={`type-container`}> + <div className={`lb-label`}>Type</div> + <div className={`lb-type`}>{getType(type!)}</div> + </div> + )} + {!loading && ( + <div className={`distance-container`}> + <div className={`lb-label`}>Distance</div> + <div className={`lb-distance`}>{distance}</div> + </div> + )} + </div> + <div className={`source`}> + {!loading && ( + <div className={`source-container`}> + <div className={`lb-label`}>Source</div> + <div className={`lb-source`}>{source}</div> + </div> + )} + </div> + <div className={`explainer`}> + {!loading && ( + <div> + You are seeing this recommendation because this document also explores + <div className={`concepts-container`}> + {related_concepts?.map(val => { + return <div className={'concept'}>{val}</div>; + })} + </div> + </div> + )} + </div> + <div className={`hide-rec`}> + {!loading && ( + <> + <div>Hide Recommendation</div> + <div style={{ fontSize: 15, paddingRight: 5 }}> + <FaEyeSlash /> + </div> + </> + )} + </div> </div> - </div> -}
\ No newline at end of file + ); +}; diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 7e8eef0a5..2d8663c9c 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -18,7 +18,6 @@ import { SearchBox } from '../search/SearchBox'; import { DashWebRTCVideo } from '../webcam/DashWebRTCVideo'; import { YoutubeBox } from './../../apis/youtube/YoutubeBox'; import { AudioBox } from './AudioBox'; -import { FontIconBox } from './FontIconBox/FontIconBox'; import { ColorBox } from './ColorBox'; import { ComparisonBox } from './ComparisonBox'; import { DataVizBox } from './DataVizBox/DataVizBox'; @@ -26,9 +25,11 @@ import { DocumentViewProps } from './DocumentView'; import './DocumentView.scss'; import { EquationBox } from './EquationBox'; import { FieldView, FieldViewProps } from './FieldView'; +import { FontIconBox } from './FontIconBox/FontIconBox'; import { FormattedTextBox } from './formattedText/FormattedTextBox'; import { FunctionPlotBox } from './FunctionPlotBox'; import { ImageBox } from './ImageBox'; +import { ImportElementBox } from './importBox/ImportElementBox'; import { KeyValueBox } from './KeyValueBox'; import { LabelBox } from './LabelBox'; import { LinkAnchorBox } from './LinkAnchorBox'; @@ -269,6 +270,7 @@ export class DocumentContentsView extends React.Component< LoadingBox, PhysicsSimulationBox, SchemaRowBox, + ImportElementBox, }} bindings={bindings} jsx={layoutFrame} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 66352678c..70d2f95ea 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,5 +1,5 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, reaction, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal'; @@ -314,7 +314,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps @computed get titleHeight() { return this.props?.styleProvider?.(this.layoutDoc, this.props, StyleProp.TitleHeight) || 0; } - get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined { + @computed get pointerEvents(): 'none' | 'all' | 'visiblePainted' | undefined { return this.props.styleProvider?.(this.Document, this.props, StyleProp.PointerEvents + (this.props.isSelected() ? ':selected' : '')); } @computed get finalLayoutKey() { @@ -332,8 +332,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps return ( DocumentView.LongPress || onScriptDisable === 'always' || - (onScriptDisable !== 'never' && (this.rootSelected() || this.props.isSelected())) || - this._componentView?.isAnyChildContentActive?.() + (onScriptDisable !== 'never' && (this.rootSelected() || this._componentView?.isAnyChildContentActive?.())) ); } @computed get onClickHandler() { @@ -800,7 +799,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps } const constantItems: ContextMenuProps[] = []; if (!Doc.IsSystem(this.rootDoc)) { - constantItems.push({ description: 'Export as Zip file', icon: 'download', event: async () => Doc.Zip(this.props.Document) }); + constantItems.push({ description: 'Zip Export', icon: 'download', event: async () => Doc.Zip(this.props.Document) }); (this.rootDoc._type_collection !== CollectionViewType.Docking || !Doc.noviceMode) && constantItems.push({ description: 'Share', event: () => SharingManager.Instance.open(this.props.DocumentView()), icon: 'users' }); if (this.props.removeDocument && Doc.ActiveDashboard !== this.props.Document) { // need option to gray out menu items ... preferably with a '?' that explains why they're grayed out (eg., no permissions) @@ -863,34 +862,32 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15); }; - rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false; + @computed get _rootSelected() { + return this.props.isSelected(false) || (this.props.Document.rootDocument && this.props.rootSelected?.(false)) || false; + } + rootSelected = (outsideReaction?: boolean) => this._rootSelected; panelHeight = () => this.props.PanelHeight() - this.headerMargin; screenToLocal = () => this.props.ScreenToLocalTransform().translate(0, -this.headerMargin); onClickFunc: any = () => (this.disableClickScriptFunc ? undefined : this.onClickHandler); setHeight = (height: number) => (this.layoutDoc._height = height); setContentView = action((view: { getAnchor?: (addAsAnnotation: boolean) => Doc; forward?: () => boolean; back?: () => boolean }) => (this._componentView = view)); - isContentActive = (outsideReaction?: boolean): boolean | undefined => { + @computed get _isContentActive() { // true - if the document has been activated directly or indirectly (by having its children selected) // false - if its pointer events are explicitly turned off or if it's container tells it that it's inactive // undefined - it is not active, but it should be responsive to actions that might active it or its contents (eg clicking) - return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none' || (this.rootDoc.pointerEvents === 'none' && !StrCast(this.props.LayoutTemplateString).includes(KeyValueBox.name)) + return this.props.isContentActive() === false || this.props.pointerEvents?.() === 'none' ? false - : Doc.ActiveTool !== InkTool.None || - SnappingManager.GetIsDragging() || - this.rootSelected() || - this.rootDoc.forceActive || - this.props.isSelected(outsideReaction) || - this._componentView?.isAnyChildContentActive?.() || - this.props.isContentActive() + : Doc.ActiveTool !== InkTool.None || SnappingManager.GetIsDragging() || this.rootSelected() || this.rootDoc.forceActive || this._componentView?.isAnyChildContentActive?.() || this.props.isContentActive() ? true : undefined; - }; + } + isContentActive = (): boolean | undefined => this._isContentActive; @observable _retryThumb = 1; - thumbShown = () => { - const childHighlighted = () => - Array.from(Doc.highlightedDocs.keys()) - .concat(Array.from(Doc.brushManager.BrushedDoc.keys())) - .some(doc => Doc.AreProtosEqual(DocCast(doc.annotationOn), this.rootDoc)); + @computed get _thumbShown() { + const childHighlighted = () => false; + // Array.from(Doc.highlightedDocs.keys()) + // .concat(Array.from(Doc.brushManager.BrushedDoc.keys())) + // .some(doc => Doc.AreProtosEqual(DocCast(doc.annotationOn), this.rootDoc)); const childOverlayed = () => Array.from(DocumentManager._overlayViews).some(view => Doc.AreProtosEqual(view.rootDoc, this.rootDoc)); return !this.props.LayoutTemplateString && !this.isContentActive() && @@ -900,12 +897,15 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps ((!childHighlighted() && !childOverlayed() && !Doc.isBrushedHighlightedDegree(this.rootDoc)) || this.rootDoc._type_collection === CollectionViewType.Docking) ? true : false; - }; + } + thumbShown = () => this._thumbShown; childFilters = () => [...this.props.childFilters(), ...StrListCast(this.layoutDoc.childFilters)]; - /// disable pointer events on content when there's an enabled onClick script (but not the browse script), or if contents are marked inactive - contentPointerEvents = () => ((!this.disableClickScriptFunc && this.onClickHandler && !this.props.onBrowseClick?.()) || this.isContentActive() === false ? 'none' : this.pointerEvents); - + /// disable pointer events on content when there's an enabled onClick script (but not the browse script) and the contents aren't forced active, or if contents are marked inactive + @computed get _contentPointerEvents() { + return (!this.disableClickScriptFunc && this.onClickHandler && !this.props.onBrowseClick?.() && this.isContentActive() !== true) || this.isContentActive() === false ? 'none' : this.pointerEvents; + } + contentPointerEvents = () => this._contentPointerEvents; @computed get contents() { TraceMobx(); const isInk = StrCast(this.layoutDoc.layout).includes(InkingStroke.name) && !this.props.LayoutTemplateString; @@ -1088,7 +1088,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps <div className="documentView-captionWrapper" style={{ - pointerEvents: this.Document.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined, + pointerEvents: this.rootDoc.ignoreClick ? 'none' : this.isContentActive() || this.props.isDocumentActive?.() ? 'all' : undefined, minWidth: 50 * ffscale(), maxHeight: `max(100%, ${20 * ffscale()}px)`, }}> @@ -1223,16 +1223,22 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps case PresEffect.Lightspeed: return <LightSpeed {...effectProps}>{renderDoc}</LightSpeed>; } } + @computed get highlighting() { + return this.props.styleProvider?.(this.props.Document, this.props, StyleProp.Highlighting); + } + @computed get borderPath() { + return this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BorderPath); + } render() { TraceMobx(); - const highlighting = this.props.styleProvider?.(this.props.Document, this.props, StyleProp.Highlighting); - const borderPath = this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BorderPath); + const highlighting = this.highlighting; + const borderPath = this.borderPath; const boxShadow = this.props.treeViewDoc || !highlighting ? this.boxShadow : highlighting && this.borderRounding && highlighting.highlightStyle !== 'dashed' ? `0 0 0 ${highlighting.highlightIndex}px ${highlighting.highlightColor}` - : this.boxShadow || (this.props.Document.isTemplateForField ? 'black 0.2vw 0.2vw 0.8vw' : undefined); + : this.boxShadow || (this.rootDoc.isTemplateForField ? 'black 0.2vw 0.2vw 0.8vw' : undefined); const renderDoc = this.renderDoc({ borderRadius: this.borderRounding, outline: highlighting && !this.borderRounding && !highlighting.highlightStroke ? `${highlighting.highlightColor} ${highlighting.highlightStyle} ${highlighting.highlightIndex}px` : 'solid 0px', @@ -1248,9 +1254,9 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps onContextMenu={this.onContextMenu} onPointerDown={this.onPointerDown} onClick={this.onClick} - onPointerEnter={e => (!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.props.Document)} - onPointerOver={e => (!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.props.Document)} - onPointerLeave={e => !isParentOf(this.ContentDiv, document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y)) && Doc.UnBrushDoc(this.props.Document)} + onPointerEnter={e => (!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.rootDoc)} + onPointerOver={e => (!SnappingManager.GetIsDragging() || DragManager.CanEmbed) && Doc.BrushDoc(this.rootDoc)} + onPointerLeave={e => !isParentOf(this.ContentDiv, document.elementFromPoint(e.nativeEvent.x, e.nativeEvent.y)) && Doc.UnBrushDoc(this.rootDoc)} style={{ borderRadius: this.borderRounding, pointerEvents: this.pointerEvents === 'visiblePainted' ? 'none' : this.pointerEvents, diff --git a/src/client/views/nodes/FontIconBox/FontIconBox.tsx b/src/client/views/nodes/FontIconBox/FontIconBox.tsx index 3ec53beac..ad3532502 100644 --- a/src/client/views/nodes/FontIconBox/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox/FontIconBox.tsx @@ -1,6 +1,6 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Button, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components'; +import { Button, MultiToggle, ColorPicker, Dropdown, DropdownType, EditableText, IconButton, IListItemProps, NumberDropdown, NumberDropdownType, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -21,6 +21,7 @@ import { RichTextMenu } from '../formattedText/RichTextMenu'; import './FontIconBox.scss'; import { SelectedDocView } from '../../selectedDoc'; import { Utils } from '../../../../Utils'; +import { FaAlignCenter, FaAlignJustify, FaAlignLeft, FaAlignRight } from 'react-icons/fa'; export enum ButtonType { TextButton = 'textBtn', @@ -86,10 +87,10 @@ export class FontIconBox extends DocComponent<ButtonProps>() { } Icon = (color: string, iconFalse?: boolean) => { let icon; - if (iconFalse ) { + if (iconFalse) { icon = StrCast(this.dataDoc[this.fieldKey ?? 'iconFalse'] ?? this.dataDoc.icon, 'user') as any; - if (icon) return <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} /> - else return null + if (icon) return <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />; + else return null; } icon = StrCast(this.dataDoc[this.fieldKey ?? 'icon'] ?? this.dataDoc.icon, 'user') as any; const trailsIcon = () => <img src={`/assets/${'presTrails.png'}`} style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? '0%' : '100%'})` }} />; @@ -124,36 +125,38 @@ export class FontIconBox extends DocComponent<ButtonProps>() { */ @computed get numberDropdown() { let type: NumberDropdownType; - switch(this.type) { - case ButtonType.NumberDropdownButton: - type = 'dropdown' + switch (this.type) { + case ButtonType.NumberDropdownButton: + type = 'dropdown'; break; case ButtonType.NumberInlineButton: - type = 'input' + type = 'input'; break; case ButtonType.NumberSliderButton: default: - type = 'slider' + type = 'slider'; break; } - const numScript = (value?: number) => ScriptCast(this.rootDoc.script).script.run({ self: this.rootDoc, value, _readOnly_: value === undefined }); + const numScript = (value?: number) => ScriptCast(this.rootDoc.script).script.run({ this: this.layoutDoc, self: this.rootDoc, value, _readOnly_: value === undefined }); const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); // Script for checking the outcome of the toggle const checkResult = Number(Number(numScript().result ?? 0).toPrecision(NumCast(this.dataDoc.numPrecision, 3))); const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>; - return <NumberDropdown - color={color} - numberDropdownType={type} - showPlusMinus={false} - tooltip={this.label} - type={Type.PRIM} - min={NumCast(this.rootDoc.numBtnMin, 0)} - max={NumCast(this.rootDoc.numBtnMax, 100)} - number={checkResult} - setNumber={undoable(value => numScript(value), `${this.rootDoc.title} button set from list`)} - fillWidth - /> + return ( + <NumberDropdown + color={color} + numberDropdownType={type} + showPlusMinus={false} + tooltip={this.label} + type={Type.PRIM} + min={NumCast(this.rootDoc.numBtnMin, 0)} + max={NumCast(this.rootDoc.numBtnMax, 100)} + number={checkResult} + setNumber={undoable(value => numScript(value), `${this.rootDoc.title} button set from list`)} + fillWidth + /> + ); } /** @@ -202,84 +205,64 @@ export class FontIconBox extends DocComponent<ButtonProps>() { let dropdown = true; let getStyle: (val: string) => any = () => {}; let icon: IconProp = 'caret-down'; - let isViewDropdown: boolean = script?.script.originalScript.startsWith('setView') + let isViewDropdown: boolean = script?.script.originalScript.startsWith('setView'); try { if (isViewDropdown) { const selectedDocs: Doc[] = SelectionManager.Docs(); const selected = SelectionManager.Docs().lastElement(); - console.log('selected') if (selected) { if (StrCast(selected.type) === DocumentType.COL) { text = StrCast(selected._type_collection); - console.log("collection selected", text) } else { - console.log("doc selected", selected.title); dropdown = false; if (selectedDocs.length > 1) { - text = selectedDocs.length + " selected" + text = selectedDocs.length + ' selected'; } else { text = Utils.cleanDocumentType(StrCast(selected.type) as DocumentType); icon = Doc.toIcon(selected); } - return <Popup - icon={<FontAwesomeIcon size={'1x'} icon={icon} />} - text={text} - type={Type.TERT} - color={color} - popup={<SelectedDocView selectedDocs={selectedDocs}/>} - fillWidth - /> + return <Popup icon={<FontAwesomeIcon size={'1x'} icon={icon} />} text={text} type={Type.TERT} color={color} popup={<SelectedDocView selectedDocs={selectedDocs} />} fillWidth />; } } else { dropdown = false; - return <Button - text={`None Selected`} - type={Type.TERT} - color={color} - fillWidth - inactive - /> + return <Button text={`None Selected`} type={Type.TERT} color={color} fillWidth inactive />; } noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking]; - } else { + } else { text = StrCast((RichTextMenu.Instance?.TextView?.EditorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); - getStyle = (val: string) => { return { fontFamily: val } } + getStyle = (val: string) => { + return { fontFamily: val }; + }; } } catch (e) { console.log(e); } - console.log("current item: ", text); - // Get items to place into the list const list: IListItemProps[] = this.buttonList .filter(value => !Doc.noviceMode || !noviceList.length || noviceList.includes(value)) - .map(value => ( - { - text: value.charAt(0).toUpperCase() + value.slice(1), - val: value, - style: getStyle(value), - onClick: undoable(() => script.script.run({ self: this.rootDoc, value }), value) - // shortcut: '#', - } - )); - + .map(value => ({ + text: value.charAt(0).toUpperCase() + value.slice(1), + val: value, + style: getStyle(value), + onClick: undoable(() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value }), value), + // shortcut: '#', + })); return ( - <Dropdown + <Dropdown selectedVal={text} - setSelectedVal={undoable((val) => script.script.run({ self: this.rootDoc, val }), `dropdown select ${this.label}`)} - color={color} - type={isViewDropdown ? Type.TERT : Type.PRIM} - dropdownType={DropdownType.SELECT} + setSelectedVal={undoable(val => script.script.run({ this: this.layoutDoc, self: this.rootDoc, val }), `dropdown select ${this.label}`)} + color={color} + type={isViewDropdown ? Type.TERT : Type.PRIM} + dropdownType={DropdownType.SELECT} items={list} tooltip={this.label} fillWidth /> - ) + ); } - @computed get colorScript() { return ScriptCast(this.rootDoc.script); } @@ -290,14 +273,14 @@ export class FontIconBox extends DocComponent<ButtonProps>() { @computed get colorButton() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const curColor = this.colorScript?.script.run({ self: this.rootDoc, value: undefined, _readOnly_: true }).result ?? 'transparent'; + const curColor = this.colorScript?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result ?? 'transparent'; const tooltip: string = StrCast(this.rootDoc.toolTip); return ( - <ColorPicker - setSelectedColor={(value) => { + <ColorPicker + setSelectedColor={value => { const s = this.colorScript; - s && undoable(() => s.script.run({ self: this.rootDoc, value: value, _readOnly_: false }).result, `Set ${tooltip} to ${value}`)(); + s && undoable(() => s.script.run({ this: this.layoutDoc, self: this.rootDoc, value: value, _readOnly_: false }).result, `Set ${tooltip} to ${value}`)(); }} selectedColor={curColor} type={Type.PRIM} @@ -306,7 +289,35 @@ export class FontIconBox extends DocComponent<ButtonProps>() { tooltip={tooltip} label={this.label} /> - ) + ); + } + @computed get multiToggleButton() { + // Determine the type of toggle button + const tooltip: string = StrCast(this.rootDoc.toolTip); + + const script = ScriptCast(this.rootDoc.onClick); + const toggleStatus = script ? script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result : false; + // Colors + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const items = DocListCast(this.rootDoc.data); + return ( + <MultiToggle + tooltip={`Toggle ${tooltip}`} + type={Type.PRIM} + color={color} + label={this.label} + items={DocListCast(this.rootDoc.data).map(item => ({ + icon: <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={StrCast(item.icon) as any} color={color} />, + tooltip: StrCast(item.toolTip), + val: StrCast(item.toolType), + }))} + selectedVal={StrCast(items.find(itemDoc => ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, self: itemDoc, value: undefined, _readOnly_: true }).result)?.toolType)} + setSelectedVal={(val: string | number) => { + const itemDoc = items.find(item => item.toolType === val); + itemDoc && ScriptCast(itemDoc.onClick).script.run({ this: itemDoc, self: itemDoc, value: val, _readOnly_: false }); + }} + /> + ); } @computed get toggleButton() { @@ -315,25 +326,24 @@ export class FontIconBox extends DocComponent<ButtonProps>() { const tooltip: string = StrCast(this.rootDoc.toolTip); const script = ScriptCast(this.rootDoc.onClick); - const toggleStatus = script ? script.script.run({ self: this.rootDoc, value: undefined, _readOnly_: true }).result : false; + const toggleStatus = script ? script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: undefined, _readOnly_: true }).result : false; // Colors const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - console.log(tooltip, toggleStatus); return ( - <Toggle - tooltip={`Toggle ${tooltip}`} - toggleType={ToggleType.BUTTON} - type={Type.PRIM} - toggleStatus={toggleStatus} - text={buttonText} - color={color} - icon={this.Icon(color)!} + <Toggle + tooltip={`Toggle ${tooltip}`} + toggleType={ToggleType.BUTTON} + type={Type.PRIM} + toggleStatus={toggleStatus} + text={buttonText} + color={color} + icon={this.Icon(color)!} label={this.label} - onPointerDown={() => script.script.run({ self: this.rootDoc, value: !toggleStatus, _readOnly_: false })} + onPointerDown={() => script.script.run({ this: this.layoutDoc, self: this.rootDoc, value: !toggleStatus, _readOnly_: false })} /> - ) + ); } /** @@ -344,28 +354,24 @@ export class FontIconBox extends DocComponent<ButtonProps>() { const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); const tooltip: string = StrCast(this.rootDoc.toolTip); - return ( - <IconButton tooltip={tooltip} icon={this.Icon(color)!} label={this.label}/> - ) + return <IconButton tooltip={tooltip} icon={this.Icon(color)!} label={this.label} />; } @computed get editableText() { // Script for running the toggle const script = ScriptCast(this.rootDoc.script); // Function to run the script - const checkResult = script?.script.run({ value: '', _readOnly_: true }).result; + const checkResult = script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: '', _readOnly_: true }).result; + + const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value, _readOnly_: false }).result; + + return <EditableText editing={false} setEditing={(editing: boolean) => {}} />; - const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ value, _readOnly_: false }).result; - - return <EditableText - editing={false} setEditing={(editing: boolean) => {}} - /> - return ( <div className="menuButton editableText"> <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'lock'} /> <div style={{ width: 'calc(100% - .875em)', paddingLeft: '4px' }}> - <EditableView GetValue={() => script?.script.run({ value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} /> + <EditableView GetValue={() => script?.script.run({ this: this.layoutDoc, self: this.rootDoc, value: '', _readOnly_: true }).result} SetValue={setValue} oneLine={true} contents={checkResult} /> </div> </div> ); @@ -374,47 +380,34 @@ export class FontIconBox extends DocComponent<ButtonProps>() { render() { // determine dash button metadata const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); const tooltip: string = StrCast(this.rootDoc.toolTip); const onClickScript = ScriptCast(this.rootDoc.onClick); - const script = ScriptCast(this.rootDoc.script); // TODO:glr Add label of button type let button: JSX.Element = this.defaultButton; // prettier-ignore switch (this.type) { case ButtonType.EditableText: - button = this.editableText; - break; + button = this.editableText; break; case ButtonType.DropdownList: - button = this.dropdownListButton; - break; + button = this.dropdownListButton; break; case ButtonType.ColorButton: - button = this.colorButton; - break; + button = this.colorButton; break; case ButtonType.NumberDropdownButton: case ButtonType.NumberInlineButton: case ButtonType.NumberSliderButton: - button = this.numberDropdown; - break; + button = this.numberDropdown; break; case ButtonType.DropdownButton: - button = this.dropdownButton; - break; + button = this.dropdownButton; break; + case ButtonType.MultiToggleButton: + button = this.multiToggleButton; break; case ButtonType.ToggleButton: button = this.toggleButton; break; case ButtonType.ClickButton: case ButtonType.ToolButton: - button = ( - <IconButton tooltip={tooltip} onPointerDown={() => onClickScript?.script.run({ _readOnly_: false })} color={color} icon={this.Icon(color)!} label={this.label}/> - ); - break; + button = <IconButton tooltip={tooltip} color={color} icon={this.Icon(color)!} label={this.label}/>; break; case ButtonType.TextButton: - button = ( - <Button tooltip={tooltip} icon={this.Icon(color)!} text={StrCast(this.rootDoc.buttonText)} label={this.label}/> - ); - break; - case ButtonType.MenuButton: button = ( - <IconButton tooltip={tooltip} onPointerDown={() => onClickScript?.script.run({ self: this.rootDoc, _readOnly_: false })} tooltipPlacement='right' size={Size.LARGE} color={color} icon={this.Icon(color)!} label={this.label}/> - ); - break; + button = <Button tooltip={tooltip} icon={this.Icon(color)!} text={StrCast(this.rootDoc.buttonText)} label={this.label}/>; break; + case ButtonType.MenuButton: + button = <IconButton tooltip={tooltip} onPointerDown={() => onClickScript?.script.run({ this: this.layoutDoc, self: this.rootDoc, _readOnly_: false })} tooltipPlacement='right' size={Size.LARGE} color={color} icon={this.Icon(color)!} label={this.label}/>; break; } return button; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 909a420fe..d763753a5 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -295,7 +295,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp choosePath(url: URL) { const lower = url.href.toLowerCase(); if (url.protocol === 'data') return url.href; - if (url.href.indexOf(window.location.origin) === -1) return Utils.CorsProxy(url.href); + if (url.href.indexOf(window.location.origin) === -1 && url.href.indexOf("dashblobstore") === -1) return Utils.CorsProxy(url.href); if (!/\.(png|jpg|jpeg|gif|webp)$/.test(lower)) return `/assets/unknown-file-icon-hi.png`; const ext = extname(url.href); diff --git a/src/client/views/nodes/LoadingBox.scss b/src/client/views/nodes/LoadingBox.scss index 4c3b8dabe..d4a7e18f2 100644 --- a/src/client/views/nodes/LoadingBox.scss +++ b/src/client/views/nodes/LoadingBox.scss @@ -12,6 +12,10 @@ text-overflow: ellipsis; max-width: 80%; text-align: center; + display: flex; + flex-direction: column; + gap: 8px; + align-items: center; } } diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index b4fb7a44e..d5ad128fe 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -17,6 +17,7 @@ import { OpenWhere } from '../DocumentView'; import './DashFieldView.scss'; import { FormattedTextBox } from './FormattedTextBox'; import React = require('react'); +import { Transform } from '../../../util/Transform'; export class DashFieldView { dom: HTMLDivElement; // container for label and value @@ -113,6 +114,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna componentWillUnmount() { this._reactionDisposer?.(); } + return100 = () => 100; // set the display of the field's value (checkbox for booleans, span of text for strings) @computed get fieldValueContent() { @@ -123,7 +125,7 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna col={0} deselectCell={emptyFunction} selectCell={emptyFunction} - maxWidth={this.props.hideKey ? undefined : () => 100} + maxWidth={this.props.hideKey ? undefined : this.return100} columnWidth={this.props.hideKey ? () => this.props.tbox.props.PanelWidth() - 20 : returnZero} selectedCell={() => [this._dashDoc!, 0]} fieldKey={this._fieldKey} @@ -135,6 +137,8 @@ export class DashFieldViewInternal extends React.Component<IDashFieldViewInterna allowCRs={true} oneLine={!this._expanded} finishEdit={action(() => (this._expanded = false))} + transform={Transform.Identity} + menuTarget={null} /> </div> ); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss index 109b62e6f..348bdd79e 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.scss +++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss @@ -84,7 +84,6 @@ audiotag:hover { height: 11; } -.formattedTextBox-outer-selected, .formattedTextBox-outer { position: relative; overflow: auto; @@ -92,9 +91,6 @@ audiotag:hover { width: 100%; height: unset; } -.formattedTextBox-outer-selected { - cursor: text; -} .formattedTextBox-sidebar-handle { position: absolute; @@ -148,10 +144,8 @@ audiotag:hover { } .formattedTextBox-inner-rounded, -.formattedTextBox-inner-rounded-selected, .formattedTextBox-inner, -.formattedTextBox-inner-minimal, -.formattedTextBox-inner-selected { +.formattedTextBox-inner-minimal { height: 100%; white-space: pre-wrap; .ProseMirror:hover { @@ -169,17 +163,6 @@ audiotag:hover { border-width: 1px; } } -.formattedTextBox-inner-rounded-selected, -.formattedTextBox-inner-selected { - > .ProseMirror { - padding: 10px; - } -} -.formattedTextBox-outer-selected { - > .ProseMirror:hover { - background: unset; - } -} .gpt-typing-wrapper { padding: 10px; @@ -640,7 +623,6 @@ footnote::before { } } - .formattedTextBox-outer-selected, .formattedTextBox-outer { position: relative; overflow: auto; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index a0eb328a1..24d2f0e13 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1557,7 +1557,11 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps ); }; + @action componentWillUnmount() { + if (this._recording) { + this._recording = !this._recording; + } Object.values(this._disposers).forEach(disposer => disposer?.()); this.endUndoTypingBatch(); FormattedTextBox.LiveTextUndo?.end(); @@ -1596,8 +1600,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } if (this._recording && !e.ctrlKey && e.button === 0) { this.breakupDictation(); - e.preventDefault(); - e.stopPropagation(); } this._downX = e.clientX; this._downY = e.clientY; @@ -2043,17 +2045,25 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps } }; _oldWheel: any; + @computed get fontColor() { + return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color); + } + @computed get fontSize() { + return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize); + } + @computed get fontFamily() { + return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily); + } + @computed get fontWeight() { + return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontWeight); + } render() { TraceMobx(); - const active = this.props.isContentActive(); const scale = (this.props.NativeDimScaling?.() || 1) * NumCast(this.layoutDoc._freeform_scale, 1); const rounded = StrCast(this.layoutDoc.layout_borderRounding) === '100%' ? '-rounded' : ''; - if (!active && FormattedTextBoxComment.textBox === this) setTimeout(FormattedTextBoxComment.Hide); - const minimal = this.props.ignoreAutoHeight; + setTimeout(() => !this.props.isContentActive() && FormattedTextBoxComment.textBox === this && FormattedTextBoxComment.Hide); const paddingX = NumCast(this.layoutDoc._xMargin, this.props.xPadding || 0); const paddingY = NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0); - const selPad = (active && !this.layoutDoc._createDocOnCR) || minimal ? Math.min(paddingY, Math.min(paddingX, 10)) : 0; - const selPaddingClass = active && !this.layoutDoc._createDocOnCR && paddingY >= 10 ? '-selected' : ''; const styleFromLayoutString = Doc.styleFromLayoutString(this.rootDoc, this.layoutDoc, this.props, scale); // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' > return styleFromLayoutString?.height === '0px' ? null : ( <div @@ -2074,19 +2084,20 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps width: `${100 / scale}%`, height: `${100 / scale}%`, }), - display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined, + // display: !this.props.isContentActive() && this.props.thumbShown?.() ? 'none' : undefined, transition: 'inherit', // overflowY: this.layoutDoc._layout_autoHeight ? "hidden" : undefined, - color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color), - fontSize: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize), - fontFamily: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily), - fontWeight: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontWeight), + color: this.fontColor, + fontSize: this.fontSize, + fontFamily: this.fontFamily, + fontWeight: this.fontWeight, ...styleFromLayoutString, }}> <div className="formattedTextBox-cont" ref={this._ref} style={{ + cursor: this.props.isContentActive() ? 'text' : undefined, overflow: this.layout_autoHeight && this.props.CollectionFreeFormDocumentView?.() ? 'hidden' : undefined, //x this breaks viewing an layout_autoHeight doc in its own tab, or in the lightbox height: this.props.height || (this.layout_autoHeight && this.props.renderDepth && !this.props.suppressSetHeight ? 'max-content' : undefined), pointerEvents: Doc.ActiveTool === InkTool.None && !this.props.onBrowseClick?.() ? undefined : 'none', @@ -2101,7 +2112,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps onPointerDown={this.onPointerDown} onDoubleClick={this.onDoubleClick}> <div - className={`formattedTextBox-outer${active ? '-selected' : ''}`} + className={`formattedTextBox-outer`} ref={this._scrollRef} style={{ width: this.props.dontSelectOnLoad ? '100%' : `calc(100% - ${this.layout_sidebarWidthPercent})`, @@ -2110,15 +2121,14 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<FieldViewProps onScroll={this.onScroll} onDrop={this.ondrop}> <div - className={minimal ? 'formattedTextBox-minimal' : `formattedTextBox-inner${rounded}${selPaddingClass}`} + className={`formattedTextBox-inner${rounded}`} ref={this.createDropTarget} style={{ padding: StrCast(this.layoutDoc._textBoxPadding), - paddingLeft: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`), - paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX - selPad}px`), - paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`), - paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY - selPad}px`), - // pointerEvents: !active && IsFollowLinkScript(this.layoutDoc.onClick) ? 'none' : undefined, + paddingLeft: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX}px`), + paddingRight: StrCast(this.layoutDoc._textBoxPaddingX, `${paddingX}px`), + paddingTop: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY}px`), + paddingBottom: StrCast(this.layoutDoc._textBoxPaddingY, `${paddingY}px`), }} /> </div> diff --git a/src/client/views/nodes/importBox/ImportElementBox.tsx b/src/client/views/nodes/importBox/ImportElementBox.tsx index a5d21cc8e..58f0b29e4 100644 --- a/src/client/views/nodes/importBox/ImportElementBox.tsx +++ b/src/client/views/nodes/importBox/ImportElementBox.tsx @@ -1,13 +1,13 @@ -import { observer } from "mobx-react"; -import { ViewBoxBaseComponent } from "../../DocComponent"; -import { FieldView, FieldViewProps } from "../FieldView"; -import { computed } from "mobx"; -import { Id } from "../../../../fields/FieldSymbols"; -import React from "react"; -import { EditableView } from "../../EditableView"; -import { DocListCast } from "../../../../fields/Doc"; -import { StrCast } from "../../../../fields/Types"; - +import { computed } from 'mobx'; +import { observer } from 'mobx-react'; +import { Doc } from '../../../../fields/Doc'; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnOne, returnTrue } from '../../../../Utils'; +import { Transform } from '../../../util/Transform'; +import { ViewBoxBaseComponent } from '../../DocComponent'; +import { DefaultStyleProvider } from '../../StyleProvider'; +import { DocumentView, DocumentViewInternal } from '../DocumentView'; +import { FieldView, FieldViewProps } from '../FieldView'; +import React = require('react'); @observer export class ImportElementBox extends ViewBoxBaseComponent<FieldViewProps>() { @@ -15,56 +15,24 @@ export class ImportElementBox extends ViewBoxBaseComponent<FieldViewProps>() { return FieldView.LayoutString(ImportElementBox, fieldKey); } - private _itemRef: React.RefObject<HTMLDivElement> = React.createRef(); - private _dragRef: React.RefObject<HTMLDivElement> = React.createRef(); - private _titleRef: React.RefObject<EditableView> = React.createRef(); - - @computed importBoxVoew() { - return this.props.DocumentView?.()?.props.docViewPath().lastElement()?.ComponentView as PresBox; - } - - @computed get indexInPres() { - return DocListCast(this.presBox?.[StrCast(this.presBox.presFieldKey, 'data')]).indexOf(this.rootDoc); + screenToLocalXf = () => this.props.ScreenToLocalTransform().scale(1 * (this.props.NativeDimScaling?.() || 1)); + @computed get mainItem() { + return ( + <div style={{ backgroundColor: 'pink' }}> + <DocumentView + {...this.props} // + LayoutTemplateString={undefined} + Document={this.rootDoc} + isContentActive={returnFalse} + DataDoc={undefined} + addDocument={returnFalse} + ScreenToLocalTransform={this.screenToLocalXf} + hideResizeHandles={true} + /> + </div> + ); } - - @computed get presBox() { - return this.props.DocumentView?.().props.docViewPath().lastElement()?.rootDoc; + render() { + return !(this.rootDoc instanceof Doc) ? null : this.mainItem; } - - // @computed get selectedArray() { - // return this.presBoxView?.selectedArray; - // } - -@computed get mainItem() { - const isCurrent: boolean = this.presBox?._itemIndex === this.indexInPres; - //const isSelected: boolean = this.selectedArray?.has(this.rootDoc) ? true : false; - // const activeItem: Doc = this.rootDoc; - - return( - <div - className = {`presItem-container`} - // key={this.props.Document[Id] + this.indexInPres} - style = {{backgroundColor: 'pink'}} - - > - <div - ref = {this._dragRef} - className = {`presItem-slide ${isCurrent ? 'active' : ''}`} - style = {{ - backgroundColor: 'green' - }}> - <div - className="presItem-number" - title = "select without navigation" - - > - {/* <EditableView ref={this._titleRef} oneLine={true} editing={!isSelected ? false : undefined} contents={activeItem.title} overflow={'ellipsis'} GetValue={() => StrCast(activeItem.title)} SetValue={this.onSetValue} /> */} - - </div> - </div> - - </div> - ) } - -}
\ No newline at end of file diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 07b2afd91..b877cc36a 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -253,21 +253,18 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { }; @computed get highlighter() { - return <Group> - <IconButton - icon={<FontAwesomeIcon icon="highlighter" style={{ transition: 'transform 0.1s', transform: 'rotate(-45deg)' }} />} - tooltip={'Click to Highlight'} - onClick={this.highlightClicked} - colorPicker={this.highlightColor} - color={StrCast(Doc.UserDoc().userColor)} - /> - <ColorPicker - colorPickerType={'github'} - selectedColor={this.highlightColor} - setSelectedColor={color => this.changeHighlightColor(color)} - size={Size.XSMALL} - /> - </Group> + return ( + <Group> + <IconButton + icon={<FontAwesomeIcon icon="highlighter" style={{ transition: 'transform 0.1s', transform: 'rotate(-45deg)' }} />} + tooltip={'Click to Highlight'} + onClick={this.highlightClicked} + colorPicker={this.highlightColor} + color={StrCast(Doc.UserDoc().userColor)} + /> + <ColorPicker colorPickerType={'github'} selectedColor={this.highlightColor} setSelectedColor={color => this.changeHighlightColor(color)} size={Size.XSMALL} /> + </Group> + ); } @action changeHighlightColor = (color: string) => { @@ -312,19 +309,20 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { this.Status === 'marquee' ? ( <> {this.highlighter} - <IconButton - tooltip={'Drag to Place Annotation'} - onPointerDown={this.pointerDown} - icon={<FontAwesomeIcon icon="comment-alt"/>} + <IconButton + tooltip="Drag to Place Annotation" // + onPointerDown={this.pointerDown} + icon={<FontAwesomeIcon icon="comment-alt" />} color={StrCast(Doc.UserDoc().userColor)} /> {/* GPT Summarize icon only shows up when text is highlighted, not on marquee selection*/} {AnchorMenu.Instance.StartCropDrag === unimplementedFunction && this.canSummarize() && ( - <Tooltip key="gpt" title={<div className="dash-tooltip">Summarize with AI</div>}> - <button className="antimodeMenu-button annotate" onPointerDown={this.gptSummarize} style={{ cursor: 'grab' }}> - <FontAwesomeIcon icon="comment-dots" size="lg" /> - </button> - </Tooltip> + <IconButton + tooltip="Summarize with AI" // + onPointerDown={this.gptSummarize} + icon={<FontAwesomeIcon icon="comment-dots" size="lg" />} + color={StrCast(Doc.UserDoc().userColor)} + /> )} <GPTPopup key="gptpopup" @@ -338,66 +336,74 @@ export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> { mode={this.GPTMode} /> {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : ( - <IconButton - tooltip={'Click to Record Annotation'} + <IconButton + tooltip="Click to Record Annotation" // onPointerDown={this.audioDown} icon={<FontAwesomeIcon icon="microphone" />} color={StrCast(Doc.UserDoc().userColor)} /> )} {this.canEdit() && ( - <IconButton - tooltip={'AI edit suggestions'} + <IconButton + tooltip="AI edit suggestions" // onPointerDown={this.gptEdit} icon={<FontAwesomeIcon icon="pencil-alt" />} color={StrCast(Doc.UserDoc().userColor)} /> )} - <Popup - tooltip='Find document to link to selected text' + <Popup + tooltip="Find document to link to selected text" // type={Type.PRIM} icon={<FontAwesomeIcon icon={'search'} />} popup={<LinkPopup key="popup" linkCreateAnchor={this.onMakeAnchor} />} color={StrCast(Doc.UserDoc().userColor)} /> {AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? null : ( - <IconButton - tooltip={'Click/Drag to create cropped image'} + <IconButton + tooltip="Click/Drag to create cropped image" // onPointerDown={this.cropDown} - icon={<FontAwesomeIcon icon="image"/>} + icon={<FontAwesomeIcon icon="image" />} color={StrCast(Doc.UserDoc().userColor)} /> )} </> ) : ( <> - {this.Delete !== returnFalse && <IconButton - tooltip={'Remove Link Anchor'} - onPointerDown={this.Delete} - icon={<FontAwesomeIcon icon="trash-alt" />} - color={StrCast(Doc.UserDoc().userColor)} - />} - {this.PinToPres !== returnFalse && <IconButton - tooltip={'Pin to Presentation'} - onPointerDown={this.PinToPres} - icon={<FontAwesomeIcon icon="map-pin" />} - color={StrCast(Doc.UserDoc().userColor)} - />} - {this.ShowTargetTrail !== returnFalse && <IconButton - tooltip={'Show Linked Trail'} - onPointerDown={this.ShowTargetTrail} - icon={<FontAwesomeIcon icon="taxi" />} - color={StrCast(Doc.UserDoc().userColor)} - />} - {this.IsTargetToggler !== returnFalse && <Toggle - tooltip={'Make target visibility toggle on click'} - type={Type.PRIM} - toggleType={ToggleType.BUTTON} - toggleStatus={this.IsTargetToggler()} - onClick={this.MakeTargetToggle} - icon={<FontAwesomeIcon icon="thumbtack" />} - color={StrCast(Doc.UserDoc().userColor)} - />} + {this.Delete !== returnFalse && ( + <IconButton + tooltip="Remove Link Anchor" // + onPointerDown={this.Delete} + icon={<FontAwesomeIcon icon="trash-alt" />} + color={StrCast(Doc.UserDoc().userColor)} + /> + )} + {this.PinToPres !== returnFalse && ( + <IconButton + tooltip="Pin to Presentation" // + onPointerDown={this.PinToPres} + icon={<FontAwesomeIcon icon="map-pin" />} + color={StrCast(Doc.UserDoc().userColor)} + /> + )} + {this.ShowTargetTrail !== returnFalse && ( + <IconButton + tooltip="Show Linked Trail" // + onPointerDown={this.ShowTargetTrail} + icon={<FontAwesomeIcon icon="taxi" />} + color={StrCast(Doc.UserDoc().userColor)} + /> + )} + {this.IsTargetToggler !== returnFalse && ( + <Toggle + tooltip={'Make target visibility toggle on click'} + type={Type.PRIM} + toggleType={ToggleType.BUTTON} + toggleStatus={this.IsTargetToggler()} + onClick={this.MakeTargetToggle} + icon={<FontAwesomeIcon icon="thumbtack" />} + color={StrCast(Doc.UserDoc().userColor)} + /> + )} </> ); diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 79f41fe9d..cb8eda9de 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -10,7 +10,7 @@ import { StrCast } from '../../../fields/Types'; import { GetEffectiveAcl } from '../../../fields/util'; import { DocumentManager } from '../../util/DocumentManager'; import { PingManager } from '../../util/PingManager'; -import { ReportManager } from '../../util/ReportManager'; +import { ReportManager } from '../../util/reportManager/ReportManager'; import { ServerStats } from '../../util/ServerStats'; import { SettingsManager } from '../../util/SettingsManager'; import { SharingManager } from '../../util/SharingManager'; @@ -134,14 +134,16 @@ export class TopBar extends React.Component { @computed get topbarRight() { return ( <div className="topbar-right"> - <Button - text={GetEffectiveAcl(Doc.ActiveDashboard) === AclAdmin ? 'Share' : 'View Original'} - type={Type.TERT} - color={this.variantColor} - onClick={() => { - SharingManager.Instance.open(undefined, Doc.ActiveDashboard); - }} - /> + {Doc.ActiveDashboard ? + <Button + text={GetEffectiveAcl(Doc.ActiveDashboard) === AclAdmin ? 'Share' : 'View Original'} + type={Type.TERT} + color={this.variantColor} + onClick={() => { + SharingManager.Instance.open(undefined, Doc.ActiveDashboard); + }} + /> + : null } <IconButton tooltip={"Issue Reporter ⌘I"} size={Size.SMALL} color={this.color} onClick={ReportManager.Instance.open} icon={<FaBug />} /> <IconButton tooltip={"Documentation ⌘D"} size={Size.SMALL} color={this.color} onClick={() => window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')} icon={<FontAwesomeIcon icon="question-circle" />} /> <IconButton tooltip={"Settings ⌘⇧S"} size={Size.SMALL} color={this.color} onClick={SettingsManager.Instance.open} icon={<FontAwesomeIcon icon="cog" />} /> |
