diff options
Diffstat (limited to 'src/client/views/nodes')
32 files changed, 1043 insertions, 422 deletions
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index a2e36f12e..82bad971d 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -196,7 +196,7 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp this._recorder.ondataavailable = async (e: any) => { const [{ result }] = await Networking.UploadFilesToServer(e.data); if (!(result instanceof Error)) { - this.props.Document[this.props.fieldKey] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client)); + this.props.Document[this.props.fieldKey] = new AudioField(result.accessPaths.agnostic.client); } }; this._recordStart = new Date().getTime(); diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index a0a40becb..3d2cdf5a4 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -11,7 +11,7 @@ import { CollectionFreeFormView } from "../collections/collectionFreeForm/Collec import { CollectionSchemaView } from "../collections/collectionSchema/CollectionSchemaView"; import { CollectionView } from "../collections/CollectionView"; import { InkingStroke } from "../InkingStroke"; -import { PresElementBox } from "../presentationview/PresElementBox"; +import { PresElementBox } from "../nodes/trails/PresElementBox"; import { SearchBox } from "../search/SearchBox"; import { DashWebRTCVideo } from "../webcam/DashWebRTCVideo"; import { YoutubeBox } from "./../../apis/youtube/YoutubeBox"; @@ -32,7 +32,7 @@ import { LabelBox } from "./LabelBox"; import { LinkAnchorBox } from "./LinkAnchorBox"; import { LinkBox } from "./LinkBox"; import { PDFBox } from "./PDFBox"; -import { PresBox } from "./PresBox"; +import { PresBox } from "./trails/PresBox"; import { ScreenshotBox } from "./ScreenshotBox"; import { ScriptingBox } from "./ScriptingBox"; import { SliderBox } from "./SliderBox"; @@ -64,6 +64,7 @@ interface HTMLtagProps { htmltag: string; onClick?: ScriptField; onInput?: ScriptField; + scaling: number; } //"<HTMLdiv borderRadius='100px' onClick={this.bannerColor=this.bannerColor==='red'?'green':'red'} overflow='hidden' position='absolute' width='100%' height='100%' transform='rotate({2*this.x+this.y}deg)'> <ImageBox {...props} fieldKey={'data'}/> <HTMLspan width='200px' top='0' height='35px' textAlign='center' paddingTop='10px' transform='translate(-40px, 45px) rotate(-45deg)' position='absolute' color='{this.bannerColor===`green`?`light`:`dark`}blue' backgroundColor='{this.bannerColor===`green`?`dark`:`light`}blue'> {this.title}</HTMLspan></HTMLdiv>" @@ -82,7 +83,7 @@ interface HTMLtagProps { export class HTMLtag extends React.Component<HTMLtagProps> { click = (e: React.MouseEvent) => { const clickScript = (this.props as any).onClick as Opt<ScriptField>; - clickScript?.script.run({ this: this.props.Document, self: this.props.RootDoc }); + clickScript?.script.run({ this: this.props.Document, self: this.props.RootDoc, scale: this.props.scaling }); } onInput = (e: React.FormEvent<HTMLDivElement>) => { const onInputScript = (this.props as any).onInput as Opt<ScriptField>; @@ -90,9 +91,9 @@ export class HTMLtag extends React.Component<HTMLtagProps> { } render() { const style: { [key: string]: any } = {}; - const divKeys = OmitKeys(this.props, ["children", "htmltag", "RootDoc", "Document", "key", "onInput", "onClick", "__proto__"]).omit; + const divKeys = OmitKeys(this.props, ["children", "htmltag", "RootDoc", "scaling", "Document", "key", "onInput", "onClick", "__proto__"]).omit; const replacer = (match: any, expr: string, offset: any, string: any) => { // bcz: this executes a script to convert a propery expression string: { script } into a value - return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name })?.script.run({ self: this.props.RootDoc, this: this.props.Document }).result as string || ""; + return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: "number" })?.script.run({ self: this.props.RootDoc, this: this.props.Document, scale: this.props.scaling }).result as string || ""; }; Object.keys(divKeys).map((prop: string) => { const p = (this.props as any)[prop] as string; @@ -184,7 +185,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo // replace HTML<tag> with corresponding HTML tag as in: <HTMLdiv> becomes <HTMLtag Document={props.Document} htmltag='div'> const replacer2 = (match: any, p1: string, offset: any, string: any) => { - return `<HTMLtag RootDoc={props.RootDoc} Document={props.Document} htmltag='${p1}'`; + return `<HTMLtag RootDoc={props.RootDoc} Document={props.Document} scaling='${this.props.scaling?.() || 1}' htmltag='${p1}'`; }; layoutFrame = layoutFrame.replace(/<HTML([a-zA-Z0-9_-]+)/g, replacer2); @@ -200,7 +201,7 @@ export class DocumentContentsView extends React.Component<DocumentViewProps & Fo if (splits.length > 1) { const code = XRegExp.matchRecursive(splits[1], "{", "}", "", { valueNames: ["between", "left", "match", "right", "between"] }); layoutFrame = splits[0] + ` ${func}={props.${func}} ` + splits[1].substring(code[1].end + 1); - return ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name, value: "string" }); + return ScriptField.MakeScript(code[1].value, { this: Doc.name, self: Doc.name, scale: "number", value: "string" }); } return undefined; // add input function to props diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss index 735aa669f..b37b68249 100644 --- a/src/client/views/nodes/DocumentLinksButton.scss +++ b/src/client/views/nodes/DocumentLinksButton.scss @@ -1,16 +1,27 @@ -@import "../globalCssVariables.scss"; +@import "../global/globalCssVariables.scss"; +.documentLinksButton-menu { + width: 100%; + height: 100%; + position: relative; + display: flex; + align-content: center; + justify-content: center; + align-items: center; +} + .documentLinksButton-cont { min-width: 20; min-height: 20; position: absolute; } + .documentLinksButton, .documentLinksButton-endLink, .documentLinksButton-startLink { - height: 20px; - width: 20px; + height: 25px; + width: 25px; position: absolute; border-radius: 50%; opacity: 0.9; @@ -33,27 +44,43 @@ } .documentLinksButton { - background-color: black; + background-color: $dark-gray; + color: $white; font-weight: bold; + width: 80%; + height: 80%; + font-size: 100%; + transition: 0.2s ease all; &:hover { - background: $main-accent; - transform: scale(1.05); - cursor: pointer; + background-color: $black; } } -.documentLinksButton-endLink { - border: red solid 2px; +.documentLinksButton.startLink { + background-color: $medium-blue; + color: $white; + font-weight: bold; + width: 80%; + height: 80%; + font-size: 100%; + transition: 0.2s ease all; &:hover { - background: deepskyblue; - transform: scale(1.05); - cursor: pointer; + background-color: $black; } } -.documentLinksButton-startLink { - border: red solid 2px; - background-color: rgba(255, 192, 203, 0.5); +.documentLinksButton-endLink { + border: $medium-blue 2px dashed; + color: $medium-blue; + background-color: none !important; + width: 80%; + height: 80%; + font-size: 100%; + transition: 0.2s ease all; + + &:hover { + background-color: $light-blue; + } }
\ No newline at end of file diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index a6d07374a..b63174e54 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -20,6 +20,7 @@ import './DocumentLinksButton.scss'; import { DocServer } from "../../DocServer"; import { LightboxView } from "../LightboxView"; import { cat } from "shelljs"; +import { Colors } from "../global/globalEnums"; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; @@ -30,12 +31,12 @@ interface DocumentLinksButtonProps { Offset?: (number | undefined)[]; AlwaysOn?: boolean; InMenu?: boolean; - StartLink?: boolean; + StartLink?: boolean; //whether the link HAS been started (i.e. now needs to be completed) } @observer export class DocumentLinksButton extends React.Component<DocumentLinksButtonProps, {}> { private _linkButton = React.createRef<HTMLDivElement>(); - @observable public static StartLink: Doc | undefined; + @observable public static StartLink: Doc | undefined; //origin's Doc, if defined @observable public static StartLinkView: DocumentView | undefined; @observable public static AnnotationId: string | undefined; @observable public static AnnotationUri: string | undefined; @@ -45,6 +46,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp public static invisibleWebRef = React.createRef<HTMLDivElement>(); @action public static ClearLinkEditor() { DocumentLinksButton.LinkEditorDocView = undefined; } + @action @undoBatch onLinkButtonMoved = (e: PointerEvent) => { if (this.props.InMenu && this.props.StartLink) { @@ -66,6 +68,40 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp return false; } + onLinkMenuOpen = (e: React.PointerEvent): void => { + setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => { + if (doubleTap) { + const rootDoc = this.props.View.rootDoc; + const docid = Doc.CurrentUserEmail + Doc.GetProto(rootDoc)[Id] + "-pivotish"; + DocServer.GetRefField(docid).then(async docx => { + const rootAlias = () => { + const rootAlias = Doc.MakeAlias(rootDoc); + rootAlias.x = rootAlias.y = 0; + return rootAlias; + }; + let wid = rootDoc[WidthSym](); + const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([rootAlias()], { title: this.props.View.Document.title + "-pivot", _width: 500, _height: 500, }, docid); + const docs = await DocListCastAsync(Doc.GetProto(target).data); + if (!target.pivotFocusish) (Doc.GetProto(target).pivotFocusish = target); + DocListCast(rootDoc.links).forEach(link => { + const other = LinkManager.getOppositeAnchor(link, rootDoc); + const otherdoc = !other ? undefined : other.annotationOn ? Cast(other.annotationOn, Doc, null) : other; + if (otherdoc && !docs?.some(d => Doc.AreProtosEqual(d, otherdoc))) { + const alias = Doc.MakeAlias(otherdoc); + alias.x = wid; + alias.y = 0; + alias._lockedPosition = false; + wid += otherdoc[WidthSym](); + Doc.AddDocToList(Doc.GetProto(target), "data", alias); + } + }); + LightboxView.SetLightboxDoc(target); + }); + } + else DocumentLinksButton.LinkEditorDocView = this.props.View; + })); + } + @undoBatch onLinkButtonDown = (e: React.PointerEvent): void => { setupMoveUpEvents(this, e, this.onLinkButtonMoved, emptyFunction, action((e, doubleTap) => { @@ -78,37 +114,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp DocumentLinksButton.StartLink = this.props.View.props.Document; DocumentLinksButton.StartLinkView = this.props.View; } - } else if (!this.props.InMenu) { - if (doubleTap) { - const rootDoc = this.props.View.rootDoc; - const docid = Doc.CurrentUserEmail + Doc.GetProto(rootDoc)[Id] + "-pivotish"; - DocServer.GetRefField(docid).then(async docx => { - const rootAlias = () => { - const rootAlias = Doc.MakeAlias(rootDoc); - rootAlias.x = rootAlias.y = 0; - return rootAlias; - }; - let wid = rootDoc[WidthSym](); - const target = ((docx instanceof Doc) && docx) || Docs.Create.FreeformDocument([rootAlias()], { title: this.props.View.Document.title + "-pivot", _width: 500, _height: 500, }, docid); - const docs = await DocListCastAsync(Doc.GetProto(target).data); - if (!target.pivotFocusish) (Doc.GetProto(target).pivotFocusish = target); - DocListCast(rootDoc.links).forEach(link => { - const other = LinkManager.getOppositeAnchor(link, rootDoc); - const otherdoc = !other ? undefined : other.annotationOn ? Cast(other.annotationOn, Doc, null) : other; - if (otherdoc && !docs?.some(d => Doc.AreProtosEqual(d, otherdoc))) { - const alias = Doc.MakeAlias(otherdoc); - alias.x = wid; - alias.y = 0; - alias._lockedPosition = false; - wid += otherdoc[WidthSym](); - Doc.AddDocToList(Doc.GetProto(target), "data", alias); - } - }); - LightboxView.SetLightboxDoc(target); - }); - } - else DocumentLinksButton.LinkEditorDocView = this.props.View; - } + }; })); } @@ -120,17 +126,17 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp if (DocumentLinksButton.StartLink === this.props.View.props.Document) { DocumentLinksButton.StartLink = undefined; DocumentLinksButton.StartLinkView = undefined; - } else { + } else { //if this LinkButton's Document is undefined DocumentLinksButton.StartLink = this.props.View.props.Document; DocumentLinksButton.StartLinkView = this.props.View; } - //action(() => Doc.BrushDoc(this.props.View.Document)); } else if (!this.props.InMenu) { DocumentLinksButton.LinkEditorDocView = this.props.View; } } + completeLink = (e: React.PointerEvent): void => { setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action((e, doubleTap) => { if (doubleTap && !this.props.StartLink) { @@ -141,7 +147,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp } else if (DocumentLinksButton.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document) { const sourceDoc = DocumentLinksButton.StartLink; const targetDoc = this.props.View.ComponentView?.getAnchor?.() || this.props.View.Document; - const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, "long drag"); + const linkDoc = DocUtils.MakeLink({ doc: sourceDoc }, { doc: targetDoc }, "links"); //why is long drag here when this is used for completing links by clicking? LinkManager.currentLink = linkDoc; @@ -184,7 +190,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp } else if (startLink !== endLink) { endLink = endLinkView?.docView?._componentView?.getAnchor?.() || endLink; startLink = DocumentLinksButton.StartLinkView?.docView?._componentView?.getAnchor?.() || startLink; - const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "long drag", undefined, undefined, true); + const linkDoc = DocUtils.MakeLink({ doc: startLink }, { doc: endLink }, DocumentLinksButton.AnnotationId ? "hypothes.is annotation" : "link", undefined, undefined, true); LinkManager.currentLink = linkDoc; @@ -192,7 +198,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp Doc.GetProto(linkDoc as Doc).linksToAnnotation = true; Doc.GetProto(linkDoc as Doc).annotationId = DocumentLinksButton.AnnotationId; Doc.GetProto(linkDoc as Doc).annotationUri = DocumentLinksButton.AnnotationUri; - const dashHyperlink = Utils.prepend("/doc/" + (startIsAnnotation ? endLink[Id] : startLink[Id])); + const dashHyperlink = Doc.globalServerPath(startIsAnnotation ? endLink : startLink); Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId, (startIsAnnotation ? startLink : endLink)); // edit annotation to add a Dash hyperlink to the linked doc } @@ -242,45 +248,50 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp return results; } + /** + * gets the JSX of the link button (btn used to start/complete links) OR the link-view button (btn on bottom left of each linked node) + * + * todo:glr / anh seperate functionality such as onClick onPointerDown of link menu button + */ @computed get linkButtonInner() { - const btnDim = this.props.InMenu ? "20px" : "30px"; + const btnDim = "30px"; const link = <img style={{ width: "22px", height: "16px" }} src={`/assets/${"link.png"}`} />; - - return <div className="documentLinksButton-cont" ref={this._linkButton} - style={{ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }} - > - <div className={"documentLinksButton"} - onPointerDown={this.onLinkButtonDown} onClick={this.onLinkClick} - style={{ - backgroundColor: this.props.InMenu ? "" : "#add8e6", - color: this.props.InMenu ? "white" : "black", - width: btnDim, - height: btnDim, - }} > - {this.props.InMenu ? - this.props.StartLink ? - <FontAwesomeIcon className="documentdecorations-icon" icon="link" size="sm" /> - : link - : Array.from(this.filteredLinks).length} - </div> - {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? - <div className={"documentLinksButton-endLink"} + const isActive = (DocumentLinksButton.StartLink === this.props.View.props.Document) && this.props.StartLink; + return (!this.props.InMenu ? + <div className="documentLinksButton-cont" + style={{ left: this.props.Offset?.[0], top: this.props.Offset?.[1], right: this.props.Offset?.[2], bottom: this.props.Offset?.[3] }} + > + <div className={"documentLinksButton"} + onPointerDown={this.onLinkMenuOpen} onClick={this.onLinkClick} style={{ - width: btnDim, height: btnDim, - backgroundColor: DocumentLinksButton.StartLink ? "" : "grey", - opacity: DocumentLinksButton.StartLink ? "" : "50%", - border: DocumentLinksButton.StartLink ? "" : "none", - cursor: DocumentLinksButton.StartLink ? "pointer" : "default" - }} - onPointerDown={DocumentLinksButton.StartLink && this.completeLink} - onClick={e => DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)} /> - : (null) - } - {DocumentLinksButton.StartLink === this.props.View.props.Document && this.props.InMenu && this.props.StartLink ? - <div className={"documentLinksButton-startLink"} onPointerDown={this.clearLinks} onClick={this.clearLinks} style={{ width: btnDim, height: btnDim }} /> - : (null) - } - </div >; + backgroundColor: Colors.LIGHT_BLUE, + color: Colors.BLACK, + width: btnDim, + height: btnDim, + }}> + {Array.from(this.filteredLinks).length} + </div> + </div> + : + <div className="documentLinksButton-menu" ref={this._linkButton}> + {this.props.InMenu && !this.props.StartLink && DocumentLinksButton.StartLink !== this.props.View.props.Document ? //if the origin node is not this node + <div className={"documentLinksButton-endLink"} + onPointerDown={DocumentLinksButton.StartLink && this.completeLink} + onClick={e => DocumentLinksButton.StartLink && DocumentLinksButton.finishLinkClick(e.clientX, e.clientY, DocumentLinksButton.StartLink, this.props.View.props.Document, true, this.props.View)}> + <FontAwesomeIcon className="documentdecorations-icon" icon="link" /> + </div> + : (null) + } + { + this.props.InMenu && this.props.StartLink ? //if link has been started from current node, then set behavior of link button to deactivate linking when clicked again + <div className={`documentLinksButton ${isActive ? `startLink` : ``}`} onPointerDown={isActive ? undefined : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}> + <FontAwesomeIcon className="documentdecorations-icon" icon="link" /> + </div> + : + (null) + } + </div> + ) } render() { @@ -290,6 +301,7 @@ export class DocumentLinksButton extends React.Component<DocumentLinksButtonProp const buttonTitle = "Tap to view links; double tap to open link collection"; const title = this.props.InMenu ? menuTitle : buttonTitle; + //render circular tooltip if it isn't set to invisible and show the number of doc links the node has, and render inner-menu link button for starting/stopping links if currently in menu return !Array.from(this.filteredLinks).length && !this.props.AlwaysOn ? (null) : this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink) ? <Tooltip title={<div className="dash-tooltip">{title}</div>}> diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index bdbece621..7f164ca48 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -1,4 +1,4 @@ -@import "../globalCssVariables"; +@import "../global/globalCssVariables"; .documentView-effectsWrapper { border-radius: inherit; @@ -22,7 +22,7 @@ transition: outline .3s linear; cursor: grab; - // background: $light-color; //overflow: hidden; + // background: $white; //overflow: hidden; transform-origin: left top; &.minimized { @@ -147,7 +147,7 @@ .documentView-titleWrapper, .documentView-titleWrapper-hover { overflow: hidden; - color: white; + color: $black; transform-origin: top left; top: 0; width: 100%; @@ -218,6 +218,6 @@ .documentView-node:first-child { position: relative; - background: "#B59B66"; //$light-color; + background: "#B59B66"; //$white; } }
\ No newline at end of file diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index a4645ed8d..f716eb7b1 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -43,7 +43,7 @@ import { DocumentLinksButton } from './DocumentLinksButton'; import "./DocumentView.scss"; import { LinkAnchorBox } from './LinkAnchorBox'; import { LinkDocPreview } from "./LinkDocPreview"; -import { PresBox } from './PresBox'; +import { PresBox } from './trails/PresBox'; import { RadialMenu } from './RadialMenu'; import React = require("react"); import { ScriptingBox } from "./ScriptingBox"; @@ -746,7 +746,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps moreItems.push({ description: "Tag Child Images via Google Photos", event: () => GooglePhotos.Query.TagChildImages(this.props.Document), icon: "caret-square-right" }); moreItems.push({ description: "Write Back Link to Album", event: () => GooglePhotos.Transactions.AddTextEnrichment(this.props.Document), icon: "caret-square-right" }); } - moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Utils.prepend("/doc/" + this.props.Document[Id])), icon: "fingerprint" }); + moreItems.push({ description: "Copy ID", event: () => Utils.CopyText(Doc.globalServerPath(this.props.Document)), icon: "fingerprint" }); } } @@ -757,7 +757,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps const help = cm.findByDescription("Help..."); const helpItems: ContextMenuProps[] = help && "subitems" in help ? help.subitems : []; !Doc.UserDoc().novice && helpItems.push({ description: "Show Fields ", event: () => this.props.addDocTab(Docs.Create.KVPDocument(this.props.Document, { _width: 300, _height: 300 }), "add:right"), icon: "layer-group" }); - helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument(Utils.prepend("/assets/cheat-sheet.pdf"), { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" }); + helpItems.push({ description: "Text Shortcuts Ctrl+/", event: () => this.props.addDocTab(Docs.Create.PdfDocument("/assets/cheat-sheet.pdf", { _width: 300, _height: 300 }), "add:right"), icon: "keyboard" }); !Doc.UserDoc().novice && helpItems.push({ description: "Print Document in Console", event: () => console.log(this.props.Document), icon: "hand-point-right" }); !Doc.UserDoc().novice && helpItems.push({ description: "Print DataDoc in Console", event: () => console.log(this.props.Document[DataSym]), icon: "hand-point-right" }); cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" }); @@ -884,7 +884,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps recorder.ondataavailable = async (e: any) => { const [{ result }] = await Networking.UploadFilesToServer(e.data); if (!(result instanceof Error)) { - const audioDoc = Docs.Create.AudioDocument(Utils.prepend(result.accessPaths.agnostic.client), { title: "audio test", _width: 200, _height: 32 }); + const audioDoc = Docs.Create.AudioDocument(result.accessPaths.agnostic.client, { title: "audio test", _width: 200, _height: 32 }); audioDoc.treeViewExpandedView = "layout"; const audioAnnos = Cast(self.dataDoc[self.LayoutFieldKey + "-audioAnnotations"], listSpec(Doc)); if (audioAnnos === undefined) { @@ -970,7 +970,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps const highlightIndex = this.props.LayoutTemplateString ? (Doc.IsHighlighted(this.props.Document) ? 6 : 0) : Doc.isBrushedHighlightedDegree(this.props.Document); // bcz: Argh!! need to identify a tree view doc better than a LayoutTemlatString const highlightColor = (CurrentUserUtils.ActiveDashboard?.darkScheme ? ["transparent", "#65350c", "#65350c", "yellow", "magenta", "cyan", "orange"] : - ["transparent", "maroon", "maroon", "yellow", "magenta", "cyan", "orange"])[highlightIndex]; + ["transparent", "#4476F7", "#4476F7", "yellow", "magenta", "cyan", "orange"])[highlightIndex]; const highlightStyle = ["solid", "dashed", "solid", "solid", "solid", "solid", "solid"][highlightIndex]; const excludeTypes = !this.props.treeViewDoc ? [DocumentType.FONTICON, DocumentType.INK] : [DocumentType.FONTICON]; let highlighting = !this.props.disableDocBrushing && highlightIndex && !excludeTypes.includes(this.layoutDoc.type as any) && this.layoutDoc._viewType !== CollectionViewType.Linear; @@ -978,7 +978,7 @@ export class DocumentViewInternal extends DocComponent<DocumentViewInternalProps const borderPath = this.props.styleProvider?.(this.props.Document, this.props, StyleProp.BorderPath) || { path: undefined }; const internal = PresBox.EffectsProvider(this.layoutDoc, this.renderDoc) || this.renderDoc; - const boxShadow = highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` : + const boxShadow = this.props.treeViewDoc ? null : highlighting && this.borderRounding && highlightStyle !== "dashed" ? `0 0 0 ${highlightIndex}px ${highlightColor}` : this.boxShadow || (this.props.Document.isTemplateForField ? "black 0.2vw 0.2vw 0.8vw" : undefined); return <div className={DocumentView.ROOT_DIV} ref={this._mainCont} onContextMenu={this.onContextMenu} diff --git a/src/client/views/nodes/FieldView.tsx b/src/client/views/nodes/FieldView.tsx index 86250c9d1..ebbc1138a 100644 --- a/src/client/views/nodes/FieldView.tsx +++ b/src/client/views/nodes/FieldView.tsx @@ -64,9 +64,9 @@ export class FieldView extends React.Component<FieldViewProps> { // else if (field instaceof PresBox) { // return <PresBox {...this.props} />; // } - else if (field instanceof VideoField) { - return <VideoBox {...this.props} />; - } + // else if (field instanceof VideoField) { + // return <VideoBox {...this.props} />; + // } // else if (field instanceof AudioField) { // return <AudioBox {...this.props} />; //} diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss index 33ac85a0e..718af2c16 100644 --- a/src/client/views/nodes/FontIconBox.scss +++ b/src/client/views/nodes/FontIconBox.scss @@ -1,5 +1,7 @@ +@import "../global/globalCssVariables"; + .fontIconBox-label { - color: white; + color: $white; margin-right: 4px; margin-top: 1px; position: relative; @@ -22,8 +24,8 @@ position: absolute; top: -10px; right: -10px; - color: white; - background: #f44b42; + color: $white; + background: $pink; font-weight: 300; border-radius: 100%; width: 25px; @@ -37,7 +39,7 @@ .menuButton-circle, .menuButton-round { border-radius: 100%; - background-color: black; + background-color: $dark-gray; padding: 0; .fontIconBox-label { @@ -47,13 +49,14 @@ } &:hover { - background-color: #aaaaa3; + background-color: $light-gray; } } .menuButton-square { padding-top: 3px; padding-bottom: 3px; + background-color: $dark-gray; .fontIconBox-label { border-radius: 0px; diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx index 6ae4b9726..0d415e238 100644 --- a/src/client/views/nodes/FontIconBox.tsx +++ b/src/client/views/nodes/FontIconBox.tsx @@ -14,6 +14,7 @@ import { DocComponent } from '../DocComponent'; import { StyleProp } from '../StyleProvider'; import { FieldView, FieldViewProps } from './FieldView'; import './FontIconBox.scss'; +import { Colors } from '../global/globalEnums'; const FontIconSchema = createSchema({ icon: "string", }); @@ -47,7 +48,7 @@ export class FontIconBox extends DocComponent<FieldViewProps, FontIconDocument>( const icon = StrCast(this.dataDoc.icon, "user") as any; const presSize = shape === 'round' ? 25 : 30; const presTrailsIcon = <img src={`/assets/${"presTrails.png"}`} - style={{ width: presSize, height: presSize, filter: `invert(${color === "white" ? "100%" : "0%"})`, marginBottom: "5px" }} />; + style={{ width: presSize, height: presSize, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})`, marginBottom: "5px" }} />; const button = <button className={`menuButton-${shape}`} onContextMenu={this.specificContextMenu} style={{ backgroundColor: backgroundColor, }}> <div className="menuButton-wrap"> diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index d876ae818..2c0106960 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -238,7 +238,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp let succeeded = true; let data: ImageField | undefined; try { - data = new ImageField(Utils.prepend(accessPaths.agnostic.client)); + data = new ImageField(accessPaths.agnostic.client); } catch { succeeded = false; } @@ -340,7 +340,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp @action finishMarquee = () => { this._marqueeing = undefined; - this.props.select(false) + this.props.select(false); } render() { diff --git a/src/client/views/nodes/KeyValueBox.scss b/src/client/views/nodes/KeyValueBox.scss index eb7c2f32b..ffcba4981 100644 --- a/src/client/views/nodes/KeyValueBox.scss +++ b/src/client/views/nodes/KeyValueBox.scss @@ -1,10 +1,10 @@ -@import "../globalCssVariables"; +@import "../global/globalCssVariables"; .keyValueBox-cont { overflow-y: scroll; width:100%; height: 100%; - background-color: $light-color; - border: 1px solid $intermediate-color; + background-color: $white; + border: 1px solid $medium-gray; border-radius: $border-radius; box-sizing: border-box; display: inline-block; @@ -56,8 +56,8 @@ $header-height: 30px; width:100%; position: relative; display: inline-block; - background: $intermediate-color; - color: $light-color; + background: $medium-gray; + color: $white; text-transform: uppercase; letter-spacing: 2px; font-size: 12px; @@ -66,7 +66,7 @@ $header-height: 30px; th { font-weight: normal; &:first-child { - border-right: 1px solid $light-color; + border-right: 1px solid $white; } } } @@ -76,9 +76,9 @@ $header-height: 30px; display: flex; width:100%; height:$header-height; - background: $light-color; + background: $white; .formattedTextBox-cont { - background: $light-color; + background: $white; } } .keyValueBox-cont { @@ -116,8 +116,8 @@ $header-height: 30px; display: flex; width:100%; height:30px; - background: $light-color-secondary; + background: $light-gray; .formattedTextBox-cont { - background: $light-color-secondary; + background: $light-gray; } }
\ No newline at end of file diff --git a/src/client/views/nodes/KeyValuePair.scss b/src/client/views/nodes/KeyValuePair.scss index f78767234..5b660e582 100644 --- a/src/client/views/nodes/KeyValuePair.scss +++ b/src/client/views/nodes/KeyValuePair.scss @@ -1,4 +1,4 @@ -@import "../globalCssVariables"; +@import "../global/globalCssVariables"; .keyValuePair-td-key { diff --git a/src/client/views/nodes/LinkDescriptionPopup.scss b/src/client/views/nodes/LinkDescriptionPopup.scss index d92823ccc..a8db5d360 100644 --- a/src/client/views/nodes/LinkDescriptionPopup.scss +++ b/src/client/views/nodes/LinkDescriptionPopup.scss @@ -1,9 +1,13 @@ +@import "../global/globalCssVariables.scss"; + .linkDescriptionPopup { display: flex; - - border: 1px solid rgb(170, 26, 26); - + flex-direction: row; + justify-content: center; + align-items: center; + border: 2px solid $medium-blue; + background-color: $white; width: auto; position: absolute; @@ -11,17 +15,11 @@ z-index: 10000; border-radius: 10px; font-size: 12px; - //white-space: nowrap; - - background-color: rgba(250, 250, 250, 0.95); - padding-top: 9px; - padding-bottom: 9px; - padding-left: 9px; - padding-right: 9px; + gap: 5px; + padding: 9px; .linkDescriptionPopup-input { float: left; - background-color: rgba(250, 250, 250, 0.95); color: rgb(100, 100, 100); border: none; min-width: 160px; @@ -30,46 +28,29 @@ .linkDescriptionPopup-btn { float: right; - justify-content: center; vertical-align: middle; - .linkDescriptionPopup-btn-dismiss { - background-color: white; - color: black; + cursor: pointer; display: inline; - right: 0; - border-radius: 10px; - border: 1px solid black; - padding: 3px; - font-size: 9px; - text-align: center; - position: relative; - margin-right: 4px; - justify-content: center; - - &:hover{ - cursor: pointer; - } + white-space: nowrap; + padding: 5px; + vertical-align: middle; + background-color: $close-red; + border-radius: 3px; + color: black; } .linkDescriptionPopup-btn-add { - background-color: black; - color: white; + cursor: pointer; display: inline; - right: 0; - border-radius: 10px; - border: 1px solid black; - padding: 3px; - font-size: 9px; - text-align: center; - position: relative; - justify-content: center; - - &:hover{ - cursor: pointer; - } + white-space: nowrap; + padding: 5px; + vertical-align: middle; + background-color: $light-blue; + border-radius: 3px; + color: black; } } diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index b73fb10df..126a37eb8 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -72,14 +72,14 @@ export class LinkDocPreview extends React.Component<LinkDocPreviewProps> { @computed get href() { if (this.props.hrefs?.length) { const href = this.props.hrefs[this._hrefInd]; - if (href.indexOf(Utils.prepend("/doc/")) !== 0) { // link to a web page URL -- try to show a preview + if (href.indexOf(Doc.localServerPath()) !== 0) { // link to a web page URL -- try to show a preview if (href.startsWith("https://en.wikipedia.org/wiki/")) { wiki().page(href.replace("https://en.wikipedia.org/wiki/", "")).then(page => page.summary().then(action(summary => this._toolTipText = summary.substring(0, 500)))); } else { setTimeout(action(() => this._toolTipText = "url => " + href)); } } else { // hyperlink to a document .. decode doc id and retrieve from the server. this will trigger vals() being invalidated - const anchorDoc = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; + const anchorDoc = href.replace(Doc.localServerPath(), "").split("?")[0]; anchorDoc && DocServer.GetRefField(anchorDoc).then(action(anchor => { if (anchor instanceof Doc && DocListCast(anchor.links).length) { this._linkDoc = DocListCast(anchor.links)[0]; diff --git a/src/client/views/nodes/PDFBox.scss b/src/client/views/nodes/PDFBox.scss index 0f46da294..72dec6e4c 100644 --- a/src/client/views/nodes/PDFBox.scss +++ b/src/client/views/nodes/PDFBox.scss @@ -7,7 +7,7 @@ overflow: hidden; cursor: auto; transform-origin: top left; - z-index: 0; + //z-index: 0; .pdfBox-ui { position: absolute; @@ -30,6 +30,7 @@ justify-content: center; border-radius: 3px; pointer-events: all; + z-index: 1; // so it appears on top of the document's title, if shown } .pdfBox-pageNums { @@ -223,7 +224,7 @@ .pdfBox { width: 100%; height: 100%; - pointer-events: none; + //pointer-events: none; .pdfViewerDash-text { .textLayer { display: none; diff --git a/src/client/views/nodes/PDFBox.tsx b/src/client/views/nodes/PDFBox.tsx index 8f61e252b..0b451e2b4 100644 --- a/src/client/views/nodes/PDFBox.tsx +++ b/src/client/views/nodes/PDFBox.tsx @@ -53,30 +53,6 @@ export class PDFBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps if (PDFBox.pdfcache.get(this.pdfUrl.url.href)) runInAction(() => this._pdf = PDFBox.pdfcache.get(this.pdfUrl!.url.href)); else if (PDFBox.pdfpromise.get(this.pdfUrl.url.href)) PDFBox.pdfpromise.get(this.pdfUrl.url.href)?.then(action(pdf => this._pdf = pdf)); } - - const backup = "oldPath"; - const href = this.pdfUrl?.url.href; - if (href) { - const pathCorrectionTest = /upload\_[a-z0-9]{32}.(.*)/g; - const matches = pathCorrectionTest.exec(href); - // console.log("\nHere's the { url } being fed into the outer regex:"); - // console.log(href); - // console.log("And here's the 'properPath' build from the captured filename:\n"); - if (matches !== null && href.startsWith(window.location.origin)) { - const properPath = Utils.prepend(`/files/pdfs/${matches[0]}`); - //console.log(properPath); - if (!properPath.includes(href)) { - console.log(`The two (url and proper path) were not equal`); - const proto = Doc.GetProto(this.props.Document); - proto[this.props.fieldKey] = new PdfField(properPath); - proto[backup] = href; - } else { - //console.log(`The two (url and proper path) were equal`); - } - } else { - console.log("Outer matches was null!"); - } - } } componentWillUnmount() { this._selectReactionDisposer?.(); } diff --git a/src/client/views/nodes/RadialMenu.scss b/src/client/views/nodes/RadialMenu.scss index daa620d12..312b51013 100644 --- a/src/client/views/nodes/RadialMenu.scss +++ b/src/client/views/nodes/RadialMenu.scss @@ -1,4 +1,4 @@ -@import "../globalCssVariables"; +@import "../global/globalCssVariables"; .radialMenu-cont { position: absolute; @@ -53,7 +53,7 @@ s transition: all .1s; border-width: .11px; border-style: none; - border-color: $intermediate-color; // rgb(187, 186, 186); + border-color: $medium-gray; // rgb(187, 186, 186); // padding: 10px 0px 10px 0px; white-space: nowrap; font-size: 13px; diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 700f8a7d3..0e235a62d 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -227,7 +227,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl this._audioRec.onstop = async (e: any) => { const [{ result }] = await Networking.UploadFilesToServer(aud_chunks); if (!(result instanceof Error)) { - this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client)); + this.dataDoc[this.props.fieldKey + "-audio"] = new AudioField(result.accessPaths.agnostic.client); } }; this._videoRef!.srcObject = await (navigator.mediaDevices as any).getDisplayMedia({ video: true }); @@ -244,7 +244,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatabl this.layoutDoc.layout = VideoBox.LayoutString(this.fieldKey); this.dataDoc.nativeWidth = this.dataDoc.nativeHeight = undefined; this.layoutDoc._fitWidth = undefined; - this.dataDoc[this.props.fieldKey] = new VideoField(Utils.prepend(result.accessPaths.agnostic.client)); + this.dataDoc[this.props.fieldKey] = new VideoField(result.accessPaths.agnostic.client); } else alert("video conversion failed"); }; this._audioRec.start(); diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index fc08a2302..ce45c01e6 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -75,10 +75,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, "_timecodeToShow"/* videoStart */, "_timecodeToHide" /* videoEnd */, timecode ? timecode : undefined) || this.rootDoc; } - choosePath(url: string) { - return url.indexOf(window.location.origin) === -1 ? Utils.CorsProxy(url) : url; - } - videoLoad = () => { const aspect = this.player!.videoWidth / this.player!.videoHeight; Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth); @@ -182,8 +178,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp } } - private createRealSummaryLink = (relative: string, downX?: number, downY?: number) => { - const url = this.choosePath(Utils.prepend(relative)); + private createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => { + const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath; const width = this.layoutDoc._width || 1; const height = this.layoutDoc._height || 0; const imageSummary = Docs.Create.ImageDocument(url, { diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index ca82c049c..19b69ff5a 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -1,4 +1,4 @@ -@import "../globalCssVariables.scss"; +@import "../global/globalCssVariables.scss"; .webBox { @@ -17,6 +17,7 @@ justify-content: center; border-radius: 3px; pointer-events: all; + z-index: 1; // so it appears on top of the document's title, if shown } .pdfViewerDash-dragAnnotationBox { diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 88e38712a..f5b1f96f2 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -162,7 +162,8 @@ export class WebBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps scrollFocus = (doc: Doc, smooth: boolean) => { if (this._sidebarRef?.current?.makeDocUnfiltered(doc)) return 1; if (doc !== this.rootDoc && this._outerRef.current) { - const scrollTo = doc.type === DocumentType.TEXTANCHOR ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), this.props.PanelHeight() / (this.props.scaling?.() || 1)); + const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1); + const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.layoutDoc._scrollTop), windowHeight, windowHeight * .1); if (scrollTo !== undefined) { const focusSpeed = smooth ? 500 : 0; this._initialScroll !== undefined && (this._initialScroll = scrollTo); diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.scss b/src/client/views/nodes/formattedText/FormattedTextBox.scss index 53aceb533..3cedab1a4 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.scss +++ b/src/client/views/nodes/formattedText/FormattedTextBox.scss @@ -1,4 +1,4 @@ -@import "../../globalCssVariables"; +@import "../../global/globalCssVariables"; .ProseMirror { width: 100%; @@ -31,7 +31,7 @@ audiotag:hover { padding: 0; border-width: 0px; border-radius: inherit; - border-color: $intermediate-color; + border-color: $medium-gray; box-sizing: border-box; background-color: inherit; border-style: solid; @@ -363,7 +363,7 @@ footnote::after { @media only screen and (max-width: 1000px) { - @import "../../globalCssVariables"; + @import "../../global/globalCssVariables"; .ProseMirror { width: 100%; @@ -381,7 +381,7 @@ footnote::after { padding: 0; border-width: 0px; border-radius: inherit; - border-color: $intermediate-color; + border-color: $medium-gray; box-sizing: border-box; background-color: inherit; border-style: solid; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 95d8f555c..f7e9ee028 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -71,6 +71,7 @@ export interface FormattedTextBoxProps { xPadding?: number; // used to override document's settings for xMargin --- see CollectionCarouselView yPadding?: number; noSidebar?: boolean; + dontScale?: boolean; dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded (and mark as not being associated with scrollTop document field) } export const GoogleRef = "googleDocId"; @@ -126,7 +127,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp @computed get scrollHeight() { return NumCast(this.rootDoc[this.fieldKey + "-scrollHeight"]); } @computed get sidebarHeight() { return !this.sidebarWidth() ? 0 : NumCast(this.rootDoc[this.SidebarKey + "-height"]); } @computed get titleHeight() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin) || 0; } - @computed get autoHeightMargins() { return this.titleHeight + (this.layoutDoc._autoHeightMargins && !this.props.dontSelectOnLoad ? NumCast(this.layoutDoc._autoHeightMargins) : 0); } + @computed get autoHeightMargins() { return this.titleHeight + NumCast(this.layoutDoc._autoHeightMargins); } @computed get _recording() { return this.dataDoc?.mediaState === "recording"; } set _recording(value) { !this.dataDoc.recordingSource && (this.dataDoc.mediaState = value ? "recording" : undefined); @@ -214,6 +215,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp AnchorMenu.Instance.Status = "marquee"; AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => { this._editorView?.state && RichTextMenu.Instance.insertHighlight(color, this._editorView.state, this._editorView?.dispatch); + console.log("highlight") return undefined; }); /** @@ -369,7 +371,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; const anchor = Docs.Create.TextanchorDocument(); const alink = DocUtils.MakeLink({ doc: anchor }, { doc: target }, "automatic")!; - const allAnchors = [{ href: Utils.prepend("/doc/" + anchor[Id]), title: "a link", anchorId: anchor[Id] }]; + const allAnchors = [{ href: Doc.localServerPath(anchor), title: "a link", anchorId: anchor[Id] }]; const link = this._editorView!.state.schema.marks.linkAnchor.create({ allAnchors, title: "auto link", location }); tr = tr.addMark(flattened[i].from, flattened[i].to, link); }); @@ -703,7 +705,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp let tr = state.tr.addMark(sel.from, sel.to, splitter); if (sel.from !== sel.to) { const anchor = anchorDoc ?? Docs.Create.TextanchorDocument({ title: this._editorView?.state.doc.textBetween(sel.from, sel.to) }); - const href = targetHref ?? Utils.prepend("/doc/" + anchor[Id]); + const href = targetHref ?? Doc.localServerPath(anchor); if (anchor !== anchorDoc) this.addDocument(anchor); tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => { if (node.firstChild === null && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { @@ -786,7 +788,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } componentDidMount() { - this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. + !this.props.dontSelectOnLoad && this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. this._cachedLinks = DocListCast(this.Document.links); this._disposers.breakupDictation = reaction(() => DocumentManager.Instance.RecordingEvent, this.breakupDictation); this._disposers.autoHeight = reaction(() => this.autoHeight, autoHeight => autoHeight && this.tryUpdateScrollHeight()); @@ -1040,7 +1042,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } const marks = [...node.marks]; const linkIndex = marks.findIndex(mark => mark.type.name === "link"); - const allLinks = [{ href: Utils.prepend(`/doc/${linkId}`), title, linkId }]; + const allLinks = [{ href: Doc.globalServerPath(linkId), title, linkId }]; const link = view.state.schema.mark(view.state.schema.marks.linkAnchor, { allLinks, location: "add:right", title, docref: true }); marks.splice(linkIndex === -1 ? 0 : linkIndex, 1, link); return node.mark(marks); @@ -1524,10 +1526,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp <div className="formattedTextBox-cont" onWheel={e => this.isContentActive() && e.stopPropagation()} style={{ - transform: `scale(${scale})`, - transformOrigin: "top left", - width: `${100 / scale}%`, - height: `${100 / scale}%`, + transform: this.props.dontScale ? undefined : `scale(${scale})`, + transformOrigin: this.props.dontScale ? undefined : "top left", + width: this.props.dontScale ? undefined : `${100 / scale}%`, + height: this.props.dontScale ? undefined : `${100 / scale}%`, // overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined, ...this.styleFromLayoutString(scale) // this converts any expressions in the format string to style props. e.g., <FormattedTextBox height='{this._headerHeight}px' > }}> diff --git a/src/client/views/nodes/formattedText/RichTextMenu.scss b/src/client/views/nodes/formattedText/RichTextMenu.scss index 1d24d6833..c94e93541 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.scss +++ b/src/client/views/nodes/formattedText/RichTextMenu.scss @@ -1,4 +1,4 @@ -@import "../../globalCssVariables"; +@import "../../global/globalCssVariables"; .button-dropdown-wrapper { position: relative; @@ -24,7 +24,7 @@ top: 35px; left: 0; background-color: #323232; - color: $light-color-secondary; + color: $light-gray; border: 1px solid #4d4d4d; border-radius: 0 6px 6px 6px; box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 59b2d3753..82ad2b7db 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -821,8 +821,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { if (link) { const href = link.attrs.allAnchors.length > 0 ? link.attrs.allAnchors[0].href : undefined; if (href) { - if (href.indexOf(Utils.prepend("/doc/")) === 0) { - const linkclicked = href.replace(Utils.prepend("/doc/"), "").split("?")[0]; + if (href.indexOf(Doc.localServerPath()) === 0) { + const linkclicked = href.replace(Doc.localServerPath(), "").split("?")[0]; if (linkclicked) { const linkDoc = await DocServer.GetRefField(linkclicked); if (linkDoc instanceof Doc) { @@ -852,6 +852,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { @undoBatch makeLinkToURL = (target: string, lcoation: string) => { ((this.view as any)?.TextView as FormattedTextBox).makeLinkAnchor(undefined, "onRadd:rightight", target, target); + console.log((this.view as any)?.TextView); } @undoBatch @@ -863,8 +864,8 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { const allAnchors = linkAnchor.attrs.allAnchors.slice(); this.TextView.RemoveAnchorFromSelection(allAnchors); // bcz: Argh ... this will remove the link from the document even it's anchored somewhere else in the text which happens if only part of the anchor text was selected. - allAnchors.filter((aref: any) => aref?.href.indexOf(Utils.prepend("/doc/")) === 0).forEach((aref: any) => { - const anchorId = aref.href.replace(Utils.prepend("/doc/"), "").split("?")[0]; + allAnchors.filter((aref: any) => aref?.href.indexOf(Doc.localServerPath()) === 0).forEach((aref: any) => { + const anchorId = aref.href.replace(Doc.localServerPath(), "").split("?")[0]; anchorId && DocServer.GetRefField(anchorId).then(linkDoc => LinkManager.Instance.deleteLink(linkDoc as Doc)); }); } @@ -963,7 +964,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { this.createHighlighterButton(), this.createLinkButton(), this.createBrushButton(), - <div className="richTextMenu-divider" key="divider 2" />, + <div className="collectionMenu-divider" key="divider 2" />, this.createButton("align-left", "Align Left", this.activeAlignment === "left", this.alignLeft), this.createButton("align-center", "Align Center", this.activeAlignment === "center", this.alignCenter), this.createButton("align-right", "Align Right", this.activeAlignment === "right", this.alignRight), @@ -976,7 +977,7 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { const row2 = <div className="antimodeMenu-row row-2" key="row2"> {this.collapsed ? this.getDragger() : (null)} <div key="row 2" style={{ display: this.collapsed ? "none" : undefined }}> - <div className="richTextMenu-divider" key="divider 3" /> + <div className="collectionMenu-divider" key="divider 3" /> {[this.createMarksDropdown(this.activeFontSize, this.fontSizeOptions, "font size", action((val: string) => { this.activeFontSize = val; SelectionManager.Views().map(dv => dv.props.Document._fontSize = val); @@ -985,12 +986,12 @@ export class RichTextMenu extends AntimodeMenu<AntimodeMenuProps> { this.activeFontFamily = val; SelectionManager.Views().map(dv => dv.props.Document._fontFamily = val); })), - <div className="richTextMenu-divider" key="divider 4" />, + <div className="collectionMenu-divider" key="divider 4" />, this.createNodesDropdown(this.activeListType, this.listTypeOptions, "list type", () => ({})), this.createButton("sort-amount-down", "Summarize", undefined, this.insertSummarizer), this.createButton("quote-left", "Blockquote", undefined, this.insertBlockquote), - this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule), - <div className="richTextMenu-divider" key="divider 5" />,]} + this.createButton("minus", "Horizontal Rule", undefined, this.insertHorizontalRule) + ]} </div> {/* <div key="collapser"> {<div key="collapser"> diff --git a/src/client/views/nodes/formattedText/TooltipTextMenu.scss b/src/client/views/nodes/formattedText/TooltipTextMenu.scss index 0e4b752ac..8c4d77da9 100644 --- a/src/client/views/nodes/formattedText/TooltipTextMenu.scss +++ b/src/client/views/nodes/formattedText/TooltipTextMenu.scss @@ -1,4 +1,4 @@ -@import "../views/globalCssVariables"; +@import "../views/global/globalCssVariables"; .ProseMirror-menu-dropdown-wrap { display: inline-block; position: relative; @@ -50,7 +50,7 @@ padding: 3px; &:hover { - background-color: $light-color-secondary; + background-color: $light-gray; } } } @@ -294,9 +294,9 @@ top: 31px; background-color: #323232; border: 1px solid #4d4d4d; - color: $light-color-secondary; + color: $light-gray; // border: none; - // border: 1px solid $light-color-secondary; + // border: 1px solid $light-gray; border-radius: 0 6px 6px 6px; padding: 3px; box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.25); @@ -323,7 +323,7 @@ } .separated-button { - border-top: 1px solid $light-color-secondary; + border-top: 1px solid $light-gray; padding-top: 6px; } diff --git a/src/client/views/nodes/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss index 1ba86232b..06932d145 100644 --- a/src/client/views/nodes/PresBox.scss +++ b/src/client/views/nodes/trails/PresBox.scss @@ -1,7 +1,4 @@ -$light-blue: #AEDDF8; -$dark-blue: #5B9FDD; -$light-background: #ececec; -$dark-grey: #656565; +@import "../../global/globalCssVariables"; .presBox-cont { cursor: auto; @@ -10,7 +7,6 @@ $dark-grey: #656565; pointer-events: inherit; z-index: 2; font-family: Roboto; - box-shadow: #AAAAAA .2vw .2vw .4vw; width: 100%; min-width: 20px; height: 100%; @@ -47,8 +43,8 @@ $dark-grey: #656565; align-items: center; height: 30px; width: 100%; - color: white; - background-color: #323232; + color: $white; + background-color: $dark-gray; .toolbar-button { cursor: pointer; @@ -110,7 +106,7 @@ $dark-grey: #656565; } .toolbar-divider { - border-left: solid #ffffff70 0.5px; + border-left: solid $medium-gray 0.5px; height: 20px; } } @@ -118,7 +114,7 @@ $dark-grey: #656565; .dropdown { font-size: 10; margin-left: 5px; - color: darkgrey; + color: $medium-gray; transition: 0.5s ease; } @@ -174,7 +170,7 @@ $dark-grey: #656565; .ribbon-colorBox { cursor: pointer; - border: solid 1px black; + border: solid 1px $black; display: flex; margin-left: 5px; margin-top: 5px; @@ -191,9 +187,9 @@ $dark-grey: #656565; font-size: 11; font-weight: 200; height: 20; - background-color: #ececec; - color: black; - border: solid 1px black; + background-color: $white; + color: $black; + border: solid 1px $black; display: flex; margin-left: 5px; margin-top: 5px; @@ -220,11 +216,11 @@ $dark-grey: #656565; align-items: center; height: 100%; width: 100%; - background: black; + background: $black; } .ribbon-propertyUpDownItem:hover { - background: darkgrey; + background: $medium-gray; transform: scale(1.05); } } @@ -239,7 +235,7 @@ $dark-grey: #656565; .multiThumb-slider { display: grid; - background-color: $light-background; + background-color: $white; height: 10px; border-radius: 10px; overflow: hidden; @@ -257,8 +253,8 @@ $dark-grey: #656565; -webkit-appearance: none; height: 10px; cursor: ew-resize; - background: $dark-blue; - box-shadow: -100vw 0 0 100vw $light-background; + background: $medium-blue; + box-shadow: -100vw 0 0 100vw $white; } .toolbar-slider.end::-webkit-slider-thumb { @@ -267,7 +263,7 @@ $dark-grey: #656565; -webkit-appearance: none; height: 10px; cursor: ew-resize; - background: $dark-blue; + background: $medium-blue; box-shadow: -100vw 0 0 100vw $light-blue; } } @@ -282,7 +278,7 @@ $dark-grey: #656565; height: 10px; border-radius: 10px; -webkit-appearance: none; - background-color: $light-background; + background-color: $white; } .toolbar-slider:focus { @@ -301,7 +297,7 @@ $dark-grey: #656565; -webkit-appearance: none; height: 10px; cursor: ew-resize; - background: $dark-blue; + background: $medium-blue; box-shadow: -100vw 0 0 100vw $light-blue; } @@ -318,7 +314,7 @@ $dark-grey: #656565; width: 15px; min-width: 15px; cursor: pointer; - background: $light-background; + background: $white; } .presBox-checkbox:focus { @@ -326,7 +322,7 @@ $dark-grey: #656565; } .presBox-checkbox:hover { - background: #c0c0c0; + background: $light-gray; } .presBox-checkbox:checked { @@ -381,9 +377,9 @@ $dark-grey: #656565; text-align: center; font-size: 16; width: 90%; - color: black; + color: $black; transform: translate(5%, 0px); - border-bottom: solid 2px darkgrey; + border-bottom: solid 2px $medium-gray; } @@ -396,8 +392,8 @@ $dark-grey: #656565; justify-self: left; margin-top: 5px; padding-left: 10px; - background-color: $light-background; - border: solid 1px black; + background-color: $white; + border: solid 1px $black; min-width: 80px; max-width: 200px; width: 100%; @@ -416,7 +412,7 @@ $dark-grey: #656565; } .ribbon-frameSelector { - border: black solid 1px; + border: $black solid 1px; width: 60px; height: 20px; margin-top: 5px; @@ -433,12 +429,12 @@ $dark-grey: #656565; cursor: pointer; position: relative; height: 100%; - background: $light-background; + background: $white; display: flex; align-items: center; justify-content: center; text-align: center; - color: black; + color: $black; } .numKeyframe { @@ -446,7 +442,7 @@ $dark-grey: #656565; font-size: 10; font-weight: 600; position: relative; - color: black; + color: $black; display: flex; width: 100%; height: 100%; @@ -489,7 +485,7 @@ $dark-grey: #656565; padding-left: 10; padding-right: 10; border-radius: 10px; - background-color: #979797; + background-color: $medium-gray; } .ribbon-final-button:hover { @@ -508,13 +504,13 @@ $dark-grey: #656565; align-items: center; margin-bottom: 5px; height: 25px; - color: lightgrey; + color: $light-gray; width: 100%; max-width: 120; padding-left: 10; padding-right: 10; border-radius: 10px; - background-color: black; + background-color: $black; } .ribbon-final-button-hidden:hover { @@ -525,15 +521,15 @@ $dark-grey: #656565; .ribbon-frameList { width: calc(100% - 5px); height: 50px; - background-color: #ececec; - border: 1px solid #9f9f9f; + background-color: $white; + border: 1px solid $medium-gray; grid-template-rows: max-content; .frameList-header { display: grid; width: 100%; height: 20px; - background-color: #9f9f9f; + background-color: $medium-gray; .frameList-headerButtons { display: flex; @@ -588,7 +584,7 @@ $dark-grey: #656565; font-size: 10.5; font-weight: 300; height: 20; - background-color: #979797; + background-color: $medium-gray; color: white; display: flex; margin-top: 5px; @@ -607,8 +603,8 @@ $dark-grey: #656565; transition: all 0.4s; font-weight: 400; opacity: 1; - color: white; - background-color: black; + color: $white; + background-color: $black; } .ribbon-toggle { @@ -616,10 +612,10 @@ $dark-grey: #656565; font-size: 10.5; font-weight: 200; height: 20; - background-color: $light-background; + background-color: $white; border: solid 1px rgba(0, 0, 0, 0.5); display: flex; - color: black; + color: $black; margin-top: 5px; margin-bottom: 5px; border-radius: 5px; @@ -660,13 +656,13 @@ $dark-grey: #656565; position: relative; font-size: 13; padding-bottom: 10px; - border-bottom: solid 1px $dark-grey; + border-bottom: solid 1px $dark-gray; .presBox-dropdown:hover { - border: solid 1px $dark-blue; + border: solid 1px $medium-blue; .presBox-dropdownIcon { - color: $dark-blue; + color: $medium-blue; } } @@ -675,12 +671,12 @@ $dark-grey: #656565; display: grid; grid-template-columns: auto 20%; position: relative; - border: solid 1px black; - background-color: $light-background; + border: solid 1px $black; + background-color: $light-gray; border-radius: 5px; font-size: 10; height: 25; - color: black; + color: $black; padding-left: 5px; align-items: center; margin-top: 5px; @@ -744,7 +740,7 @@ $dark-grey: #656565; height: 100px; padding-top: 5px; padding-bottom: 5px; - border: solid 1px black; + border: solid 1px $black; // overflow: auto; ::-webkit-scrollbar { @@ -794,7 +790,7 @@ $dark-grey: #656565; cursor: pointer; position: relative; text-align: center; - border-left: solid 1px darkgrey; + border-left: solid 1px $medium-gray; width: 20%; height: 100%; display: flex; @@ -825,7 +821,7 @@ $dark-grey: #656565; box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.8); z-index: 200; background-color: white; - color: black; + color: $black; position: absolute; overflow: hidden; } @@ -841,12 +837,12 @@ $dark-grey: #656565; align-items: center; justify-content: center; transform: translate(0px, -1px); - background-color: $light-background; + background-color: $white; width: 40px; height: 15px; align-self: center; justify-self: center; - border: solid 1px black; + border: solid 1px $black; border-top: 0px; border-bottom-right-radius: 7px; border-bottom-left-radius: 7px; @@ -855,15 +851,15 @@ $dark-grey: #656565; .layout-container { padding: 5px; display: grid; - background-color: $light-background; + background-color: $white; grid-template-columns: repeat(auto-fit, minmax(90px, 100px)); width: 100%; - border: solid 1px black; + border: solid 1px $black; min-width: 100px; overflow: hidden; .layout:hover { - border: solid 2px #5c9edd; + border: solid 2px $medium-blue; } .layout { @@ -878,7 +874,7 @@ $dark-grey: #656565; width: 90px; overflow: hidden; background-color: white; - border: solid darkgrey 1px; + border: solid $medium-gray 1px; display: grid; grid-template-rows: auto; align-items: center; @@ -893,7 +889,7 @@ $dark-grey: #656565; height: 13; font-size: 12; display: flex; - background-color: #f1efec; + background-color: $white; } .subtitle { @@ -906,7 +902,7 @@ $dark-grey: #656565; height: 13; font-size: 9; display: flex; - background-color: #f1efec; + background-color: $white; } .content { @@ -919,7 +915,7 @@ $dark-grey: #656565; height: 13; font-size: 10; display: flex; - background-color: #f1efec; + background-color: $white; height: 33; text-align: left; font-size: 8px; @@ -930,7 +926,7 @@ $dark-grey: #656565; .presBox-buttons { position: relative; width: 100%; - background: gray; + background: $medium-gray; min-height: 35px; padding-top: 5px; padding-bottom: 5px; @@ -960,8 +956,8 @@ $dark-grey: #656565; select { - background: #323232; - color: white; + background: $dark-gray; + color: $white; } .presBox-button { @@ -975,8 +971,8 @@ $dark-grey: #656565; text-align: center; letter-spacing: normal; width: inherit; - background: #323232; - color: white; + background: $dark-gray; + color: $white; } .presBox-button.active { @@ -984,7 +980,7 @@ $dark-grey: #656565; } .presBox-button.active:hover { - background-color: #233163; + background-color: $medium-blue; } .presBox-button.edit { @@ -1053,8 +1049,8 @@ $dark-grey: #656565; font-size: 100; display: flex; align-items: center; - background: #323232; - color: white; + background: $dark-gray; + color: $white; } .presBox-viewPicker { @@ -1086,7 +1082,7 @@ $dark-grey: #656565; top: 10; opacity: 0.1; transition: all 0.4s; - color: white; + color: $white; } .miniPres:hover { @@ -1094,8 +1090,8 @@ $dark-grey: #656565; } .presPanelOverlay { - background-color: #323232; - color: white; + background-color: $dark-gray; + color: $white; border-radius: 5px; grid-template-rows: 100%; height: 25; @@ -1129,7 +1125,7 @@ $dark-grey: #656565; .presPanel-divider { width: 0.5px; height: 80%; - border-right: solid 1px #5a5a5a; + border-right: solid 1px $medium-gray; } .presPanel-button-frame { @@ -1161,12 +1157,12 @@ $dark-grey: #656565; } .presPanel-button:hover { - background-color: #5a5a5a; + background-color: $medium-gray; transform: scale(1.2); } .presPanel-button-text:hover { - background-color: #5a5a5a; + background-color: $medium-gray; } diff --git a/src/client/views/nodes/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index f3fb6ff17..5cb9866f8 100644 --- a/src/client/views/nodes/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -5,67 +5,33 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio import { observer } from "mobx-react"; import { ColorState, SketchPicker } from "react-color"; import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal'; -import { Doc, DocListCast, DocListCastAsync, FieldResult } from "../../../fields/Doc"; -import { documentSchema } from "../../../fields/documentSchemas"; -import { InkTool } from "../../../fields/InkField"; -import { List } from "../../../fields/List"; -import { PrefetchProxy } from "../../../fields/Proxy"; -import { listSpec, makeInterface } from "../../../fields/Schema"; -import { ScriptField } from "../../../fields/ScriptField"; -import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types"; -import { returnFalse, returnOne, returnTrue, emptyFunction } from '../../../Utils'; -import { Docs } from "../../documents/Documents"; -import { DocumentType } from "../../documents/DocumentTypes"; -import { CurrentUserUtils } from "../../util/CurrentUserUtils"; -import { DocumentManager } from "../../util/DocumentManager"; -import { Scripting } from "../../util/Scripting"; -import { SelectionManager } from "../../util/SelectionManager"; -import { undoBatch, UndoManager } from "../../util/UndoManager"; -import { CollectionDockingView } from "../collections/CollectionDockingView"; -import { CollectionView, CollectionViewType } from "../collections/CollectionView"; -import { TabDocView } from "../collections/TabDocView"; -import { ViewBoxBaseComponent } from "../DocComponent"; -import { CollectionFreeFormDocumentView } from "./CollectionFreeFormDocumentView"; -import { FieldView, FieldViewProps } from './FieldView'; +import { Doc, DocListCast, DocListCastAsync, FieldResult } from "../../../../fields/Doc"; +import { documentSchema } from "../../../../fields/documentSchemas"; +import { InkTool } from "../../../../fields/InkField"; +import { List } from "../../../../fields/List"; +import { PrefetchProxy } from "../../../../fields/Proxy"; +import { listSpec, makeInterface } from "../../../../fields/Schema"; +import { ScriptField } from "../../../../fields/ScriptField"; +import { BoolCast, Cast, NumCast, StrCast } from "../../../../fields/Types"; +import { emptyFunction, returnFalse, returnOne, returnTrue } from '../../../../Utils'; +import { Docs } from "../../../documents/Documents"; +import { DocumentType } from "../../../documents/DocumentTypes"; +import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; +import { DocumentManager } from "../../../util/DocumentManager"; +import { Scripting } from "../../../util/Scripting"; +import { SelectionManager } from "../../../util/SelectionManager"; +import { undoBatch, UndoManager } from "../../../util/UndoManager"; +import { CollectionDockingView } from "../../collections/CollectionDockingView"; +import { CollectionView, CollectionViewType } from "../../collections/CollectionView"; +import { TabDocView } from "../../collections/TabDocView"; +import { ViewBoxBaseComponent } from "../../DocComponent"; +import { Colors } from "../../global/globalEnums"; +import { LightboxView } from "../../LightboxView"; +import { CollectionFreeFormDocumentView } from "../CollectionFreeFormDocumentView"; +import { FieldView, FieldViewProps } from '../FieldView'; import "./PresBox.scss"; import Color = require("color"); -import { LightboxView } from "../LightboxView"; - -export enum PresMovement { - Zoom = "zoom", - Pan = "pan", - Jump = "jump", - None = "none", -} - -export enum PresEffect { - Zoom = "Zoom", - Lightspeed = "Lightspeed", - Fade = "Fade in", - Flip = "Flip", - Rotate = "Rotate", - Bounce = "Bounce", - Roll = "Roll", - None = "None", - Left = "left", - Right = "right", - Center = "center", - Top = "top", - Bottom = "bottom" -} - -enum PresStatus { - Autoplay = "auto", - Manual = "manual", - Edit = "edit" -} - -export enum PresColor { - LightBlue = "#AEDDF8", - DarkBlue = "#5B9FDD", - LightBackground = "#ececec", - SlideBackground = "#d5dce2", -} +import { PresEffect, PresStatus, PresMovement } from "./PresEnums"; export class PinProps { audioRange?: boolean; @@ -1198,9 +1164,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> {this.scrollable ? "Scroll to pinned view" : !isPinWithView ? "No movement" : "Pan & Zoom to pinned view"} </div> : - <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = !this.openMovementDropdown; })} style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${PresColor.DarkBlue}` : 'solid 1px black' }}> + <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openMovementDropdown = !this.openMovementDropdown; })} style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}> {this.setMovementName(activeItem.presMovement, activeItem)} - <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? PresColor.DarkBlue : 'black' }} icon={"angle-down"} /> + <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openMovementDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={"angle-down"} /> <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} onPointerDown={e => e.stopPropagation()} style={{ display: this.openMovementDropdown ? "grid" : "none" }}> <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.None ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None)}>None</div> <div className={`presBox-dropdownOption ${activeItem.presMovement === PresMovement.Zoom ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom)}>Pan {"&"} Zoom</div> @@ -1245,7 +1211,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="ribbon-doubleButton"> {isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide before presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideBefore ? "active" : ""}`} onClick={() => this.updateHideBefore(activeItem)}>Hide before</div></Tooltip>} {isPresCollection ? (null) : <Tooltip title={<><div className="dash-tooltip">{"Hide after presented"}</div></>}><div className={`ribbon-toggle ${activeItem.presHideAfter ? "active" : ""}`} onClick={() => this.updateHideAfter(activeItem)}>Hide after</div></Tooltip>} - <Tooltip title={<><div className="dash-tooltip">{"Open in lightbox view"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? PresColor.LightBlue : "" }} onClick={() => this.updateOpenDoc(activeItem)}>Lightbox</div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Open in lightbox view"}</div></>}><div className="ribbon-toggle" style={{ backgroundColor: activeItem.openDocument ? Colors.LIGHT_BLUE : "" }} onClick={() => this.updateOpenDoc(activeItem)}>Lightbox</div></Tooltip> </div> {(type === DocumentType.AUDIO || type === DocumentType.VID) ? (null) : <> <div className="ribbon-doubleButton" > @@ -1280,9 +1246,9 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> </div> {isPresCollection ? (null) : <div className="ribbon-box"> Effects - <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openEffectDropdown = !this.openEffectDropdown; })} style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? `solid 2px ${PresColor.DarkBlue}` : 'solid 1px black' }}> + <div className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); this.openEffectDropdown = !this.openEffectDropdown; })} style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}> {effect} - <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openEffectDropdown ? PresColor.DarkBlue : 'black' }} icon={"angle-down"} /> + <FontAwesomeIcon className='presBox-dropdownIcon' style={{ gridColumn: 2, color: this.openEffectDropdown ? Colors.MEDIUM_BLUE : 'black' }} icon={"angle-down"} /> <div className={'presBox-dropdownOptions'} id={'presBoxMovementDropdown'} style={{ display: this.openEffectDropdown ? "grid" : "none" }} onPointerDown={e => e.stopPropagation()}> <div className={`presBox-dropdownOption ${targetDoc.presEffect === PresEffect.None || !targetDoc.presEffect ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.None)}>None</div> <div className={`presBox-dropdownOption ${targetDoc.presEffect === PresEffect.Fade ? "active" : ""}`} onPointerDown={e => e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Fade)}>Fade In</div> @@ -1299,11 +1265,11 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> </div> </div> <div className="effectDirection" style={{ display: effect === 'None' ? "none" : "grid", width: 40 }}> - <Tooltip title={<><div className="dash-tooltip">{"Enter from left"}</div></>}><div style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Left ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Left)}><FontAwesomeIcon icon={"angle-right"} /></div></Tooltip> - <Tooltip title={<><div className="dash-tooltip">{"Enter from right"}</div></>}><div style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Right ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Right)}><FontAwesomeIcon icon={"angle-left"} /></div></Tooltip> - <Tooltip title={<><div className="dash-tooltip">{"Enter from top"}</div></>}><div style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Top ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Top)}><FontAwesomeIcon icon={"angle-down"} /></div></Tooltip> - <Tooltip title={<><div className="dash-tooltip">{"Enter from bottom"}</div></>}><div style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Bottom ? PresColor.LightBlue : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Bottom)}><FontAwesomeIcon icon={"angle-up"} /></div></Tooltip> - <Tooltip title={<><div className="dash-tooltip">{"Enter from center"}</div></>}><div style={{ gridColumn: 2, gridRow: 2, width: 10, height: 10, alignSelf: 'center', justifySelf: 'center', border: targetDoc.presEffectDirection === PresEffect.Center || !targetDoc.presEffectDirection ? `solid 2px ${PresColor.LightBlue}` : "solid 2px black", borderRadius: "100%", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Center)}></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Enter from left"}</div></>}><div style={{ gridColumn: 1, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Left ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Left)}><FontAwesomeIcon icon={"angle-right"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Enter from right"}</div></>}><div style={{ gridColumn: 3, gridRow: 2, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Right ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Right)}><FontAwesomeIcon icon={"angle-left"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Enter from top"}</div></>}><div style={{ gridColumn: 2, gridRow: 1, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Top ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Top)}><FontAwesomeIcon icon={"angle-down"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Enter from bottom"}</div></>}><div style={{ gridColumn: 2, gridRow: 3, justifySelf: 'center', color: targetDoc.presEffectDirection === PresEffect.Bottom ? Colors.LIGHT_BLUE : "black", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Bottom)}><FontAwesomeIcon icon={"angle-up"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Enter from center"}</div></>}><div style={{ gridColumn: 2, gridRow: 2, width: 10, height: 10, alignSelf: 'center', justifySelf: 'center', border: targetDoc.presEffectDirection === PresEffect.Center || !targetDoc.presEffectDirection ? `solid 2px ${Colors.LIGHT_BLUE}` : "solid 2px black", borderRadius: "100%", cursor: "pointer" }} onClick={() => this.updateEffectDirection(PresEffect.Center)}></div></Tooltip> </div> </div>} <div className="ribbon-final-box"> @@ -1356,7 +1322,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <> {this.panable || this.scrollable || this.targetDoc.type === DocumentType.COMPARISON ? 'Pinned view' : (null)} <div className="ribbon-doubleButton"> - <Tooltip title={<><div className="dash-tooltip">{activeItem.presPinView ? "Turn off pin with view" : "Turn on pin with view"}</div></>}><div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? PresColor.LightBlue : "" }} + <Tooltip title={<><div className="dash-tooltip">{activeItem.presPinView ? "Turn off pin with view" : "Turn on pin with view"}</div></>}><div className="ribbon-toggle" style={{ width: 20, padding: 0, backgroundColor: activeItem.presPinView ? Colors.LIGHT_BLUE : "" }} onClick={() => { activeItem.presPinView = !activeItem.presPinView; targetDoc.presPinView = activeItem.presPinView; @@ -1496,7 +1462,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="slider-text" style={{ fontWeight: 500 }}> Start time (s) </div> - <div id={"startTime"} className="slider-number" style={{ backgroundColor: PresColor.LightBackground }}> + <div id={"startTime"} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}> <input className="presBox-input" style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }} type="number" value={NumCast(activeItem.presStartTime)} @@ -1508,7 +1474,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="slider-text" style={{ fontWeight: 500 }}> Duration (s) </div> - <div className="slider-number" style={{ backgroundColor: PresColor.LightBlue }}> + <div className="slider-number" style={{ backgroundColor: Colors.LIGHT_BLUE }}> {Math.round((NumCast(activeItem.presEndTime) - NumCast(activeItem.presStartTime)) * 10) / 10} </div> </div> @@ -1516,7 +1482,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="slider-text" style={{ fontWeight: 500 }}> End time (s) </div> - <div id={"endTime"} className="slider-number" style={{ backgroundColor: PresColor.LightBackground }}> + <div id={"endTime"} className="slider-number" style={{ backgroundColor: Colors.LIGHT_GRAY }}> <input className="presBox-input" style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }} type="number" value={NumCast(activeItem.presEndTime)} @@ -1534,16 +1500,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> this._batch = UndoManager.StartBatch("presEndTime"); const endBlock = document.getElementById("endTime"); if (endBlock) { - endBlock.style.color = PresColor.LightBackground; - endBlock.style.backgroundColor = PresColor.DarkBlue; + endBlock.style.color = Colors.LIGHT_GRAY; + endBlock.style.backgroundColor = Colors.MEDIUM_BLUE; } }} onPointerUp={() => { this._batch?.end(); const endBlock = document.getElementById("endTime"); if (endBlock) { - endBlock.style.color = "black"; - endBlock.style.backgroundColor = PresColor.LightBackground; + endBlock.style.color = Colors.BLACK; + endBlock.style.backgroundColor = Colors.LIGHT_GRAY; } }} onChange={(e: React.ChangeEvent<HTMLInputElement>) => { @@ -1558,16 +1524,16 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> this._batch = UndoManager.StartBatch("presStartTime"); const startBlock = document.getElementById("startTime"); if (startBlock) { - startBlock.style.color = PresColor.LightBackground; - startBlock.style.backgroundColor = PresColor.DarkBlue; + startBlock.style.color = Colors.LIGHT_GRAY; + startBlock.style.backgroundColor = Colors.MEDIUM_BLUE; } }} onPointerUp={() => { this._batch?.end(); const startBlock = document.getElementById("startTime"); if (startBlock) { - startBlock.style.color = "black"; - startBlock.style.backgroundColor = PresColor.LightBackground; + startBlock.style.color = Colors.BLACK; + startBlock.style.backgroundColor = Colors.LIGHT_GRAY; } }} onChange={(e: React.ChangeEvent<HTMLInputElement>) => { @@ -1651,15 +1617,15 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div> <div className={'presBox-toolbar-dropdown'} style={{ display: this.newDocumentTools && this.layoutDoc.presStatus === "edit" ? "inline-flex" : "none" }} onClick={e => e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> <div className="layout-container" style={{ height: 'max-content' }}> - <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'blank'; this.createNewSlide(this.layout); })} /> - <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'title'; this.createNewSlide(this.layout); })}> + <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'blank'; this.createNewSlide(this.layout); })} /> + <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'title'; this.createNewSlide(this.layout); })}> <div className="title">Title</div> <div className="subtitle">Subtitle</div> </div> - <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'header'; this.createNewSlide(this.layout); })}> + <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'header'; this.createNewSlide(this.layout); })}> <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>Section header</div> </div> - <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => { this.layout = 'content'; this.createNewSlide(this.layout); })}> + <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => { this.layout = 'content'; this.createNewSlide(this.layout); })}> <div className="title" style={{ alignSelf: 'center' }}>Title</div> <div className="content">Text goes here</div> </div> @@ -1691,26 +1657,26 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="ribbon-box"> Choose type: <div className="ribbon-doubleButton"> - <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "" : PresColor.LightBlue }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div> - <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? PresColor.LightBlue : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div> + <div title="Text" className={'ribbon-toggle'} style={{ background: this.addFreeform ? "" : Colors.LIGHT_BLUE }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Text</div> + <div title="Freeform" className={'ribbon-toggle'} style={{ background: this.addFreeform ? Colors.LIGHT_BLUE : "" }} onClick={action(() => this.addFreeform = !this.addFreeform)}>Freeform</div> </div> </div> <div className="ribbon-box" style={{ display: this.addFreeform ? "grid" : "none" }}> Preset layouts: <div className="layout-container" style={{ height: this.openLayouts ? 'max-content' : '75px' }}> - <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'blank')} /> - <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'title')}> + <div className="layout" style={{ border: this.layout === 'blank' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'blank')} /> + <div className="layout" style={{ border: this.layout === 'title' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'title')}> <div className="title">Title</div> <div className="subtitle">Subtitle</div> </div> - <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'header')}> + <div className="layout" style={{ border: this.layout === 'header' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'header')}> <div className="title" style={{ alignSelf: 'center', fontSize: 10 }}>Section header</div> </div> - <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'content')}> + <div className="layout" style={{ border: this.layout === 'content' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'content')}> <div className="title" style={{ alignSelf: 'center' }}>Title</div> <div className="content">Text goes here</div> </div> - <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${PresColor.DarkBlue}` : '' }} onClick={action(() => this.layout = 'twoColumns')}> + <div className="layout" style={{ border: this.layout === 'twoColumns' ? `solid 2px ${Colors.MEDIUM_BLUE}` : '' }} onClick={action(() => this.layout = 'twoColumns')}> <div className="title" style={{ alignSelf: 'center', gridColumn: '1/3' }}>Title</div> <div className="content" style={{ gridColumn: 1, gridRow: 2 }}>Column one text</div> <div className="content" style={{ gridColumn: 2, gridRow: 2 }}>Column two text</div> @@ -1869,8 +1835,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="ribbon-box"> {this.stringType} selected <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}> - <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? PresColor.LightBlue : "" }} onClick={this.progressivizeChild}>Contents</div> - <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? PresColor.LightBlue : "" }} onClick={this.editProgressivize}>Edit</div> + <div className="ribbon-toggle" style={{ backgroundColor: activeItem.presProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeChild}>Contents</div> + <div className="ribbon-toggle" style={{ opacity: activeItem.presProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editProgressivize}>Edit</div> </div> <div className="ribbon-doubleButton" style={{ display: activeItem.presProgressivize ? "inline-flex" : "none" }}> <div className="presBox-subheading">Active text color</div> @@ -1885,12 +1851,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> </div> {this.viewedColorPicker} <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: (targetDoc.type === DocumentType.COL && targetDoc._viewType === 'freeform') || targetDoc.type === DocumentType.IMG ? "inline-flex" : "none" }}> - <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? PresColor.LightBlue : "" }} onClick={this.progressivizeZoom}>Zoom</div> - <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? PresColor.LightBlue : "" }} onClick={this.editZoomProgressivize}>Edit</div> + <div className="ribbon-toggle" style={{ backgroundColor: activeItem.zoomProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeZoom}>Zoom</div> + <div className="ribbon-toggle" style={{ opacity: activeItem.zoomProgressivize ? 1 : 0.4, backgroundColor: activeItem.editZoomProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editZoomProgressivize}>Edit</div> </div> <div className="ribbon-doubleButton" style={{ borderTop: 'solid 1px darkgrey', display: targetDoc._viewType === "stacking" || targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF ? "inline-flex" : "none" }}> - <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? PresColor.LightBlue : "" }} onClick={this.progressivizeScroll}>Scroll</div> - <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? PresColor.LightBlue : "" }} onClick={this.editScrollProgressivize}>Edit</div> + <div className="ribbon-toggle" style={{ backgroundColor: activeItem.scrollProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.progressivizeScroll}>Scroll</div> + <div className="ribbon-toggle" style={{ opacity: activeItem.scrollProgressivize ? 1 : 0.4, backgroundColor: targetDoc.editScrollProgressivize ? Colors.LIGHT_BLUE : "" }} onClick={this.editScrollProgressivize}>Edit</div> </div> </div> <div className="ribbon-final-box"> @@ -1900,7 +1866,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div key="back" title="back frame" className="backKeyframe" onClick={e => { e.stopPropagation(); this.prevKeyframe(targetDoc, activeItem); }}> <FontAwesomeIcon icon={"caret-left"} size={"lg"} /> </div> - <div key="num" title="toggle view all" className="numKeyframe" style={{ color: targetDoc.keyFrameEditing ? "white" : "black", backgroundColor: targetDoc.keyFrameEditing ? PresColor.DarkBlue : PresColor.LightBlue }} + <div key="num" title="toggle view all" className="numKeyframe" style={{ color: targetDoc.keyFrameEditing ? "white" : "black", backgroundColor: targetDoc.keyFrameEditing ? Colors.MEDIUM_BLUE : Colors.LIGHT_BLUE }} onClick={action(() => targetDoc.keyFrameEditing = !targetDoc.keyFrameEditing)} > {NumCast(targetDoc._currentFrame)} </div> @@ -1914,7 +1880,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> {this.frameListHeader} {this.frameList} </div> - <div className="ribbon-toggle" style={{ height: 20, backgroundColor: PresColor.LightBlue }} onClick={() => console.log(" TODO: play frames")}>Play</div> + <div className="ribbon-toggle" style={{ height: 20, backgroundColor: Colors.LIGHT_BLUE }} onClick={() => console.log(" TODO: play frames")}>Play</div> </div> </div> </div> @@ -2130,7 +2096,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> tags.push(<div style={{ position: 'absolute', display: doc.displayMovement ? "block" : "none" }}>{this.checkMovementLists(doc, doc["x-indexed"], doc["y-indexed"])}</div>); } tags.push( - <div className="progressivizeButton" key={index} onPointerLeave={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? PresColor.LightBlue : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}> + <div className="progressivizeButton" key={index} onPointerLeave={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0; }} onPointerOver={() => { if (NumCast(targetDoc._currentFrame) < NumCast(doc.appearFrame)) doc.opacity = 0.5; }} onClick={e => { this.toggleDisplayMovement(doc); e.stopPropagation(); }} style={{ backgroundColor: doc.displayMovement ? Colors.LIGHT_BLUE : "#c8c8c8", top: NumCast(doc.y), left: NumCast(doc.x) }}> <div className="progressivizeButton-prev"><FontAwesomeIcon icon={"caret-left"} size={"lg"} onClick={e => { e.stopPropagation(); this.prevAppearFrame(doc, index); }} /></div> <div className="progressivizeButton-frame">{doc.appearFrame}</div> <div className="progressivizeButton-next"><FontAwesomeIcon icon={"caret-right"} size={"lg"} onClick={e => { e.stopPropagation(); this.nextAppearFrame(doc, index); }} /></div> @@ -2213,6 +2179,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; const presKeyEvents: boolean = (this.isPres && this._presKeyEventsActive && this.rootDoc === Doc.UserDoc().activePresentation); + const activeColor = Colors.LIGHT_BLUE; + const inactiveColor = Colors.WHITE; return (mode === CollectionViewType.Carousel3D) ? (null) : ( <div id="toolbarContainer" className={'presBox-toolbar'}> {/* <Tooltip title={<><div className="dash-tooltip">{"Add new slide"}</div></>}><div className={`toolbar-button ${this.newDocumentTools ? "active" : ""}`} onClick={action(() => this.newDocumentTools = !this.newDocumentTools)}> @@ -2220,7 +2188,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <FontAwesomeIcon className={`dropdown ${this.newDocumentTools ? "active" : ""}`} icon={"angle-down"} /> </div></Tooltip> */} <Tooltip title={<><div className="dash-tooltip">{"View paths"}</div></>}> - <div style={{ opacity: this.childDocs.length > 1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? PresColor.DarkBlue : 'white', width: isMini ? "100%" : undefined }} className={"toolbar-button"} onClick={this.childDocs.length > 1 && this.layoutDoc.presCollection ? this.viewPaths : undefined}> + <div style={{ opacity: this.childDocs.length > 1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? "100%" : undefined }} className={"toolbar-button"} onClick={this.childDocs.length > 1 && this.layoutDoc.presCollection ? this.viewPaths : undefined}> <FontAwesomeIcon icon={"exchange-alt"} /> </div> </Tooltip> @@ -2229,7 +2197,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="toolbar-divider" /> {/* <Tooltip title={<><div className="dash-tooltip">{this._expandBoolean ? "Minimize all" : "Expand all"}</div></>}> <div className={"toolbar-button"} - style={{ color: this._expandBoolean ? PresColors.DarkBlue : 'white' }} + style={{ color: this._expandBoolean ? Colors.MEDIUM_BLUE : 'white' }} onClick={this.toggleExpandMode}> <FontAwesomeIcon icon={"eye"} /> </div> @@ -2237,12 +2205,12 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> <div className="toolbar-divider" /> */} <Tooltip title={<><div className="dash-tooltip">{presKeyEvents ? "Keys are active" : "Keys are not active - click anywhere on the presentation trail to activate keys"}</div></>}> <div className="toolbar-button" style={{ cursor: presKeyEvents ? 'default' : 'pointer', position: 'absolute', right: 30, fontSize: 16 }}> - <FontAwesomeIcon className={"toolbar-thumbtack"} icon={"keyboard"} style={{ color: presKeyEvents ? PresColor.DarkBlue : 'white' }} /> + <FontAwesomeIcon className={"toolbar-thumbtack"} icon={"keyboard"} style={{ color: presKeyEvents ? activeColor : inactiveColor }} /> </div> </Tooltip> <Tooltip title={<><div className="dash-tooltip">{propTitle}</div></>}> <div className="toolbar-button" style={{ position: 'absolute', right: 4, fontSize: 16 }} onClick={this.toggleProperties}> - <FontAwesomeIcon className={"toolbar-thumbtack"} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? PresColor.DarkBlue : 'white' }} /> + <FontAwesomeIcon className={"toolbar-thumbtack"} icon={propIcon} style={{ color: CurrentUserUtils.propertiesWidth > 0 ? activeColor : inactiveColor }} /> </div> </Tooltip> </> @@ -2379,7 +2347,7 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0); // Case 1: There are still other frames and should go through all frames before going to next slide return (<div className="presPanelOverlay" style={{ display: this.layoutDoc.presStatus !== "edit" ? "inline-flex" : "none" }}> - <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? PresColor.DarkBlue : 'white' }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : 'white' }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip> <div className="presPanel-divider"></div> <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onClick={() => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-left"} /></div> <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === PresStatus.Autoplay ? "pause" : "play"} /></div></Tooltip> @@ -2418,8 +2386,8 @@ export class PresBox extends ViewBoxBaseComponent<FieldViewProps, PresBoxSchema> const presStart: boolean = !this.layoutDoc.presLoop && (this.itemIndex === 0); return CurrentUserUtils.OverlayDocs.includes(this.rootDoc) ? <div className="miniPres"> - <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + PresColor.DarkBlue : undefined }}> - <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? PresColor.DarkBlue : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip> + <div className="presPanelOverlay" style={{ display: "inline-flex", height: 30, background: '#323232', top: 0, zIndex: 3000000, boxShadow: presKeyEvents ? '0 0 0px 3px ' + Colors.MEDIUM_BLUE : undefined }}> + <Tooltip title={<><div className="dash-tooltip">{"Loop"}</div></>}><div className="presPanel-button" style={{ color: this.layoutDoc.presLoop ? Colors.MEDIUM_BLUE : undefined }} onClick={() => this.layoutDoc.presLoop = !this.layoutDoc.presLoop}><FontAwesomeIcon icon={"redo-alt"} /></div></Tooltip> <div className="presPanel-divider"></div> <div className="presPanel-button" style={{ opacity: presStart ? 0.4 : 1 }} onClick={() => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } }}><FontAwesomeIcon icon={"arrow-left"} /></div> <Tooltip title={<><div className="dash-tooltip">{this.layoutDoc.presStatus === PresStatus.Autoplay ? "Pause" : "Autoplay"}</div></>}><div className="presPanel-button" onClick={this.startOrPause}><FontAwesomeIcon icon={this.layoutDoc.presStatus === "auto" ? "pause" : "play"} /></div></Tooltip> diff --git a/src/client/views/nodes/trails/PresElementBox.scss b/src/client/views/nodes/trails/PresElementBox.scss new file mode 100644 index 000000000..1ad4b820e --- /dev/null +++ b/src/client/views/nodes/trails/PresElementBox.scss @@ -0,0 +1,235 @@ +$light-blue: #AEDDF8; +$dark-blue: #5B9FDD; +$light-background: #ececec; +$slide-background: #d5dce2; +$slide-active: #5B9FDD; + +.presItem-container { + cursor: grab; + display: grid; + grid-template-columns: 20px auto; + font-family: Roboto; + letter-spacing: normal; + position: relative; + pointer-events: all; + width: 100%; + height: 100%; + font-weight: 400; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + align-items: center; + + .presItem-number { + margin-top: 3.5px; + font-size: 12px; + font-weight: 700; + text-align: center; + justify-self: center; + align-self: flex-start; + position: relative; + display: inline-block; + overflow: hidden; + } + +} + +.presItem-slide { + position: relative; + background-color: #d5dce2; + border-radius: 5px; + height: calc(100% - 7px); + width: calc(100% - 15px); + display: grid; + grid-template-rows: 16px 10px auto; + grid-template-columns: max-content max-content max-content max-content auto; + + .presItem-name { + min-width: 20px; + z-index: 300; + top: 2px; + align-self: center; + font-size: 11px; + font-family: Roboto; + font-weight: 500; + position: relative; + padding-left: 10px; + padding-right: 10px; + letter-spacing: normal; + width: max-content; + text-overflow: ellipsis; + overflow: hidden; + white-space: pre; + } + + .presItem-docName { + min-width: 20px; + z-index: 300; + align-self: center; + font-size: 9px; + font-family: Roboto; + font-weight: 300; + position: relative; + padding-left: 10px; + padding-right: 10px; + letter-spacing: normal; + width: max-content; + text-overflow: ellipsis; + overflow: hidden; + white-space: pre; + grid-row: 2; + grid-column: 1/6; + } + + .presItem-time { + align-self: center; + position: relative; + padding-right: 10px; + top: 1px; + font-size: 10; + font-weight: 300; + font-family: Roboto; + z-index: 300; + letter-spacing: normal; + } + + .presItem-embedded { + overflow: hidden; + grid-row: 3; + grid-column: 1/8; + position: relative; + display: flex; + width: auto; + justify-content: center; + margin: auto; + margin-bottom: 2px; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + } + + .presItem-embeddedMask { + width: 100%; + height: 100%; + position: absolute; + border-radius: 3px; + top: 0; + left: 0; + z-index: 1; + overflow: hidden; + } + + + .presItem-slideButtons { + display: flex; + grid-column: 7; + grid-row: 1/3; + width: max-content; + justify-self: right; + justify-content: flex-end; + + .slideButton { + cursor: pointer; + position: relative; + border-radius: 100px; + z-index: 300; + width: 18px; + height: 18px; + display: flex; + font-size: 12px; + justify-self: center; + align-self: center; + background-color: rgba(0, 0, 0, 0.5); + color: white; + justify-content: center; + align-items: center; + transition: 0.2s; + margin-right: 3px; + } + + .slideButton:hover { + background-color: rgba(0, 0, 0, 1); + transform: scale(1.2); + } + } +} + +// .presItem-slide:hover { +// .presItem-slideButtons { +// display: flex; +// grid-column: 7; +// grid-row: 1/3; +// width: max-content; +// justify-self: right; +// justify-content: flex-end; + +// .slideButton { +// cursor: pointer; +// position: relative; +// border-radius: 100px; +// z-index: 300; +// width: 18px; +// height: 18px; +// display: flex; +// font-size: 12px; +// justify-self: center; +// align-self: center; +// background-color: rgba(0, 0, 0, 0.5); +// color: white; +// justify-content: center; +// align-items: center; +// transition: 0.2s; +// margin-right: 3px; +// } + +// .slideButton:hover { +// background-color: rgba(0, 0, 0, 1); +// transform: scale(1.2); +// } +// } +// } + +.presItem-slide.active { + box-shadow: 0 0 0px 1.5px $dark-blue; +} + +.presItem-multiDrag { + font-family: Roboto; + font-weight: 600; + color: white; + text-align: center; + justify-content: center; + align-content: center; + width: 100px; + height: 30px; + position: absolute; + background-color: $dark-blue; + z-index: 4000; + border-radius: 10px; + box-shadow: black 0.4vw 0.4vw 0.8vw; + line-height: 30px; +} + +.presItem-miniSlide { + font-weight: 700; + font-size: 12; + grid-column: 1/8; + align-self: center; + justify-self: center; + background-color: #d5dce2; + width: 26px; + text-align: center; + height: 26px; + line-height: 28px; + border-radius: 100%; +} + +.presItem-miniSlide.active { + box-shadow: 0 0 0px 1.5px $dark-blue; +} + +// .presItem-slide:hover { +// background: $slide-hover; +// }
\ No newline at end of file diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx new file mode 100644 index 000000000..5e713c3cf --- /dev/null +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -0,0 +1,388 @@ +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { Tooltip } from "@material-ui/core"; +import { action, computed, IReactionDisposer, observable, reaction } from "mobx"; +import { observer } from "mobx-react"; +import { DataSym, Doc, Opt } from "../../../../fields/Doc"; +import { documentSchema } from '../../../../fields/documentSchemas'; +import { Id } from "../../../../fields/FieldSymbols"; +import { createSchema, makeInterface } from '../../../../fields/Schema'; +import { Cast, NumCast, StrCast } from "../../../../fields/Types"; +import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents, emptyPath, returnEmptyDoclist } from "../../../../Utils"; +import { DocumentType } from "../../../documents/DocumentTypes"; +import { CurrentUserUtils } from "../../../util/CurrentUserUtils"; +import { DocumentManager } from "../../../util/DocumentManager"; +import { DragManager } from "../../../util/DragManager"; +import { Transform } from "../../../util/Transform"; +import { undoBatch } from "../../../util/UndoManager"; +import { ViewBoxBaseComponent } from '../../DocComponent'; +import { EditableView } from "../../EditableView"; +import { DocumentView, DocumentViewProps } from "../../nodes/DocumentView"; +import { FieldView, FieldViewProps } from '../../nodes/FieldView'; +import { PresBox } from "./PresBox"; +import { Colors } from "../../global/globalEnums"; +import { StyleProp } from "../../StyleProvider"; +import "./PresElementBox.scss"; +import React = require("react"); +import { DocUtils } from "../../../documents/Documents"; +import { PresMovement } from "./PresEnums"; + +export const presSchema = createSchema({ + presentationTargetDoc: Doc, + presBox: Doc, + presZoomButton: "boolean", + presNavButton: "boolean", + presHideTillShownButton: "boolean", + presFadeButton: "boolean", + presHideAfterButton: "boolean", + presGroupButton: "boolean", + presExpandInlineButton: "boolean" +}); + +type PresDocument = makeInterface<[typeof presSchema, typeof documentSchema]>; +const PresDocument = makeInterface(presSchema, documentSchema); +/** + * This class models the view a document added to presentation will have in the presentation. + * It involves some functionality for its buttons and options. + */ +@observer +export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps, PresDocument>(PresDocument) { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(PresElementBox, fieldKey); } + _heightDisposer: IReactionDisposer | undefined; + + @observable _dragging = false; + // these fields are conditionally computed fields on the layout document that take this document as a parameter + @computed get indexInPres() { return Number(this.lookupField("indexInPres")); } // the index field is where this document is in the presBox display list (since this value is different for each presentation element, the value can't be stored on the layout template which is used by all display elements) + @computed get collapsedHeight() { return Number(this.lookupField("presCollapsedHeight")); } // the collapsed height changes depending on the state of the presBox. We could store this on the presentation element template if it's used by only one presentation - but if it's shared by multiple, then this value must be looked up + @computed get presStatus() { return StrCast(this.lookupField("presStatus")); } + @computed get itemIndex() { return NumCast(this.lookupField("_itemIndex")); } + @computed get presBox() { return Cast(this.lookupField("presBox"), Doc, null); } + @computed get targetDoc() { return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc; } + + componentDidMount() { + this.layoutDoc.hideLinkButton = true; + this._heightDisposer = reaction(() => [this.rootDoc.presExpandInlineButton, this.collapsedHeight], + params => this.layoutDoc._height = NumCast(params[1]) + (Number(params[0]) ? 100 : 0), { fireImmediately: true }); + } + componentWillUnmount() { + this._heightDisposer?.(); + } + + /** + * Returns a local transformed coordinate array for given coordinates. + */ + ScreenToLocalListTransform = (xCord: number, yCord: number) => [xCord, yCord]; + + @action + presExpandDocumentClick = () => { + this.rootDoc.presExpandInlineButton = !this.rootDoc.presExpandInlineButton; + } + + embedHeight = (): number => 97; + // embedWidth = () => this.props.PanelWidth(); + // embedHeight = () => Math.min(this.props.PanelWidth() - 20, this.props.PanelHeight() - this.collapsedHeight); + embedWidth = (): number => this.props.PanelWidth() - 35; + styleProvider = (doc: (Doc | undefined), props: Opt<DocumentViewProps>, property: string): any => { + if (property === StyleProp.Opacity) return 1; + return this.props.styleProvider?.(doc, props, property); + } + /** + * The function that is responsible for rendering a preview or not for this + * presentation element. + */ + @computed get renderEmbeddedInline() { + return !this.rootDoc.presExpandInlineButton || !this.targetDoc ? (null) : + <div className="presItem-embedded" style={{ height: this.embedHeight(), width: this.embedWidth() }}> + <DocumentView + Document={this.targetDoc} + DataDoc={this.targetDoc[DataSym] !== this.targetDoc && this.targetDoc[DataSym]} + styleProvider={this.styleProvider} + layerProvider={this.props.layerProvider} + docViewPath={returnEmptyDoclist} + rootSelected={returnTrue} + addDocument={returnFalse} + removeDocument={returnFalse} + isContentActive={this.props.isContentActive} + addDocTab={returnFalse} + pinToPres={returnFalse} + PanelWidth={this.embedWidth} + PanelHeight={this.embedHeight} + ScreenToLocalTransform={Transform.Identity} + moveDocument={this.props.moveDocument!} + renderDepth={this.props.renderDepth + 1} + focus={DocUtils.DefaultFocus} + whenChildContentsActiveChanged={returnFalse} + bringToFront={returnFalse} + docFilters={this.props.docFilters} + docRangeFilters={this.props.docRangeFilters} + searchFilterDocs={this.props.searchFilterDocs} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + hideLinkButton={true} + /> + <div className="presItem-embeddedMask" /> + </div>; + } + + @computed get duration() { + let durationInS: number; + if (this.rootDoc.type === DocumentType.AUDIO || this.rootDoc.type === DocumentType.VID) { durationInS = NumCast(this.rootDoc.presEndTime) - NumCast(this.rootDoc.presStartTime); durationInS = Math.round(durationInS * 10) / 10; } + else if (this.rootDoc.presDuration) durationInS = NumCast(this.rootDoc.presDuration) / 1000; + else durationInS = 2; + return "D: " + durationInS + "s"; + } + + @computed get transition() { + let transitionInS: number; + if (this.rootDoc.presTransition) transitionInS = NumCast(this.rootDoc.presTransition) / 1000; + else transitionInS = 0.5; + return this.rootDoc.presMovement === PresMovement.Jump || this.rootDoc.presMovement === PresMovement.None ? (null) : "M: " + transitionInS + "s"; + } + + private _itemRef: React.RefObject<HTMLDivElement> = React.createRef(); + private _dragRef: React.RefObject<HTMLDivElement> = React.createRef(); + private _titleRef: React.RefObject<EditableView> = React.createRef(); + + + @action + headerDown = (e: React.PointerEvent<HTMLDivElement>) => { + const element = e.target as any; + e.stopPropagation(); + e.preventDefault(); + if (element && !(e.ctrlKey || e.metaKey)) { + if (PresBox.Instance._selectedArray.has(this.rootDoc)) { + PresBox.Instance._selectedArray.size === 1 && PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false); + setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction); + } else { + setupMoveUpEvents(this, e, ((e: PointerEvent) => { + PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false); + return this.startDrag(e); + }), emptyFunction, emptyFunction); + } + } + } + + headerUp = (e: React.PointerEvent<HTMLDivElement>) => { + e.stopPropagation(); + e.preventDefault(); + } + + startDrag = (e: PointerEvent) => { + const miniView: boolean = this.toolbarWidth <= 100; + const activeItem = this.rootDoc; + const dragArray = PresBox.Instance._dragArray; + const dragData = new DragManager.DocumentDragData(PresBox.Instance.sortArray()); + const dragItem: HTMLElement[] = []; + if (dragArray.length === 1) { + const doc = dragArray[0]; + doc.className = miniView ? "presItem-miniSlide" : "presItem-slide"; + dragItem.push(doc); + } else if (dragArray.length >= 1) { + const doc = document.createElement('div'); + doc.className = "presItem-multiDrag"; + doc.innerText = "Move " + PresBox.Instance._selectedArray.size + " slides"; + doc.style.position = 'absolute'; + doc.style.top = (e.clientY) + 'px'; + doc.style.left = (e.clientX - 50) + 'px'; + dragItem.push(doc); + } + + // const dropEvent = () => runInAction(() => this._dragging = false); + if (activeItem) { + DragManager.StartDocumentDrag(dragItem.map(ele => ele), dragData, e.clientX, e.clientY, undefined); + // runInAction(() => this._dragging = true); + return true; + } + return false; + } + + onPointerOver = (e: any) => { + document.removeEventListener("pointermove", this.onPointerMove); + document.addEventListener("pointermove", this.onPointerMove); + } + + onPointerMove = (e: PointerEvent) => { + const slide = this._itemRef.current!; + let dragIsPresItem: boolean = DragManager.docsBeingDragged.length > 0 ? true : false; + for (const doc of DragManager.docsBeingDragged) { + if (!doc.presentationTargetDoc) dragIsPresItem = false; + } + if (slide && dragIsPresItem) { + const rect = slide.getBoundingClientRect(); + const y = e.clientY - rect.top; //y position within the element. + const height = slide.clientHeight; + const halfLine = height / 2; + if (y <= halfLine) { + slide.style.borderTop = `solid 2px ${Colors.MEDIUM_BLUE}`; + slide.style.borderBottom = "0px"; + } else if (y > halfLine) { + slide.style.borderTop = "0px"; + slide.style.borderBottom = `solid 2px ${Colors.MEDIUM_BLUE}`; + } + } + document.removeEventListener("pointermove", this.onPointerMove); + } + + onPointerLeave = (e: any) => { + this._itemRef.current!.style.borderTop = "0px"; + this._itemRef.current!.style.borderBottom = "0px"; + document.removeEventListener("pointermove", this.onPointerMove); + } + + @action + toggleProperties = () => { + if (CurrentUserUtils.propertiesWidth < 5) { + action(() => (CurrentUserUtils.propertiesWidth = 250)); + } + } + + @undoBatch + removeItem = action((e: React.MouseEvent) => { + this.props.removeDocument?.(this.rootDoc); + if (PresBox.Instance._selectedArray.has(this.rootDoc)) { + PresBox.Instance._selectedArray.delete(this.rootDoc); + } + e.stopPropagation(); + }); + + @undoBatch + @action + onSetValue = (value: string) => { + this.rootDoc.title = !value.trim().length ? "-untitled-" : value; + return true; + } + + /** + * Method called for updating the view of the currently selected document + * + * @param targetDoc + * @param activeItem + */ + @undoBatch + @action + updateView = (targetDoc: Doc, activeItem: Doc) => { + if (targetDoc.type === DocumentType.PDF || targetDoc.type === DocumentType.WEB || targetDoc.type === DocumentType.RTF) { + const scroll = targetDoc._scrollTop; + activeItem.presPinViewScroll = scroll; + } else if (targetDoc.type === DocumentType.VID) { + activeItem.presPinTimecode = targetDoc._currentTimecode; + } else if (targetDoc.type === DocumentType.COMPARISON) { + const clipWidth = targetDoc._clipWidth; + activeItem.presPinClipWidth = clipWidth; + } else { + const x = targetDoc._panX; + const y = targetDoc._panY; + const scale = targetDoc._viewScale; + activeItem.presPinViewX = x; + activeItem.presPinViewY = y; + activeItem.presPinViewScale = scale; + } + } + + @computed + get toolbarWidth(): number { + const presBoxDocView = DocumentManager.Instance.getDocumentView(this.presBox); + let width: number = NumCast(this.presBox._width); + if (presBoxDocView) width = presBoxDocView.props.PanelWidth(); + if (width === 0) width = 300; + return width; + } + + @computed get mainItem() { + const isSelected: boolean = PresBox.Instance._selectedArray.has(this.rootDoc); + const toolbarWidth: number = this.toolbarWidth; + const showMore: boolean = this.toolbarWidth >= 300; + const miniView: boolean = this.toolbarWidth <= 110; + const presBox: Doc = this.presBox; //presBox + const presBoxColor: string = StrCast(presBox._backgroundColor); + const presColorBool: boolean = presBoxColor ? (presBoxColor !== Colors.WHITE && presBoxColor !== "transparent") : false; + const targetDoc: Doc = this.targetDoc; + const activeItem: Doc = this.rootDoc; + return ( + <div className={`presItem-container`} + key={this.props.Document[Id] + this.indexInPres} + ref={this._itemRef} + style={{ + backgroundColor: presColorBool ? isSelected ? "rgba(250,250,250,0.3)" : "transparent" : isSelected ? Colors.LIGHT_BLUE : "transparent", + opacity: this._dragging ? 0.3 : 1 + }} + onClick={e => { + e.stopPropagation(); + e.preventDefault(); + PresBox.Instance.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); + }} + onDoubleClick={action(e => { + this.toggleProperties(); + PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true); + })} + onPointerOver={this.onPointerOver} + onPointerLeave={this.onPointerLeave} + onPointerDown={this.headerDown} + onPointerUp={this.headerUp} + > + {miniView ? + // when width is LESS than 110 px + <div className={`presItem-miniSlide ${isSelected ? "active" : ""}`} ref={miniView ? this._dragRef : null}> + {`${this.indexInPres + 1}.`} + </div> + : + // when width is MORE than 110 px + <div className="presItem-number"> + {`${this.indexInPres + 1}.`} + </div>} + {miniView ? (null) : <div ref={miniView ? null : this._dragRef} className={`presItem-slide ${isSelected ? "active" : ""}`} + style={{ + backgroundColor: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BackgroundColor), + boxShadow: presBoxColor && presBoxColor !== "white" && presBoxColor !== "transparent" ? isSelected ? "0 0 0px 1.5px" + presBoxColor : undefined : undefined + }}> + <div className="presItem-name" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105, cursor: isSelected ? 'text' : 'grab' }}> + <EditableView + ref={this._titleRef} + editing={!isSelected ? false : undefined} + contents={activeItem.title} + overflow={'ellipsis'} + GetValue={() => StrCast(activeItem.title)} + SetValue={this.onSetValue} + /> + </div> + <Tooltip title={<><div className="dash-tooltip">{"Movement speed"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.transition}</div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Duration"}</div></>}><div className="presItem-time" style={{ display: showMore ? "block" : "none" }}>{this.duration}</div></Tooltip> + <div className={"presItem-slideButtons"}> + <Tooltip title={<><div className="dash-tooltip">{"Update view"}</div></>}> + <div className="slideButton" + onClick={() => this.updateView(targetDoc, activeItem)} + style={{ fontWeight: 700, display: activeItem.presPinView ? "flex" : "none" }}>V</div> + </Tooltip> + {this.indexInPres === 0 ? (null) : <Tooltip title={<><div className="dash-tooltip">{activeItem.groupWithUp ? "Ungroup" : "Group with up"}</div></>}> + <div className="slideButton" + onClick={() => activeItem.groupWithUp = !activeItem.groupWithUp} + style={{ + zIndex: 1000 - this.indexInPres, + fontWeight: 700, + backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : Colors.MEDIUM_BLUE : undefined, + height: activeItem.groupWithUp ? 53 : 18, + transform: activeItem.groupWithUp ? "translate(0, -17px)" : undefined + }}> + <div style={{ transform: activeItem.groupWithUp ? "rotate(180deg) translate(0, -17.5px)" : "rotate(0deg)" }}> + <FontAwesomeIcon icon={"arrow-up"} onPointerDown={e => e.stopPropagation()} /> + </div> + </div> + </Tooltip>} + <Tooltip title={<><div className="dash-tooltip">{this.rootDoc.presExpandInlineButton ? "Minimize" : "Expand"}</div></>}><div className={"slideButton"} onClick={e => { e.stopPropagation(); this.presExpandDocumentClick(); }}> + <FontAwesomeIcon icon={this.rootDoc.presExpandInlineButton ? "eye-slash" : "eye"} onPointerDown={e => e.stopPropagation()} /> + </div></Tooltip> + <Tooltip title={<><div className="dash-tooltip">{"Remove from presentation"}</div></>}><div + className={"slideButton"} + onClick={this.removeItem}> + <FontAwesomeIcon icon={"trash"} onPointerDown={e => e.stopPropagation()} /> + </div></Tooltip> + </div> + <div className="presItem-docName" style={{ maxWidth: showMore ? (toolbarWidth - 195) : toolbarWidth - 105 }}>{activeItem.presPinView ? (<><i>View of </i> {targetDoc.title}</>) : targetDoc.title}</div> + {this.renderEmbeddedInline} + </div>} + </div >); + } + + render() { + return !(this.rootDoc instanceof Doc) || this.targetDoc instanceof Promise ? (null) : this.mainItem; + } +}
\ No newline at end of file diff --git a/src/client/views/nodes/trails/PresEnums.ts b/src/client/views/nodes/trails/PresEnums.ts new file mode 100644 index 000000000..93ab323fb --- /dev/null +++ b/src/client/views/nodes/trails/PresEnums.ts @@ -0,0 +1,28 @@ +export enum PresMovement { + Zoom = "zoom", + Pan = "pan", + Jump = "jump", + None = "none", +} + +export enum PresEffect { + Zoom = "Zoom", + Lightspeed = "Lightspeed", + Fade = "Fade in", + Flip = "Flip", + Rotate = "Rotate", + Bounce = "Bounce", + Roll = "Roll", + None = "None", + Left = "left", + Right = "right", + Center = "center", + Top = "top", + Bottom = "bottom" +} + +export enum PresStatus { + Autoplay = "auto", + Manual = "manual", + Edit = "edit" +}
\ No newline at end of file diff --git a/src/client/views/nodes/trails/index.ts b/src/client/views/nodes/trails/index.ts new file mode 100644 index 000000000..8f3f7b03a --- /dev/null +++ b/src/client/views/nodes/trails/index.ts @@ -0,0 +1,3 @@ +export * from "./PresBox"; +export * from "./PresElementBox"; +export * from "./PresEnums";
\ No newline at end of file |
