diff options
| author | bobzel <zzzman@gmail.com> | 2021-04-21 09:49:01 -0400 |
|---|---|---|
| committer | bobzel <zzzman@gmail.com> | 2021-04-21 09:49:01 -0400 |
| commit | 3c6fd425c1273f87dc2142038a9ddc48d6159b1b (patch) | |
| tree | 67c4a7a0f9b730f36ce08bcbc0c7695c82439576 /src/client/views/collections | |
| parent | eb7a6f091419318bd85e75787067ef5f036e2659 (diff) | |
| parent | 45a43e9f0417d6929a6f351fea0cf97fe11b0ef0 (diff) | |
Fixed errors merging with master
Diffstat (limited to 'src/client/views/collections')
22 files changed, 380 insertions, 236 deletions
diff --git a/src/client/views/collections/CollectionCarousel3DView.tsx b/src/client/views/collections/CollectionCarousel3DView.tsx index b2ae441d6..2251b60e5 100644 --- a/src/client/views/collections/CollectionCarousel3DView.tsx +++ b/src/client/views/collections/CollectionCarousel3DView.tsx @@ -13,6 +13,7 @@ import { DragManager } from '../../util/DragManager'; import { DocumentView } from '../nodes/DocumentView'; import "./CollectionCarousel3DView.scss"; import { CollectionSubView } from './CollectionSubView'; +import { StyleProp } from '../StyleProvider'; type Carousel3DDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>; const Carousel3DDocument = makeInterface(documentSchema, collectionSchema); @@ -40,11 +41,8 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume @computed get content() { const currentIndex = NumCast(this.layoutDoc._itemIndex); const displayDoc = (childPair: { layout: Doc, data: Doc }) => { - const script = ScriptField.MakeScript("this._showCaption = 'caption'", { this: Doc.name }); - const onChildClick = script && (() => script); - return <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} + return <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "childLayoutTemplate", "childLayoutString"]).omit} onDoubleClick={this.onChildDoubleClick} - onClick={onChildClick} renderDepth={this.props.renderDepth + 1} LayoutTemplate={this.props.childLayoutTemplate} LayoutTemplateString={this.props.childLayoutString} @@ -52,7 +50,6 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume DataDoc={childPair.data} PanelWidth={this.panelWidth} PanelHeight={this.panelHeight} - ScreenToLocalTransform={this.props.ScreenToLocalTransform} bringToFront={returnFalse} />; }; @@ -104,27 +101,6 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume }, 1500); } - _downX = 0; - _downY = 0; - onPointerDown = (e: React.PointerEvent) => { - this._downX = e.clientX; - this._downY = e.clientY; - document.addEventListener("pointerup", this.onpointerup); - } - private _lastTap: number = 0; - private _doubleTap = false; - onpointerup = (e: PointerEvent) => { - this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2); - this._lastTap = Date.now(); - } - - onClick = (e: React.MouseEvent) => { - if (this._doubleTap) { - e.stopPropagation(); - this.props.Document.isLightboxOpen = true; - } - } - @computed get buttons() { if (!this.props.isContentActive()) return null; return <div className="arrow-buttons" > @@ -167,7 +143,11 @@ export class CollectionCarousel3DView extends CollectionSubView(Carousel3DDocume const index = NumCast(this.layoutDoc._itemIndex); const translateX = this.panelWidth() * (1 - index); - return <div className="collectionCarousel3DView-outer" onClick={this.onClick} onPointerDown={this.onPointerDown} ref={this.createDashEventsTarget}> + return <div className="collectionCarousel3DView-outer" ref={this.createDashEventsTarget} + style={{ + background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor), + color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color), + }} > <div className="carousel-wrapper" style={{ transform: `translateX(${translateX}px)` }}> {this.content} </div> diff --git a/src/client/views/collections/CollectionCarouselView.scss b/src/client/views/collections/CollectionCarouselView.scss index a9a1898f5..8660113cd 100644 --- a/src/client/views/collections/CollectionCarouselView.scss +++ b/src/client/views/collections/CollectionCarouselView.scss @@ -1,13 +1,10 @@ .collectionCarouselView-outer { - background: gray; height : 100%; .collectionCarouselView-caption { - margin-left: 10%; - margin-right: 10%; height: 50; display: inline-block; - width: 80%; + width: 100%; } .collectionCarouselView-image { height: calc(100% - 50px); @@ -19,13 +16,17 @@ .carouselView-back, .carouselView-fwd { position: absolute; display: flex; - top: 50%; + top: 42.5%; width: 30; - height: 30; + height: 15%; align-items: center; border-radius: 5px; justify-content: center; - background : rgba(255, 255, 255, 0.46); + color: rgba(255,255,255,0.5); + background : rgba(0,0,0, 0.1); + &:hover { + color:white; + } } .carouselView-fwd { right: 0; diff --git a/src/client/views/collections/CollectionCarouselView.tsx b/src/client/views/collections/CollectionCarouselView.tsx index cc90b9134..cd81a99c9 100644 --- a/src/client/views/collections/CollectionCarouselView.tsx +++ b/src/client/views/collections/CollectionCarouselView.tsx @@ -2,17 +2,17 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { computed } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc } from '../../../fields/Doc'; +import { Doc, Opt } from '../../../fields/Doc'; import { collectionSchema, documentSchema } from '../../../fields/documentSchemas'; import { makeInterface } from '../../../fields/Schema'; import { NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { OmitKeys, returnFalse } from '../../../Utils'; import { DragManager } from '../../util/DragManager'; -import { DocumentView } from '../nodes/DocumentView'; +import { DocumentView, DocumentViewProps } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { StyleProp } from '../StyleProvider'; import "./CollectionCarouselView.scss"; -import { CollectionSubView } from './CollectionSubView'; +import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; type CarouselDocument = makeInterface<[typeof documentSchema, typeof collectionSchema]>; const CarouselDocument = makeInterface(documentSchema, collectionSchema); @@ -38,77 +38,70 @@ export class CollectionCarouselView extends CollectionSubView(CarouselDocument) e.stopPropagation(); this.layoutDoc._itemIndex = (NumCast(this.layoutDoc._itemIndex) - 1 + this.childLayoutPairs.length) % this.childLayoutPairs.length; } - panelHeight = () => this.props.PanelHeight() - 50; + captionStyleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => { + const captionProps = { ...this.props, fieldKey: "caption" }; + return this.props.styleProvider?.(doc, props, property) || this.props.styleProvider?.(this.layoutDoc, captionProps, property); + } + panelHeight = () => this.props.PanelHeight() - (StrCast(this.layoutDoc._showCaption) ? 50 : 0); onContentDoubleClick = () => ScriptCast(this.layoutDoc.onChildDoubleClick); onContentClick = () => ScriptCast(this.layoutDoc.onChildClick); @computed get content() { const index = NumCast(this.layoutDoc._itemIndex); const curDoc = this.childLayoutPairs?.[index]; + const captionProps = { ...this.props, fieldKey: "caption" }; + const marginX = NumCast(this.layoutDoc["caption-xMargin"]); + const marginY = NumCast(this.layoutDoc["caption-yMargin"]); return !(curDoc?.layout instanceof Doc) ? (null) : <> <div className="collectionCarouselView-image" key="image"> - <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight"]).omit} + <DocumentView {...OmitKeys(this.props, ["NativeWidth", "NativeHeight", "childLayoutTemplate", "childLayoutString"]).omit} onDoubleClick={this.onContentDoubleClick} onClick={this.onContentClick} renderDepth={this.props.renderDepth + 1} + ContainingCollectionView={this} LayoutTemplate={this.props.childLayoutTemplate} LayoutTemplateString={this.props.childLayoutString} Document={curDoc.layout} - DataDoc={curDoc.data} + DataDoc={curDoc.layout.resolvedDataDoc as Doc} PanelHeight={this.panelHeight} - ScreenToLocalTransform={this.props.ScreenToLocalTransform} bringToFront={returnFalse} /> </div> <div className="collectionCarouselView-caption" key="caption" style={{ - background: StrCast(this.layoutDoc._captionBackgroundColor, this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BackgroundColor)), - color: StrCast(this.layoutDoc._captionColor, StrCast(this.layoutDoc.color)), - borderRadius: StrCast(this.layoutDoc._captionBorderRounding), + display: StrCast(this.layoutDoc._showCaption) ? undefined : "none", + borderRadius: this.props.styleProvider?.(this.layoutDoc, captionProps, StyleProp.BorderRounding), + marginRight: marginX, marginLeft: marginX, + width: `calc(100% - ${marginX * 2}px)` }}> - <FormattedTextBox key={index} {...this.props} - xMargin={NumCast(this.layoutDoc["_carousel-caption-xMargin"])} - yMargin={NumCast(this.layoutDoc["_carousel-caption-yMargin"])} - Document={curDoc.layout} DataDoc={undefined} fieldKey={"caption"} /> + <FormattedTextBox key={index} {...captionProps} + styleProvider={this.captionStyleProvider} + Document={curDoc.layout} DataDoc={undefined} + fontSize={NumCast(this.layoutDoc["caption-fontSize"])} + xPadding={NumCast(this.layoutDoc["caption-xPadding"])} + yPadding={NumCast(this.layoutDoc["caption-yPadding"])} /> </div> </>; } @computed get buttons() { return <> - <div key="back" className="carouselView-back" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={this.goback}> - <FontAwesomeIcon icon={"caret-left"} size={"2x"} /> + <div key="back" className="carouselView-back" onClick={this.goback}> + <FontAwesomeIcon icon={"chevron-left"} size={"2x"} /> </div> - <div key="fwd" className="carouselView-fwd" style={{ background: `${StrCast(this.props.Document.backgroundColor)}` }} onClick={this.advance}> - <FontAwesomeIcon icon={"caret-right"} size={"2x"} /> + <div key="fwd" className="carouselView-fwd" onClick={this.advance}> + <FontAwesomeIcon icon={"chevron-right"} size={"2x"} /> </div> </>; } - _downX = 0; - _downY = 0; - onPointerDown = (e: React.PointerEvent) => { - this._downX = e.clientX; - this._downY = e.clientY; - document.addEventListener("pointerup", this.onpointerup); - } - private _lastTap: number = 0; - private _doubleTap = false; - onpointerup = (e: PointerEvent) => { - this._doubleTap = (Date.now() - this._lastTap < 300 && e.button === 0 && Math.abs(e.clientX - this._downX) < 2 && Math.abs(e.clientY - this._downY) < 2); - this._lastTap = Date.now(); - } - - onClick = (e: React.MouseEvent) => { - if (this._doubleTap) { - e.stopPropagation(); - this.props.Document.isLightboxOpen = true; - } - } - render() { - return <div className="collectionCarouselView-outer" onClick={this.onClick} onPointerDown={this.onPointerDown} ref={this.createDashEventsTarget}> + return <div className="collectionCarouselView-outer" ref={this.createDashEventsTarget} + style={{ + background: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor), + color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color), + }}> {this.content} - {!this.props.Document._chromeHidden ? (null) : this.buttons} + {this.props.Document._chromeHidden ? (null) : this.buttons} </div>; } }
\ No newline at end of file diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss index 2c81d727e..dc5231a3a 100644 --- a/src/client/views/collections/CollectionMenu.scss +++ b/src/client/views/collections/CollectionMenu.scss @@ -14,35 +14,53 @@ top: 0; width: 100%; - .antimodeMenu-button { - padding: 0; - width: 30px; + .recordButtonOutline { + border-radius: 100%; + width: 18px; + height: 18px; + border: solid 1px #f5f5f5; display: flex; + align-items: center; + justify-content: center; + } - svg { - margin: auto; - } + .recordButtonInner { + border-radius: 100%; + width: 70%; + height: 70%; + background: white; } .collectionMenu { display: flex; - padding-bottom: 1px; - height: 32px; - border-bottom: .5px solid rgb(180, 180, 180); + height: 100%; overflow: visible; z-index: 901; border: unset; + .collectionMenu-divider { + height: 85%; + margin-left: 3px; + margin-right: 3px; + width: 1.5px; + background-color: #656060; + } + .collectionViewBaseChrome { display: flex; + align-items: center; .collectionViewBaseChrome-viewPicker { font-size: 75%; - background: #323232; outline-color: black; color: white; border: none; - border-right: solid gray 1px; + background: #323232; + } + + .collectionViewBaseChrome-viewPicker:focus { + outline: none; + border: none; } .collectionViewBaseChrome-viewPicker:active { @@ -65,18 +83,26 @@ margin-left: 3px; margin-right: 0px; font-size: 75%; - background: #323232; + text-transform: capitalize; color: white; border: none; - border-right: solid gray 1px; + background: #323232; + } + + .collectionViewBaseChrome-cmdPicker:focus { + border: none; + outline: none; } .commandEntry-outerDiv { pointer-events: all; - background-color: #323232; + background-color: transparent; display: flex; flex-direction: row; - height: 30px; + align-items: center; + justify-content: center; + height: 100%; + overflow: hidden; .commandEntry-drop { color: white; @@ -86,6 +112,15 @@ } } + .commandEntry-outerDiv:hover{ + background-color: rgba(0,0,0,0.2); + + .collectionViewBaseChrome-viewPicker, + .collectionViewBaseChrome-cmdPicker{ + background: rgb(41,41,41); + } + } + .collectionViewBaseChrome-collapse { transition: all .5s, opacity 0.3s; position: absolute; @@ -103,11 +138,12 @@ .collectionViewBaseChrome-template, .collectionViewBaseChrome-viewModes { - display: grid; - background: rgb(238, 238, 238); + align-items: center; + height: 100%; + display: flex; + background: transparent; color: grey; - margin-top: auto; - margin-bottom: auto; + justify-content: center; } .collectionViewBaseChrome-viewSpecs { @@ -263,27 +299,52 @@ .collectionTreeViewChrome-pivotField-cont, .collection3DCarouselViewChrome-scrollSpeed-cont { justify-self: right; + align-items: center; display: flex; + grid-auto-columns: auto; font-size: 75%; letter-spacing: 2px; .collectionStackingViewChrome-pivotField-label, .collectionTreeViewChrome-pivotField-label, .collection3DCarouselViewChrome-scrollSpeed-label { - vertical-align: center; - padding-left: 10px; - margin: auto; + grid-column: 1; + margin-right: 7px; + user-select: none; + font-family: 'Roboto'; + letter-spacing: normal; + } + + .collectionStackingViewChrome-sortIcon { + transition: transform .5s; + grid-column: 3; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + width: 25px; + height: 25px; + border-radius: 100%; + } + + .collectionStackingViewChrome-sortIcon:hover { + background-color: rgba(0,0,0,0.2); } .collectionStackingViewChrome-pivotField, .collectionTreeViewChrome-pivotField, .collection3DCarouselViewChrome-scrollSpeed { color: white; - width: 100%; + grid-column: 2; + grid-row: 1; + width: 90%; min-width: 100px; display: flex; + height: 80%; + border-radius: 7px; align-items: center; - background: rgb(238, 238, 238); + background: #eeeeee; .editable-view-input, input, @@ -378,20 +439,13 @@ display: inline-flex; position: relative; align-items: center; - margin-left: 10px; - - .antimodeMenu-button { - text-align: center; - display: block; - position: relative; - } + height: 100%; .color-previewI { - width: 80%; - height: 20%; - bottom: 0; + width: 60%; + top: 80%; position: absolute; - margin-left: 2px; + height: 4px; } .color-previewII { diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 623e05b33..ab4f197a6 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -34,6 +34,10 @@ import "./CollectionMenu.scss"; import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView"; import { TabDocView } from "./TabDocView"; import { LightboxView } from "../LightboxView"; +import { Docs } from "../../documents/Documents"; +import { DocumentManager } from "../../util/DocumentManager"; +import { CollectionDockingView } from "./CollectionDockingView"; +import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox"; @observer export class CollectionMenu extends AntimodeMenu<AntimodeMenuProps> { @@ -257,6 +261,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp case CollectionViewType.Schema: return (<CollectionSchemaViewChrome key="collchrome" {...this.props} />); case CollectionViewType.Tree: return (<CollectionTreeViewChrome key="collchrome" {...this.props} />); case CollectionViewType.Masonry: return (<CollectionStackingViewChrome key="collchrome" {...this.props} />); + case CollectionViewType.Carousel: case CollectionViewType.Carousel3D: return (<Collection3DCarouselViewChrome key="collchrome" {...this.props} />); case CollectionViewType.Grid: return (<CollectionGridViewChrome key="collchrome" {...this.props} />); case CollectionViewType.Docking: return (<CollectionDockingChrome key="collchrome" {...this.props} />); @@ -388,7 +393,29 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp return !targetDoc ? (null) : <Tooltip key="pin" title={<div className="dash-tooltip">{Doc.isDocPinned(targetDoc) ? "Unpin from presentation" : "Pin to presentation"}</div>} placement="top"> <button className="antimodeMenu-button" style={{ backgroundColor: isPinned ? "121212" : undefined, borderLeft: "1px solid gray" }} onClick={e => TabDocView.PinDoc(targetDoc, { unpin: isPinned })}> - <FontAwesomeIcon className="documentdecorations-icon" size="lg" icon="map-pin" /> + <FontAwesomeIcon className="colMenu-icon" size="lg" icon="map-pin" /> + </button> + </Tooltip>; + } + + @undoBatch + @action + startRecording = () => { + const doc = Docs.Create.ScreenshotDocument("screen recording", { _fitWidth: true, _width: 400, _height: 200, mediaState: "pendingRecording" }); + //Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc); + CollectionDockingView.AddSplit(doc, "right"); + } + + @computed + get recordButton() { + const targetDoc = this.selectedDoc; + return <Tooltip key="record" title={<div className="dash-tooltip">{"Capture screen"}</div>} placement="top"> + <button className="antimodeMenu-button" + onClick={e => this.startRecording()}> + <div className="recordButtonOutline" style={{}}> + <div className="recordButtonInner" style={{}}> + </div> + </div> </button> </Tooltip>; } @@ -478,7 +505,7 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp const targetDoc = this.selectedDoc; return !targetDoc || targetDoc.type === DocumentType.PRES ? (null) : <Tooltip title={<div className="dash-tooltip">{"Tap or Drag to create an alias"}</div>} placement="top"> <button className="antimodeMenu-button" onPointerDown={this.onAliasButtonDown} onClick={this.onAlias} style={{ cursor: "drag" }}> - <FontAwesomeIcon className="documentdecorations-icon" icon="copy" size="lg" /> + <FontAwesomeIcon className="colMenu-icon" icon="copy" size="lg" /> </button> </Tooltip>; } @@ -486,37 +513,47 @@ export class CollectionViewBaseChrome extends React.Component<CollectionMenuProp @computed get lightboxButton() { const targetDoc = this.selectedDoc; return !targetDoc ? (null) : <Tooltip title={<div className="dash-tooltip">{"View in Lightbox"}</div>} placement="top"> - <button className="antimodeMenu-button" style={{ borderRight: "1px solid gray", justifyContent: 'center' }} onPointerDown={() => { + <button className="antimodeMenu-button" onPointerDown={() => { const docs = DocListCast(targetDoc[Doc.LayoutFieldKey(targetDoc)]); LightboxView.SetLightboxDoc(targetDoc, undefined, docs); }}> - <FontAwesomeIcon className="documentdecorations-icon" icon="desktop" size="lg" /> + <FontAwesomeIcon className="colMenu-icon" icon="desktop" size="lg" /> </button> </Tooltip>; } + @computed get toggleOverlayButton() { + return <> + <Tooltip title={<div className="dash-tooltip">Toggle Overlay Layer</div>} placement="bottom"> + <button className={"antimodeMenu-button"} key="float" + style={{ + backgroundColor: this.props.docView.layoutDoc.z ? "121212" : undefined, + pointerEvents: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "none" : undefined, + color: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "dimgrey" : undefined + }} + onClick={undoBatch(() => this.props.docView.props.CollectionFreeFormDocumentView?.().float())}> + <FontAwesomeIcon icon={["fab", "buffer"]} size={"lg"} /> + </button> + </Tooltip> + </> + } + render() { return ( <div className="collectionMenu-cont" > <div className="collectionMenu"> <div className="collectionViewBaseChrome"> + {this.notACollection || this.props.type === CollectionViewType.Invalid ? (null) : this.viewModes} + <div className="collectionMenu-divider" key="divider1"></div> {this.aliasButton} {/* {this.pinButton} */} + {this.toggleOverlayButton} {this.pinWithViewButton} - {this.lightboxButton} - <Tooltip title={<div className="dash-tooltip">Toggle Overlay Layer</div>} placement="bottom"> - <button className={"antimodeMenu-button"} key="float" - style={{ - backgroundColor: this.props.docView.layoutDoc.z ? "121212" : undefined, borderRight: "1px solid gray", - pointerEvents: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "none" : undefined, - color: this.props.docView.props.ContainingCollectionDoc?._viewType !== CollectionViewType.Freeform ? "dimgrey" : undefined - }} - onClick={undoBatch(() => this.props.docView.props.CollectionFreeFormDocumentView?.().float())}> - <FontAwesomeIcon icon={["fab", "buffer"]} size={"lg"} /> - </button> - </Tooltip> + <div className="collectionMenu-divider" key="divider2"></div> {this.subChrome} - {this.notACollection || this.props.type === CollectionViewType.Invalid ? (null) : this.viewModes} + <div className="collectionMenu-divider" key="divider3"></div> + {this.lightboxButton} + {this.recordButton} {!this._buttonizableCommands ? (null) : this.templateChrome} </div> </div> @@ -986,6 +1023,7 @@ export class Collection3DCarouselViewChrome extends React.Component<CollectionMe return ( <div className="collection3DCarouselViewChrome-cont"> <div className="collection3DCarouselViewChrome-scrollSpeed-cont"> + {FormattedTextBox.Focused ? <RichTextMenu /> : (null)} <div className="collectionStackingViewChrome-scrollSpeed-label"> AUTOSCROLL SPEED: </div> diff --git a/src/client/views/collections/CollectionPileView.tsx b/src/client/views/collections/CollectionPileView.tsx index 6f6cdd5d2..6baf633dd 100644 --- a/src/client/views/collections/CollectionPileView.tsx +++ b/src/client/views/collections/CollectionPileView.tsx @@ -2,7 +2,7 @@ import { action, computed } from "mobx"; import { observer } from "mobx-react"; import { Doc, HeightSym, WidthSym } from "../../../fields/Doc"; import { NumCast, StrCast } from "../../../fields/Types"; -import { emptyFunction, setupMoveUpEvents } from "../../../Utils"; +import { emptyFunction, setupMoveUpEvents, returnTrue } from "../../../Utils"; import { DocUtils } from "../../documents/Documents"; import { SelectionManager } from "../../util/SelectionManager"; import { SnappingManager } from "../../util/SnappingManager"; @@ -31,9 +31,16 @@ export class CollectionPileView extends CollectionSubView(doc => doc) { // returns the contents of the pileup in a CollectionFreeFormView @computed get contents() { + const isStarburst = this.layoutEngine() === "starburst"; const draggingSelf = this.props.isSelected(); - return <div className="collectionPileView-innards" style={{ pointerEvents: this.layoutEngine() === "starburst" || (SnappingManager.GetIsDragging() && !draggingSelf) ? undefined : "none", zIndex: this.layoutEngine() === "starburst" && !SnappingManager.GetIsDragging() ? -10 : "auto" }} > - <CollectionFreeFormView {...this.props} layoutEngine={this.layoutEngine} + return <div className="collectionPileView-innards" + style={{ + pointerEvents: isStarburst || (SnappingManager.GetIsDragging() && !draggingSelf) ? undefined : "none", + zIndex: isStarburst && !SnappingManager.GetIsDragging() ? -10 : "auto" + }} > + <CollectionFreeFormView {...this.props} + layoutEngine={this.layoutEngine} + childDocumentsActive={isStarburst ? returnTrue : undefined} addDocument={undoBatch((doc: Doc | Doc[]) => { (doc instanceof Doc ? [doc] : doc).map((d) => DocUtils.iconify(d)); return this.props.addDocument?.(doc) || false; diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss index 19ad5cefa..e456c0664 100644 --- a/src/client/views/collections/CollectionStackedTimeline.scss +++ b/src/client/views/collections/CollectionStackedTimeline.scss @@ -62,6 +62,7 @@ .collectionStackedTimeline-waveform { position: absolute; width: 100%; + height: 100%; top: 0; left: 0; pointer-events: none; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 6a1242f20..ab9c4aa2c 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -234,8 +234,9 @@ export class CollectionStackedTimeline extends CollectionSubView<PanZoomDocument return level; } - dictationHeight = () => this.props.PanelHeight() / 3; - timelineContentHeight = () => this.props.PanelHeight() * 2 / 3; + dictationHeightPercent = 50; + dictationHeight = () => `${this.dictationHeightPercent}%`; + timelineContentHeight = () => this.props.PanelHeight() * this.dictationHeightPercent / 100; dictationScreenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.timelineContentHeight()); @computed get renderDictation() { const dictation = Cast(this.dataDoc[this.props.dictationKey], Doc, null); @@ -348,7 +349,11 @@ class StackedTimelineAnchor extends React.Component<StackedTimelineAnchorProps> (time) => { const dictationDoc = Cast(this.props.layoutDoc["data-dictation"], Doc, null); const isDictation = dictationDoc && DocListCast(this.props.mark.links).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc); - if ((isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this.props.layoutDoc)) && DocListCast(this.props.mark.links).length && + if (!LightboxView.LightboxDoc + // bcz: when should links be followed? we don't want to move away from the video to follow a link but we can open it in a sidebar/etc. But we don't know that upfront. + // for now, we won't follow any links when the lightbox is oepn to avoid "losing" the video. + /*(isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this.props.layoutDoc))*/ + && DocListCast(this.props.mark.links).length && time > NumCast(this.props.mark[this.props.startTag]) && time < NumCast(this.props.mark[this.props.endTag]) && this._lastTimecode < NumCast(this.props.mark[this.props.startTag])) { diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 8d8c69fd5..30f8e0112 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -55,10 +55,10 @@ export class CollectionStackingView extends CollectionSubView<StackingDocument, @computed get chromeHidden() { return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden); } @computed get columnHeaders() { return Cast(this.layoutDoc._columnHeaders, listSpec(SchemaHeaderField), null); } @computed get pivotField() { return StrCast(this.layoutDoc._pivotField); } - @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => pair.layout instanceof Doc).map(pair => pair.layout); } + @computed get filteredChildren() { return this.childLayoutPairs.filter(pair => (pair.layout instanceof Doc) && !pair.layout.hidden).map(pair => pair.layout); } @computed get headerMargin() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin); } @computed get xMargin() { return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, .05 * this.props.PanelWidth())); } - @computed get yMargin() { return this.props.yMargin || NumCast(this.layoutDoc._yMargin, 5); } // 2 * this.gridGap)); } + @computed get yMargin() { return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5); } // 2 * this.gridGap)); } @computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); } @computed get isStackingView() { return (this.props.viewType ?? this.layoutDoc._viewType) === CollectionViewType.Stacking; } @computed get numGroupColumns() { return this.isStackingView ? Math.max(1, this.Sections.size + (this.showAddAGroup ? 1 : 0)) : 1; } diff --git a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx index bee7aeb87..47733994b 100644 --- a/src/client/views/collections/CollectionStackingViewFieldColumn.tsx +++ b/src/client/views/collections/CollectionStackingViewFieldColumn.tsx @@ -235,7 +235,7 @@ export class CollectionStackingViewFieldColumn extends React.Component<CSVFieldC } }); const pt = this.props.screenToLocalTransform().inverse().transformPoint(x, y); - ContextMenu.Instance.displayMenu(x, y); + ContextMenu.Instance.displayMenu(x, y, undefined, true); } @computed get innards() { TraceMobx(); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index a5d679df0..a5d62acb4 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -74,7 +74,7 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: const { Document, DataDoc } = this.props; const validPairs = this.childDocs.map(doc => Doc.GetLayoutDataDocPair(Document, !this.props.isAnnotationOverlay ? DataDoc : undefined, doc)). filter(pair => { // filter out any documents that have a proto that we don't have permissions to (which we determine by not having any keys - return pair.layout && !pair.layout.hidden && (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate));// Object.keys(pair.layout.proto).length)); + return pair.layout && /*!pair.layout.hidden &&*/ (!pair.layout.proto || (pair.layout.proto instanceof Doc && GetEffectiveAcl(pair.layout.proto) !== AclPrivate));// Object.keys(pair.layout.proto).length)); }); return validPairs.map(({ data, layout }) => ({ data: data as Doc, layout: layout! })); // this mapping is a bit of a hack to coerce types } @@ -90,7 +90,8 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: searchFilterDocs = () => { return [...this.props.searchFilterDocs(), ...DocListCast(this.props.Document._searchFilterDocs)]; } - @computed get childDocs() { + @computed.struct get childDocs() { + TraceMobx(); let rawdocs: (Doc | Promise<Doc>)[] = []; if (this.dataField instanceof Doc) { // if collection data is just a document, then promote it to a singleton list; rawdocs = [this.dataField]; @@ -114,10 +115,14 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: return childDocs.filter(cd => !cd.cookies); // remove any documents that require a cookie if there are no filters to provide one } + // console.log(CurrentUserUtils.ActiveDashboard._docFilters); + // if (!this.props.Document._docFilters && this.props.Document.currentFilter) { + // (this.props.Document.currentFilter as Doc).filterBoolean = (this.props.ContainingCollectionDoc?.currentFilter as Doc)?.filterBoolean; + // } const docsforFilter: Doc[] = []; childDocs.forEach((d) => { - if (DocUtils.Excluded(d, docFilters)) return; - let notFiltered = d.z || ((!searchDocs.length || searchDocs.includes(d)) && (DocUtils.FilterDocs([d], docFilters, docRangeFilters, viewSpecScript).length > 0)); + // if (DocUtils.Excluded(d, docFilters)) return; + let notFiltered = d.z || Doc.IsSystem(d) || ((!searchDocs.length || searchDocs.includes(d)) && (DocUtils.FilterDocs([d], docFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0)); const fieldKey = Doc.LayoutFieldKey(d); const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView"); const data = d[annos ? fieldKey + "-annotations" : fieldKey]; @@ -125,13 +130,13 @@ export function CollectionSubView<T, X>(schemaCtor: (doc: Doc) => T, moreProps?: let subDocs = DocListCast(data); if (subDocs.length > 0) { let newarray: Doc[] = []; - notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, docFilters, docRangeFilters, viewSpecScript).length); + notFiltered = notFiltered || (!searchDocs.length && DocUtils.FilterDocs(subDocs, docFilters, docRangeFilters, viewSpecScript, d).length); while (subDocs.length > 0 && !notFiltered) { newarray = []; subDocs.forEach((t) => { const fieldKey = Doc.LayoutFieldKey(t); const annos = !Field.toString(Doc.LayoutField(t) as Field).includes("CollectionView"); - notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!docFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], docFilters, docRangeFilters, viewSpecScript).length)); + notFiltered = notFiltered || ((!searchDocs.length || searchDocs.includes(t)) && ((!docFilters.length && !docRangeFilters.length) || DocUtils.FilterDocs([t], docFilters, docRangeFilters, viewSpecScript, d).length)); DocListCast(t[annos ? fieldKey + "-annotations" : fieldKey]).forEach((newdoc) => newarray.push(newdoc)); }); subDocs = newarray; @@ -474,5 +479,6 @@ import { CollectionView, CollectionViewType, CollectionViewProps } from "./Colle import { SelectionManager } from "../../util/SelectionManager"; import { OverlayView } from "../OverlayView"; import { Hypothesis } from "../../util/HypothesisUtils"; -import { GetEffectiveAcl } from "../../../fields/util"; +import { GetEffectiveAcl, TraceMobx } from "../../../fields/util"; +import { FilterBox } from "../nodes/FilterBox"; diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 5a4864d2d..82c8a9114 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -49,7 +49,7 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll @computed get doc() { return this.props.Document; } @computed get dataDoc() { return this.props.DataDoc || this.doc; } @computed get treeViewtruncateTitleWidth() { return NumCast(this.doc.treeViewTruncateTitleWidth, this.panelWidth()); } - @computed get treeChildren() { return this.props.childDocuments || this.childDocs; } + @computed get treeChildren() { TraceMobx(); return this.props.childDocuments || this.childDocs; } @computed get outlineMode() { return this.doc.treeViewType === "outline"; } @computed get fileSysMode() { return this.doc.treeViewType === "fileSystem"; } @@ -67,19 +67,23 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll Object.values(this._disposers).forEach(disposer => disposer?.()); } - componentWillMount() { + componentDidMount() { this._disposers.autoheight = reaction(() => this.rootDoc.autoHeight, auto => auto && this.computeHeight(), - { fireImmediately: true }) + { fireImmediately: true }); } refList: Set<any> = new Set(); observer: any; computeHeight = () => { - this.rootDoc._height = this.paddingTop() + 26/* bcz: ugh: title bar height hack ... get ref and compute instead */ + + const hgt = this.paddingTop() + 26/* bcz: ugh: title bar height hack ... get ref and compute instead */ + Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace("px", "")), 0); + this.props.setHeight(hgt); + } + unobserveHeight = (ref: any) => { + this.refList.delete(ref); + this.rootDoc.autoHeight && this.computeHeight(); } - unobserveHeight = (ref: any) => this.refList.delete(ref); observerHeight = (ref: any) => { if (ref) { this.refList.add(ref); @@ -111,15 +115,14 @@ export class CollectionTreeView extends CollectionSubView<Document, Partial<coll const targetDataDoc = this.doc[DataSym]; const value = DocListCast(targetDataDoc[this.props.fieldKey]); const result = value.filter(v => !docs.includes(v)); - SelectionManager.DeselectAll(); + if ((doc instanceof Doc ? [doc] : doc).some(doc => SelectionManager.Views().some(dv => Doc.AreProtosEqual(dv.rootDoc, doc)))) SelectionManager.DeselectAll(); if (result.length !== value.length) { const ind = targetDataDoc[this.props.fieldKey].indexOf(doc); const prev = ind && targetDataDoc[this.props.fieldKey][ind - 1]; - targetDataDoc[this.props.fieldKey] = new List<Doc>(result); + this.props.removeDocument?.(doc); if (ind > 0) { FormattedTextBox.SelectOnLoad = prev[Id]; - const prevView = DocumentManager.Instance.getDocumentView(prev, this.props.CollectionView); - prevView?.select(false); + DocumentManager.Instance.getDocumentView(prev, this.props.CollectionView)?.select(false); } return true; } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index 21dd4b206..85ae66fdc 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -1,4 +1,4 @@ -import { action, computed, observable } from 'mobx'; +import { computed, observable } from 'mobx'; import { observer } from "mobx-react"; import * as React from 'react'; import 'react-image-lightbox-with-rotate/style.css'; // This only needs to be imported once in your app @@ -66,6 +66,7 @@ export interface CollectionViewProps extends FieldViewProps { // property overrides for child documents children?: never | (() => JSX.Element[]) | React.ReactNode; childDocuments?: Doc[]; // used to override the documents shown by the sub collection to an explicit list (see LinkBox) + childDocumentsActive?: () => boolean;// whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode) childFitWidth?: () => boolean; childOpacity?: () => number; childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index f6aecbb14..7e2f7811e 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -9,7 +9,7 @@ import * as ReactDOM from 'react-dom'; import { DataSym, Doc, DocListCast, DocListCastAsync, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; import { Id } from '../../../fields/FieldSymbols'; import { FieldId } from "../../../fields/RefField"; -import { Cast, NumCast, StrCast } from "../../../fields/Types"; +import { Cast, NumCast, StrCast, BoolCast } from "../../../fields/Types"; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils"; import { DocServer } from "../../DocServer"; @@ -92,7 +92,8 @@ export class TabDocView extends React.Component<TabDocViewProps> { }; tab.element[0].style.borderTopRightRadius = "8px"; tab.element[0].children[1].appendChild(toggle); - tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }), + tab._disposers.layerDisposer = reaction(() => + ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }), ({ layer, color }) => toggle.style.background = !layer ? color : "dimgrey", { fireImmediately: true }); } // shifts the focus to this tab when another tab is dragged over it @@ -298,6 +299,8 @@ export class TabDocView extends React.Component<TabDocViewProps> { PanelHeight = () => this._panelHeight; miniMapColor = () => this.tabColor; tabView = () => this._view; + disableMinimap = () => !this._document || (this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform); + hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.hideMinimap); @computed get layerProvider() { return this._document && DefaultLayerProvider(this._document); } @computed get docView() { @@ -329,13 +332,14 @@ export class TabDocView extends React.Component<TabDocViewProps> { bringToFront={emptyFunction} pinToPres={TabDocView.PinDoc} /> <TabMinimapView key="minimap" + hideMinimap={this.hideMinimap} addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} /> - <Tooltip style={{ display: this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform ? "none" : undefined }} key="ttip" title={<div className="dash-tooltip">{"toggle minimap"}</div>}> + <Tooltip style={{ display: this.disableMinimap() ? "none" : undefined }} key="ttip" title={<div className="dash-tooltip">{"toggle minimap"}</div>}> <div className="miniMap-hidden" onPointerDown={e => e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} > <FontAwesomeIcon icon={"globe-asia"} size="lg" /> </div> @@ -346,7 +350,10 @@ export class TabDocView extends React.Component<TabDocViewProps> { render() { this.tab && CollectionDockingView.Instance.tabMap.delete(this.tab); return ( - <div className="collectionDockingView-content" style={{ height: "100%", width: "100%" }} ref={ref => { + <div className="collectionDockingView-content" style={{ + fontFamily: Doc.UserDoc().renderStyle === "comic" ? "Comic Sans MS" : undefined, + height: "100%", width: "100%" + }} ref={ref => { if (this._mainCont = ref) { (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document); DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document))); @@ -360,6 +367,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { interface TabMinimapViewProps { document: Doc; + hideMinimap: () => boolean; tabView: () => DocumentView | undefined; addDocTab: (doc: Doc, where: string) => boolean; PanelWidth: () => number; @@ -410,7 +418,7 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> { const miniLeft = 50 + (NumCast(this.props.document._panX) - this.renderBounds.cx) / this.renderBounds.dim * 100 - miniWidth / 2; const miniTop = 50 + (NumCast(this.props.document._panY) - this.renderBounds.cy) / this.renderBounds.dim * 100 - miniHeight / 2; const miniSize = this.returnMiniSize(); - return this.props.document.hideMinimap ? (null) : + return this.props.hideMinimap() ? (null) : <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}> <CollectionFreeFormView Document={this.props.document} diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss index bf896b8d3..5b0c04f33 100644 --- a/src/client/views/collections/TreeView.scss +++ b/src/client/views/collections/TreeView.scss @@ -8,6 +8,7 @@ width: 100%; overflow: hidden; } + .treeView-container, .treeView-container-active { .bullet-outline { @@ -21,21 +22,26 @@ .treeView-bulletIcons { width: $TREE_BULLET_WIDTH; + .treeView-expandIcon { display: none; left: -10px; position: absolute; } + .treeView-checkIcon { - left: -10px; + left: 3.5px; + top: 2px; position: absolute; } + &:hover { .treeView-expandIcon { display: unset; } } } + .bullet { position: relative; width: $TREE_BULLET_WIDTH; @@ -46,9 +52,11 @@ border-radius: 4px; } } + .treeView-container-active { z-index: 100; position: relative; + .formattedTextbox-sidebar { background-color: #ffff001f !important; height: 500px !important; @@ -71,7 +79,8 @@ display: flex; overflow: hidden; } -.treeView-border{ + +.treeView-border { border-left: dashed 1px #00000042; } @@ -79,15 +88,20 @@ .treeView-header { border: transparent 1px solid; display: flex; + //align-items: center; ::-webkit-scrollbar { - display: none; + display: none; } + .formattedTextBox-cont { - .formattedTextbox-sidebar, .formattedTextbox-sidebar-inking { + + .formattedTextbox-sidebar, + .formattedTextbox-sidebar-inking { overflow: visible !important; border-left: unset; } + overflow: visible !important; } @@ -105,12 +119,18 @@ margin-left: 0.25rem; opacity: 0.75; - >svg, .styleProvider-treeView-lock, .styleProvider-treeView-hide, .styleProvider-treeView-lock-active, .styleProvider-treeView-hide-active { + >svg, + .styleProvider-treeView-lock, + .styleProvider-treeView-hide, + .styleProvider-treeView-lock-active, + .styleProvider-treeView-hide-active { margin-left: 0.25rem; - margin-right: 0.25rem; + margin-right: 0.25rem; } - - >svg, .styleProvider-treeView-lock, .styleProvider-treeView-hide { + + >svg, + .styleProvider-treeView-lock, + .styleProvider-treeView-hide { display: none; } } @@ -135,7 +155,10 @@ } .right-buttons-container { - >svg, .styleProvider-treeView-lock, .styleProvider-treeView-hide { + + >svg, + .styleProvider-treeView-lock, + .styleProvider-treeView-hide { display: inherit; } } diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 2c3a6c0d7..e1fd78270 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -21,7 +21,7 @@ import { Transform } from '../../util/Transform'; import { undoBatch, UndoManager } from '../../util/UndoManager'; import { EditableView } from "../EditableView"; import { TREE_BULLET_WIDTH } from '../globalCssVariables.scss'; -import { DocumentView, DocumentViewProps, StyleProviderFunc } from '../nodes/DocumentView'; +import { DocumentView, DocumentViewProps, StyleProviderFunc, DocumentViewInternal } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; import { KeyValueBox } from '../nodes/KeyValueBox'; @@ -150,9 +150,9 @@ export class TreeView extends React.Component<TreeViewProps> { if (this.props.document.isFolder || Doc.IsSystem(this.props.document)) { this.treeViewOpen = !this.treeViewOpen; } else { + // choose an appropriate alias or make one. --- choose the first alias that (1) user owns, (2) has no context field ... otherwise make a new alias this.props.addDocTab(this.props.document, "add:right"); } - docView?.select(false); } constructor(props: any) { super(props); @@ -174,7 +174,7 @@ export class TreeView extends React.Component<TreeViewProps> { componentWillUnmount() { this._selDisposer?.(); - this._treeEle && this.props.unobserveHeight(this._treeEle) + this._treeEle && this.props.unobserveHeight(this._treeEle); document.removeEventListener("pointermove", this.onDragMove, true); document.removeEventListener("pointermove", this.onDragUp, true); } @@ -442,7 +442,7 @@ export class TreeView extends React.Component<TreeViewProps> { @computed get renderBullet() { TraceMobx(); - const iconType = this.doc.isFolder ? (this.treeViewOpen ? "chevron-down" : "chevron-right") : Doc.toIcon(this.doc); + const iconType = this.props.treeView.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewIcon + (this.treeViewOpen ? ":open" : "")) || "question"; const checked = this.onCheckedClick ? (this.doc.treeViewChecked ?? "unchecked") : undefined; return <div className={`bullet${this.props.treeView.outlineMode ? "-outline" : ""}`} key={"bullet"} title={this.childDocs?.length ? `click to see ${this.childDocs?.length} items` : "view fields"} @@ -483,22 +483,29 @@ export class TreeView extends React.Component<TreeViewProps> { } @computed get headerElements() { - return this.props.treeViewHideHeaderFields() || Doc.IsSystem(this.doc) ? (null) + return this.props.treeViewHideHeaderFields() || this.doc.treeViewHideHeaderFields ? (null) : <> - <FontAwesomeIcon key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} /> - {this.doc.treeViewExpandedViewLock ? (null) : + {this.doc.hideContextMenu ? (null) : <FontAwesomeIcon key="bars" icon="bars" size="sm" onClick={e => { this.showContextMenu(e); e.stopPropagation(); }} />} + {this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? (null) : <span className="collectionTreeView-keyHeader" key={this.treeViewExpandedView} onPointerDown={this.expandNextviewType}> {this.treeViewExpandedView} </span>} </>; } - showContextMenu = (e: React.MouseEvent) => simulateMouseClick(this._docRef?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); - contextMenuItems = () => Doc.IsSystem(this.doc) ? [] : this.doc.isFolder ? - [{ script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, label: "New Folder" }] : - this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ? - [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }] : - [{ script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" }] + showContextMenu = (e: React.MouseEvent) => { + DocumentViewInternal.SelectAfterContextMenu = false; + simulateMouseClick(this._docRef?.ContentDiv, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); + DocumentViewInternal.SelectAfterContextMenu = true; + } + contextMenuItems = () => { + const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, label: "New Folder" }; + return this.doc.isFolder ? [makeFolder] : + Doc.IsSystem(this.doc) ? [] : + this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ? + [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }, makeFolder] : + [{ script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" }]; + } onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); onChildDoubleClick = () => (!this.props.treeView.outlineMode && this._openScript?.()) || ScriptCast(this.doc.treeChildDoubleClick); @@ -522,7 +529,6 @@ export class TreeView extends React.Component<TreeViewProps> { }}> {StrCast(doc?.title)} </div>; - case StyleProp.Decorations: return (null); default: return this.props?.treeView?.props.styleProvider?.(doc, props, property); } } @@ -542,7 +548,7 @@ export class TreeView extends React.Component<TreeViewProps> { } } } - titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth() - treeBulletWidth())); + titleWidth = () => Math.max(20, Math.min(this.props.treeView.truncateTitleWidth(), this.props.panelWidth() - 2 * treeBulletWidth())); /** * Renders the EditableView title element for placement into the tree. @@ -726,7 +732,7 @@ export class TreeView extends React.Component<TreeViewProps> { return this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? "<" + this.doc.title + ">" : // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles <div className={`treeView-container${this.props.isContentActive() ? "-active" : ""}`} ref={this.createTreeDropTarget} - onPointerDown={e => this.props.isContentActive(true) && SelectionManager.DeselectAll()} + //onPointerDown={e => this.props.isContentActive(true) && SelectionManager.DeselectAll()} // bcz: this breaks entering a text filter in a filterBox since it deselects the filter's target document onKeyDown={this.onKeyDown}> <li className="collection-child"> {hideTitle && this.doc.type !== DocumentType.RTF ? diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index c623ce653..407216217 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1032,7 +1032,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P docRangeFilters={this.freeformRangeDocFilters} searchFilterDocs={this.searchFilterDocs} isContentActive={this.isAnnotationOverlay ? this.props.isContentActive : returnFalse} - isDocumentActive={this.isContentActive} + isDocumentActive={this.props.childDocumentsActive ? this.props.isDocumentActive : this.isContentActive} focus={this.focusDocument} addDocTab={this.addDocTab} addDocument={this.props.addDocument} @@ -1051,7 +1051,7 @@ export class CollectionFreeFormView extends CollectionSubView<PanZoomDocument, P dontRegisterView={this.props.dontRegisterView} pointerEvents={this.backgroundActive || this.props.childPointerEvents ? "all" : (this.props.viewDefDivClick || (engine === "pass" && !this.props.isSelected(true))) ? "none" : undefined} - jitterRotation={NumCast(this.props.Document._jitterRotation) || ((Doc.UserDoc().renderStyle === "comic" ? 10 : 0))} + jitterRotation={this.props.styleProvider?.(childLayout, this.props, StyleProp.JitterRotation) || 0} //fitToBox={this.props.fitToBox || BoolCast(this.props.freezeChildDimensions)} // bcz: check this />; } diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index d14b68fa7..b1f2750c3 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -101,7 +101,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque else if (e.key === ":") { DocUtils.addDocumentCreatorMenuItems(this.props.addLiveTextDocument, this.props.addDocument || returnFalse, x, y); - cm.displayMenu(this._downX, this._downY); + cm.displayMenu(this._downX, this._downY, undefined, true); e.stopPropagation(); } else if (e.key === "a" && (e.ctrlKey || e.metaKey)) { e.preventDefault(); @@ -155,7 +155,7 @@ export class MarqueeView extends React.Component<SubCollectionViewProps & Marque } else if (!e.ctrlKey && !e.metaKey && SelectionManager.Views().length < 2) { FormattedTextBox.SelectOnLoadChar = Doc.UserDoc().defaultTextLayout && !this.props.childLayoutString ? e.key : ""; FormattedTextBox.LiveTextUndo = UndoManager.StartBatch("live text batch"); - this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xMargin === 0)); + this.props.addLiveTextDocument(CurrentUserUtils.GetNewTextDoc("-typed text-", x, y, 200, 100, this.props.xPadding === 0)); e.stopPropagation(); } } diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx index 8b5c02b75..f3a39a262 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMulticolumnView.tsx @@ -49,6 +49,11 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, "*") === DimUnit.Ratio); } + @computed + private get minimumDim() { + return Math.min(...this.ratioDefinedDocs.filter(layout => layout._dimMagnitude).map(layout => NumCast(layout._dimMagnitude))); + } + /** * This loops through all childLayoutPairs and extracts the values for _dimUnit * and _dimMagnitude, ignoring any that are malformed. Additionally, it then @@ -63,7 +68,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu const widthSpecifiers: WidthSpecifier[] = []; this.childLayoutPairs.map(pair => { const unit = StrCast(pair.layout._dimUnit, "*"); - const magnitude = NumCast(pair.layout._dimMagnitude, 1); + const magnitude = NumCast(pair.layout._dimMagnitude, this.minimumDim); if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) { (unit === DimUnit.Ratio) && (starSum += magnitude); widthSpecifiers.push({ magnitude, unit }); @@ -80,15 +85,15 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu * themselves to drift toward zero. Thus, whenever we change any of the values, * we normalize everything (dividing by the smallest magnitude). */ - setTimeout(() => { - const { ratioDefinedDocs } = this; - if (this.childLayoutPairs.length) { - const minimum = Math.min(...ratioDefinedDocs.map(doc => NumCast(doc._dimMagnitude, 1))); - if (minimum !== 0) { - ratioDefinedDocs.forEach(layout => layout._dimMagnitude = NumCast(layout._dimMagnitude, 1) / minimum, 1); - } - } - }); + // setTimeout(() => { + // const { ratioDefinedDocs } = this; + // if (this.childLayoutPairs.length) { + // const minimum = this.minimumDim; + // if (minimum !== 0) { + // ratioDefinedDocs.forEach(layout => layout._dimMagnitude = NumCast(layout._dimMagnitude, 1) / minimum, 1); + // } + // } + // }); return { widthSpecifiers, starSum }; } @@ -161,7 +166,7 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu if (columnUnitLength === undefined) { return 0; // we're still waiting on promises to resolve } - let width = NumCast(layout._dimMagnitude, 1); + let width = NumCast(layout._dimMagnitude, this.minimumDim); if (StrCast(layout._dimUnit, "*") === DimUnit.Ratio) { width *= columnUnitLength; } @@ -273,6 +278,8 @@ export class CollectionMulticolumnView extends CollectionSubView(MulticolumnDocu <ResizeBar width={resizerWidth} key={"resizer" + i} + styleProvider={this.props.styleProvider} + isContentActive={this.props.isContentActive} select={this.props.select} columnUnitLength={this.getColumnUnitLength} toLeft={layout} diff --git a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx index 2c5e40d02..29cb3511a 100644 --- a/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx +++ b/src/client/views/collections/collectionMulticolumn/CollectionMultirowView.tsx @@ -49,6 +49,11 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) return this.childLayoutPairs.map(pair => pair.layout).filter(layout => StrCast(layout._dimUnit, "*") === DimUnit.Ratio); } + @computed + private get minimumDim() { + return Math.min(...this.ratioDefinedDocs.filter(layout => layout._dimMagnitude).map(layout => NumCast(layout._dimMagnitude))); + } + /** * This loops through all childLayoutPairs and extracts the values for _dimUnit * and _dimUnit, ignoring any that are malformed. Additionally, it then @@ -63,7 +68,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) const heightSpecifiers: HeightSpecifier[] = []; this.childLayoutPairs.map(pair => { const unit = StrCast(pair.layout._dimUnit, "*"); - const magnitude = NumCast(pair.layout._dimMagnitude, 1); + const magnitude = NumCast(pair.layout._dimMagnitude, this.minimumDim); if (unit && magnitude && magnitude > 0 && resolvedUnits.includes(unit)) { (unit === DimUnit.Ratio) && (starSum += magnitude); heightSpecifiers.push({ magnitude, unit }); @@ -80,15 +85,15 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) * themselves to drift toward zero. Thus, whenever we change any of the values, * we normalize everything (dividing by the smallest magnitude). */ - setTimeout(() => { - const { ratioDefinedDocs } = this; - if (this.childLayoutPairs.length) { - const minimum = Math.min(...ratioDefinedDocs.map(layout => NumCast(layout._dimMagnitude, 1))); - if (minimum !== 0) { - ratioDefinedDocs.forEach(layout => layout._dimMagnitude = NumCast(layout._dimMagnitude, 1) / minimum); - } - } - }); + // setTimeout(() => { + // const { ratioDefinedDocs } = this; + // if (this.childLayoutPairs.length) { + // const minimum = Math.min(...ratioDefinedDocs.map(layout => NumCast(layout._dimMagnitude, 1))); + // if (minimum !== 0) { + // ratioDefinedDocs.forEach(layout => layout._dimMagnitude = NumCast(layout._dimMagnitude, 1) / minimum); + // } + // } + // }); return { heightSpecifiers, starSum }; } @@ -161,7 +166,7 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) if (rowUnitLength === undefined) { return 0; // we're still waiting on promises to resolve } - let height = NumCast(layout._dimMagnitude, 1); + let height = NumCast(layout._dimMagnitude, this.minimumDim); if (StrCast(layout._dimUnit, "*") === DimUnit.Ratio) { height *= rowUnitLength; } @@ -261,12 +266,16 @@ export class CollectionMultirowView extends CollectionSubView(MultirowDocument) const height = () => this.lookupPixels(layout); const width = () => PanelWidth() - 2 * NumCast(Document._xMargin) - (BoolCast(Document.showWidthLabels) ? 20 : 0); collector.push( - <div className={"document-wrapper"} key={"wrapper" + i} > + <div className={"document-wrapper"} + style={{ height: height() }} + key={"wrapper" + i} > {this.getDisplayDoc(layout, dxf, width, height)} <HeightLabel layout={layout} collectionDoc={Document} /> </div>, <ResizeBar height={resizerHeight} + styleProvider={this.props.styleProvider} + isContentActive={this.props.isContentActive} key={"resizer" + i} columnUnitLength={this.getRowUnitLength} toTop={layout} diff --git a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx index 734915a93..9fe18d118 100644 --- a/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx +++ b/src/client/views/collections/collectionMulticolumn/MulticolumnResizer.tsx @@ -5,9 +5,13 @@ import { Doc } from "../../../../fields/Doc"; import { NumCast, StrCast } from "../../../../fields/Types"; import { DimUnit } from "./CollectionMulticolumnView"; import { UndoManager } from "../../../util/UndoManager"; +import { StyleProviderFunc } from "../../nodes/DocumentView"; +import { StyleProp } from "../../StyleProvider"; interface ResizerProps { width: number; + styleProvider?: StyleProviderFunc; + isContentActive?: () => boolean; columnUnitLength(): number | undefined; toLeft?: Doc; toRight?: Doc; @@ -85,19 +89,16 @@ export default class ResizeBar extends React.Component<ResizerProps> { } render() { - return ( - <div - className={"multiColumnResizer"} - style={{ - width: this.props.width, - opacity: this.isActivated && this.isHoverActive ? resizerOpacity : 0 - }} - onPointerEnter={action(() => this.isHoverActive = true)} - onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))} - > - <div className={"multiColumnResizer-hdl"} onPointerDown={e => this.registerResizing(e)} /> - </div> - ); + return <div className="multiColumnResizer" + style={{ + width: this.props.width, + backgroundColor: !this.props.isContentActive?.() ? "" : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor) + }} + onPointerEnter={action(() => this.isHoverActive = true)} + onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))} + > + <div className={"multiColumnResizer-hdl"} onPointerDown={e => this.registerResizing(e)} /> + </div>; } }
\ No newline at end of file diff --git a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx index d0bc4d01c..5478bf709 100644 --- a/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx +++ b/src/client/views/collections/collectionMulticolumn/MultirowResizer.tsx @@ -5,9 +5,13 @@ import { Doc } from "../../../../fields/Doc"; import { NumCast, StrCast } from "../../../../fields/Types"; import { DimUnit } from "./CollectionMultirowView"; import { UndoManager } from "../../../util/UndoManager"; +import { StyleProp } from "../../StyleProvider"; +import { StyleProviderFunc } from "../../nodes/DocumentView"; interface ResizerProps { height: number; + styleProvider?: StyleProviderFunc; + isContentActive?: () => boolean; columnUnitLength(): number | undefined; toTop?: Doc; toBottom?: Doc; @@ -83,19 +87,16 @@ export default class ResizeBar extends React.Component<ResizerProps> { } render() { - return ( - <div - className={"multiRowResizer"} - style={{ - height: this.props.height, - opacity: this.isActivated && this.isHoverActive ? resizerOpacity : 0 - }} - onPointerEnter={action(() => this.isHoverActive = true)} - onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))} - > - <div className={"multiRowResizer-hdl"} onPointerDown={e => this.registerResizing(e)} /> - </div> - ); + return <div className="multiRowResizer" + style={{ + height: this.props.height, + backgroundColor: !this.props.isContentActive?.() ? "" : this.props.styleProvider?.(undefined, undefined, StyleProp.WidgetColor) + }} + onPointerEnter={action(() => this.isHoverActive = true)} + onPointerLeave={action(() => !this.isResizingActive && (this.isHoverActive = false))} + > + <div className={"multiRowResizer-hdl"} onPointerDown={e => this.registerResizing(e)} /> + </div>; } }
\ No newline at end of file |
