diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/MainView.tsx | 1220 | ||||
-rw-r--r-- | src/client/views/nodes/RecordingBox/RecordingView.tsx | 41 | ||||
-rw-r--r-- | src/client/views/nodes/button/FontIconBox.tsx | 1300 | ||||
-rw-r--r-- | src/client/views/nodes/trails/PresElementBox.tsx | 55 |
4 files changed, 1306 insertions, 1310 deletions
diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 8e9be1eeb..7ee9f5b70 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -68,310 +68,342 @@ const _global = (window /* browser */ || global /* node */) as any; @observer export class MainView extends React.Component { - public static Instance: MainView; - public static Live: boolean = false; - private _docBtnRef = React.createRef<HTMLDivElement>(); - @observable public LastButton: Opt<Doc>; - @observable private _windowWidth: number = 0; - @observable private _windowHeight: number = 0; - @observable private _dashUIWidth: number = 0; // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row) - @observable private _dashUIHeight: number = 0; // height of entire main dashboard region including top menu buttons - @observable private _panelContent: string = "none"; - @observable private _sidebarContent: any = this.userDoc?.sidebar; - @observable private _leftMenuFlyoutWidth: number = 0; - - @computed private get dashboardTabHeight() { return 27; } // 27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js - @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); } - @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight(); } - @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; } - @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; } - @computed private get userDoc() { return Doc.UserDoc(); } - @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); } - @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; } - @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } - - topMenuHeight = () => 35; - topMenuWidth = returnZero; // value is ignored ... - leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace("px", "")); - leftMenuHeight = () => this._dashUIHeight; - leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth; - leftMenuFlyoutHeight = () => this._dashUIHeight; - propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0)); - propertiesHeight = () => this._dashUIHeight; - mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth(); - mainDocViewHeight = () => this._dashUIHeight; - - componentDidMount() { - document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!)); - const ele = document.getElementById("loader"); - const prog = document.getElementById("dash-progress"); - if (ele && prog) { - // remove from DOM - setTimeout(() => { - clearTimeout(); - prog.style.transition = "1s"; - prog.style.width = "100%"; - }, 0); - setTimeout(() => ele.outerHTML = '', 1000); - } - this._sidebarContent.proto = undefined; - if (!MainView.Live) { - DocServer.setPlaygroundFields(["dataTransition", "treeViewOpen", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition", - "panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height", "hideMinimap", - "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's - } - DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg))); - - const tag = document.createElement('script'); - tag.src = "https://www.youtube.com/iframe_api"; - const firstScriptTag = document.getElementsByTagName('script')[0]; - firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag); - window.removeEventListener("keydown", KeyManager.Instance.handle); - window.addEventListener("keydown", KeyManager.Instance.handle); - window.removeEventListener("keyup", KeyManager.Instance.unhandle); - window.addEventListener("keyup", KeyManager.Instance.unhandle); - window.addEventListener("paste", KeyManager.Instance.paste as any); - document.addEventListener("dash", (e: any) => { // event used by chrome plugin to tell Dash which document to focus on - const id = FormattedTextBox.GetDocFromUrl(e.detail); - DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined) : (null)); - }); - document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener); - this.initEventListeners(); - } - - componentWillUnMount() { - window.removeEventListener("keyup", KeyManager.Instance.unhandle); - window.removeEventListener("keydown", KeyManager.Instance.handle); - window.removeEventListener("pointerdown", this.globalPointerDown); - window.removeEventListener("paste", KeyManager.Instance.paste as any); - document.removeEventListener("linkAnnotationToDash", Hypothesis.linkListener); - } - - constructor(props: Readonly<{}>) { - super(props); - MainView.Instance = this; - CurrentUserUtils._urlState = HistoryUtil.parseUrl(window.location) || {} as any; - - // causes errors to be generated when modifying an observable outside of an action - configure({ enforceActions: "observed" }); - - if (window.location.pathname !== "/home") { - const pathname = window.location.pathname.substr(1).split("/"); - if (pathname.length > 1 && pathname[0] === "doc") { - CurrentUserUtils.MainDocId = pathname[1]; - !this.userDoc && DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (CurrentUserUtils.GuestTarget = field))); + public static Instance: MainView; + public static Live: boolean = false; + private _docBtnRef = React.createRef<HTMLDivElement>(); + @observable public LastButton: Opt<Doc>; + @observable private _windowWidth: number = 0; + @observable private _windowHeight: number = 0; + @observable private _dashUIWidth: number = 0; // width of entire main dashboard region including left menu buttons and properties panel (but not including the dashboard selector button row) + @observable private _dashUIHeight: number = 0; // height of entire main dashboard region including top menu buttons + @observable private _panelContent: string = "none"; + @observable private _sidebarContent: any = this.userDoc?.sidebar; + @observable private _leftMenuFlyoutWidth: number = 0; + + @computed private get dashboardTabHeight() { return 27; } // 27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js + @computed private get topOfDashUI() { return Number(DASHBOARD_SELECTOR_HEIGHT.replace("px", "")); } + @computed private get topOfMainDoc() { return this.topOfDashUI + this.topMenuHeight(); } + @computed private get topOfMainDocContent() { return this.topOfMainDoc + this.dashboardTabHeight; } + @computed private get leftScreenOffsetOfMainDocView() { return this.leftMenuWidth() - 2; } + @computed private get userDoc() { return Doc.UserDoc(); } + @computed private get colorScheme() { return StrCast(CurrentUserUtils.ActiveDashboard?.colorScheme); } + @computed private get mainContainer() { return this.userDoc ? CurrentUserUtils.ActiveDashboard : CurrentUserUtils.GuestDashboard; } + @computed public get mainFreeform(): Opt<Doc> { return (docs => (docs?.length > 1) ? docs[1] : undefined)(DocListCast(this.mainContainer!.data)); } + + topMenuHeight = () => 35; + topMenuWidth = returnZero; // value is ignored ... + leftMenuWidth = () => Number(LEFT_MENU_WIDTH.replace("px", "")); + leftMenuHeight = () => this._dashUIHeight; + leftMenuFlyoutWidth = () => this._leftMenuFlyoutWidth; + leftMenuFlyoutHeight = () => this._dashUIHeight; + propertiesWidth = () => Math.max(0, Math.min(this._dashUIWidth - 50, CurrentUserUtils.propertiesWidth || 0)); + propertiesHeight = () => this._dashUIHeight; + mainDocViewWidth = () => this._dashUIWidth - this.propertiesWidth() - this.leftMenuWidth(); + mainDocViewHeight = () => this._dashUIHeight; + + componentDidMount() { + document.getElementById("root")?.addEventListener("scroll", e => ((ele) => ele.scrollLeft = ele.scrollTop = 0)(document.getElementById("root")!)); + const ele = document.getElementById("loader"); + const prog = document.getElementById("dash-progress"); + if (ele && prog) { + // remove from DOM + setTimeout(() => { + clearTimeout(); + prog.style.transition = "1s"; + prog.style.width = "100%"; + }, 0); + setTimeout(() => ele.outerHTML = '', 1000); } - } - - library.add(...[fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar, - fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, - fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard, - fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight, - fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow, - fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft, - fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp, - fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt, - fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer, - fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV, - fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, - fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined, - fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript, - fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any, - fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any, - fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp, - fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl, - fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo, - fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical, - fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll, - fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines, - fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeDown]); - this.initAuthenticationRouters(); - } - - globalPointerDown = action((e: PointerEvent) => { - AudioBox.Enabled = true; - const targets = document.elementsFromPoint(e.x, e.y); - if (targets.length) { - const targClass = targets[0].className.toString(); - !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu(); - !["timeline-menu-desc", "timeline-menu-item", "timeline-menu-input"].includes(targClass) && TimelineMenu.Instance.closeMenu(); - } - }); - - initEventListeners = () => { - window.addEventListener("drop", e => e.preventDefault(), false); // prevent default behavior of navigating to a new web page - window.addEventListener("dragover", e => e.preventDefault(), false); - // document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined)); - document.addEventListener("pointerdown", this.globalPointerDown); - document.addEventListener("click", (e: MouseEvent) => { - if (!e.cancelBubble) { - const pathstr = (e as any)?.path?.map((p: any) => p.classList?.toString()).join(); - if (pathstr?.includes("libraryFlyout")) { - SelectionManager.DeselectAll(); - } + this._sidebarContent.proto = undefined; + if (!MainView.Live) { + DocServer.setPlaygroundFields(["dataTransition", "treeViewOpen", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition", + "panX", "panY", "width", "height", "nativeWidth", "nativeHeight", "text-scrollHeight", "text-height", "hideMinimap", + "viewScale", "scrollTop", "hidden", "curPage", "viewType", "chromeHidden", "nativeWidth"]); // can play with these fields on someone else's } - }, false); - document.oncontextmenu = () => false; - } - - initAuthenticationRouters = async () => { - // Load the user's active dashboard, or create a new one if initial session after signup - const received = CurrentUserUtils.MainDocId; - if (received && !this.userDoc) { - reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true }); - } else { - if (received && CurrentUserUtils._urlState.sharing) { - reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized, - initialized => initialized && received && DocServer.GetRefField(received).then(docField => { - if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) { - CollectionDockingView.AddSplit(docField, "right"); + DocServer.GetRefField("rtfProto").then(proto => (proto instanceof Doc) && reaction(() => StrCast(proto.BROADCAST_MESSAGE), msg => msg && alert(msg))); + + const tag = document.createElement('script'); + tag.src = "https://www.youtube.com/iframe_api"; + const firstScriptTag = document.getElementsByTagName('script')[0]; + firstScriptTag.parentNode!.insertBefore(tag, firstScriptTag); + window.removeEventListener("keydown", KeyManager.Instance.handle); + window.addEventListener("keydown", KeyManager.Instance.handle); + window.removeEventListener("keyup", KeyManager.Instance.unhandle); + window.addEventListener("keyup", KeyManager.Instance.unhandle); + window.addEventListener("paste", KeyManager.Instance.paste as any); + document.addEventListener("dash", (e: any) => { // event used by chrome plugin to tell Dash which document to focus on + const id = FormattedTextBox.GetDocFromUrl(e.detail); + DocServer.GetRefField(id).then(doc => (doc instanceof Doc) ? DocumentManager.Instance.jumpToDocument(doc, false, undefined) : (null)); + }); + document.addEventListener("linkAnnotationToDash", Hypothesis.linkListener); + this.initEventListeners(); + } + + componentWillUnMount() { + window.removeEventListener("keyup", KeyManager.Instance.unhandle); + window.removeEventListener("keydown", KeyManager.Instance.handle); + window.removeEventListener("pointerdown", this.globalPointerDown); + window.removeEventListener("paste", KeyManager.Instance.paste as any); + document.removeEventListener("linkAnnotationToDash", Hypothesis.linkListener); + } + + constructor(props: Readonly<{}>) { + super(props); + MainView.Instance = this; + CurrentUserUtils._urlState = HistoryUtil.parseUrl(window.location) || {} as any; + + // causes errors to be generated when modifying an observable outside of an action + configure({ enforceActions: "observed" }); + + if (window.location.pathname !== "/home") { + const pathname = window.location.pathname.substr(1).split("/"); + if (pathname.length > 1 && pathname[0] === "doc") { + CurrentUserUtils.MainDocId = pathname[1]; + !this.userDoc && DocServer.GetRefField(pathname[1]).then(action(field => field instanceof Doc && (CurrentUserUtils.GuestTarget = field))); + } + } + + library.add(...[fa.faEdit, fa.faTrash, fa.faTrashAlt, fa.faShare, fa.faDownload, fa.faExpandArrowsAlt, fa.faLayerGroup, fa.faExternalLinkAlt, fa.faCalendar, fa.faVideoSlash, fa.faVideo, + fa.faSquare, far.faSquare as any, fa.faConciergeBell, fa.faWindowRestore, fa.faFolder, fa.faMapPin, fa.faMapMarker, fa.faFingerprint, fa.faCrosshairs, fa.faDesktop, fa.faUnlock, + fa.faLock, fa.faLaptopCode, fa.faMale, fa.faCopy, fa.faHandPointLeft, fa.faHandPointRight, fa.faCompass, fa.faSnowflake, fa.faMicrophone, fa.faKeyboard, + fa.faQuestion, fa.faTasks, fa.faPalette, fa.faAngleLeft, fa.faAngleRight, fa.faBell, fa.faCamera, fa.faExpand, fa.faCaretDown, fa.faCaretLeft, fa.faCaretRight, + fa.faCaretSquareDown, fa.faCaretSquareRight, fa.faArrowsAltH, fa.faPlus, fa.faMinus, fa.faTerminal, fa.faToggleOn, fa.faFile, fa.faLocationArrow, + fa.faSearch, fa.faFileDownload, fa.faFileUpload, fa.faStop, fa.faCalculator, fa.faWindowMaximize, fa.faAddressCard, fa.faQuestionCircle, fa.faArrowLeft, + fa.faArrowRight, fa.faArrowDown, fa.faArrowUp, fa.faBolt, fa.faBullseye, fa.faCaretUp, fa.faCat, fa.faCheck, fa.faChevronRight, fa.faChevronLeft, fa.faChevronDown, fa.faChevronUp, + fa.faClone, fa.faCloudUploadAlt, fa.faCommentAlt, fa.faCompressArrowsAlt, fa.faCut, fa.faEllipsisV, fa.faEraser, fa.faExclamation, fa.faFileAlt, + fa.faFileAudio, fa.faFileVideo, fa.faFilePdf, fa.faFilm, fa.faFilter, fa.faFont, fa.faGlobeAmericas, fa.faGlobeAsia, fa.faHighlighter, fa.faLongArrowAltRight, fa.faMousePointer, + fa.faMusic, fa.faObjectGroup, fa.faPause, fa.faPen, fa.faPenNib, fa.faPhone, fa.faPlay, fa.faPortrait, fa.faRedoAlt, fa.faStamp, fa.faStickyNote, fa.faArrowsAltV, + fa.faTimesCircle, fa.faThumbtack, fa.faTree, fa.faTv, fa.faUndoAlt, fa.faVideo, fa.faAsterisk, fa.faBrain, fa.faImage, fa.faPaintBrush, fa.faTimes, + fa.faEye, fa.faArrowsAlt, fa.faQuoteLeft, fa.faSortAmountDown, fa.faAlignLeft, fa.faAlignCenter, fa.faAlignRight, fa.faHeading, fa.faRulerCombined, + fa.faFillDrip, fa.faLink, fa.faUnlink, fa.faBold, fa.faItalic, fa.faClipboard, fa.faUnderline, fa.faStrikethrough, fa.faSuperscript, fa.faSubscript, + fa.faIndent, fa.faEyeDropper, fa.faPaintRoller, fa.faBars, fa.faBrush, fa.faShapes, fa.faEllipsisH, fa.faHandPaper, fa.faMap, fa.faUser, faHireAHelper as any, + fa.faTrashRestore, fa.faUsers, fa.faWrench, fa.faCog, fa.faMap, fa.faBellSlash, fa.faExpandAlt, fa.faArchive, fa.faBezierCurve, fa.faCircle, far.faCircle as any, + fa.faLongArrowAltRight, fa.faPenFancy, fa.faAngleDoubleRight, faBuffer as any, fa.faExpand, fa.faUndo, fa.faSlidersH, fa.faAngleDoubleLeft, fa.faAngleUp, + fa.faAngleDown, fa.faPlayCircle, fa.faClock, fa.faRocket, fa.faExchangeAlt, fa.faHashtag, fa.faAlignJustify, fa.faCheckSquare, fa.faListUl, + fa.faWindowMinimize, fa.faWindowRestore, fa.faTextWidth, fa.faTextHeight, fa.faClosedCaptioning, fa.faInfoCircle, fa.faTag, fa.faSyncAlt, fa.faPhotoVideo, + fa.faArrowAltCircleDown, fa.faArrowAltCircleUp, fa.faArrowAltCircleLeft, fa.faArrowAltCircleRight, fa.faStopCircle, fa.faCheckCircle, fa.faGripVertical, + fa.faSortUp, fa.faSortDown, fa.faTable, fa.faTh, fa.faThList, fa.faProjectDiagram, fa.faSignature, fa.faColumns, fa.faChevronCircleUp, fa.faUpload, fa.faBorderAll, + fa.faBraille, fa.faChalkboard, fa.faPencilAlt, fa.faEyeSlash, fa.faSmile, fa.faIndent, fa.faOutdent, fa.faChartBar, fa.faBan, fa.faPhoneSlash, fa.faGripLines, + fa.faSave, fa.faBookmark, fa.faList, fa.faListOl, fa.faFolderPlus, fa.faLightbulb, fa.faBookOpen, fa.faMapMarkerAlt, fa.faSearchPlus, fa.faVolumeUp, fa.faVolumeDown]); + this.initAuthenticationRouters(); + } + + globalPointerDown = action((e: PointerEvent) => { + AudioBox.Enabled = true; + const targets = document.elementsFromPoint(e.x, e.y); + if (targets.length) { + const targClass = targets[0].className.toString(); + !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu(); + !["timeline-menu-desc", "timeline-menu-item", "timeline-menu-input"].includes(targClass) && TimelineMenu.Instance.closeMenu(); + } + }); + + initEventListeners = () => { + window.addEventListener("drop", e => e.preventDefault(), false); // prevent default behavior of navigating to a new web page + window.addEventListener("dragover", e => e.preventDefault(), false); + // document.addEventListener("pointermove", action(e => SearchBox.Instance._undoBackground = UndoManager.batchCounter ? "#000000a8" : undefined)); + document.addEventListener("pointerdown", this.globalPointerDown); + document.addEventListener("click", (e: MouseEvent) => { + if (!e.cancelBubble) { + const pathstr = (e as any)?.path?.map((p: any) => p.classList?.toString()).join(); + if (pathstr?.includes("libraryFlyout")) { + SelectionManager.DeselectAll(); } - }), - ); + } + }, false); + document.oncontextmenu = () => false; + } + + initAuthenticationRouters = async () => { + // Load the user's active dashboard, or create a new one if initial session after signup + const received = CurrentUserUtils.MainDocId; + if (received && !this.userDoc) { + reaction(() => CurrentUserUtils.GuestTarget, target => target && CurrentUserUtils.createNewDashboard(Doc.UserDoc()), { fireImmediately: true }); + } else { + if (received && CurrentUserUtils._urlState.sharing) { + reaction(() => CollectionDockingView.Instance && CollectionDockingView.Instance.initialized, + initialized => initialized && received && DocServer.GetRefField(received).then(docField => { + if (docField instanceof Doc && docField._viewType !== CollectionViewType.Docking) { + CollectionDockingView.AddSplit(docField, "right"); + } + }), + ); + } + const activeDash = PromiseValue(this.userDoc.activeDashboard); + activeDash.then(dash => { + if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash); + else CurrentUserUtils.createNewDashboard(this.userDoc); + }); } - const activeDash = PromiseValue(this.userDoc.activeDashboard); - activeDash.then(dash => { - if (dash instanceof Doc) CurrentUserUtils.openDashboard(this.userDoc, dash); - else CurrentUserUtils.createNewDashboard(this.userDoc); - }); - } - } - - @action - createNewPresentation = async () => { - if (!await this.userDoc.myTrails) { - this.userDoc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true - })); - } - const pres = Docs.Create.PresDocument(new List<Doc>(), - { title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" }); - CollectionDockingView.AddSplit(pres, "right"); - this.userDoc.activePresentation = pres; - Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres); - } - - @action - createNewFolder = async () => { - if (!await this.userDoc.myFilesystem) { - this.userDoc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true }); - const newFolder = ScriptField.MakeFunction(`createNewFolder()`, { scriptContext: "any" })!; - const newFolderButton: Doc = Docs.Create.FontIconDocument({ - onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", - btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true - }); - this.userDoc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([this.userDoc.myFileOrphans as Doc], { - title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100, - treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, ignoreClick: true, - isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true, - explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard." - })); - } - const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true }); - Doc.AddDocToList(this.userDoc.myFilesystem as Doc, "data", folder); - } - - @computed get mainDocView() { - return <DocumentView key="main" - Document={this.mainContainer!} - DataDoc={undefined} - addDocument={undefined} - addDocTab={this.addDocTabFunc} - pinToPres={emptyFunction} - docViewPath={returnEmptyDoclist} - layerProvider={undefined} - styleProvider={undefined} - rootSelected={returnTrue} - isContentActive={returnTrue} - removeDocument={undefined} - ScreenToLocalTransform={Transform.Identity} - PanelWidth={this.mainDocViewWidth} - PanelHeight={this.mainDocViewHeight} - focus={DocUtils.DefaultFocus} - whenChildContentsActiveChanged={emptyFunction} - bringToFront={emptyFunction} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - suppressSetHeight={true} - renderDepth={-1} - />; - } - - @computed get dockingContent() { - return <div key="docking" className={`mainView-dockingContent${this._leftMenuFlyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }} - style={{ - minWidth: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`, - transform: LightboxView.LightboxDoc ? "scale(0.0001)" : undefined, - }}> - {!this.mainContainer ? (null) : this.mainDocView} - </div>; - } - - @action - onPropertiesPointerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, - action(e => (CurrentUserUtils.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX)) ? false : false), - action(() => CurrentUserUtils.propertiesWidth < 5 && (CurrentUserUtils.propertiesWidth = 0)), - action(() => CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0), false); - } - - @action - onFlyoutPointerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, - action(e => (this._leftMenuFlyoutWidth = Math.max(e.clientX - 58, 0)) ? false : false), - () => this._leftMenuFlyoutWidth < 5 && this.closeFlyout(), - this.closeFlyout); - } - - sidebarScreenToLocal = () => new Transform(0, -this.topOfMainDoc, 1); - mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0); - addDocTabFunc = (doc: Doc, location: string): boolean => { - const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":"); - const locationParams = locationFields.length > 1 ? locationFields[1] : ""; - if (doc.dockingConfig) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); - switch (locationFields[0]) { - case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); - case "close": return CollectionDockingView.CloseSplit(doc, locationParams); - case "fullScreen": return CollectionDockingView.OpenFullScreen(doc); - case "lightbox": return LightboxView.AddDocTab(doc, location); - case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams); - case "inPlace": - case "add": - default: - return CollectionDockingView.AddSplit(doc, locationParams); - } - } - - - @computed get flyout() { - return !this._leftMenuFlyoutWidth ? <div key="flyout" className={`mainView-libraryFlyout-out`}> - {this.docButtons} - </div> : - <div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._leftMenuFlyoutWidth, width: this._leftMenuFlyoutWidth }} > - <div className="mainView-contentArea" > - <DocumentView - Document={this._sidebarContent.proto || this._sidebarContent} + } + + @action + createNewPresentation = async () => { + if (!await this.userDoc.myTrails) { + this.userDoc.myTrails = new PrefetchProxy(Docs.Create.TreeDocument([], { + title: "TRAILS", childDontRegisterViews: true, _height: 100, _forceActive: true, boxShadow: "0 0", _lockedPosition: true, treeViewOpen: true, system: true + })); + } + const pres = Docs.Create.PresDocument(new List<Doc>(), + { title: "Untitled Trail", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0" }); + CollectionDockingView.AddSplit(pres, "right"); + this.userDoc.activePresentation = pres; + Doc.AddDocToList(this.userDoc.myTrails as Doc, "data", pres); + } + + @action + createNewFolder = async () => { + if (!await this.userDoc.myFilesystem) { + this.userDoc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true }); + const newFolder = ScriptField.MakeFunction(`createNewFolder()`, { scriptContext: "any" })!; + const newFolderButton: Doc = Docs.Create.FontIconDocument({ + onClick: newFolder, _forceActive: true, toolTip: "New folder", _stayInCollection: true, _hideContextMenu: true, title: "New folder", + btnType: ButtonType.ClickButton, _width: 30, _height: 30, buttonText: "New folder", icon: "folder-plus", system: true + }); + this.userDoc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([this.userDoc.myFileOrphans as Doc], { + title: "My Documents", _showTitle: "title", buttonMenu: true, buttonMenuDoc: newFolderButton, _height: 100, + treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", + treeViewTruncateTitleWidth: 150, ignoreClick: true, + isFolder: true, treeViewType: "fileSystem", childHideLinkButton: true, + _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "proto", system: true, + explainer: "This is your file manager where you can create folders to keep track of documents independently of your dashboard." + })); + } + const folder = Docs.Create.TreeDocument([], { title: "Untitled folder", _stayInCollection: true, isFolder: true }); + Doc.AddDocToList(this.userDoc.myFilesystem as Doc, "data", folder); + } + + @computed get mainDocView() { + return <DocumentView key="main" + Document={this.mainContainer!} + DataDoc={undefined} + addDocument={undefined} + addDocTab={this.addDocTabFunc} + pinToPres={emptyFunction} + docViewPath={returnEmptyDoclist} + layerProvider={undefined} + styleProvider={undefined} + rootSelected={returnTrue} + isContentActive={returnTrue} + removeDocument={undefined} + ScreenToLocalTransform={Transform.Identity} + PanelWidth={this.mainDocViewWidth} + PanelHeight={this.mainDocViewHeight} + focus={DocUtils.DefaultFocus} + whenChildContentsActiveChanged={emptyFunction} + bringToFront={emptyFunction} + docFilters={returnEmptyFilter} + docRangeFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + suppressSetHeight={true} + renderDepth={-1} + />; + } + + @computed get dockingContent() { + return <div key="docking" className={`mainView-dockingContent${this._leftMenuFlyoutWidth ? "-flyout" : ""}`} onDrop={e => { e.stopPropagation(); e.preventDefault(); }} + style={{ + minWidth: `calc(100% - ${this._leftMenuFlyoutWidth + this.leftMenuWidth() + this.propertiesWidth()}px)`, + transform: LightboxView.LightboxDoc ? "scale(0.0001)" : undefined, + }}> + {!this.mainContainer ? (null) : this.mainDocView} + </div>; + } + + @action + onPropertiesPointerDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, + action(e => (CurrentUserUtils.propertiesWidth = Math.max(0, this._dashUIWidth - e.clientX)) ? false : false), + action(() => CurrentUserUtils.propertiesWidth < 5 && (CurrentUserUtils.propertiesWidth = 0)), + action(() => CurrentUserUtils.propertiesWidth = this.propertiesWidth() < 15 ? Math.min(this._dashUIWidth - 50, 250) : 0), false); + } + + @action + onFlyoutPointerDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, + action(e => (this._leftMenuFlyoutWidth = Math.max(e.clientX - 58, 0)) ? false : false), + () => this._leftMenuFlyoutWidth < 5 && this.closeFlyout(), + this.closeFlyout); + } + + sidebarScreenToLocal = () => new Transform(0, -this.topOfMainDoc, 1); + mainContainerXf = () => this.sidebarScreenToLocal().translate(-this.leftScreenOffsetOfMainDocView, 0); + addDocTabFunc = (doc: Doc, location: string): boolean => { + const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":"); + const locationParams = locationFields.length > 1 ? locationFields[1] : ""; + if (doc.dockingConfig) return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); + switch (locationFields[0]) { + case "dashboard": return CurrentUserUtils.openDashboard(Doc.UserDoc(), doc); + case "close": return CollectionDockingView.CloseSplit(doc, locationParams); + case "fullScreen": return CollectionDockingView.OpenFullScreen(doc); + case "lightbox": return LightboxView.AddDocTab(doc, location); + case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams); + case "inPlace": + case "add": + default: + return CollectionDockingView.AddSplit(doc, locationParams); + } + } + + + @computed get flyout() { + return !this._leftMenuFlyoutWidth ? <div key="flyout" className={`mainView-libraryFlyout-out`}> + {this.docButtons} + </div> : + <div key="libFlyout" className="mainView-libraryFlyout" style={{ minWidth: this._leftMenuFlyoutWidth, width: this._leftMenuFlyoutWidth }} > + <div className="mainView-contentArea" > + <DocumentView + Document={this._sidebarContent.proto || this._sidebarContent} + DataDoc={undefined} + addDocument={undefined} + addDocTab={this.addDocTabFunc} + pinToPres={emptyFunction} + docViewPath={returnEmptyDoclist} + layerProvider={undefined} + styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === Doc.UserDoc().myFilesystem ? DashboardStyleProvider : DefaultStyleProvider} + rootSelected={returnTrue} + removeDocument={returnFalse} + ScreenToLocalTransform={this.mainContainerXf} + PanelWidth={this.leftMenuFlyoutWidth} + PanelHeight={this.leftMenuFlyoutHeight} + renderDepth={0} + isContentActive={returnTrue} + scriptContext={CollectionDockingView.Instance?.props.Document} + focus={DocUtils.DefaultFocus} + whenChildContentsActiveChanged={emptyFunction} + bringToFront={emptyFunction} + docFilters={returnEmptyFilter} + docRangeFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + /> + </div> + {this.docButtons} + </div>; + } + + @computed get leftMenuPanel() { + return <div key="menu" className="mainView-leftMenuPanel"> + <DocumentView + Document={Doc.UserDoc().menuStack as Doc} DataDoc={undefined} addDocument={undefined} addDocTab={this.addDocTabFunc} pinToPres={emptyFunction} - docViewPath={returnEmptyDoclist} - layerProvider={undefined} - styleProvider={this._sidebarContent.proto === Doc.UserDoc().myDashboards || this._sidebarContent.proto === Doc.UserDoc().myFilesystem ? DashboardStyleProvider : DefaultStyleProvider} rootSelected={returnTrue} removeDocument={returnFalse} - ScreenToLocalTransform={this.mainContainerXf} - PanelWidth={this.leftMenuFlyoutWidth} - PanelHeight={this.leftMenuFlyoutHeight} + ScreenToLocalTransform={this.sidebarScreenToLocal} + PanelWidth={this.leftMenuWidth} + PanelHeight={this.leftMenuHeight} renderDepth={0} - isContentActive={returnTrue} - scriptContext={CollectionDockingView.Instance?.props.Document} + docViewPath={returnEmptyDoclist} focus={DocUtils.DefaultFocus} + styleProvider={DefaultStyleProvider} + layerProvider={undefined} + isContentActive={returnTrue} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} docFilters={returnEmptyFilter} @@ -379,333 +411,301 @@ export class MainView extends React.Component { searchFilterDocs={returnEmptyDoclist} ContainingCollectionView={undefined} ContainingCollectionDoc={undefined} - /> - </div> - {this.docButtons} + scriptContext={this} + /> </div>; - } - - @computed get leftMenuPanel() { - return <div key="menu" className="mainView-leftMenuPanel"> - <DocumentView - Document={Doc.UserDoc().menuStack as Doc} - DataDoc={undefined} - addDocument={undefined} - addDocTab={this.addDocTabFunc} - pinToPres={emptyFunction} - rootSelected={returnTrue} - removeDocument={returnFalse} - ScreenToLocalTransform={this.sidebarScreenToLocal} - PanelWidth={this.leftMenuWidth} - PanelHeight={this.leftMenuHeight} - renderDepth={0} - docViewPath={returnEmptyDoclist} - focus={DocUtils.DefaultFocus} - styleProvider={DefaultStyleProvider} - layerProvider={undefined} - isContentActive={returnTrue} - whenChildContentsActiveChanged={emptyFunction} - bringToFront={emptyFunction} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - scriptContext={this} - /> - </div>; - } - - @action - selectMenu = (button: Doc) => { - const title = StrCast(Doc.GetProto(button).title); - const willOpen = !this._leftMenuFlyoutWidth || this._panelContent !== title; - this.closeFlyout(); - if (willOpen) { - switch (this._panelContent = title) { - case "Settings": - SettingsManager.Instance.open(); - break; - case "Help": - break; - default: - this.expandFlyout(button); + } + + @action + selectMenu = (button: Doc) => { + const title = StrCast(Doc.GetProto(button).title); + const willOpen = !this._leftMenuFlyoutWidth || this._panelContent !== title; + this.closeFlyout(); + if (willOpen) { + switch (this._panelContent = title) { + case "Settings": + SettingsManager.Instance.open(); + break; + case "Help": + break; + default: + this.expandFlyout(button); + } } - } - return true; - } - - @computed get mainInnerContent() { - const leftMenuFlyoutWidth = this._leftMenuFlyoutWidth + this.leftMenuWidth(); - const width = this.propertiesWidth() + leftMenuFlyoutWidth; - return <> - {this.leftMenuPanel} - <div key="inner" className={`mainView-innerContent${this.colorScheme}`}> - {this.flyout} - <div className="mainView-libraryHandle" style={{ left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} > - <FontAwesomeIcon icon="chevron-left" color={this.colorScheme === ColorScheme.Dark ? "white" : "black"} style={{ opacity: "50%" }} size="sm" /> - </div> - <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}> - - {this.dockingContent} - - <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}> - <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" /> - </div> - <div className="properties-container" style={{ width: this.propertiesWidth() }}> - {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={this.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />} - </div> - </div> - </div> - </>; - } - - @computed get mainDashboardArea() { - return !this.userDoc ? (null) : - <div className="mainView-dashboardArea" ref={r => { - r && new _global.ResizeObserver(action(() => { - this._dashUIWidth = r.getBoundingClientRect().width; - this._dashUIHeight = r.getBoundingClientRect().height; - })).observe(r); - }} style={{ - color: this.colorScheme === ColorScheme.Dark ? "rgb(205,205,205)" : "black", - height: `calc(100% - ${this.topOfDashUI + this.topMenuHeight()}px)`, - width: "100%", - }} > - {this.mainInnerContent} - </div>; - } - - - expandFlyout = action((button: Doc) => { - // bcz: What's going on here!? - // Chrome(not firefox) seems to have a bug when the flyout expands and there's a zoomed freeform tab. All of the div below the CollectionFreeFormView's main div - // generate the wrong value from getClientRectangle() -- specifically they return an 'x' that is the flyout's width greater than it should be. - // interactively adjusting the flyout fixes the problem. So does programmatically changing the value after a timeout to something *fractionally* different (ie, 1.5, not 1);) - this._leftMenuFlyoutWidth = (this._leftMenuFlyoutWidth || 250); - setTimeout(action(() => this._leftMenuFlyoutWidth += 0.5), 0); - - this._sidebarContent.proto = button.target as any; - this.LastButton = button; - }); - - closeFlyout = action(() => { - this.LastButton = undefined; - this._panelContent = "none"; - this._sidebarContent.proto = undefined; - this._leftMenuFlyoutWidth = 0; - }); - - remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true); - moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc); - addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true); - - buttonBarXf = () => { - if (!this._docBtnRef.current) return Transform.Identity(); - const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current); - return new Transform(-translateX, -translateY, 1 / scale); - } - - @computed get docButtons() { - return !(this.userDoc.dockedBtns instanceof Doc) ? (null) : - <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !this.userDoc.dockedBtns.linearViewIsExpanded ? "42px" : undefined }} > - <CollectionLinearView - Document={this.userDoc.dockedBtns} - DataDoc={undefined} - fieldKey={"data"} - dropAction={"alias"} - setHeight={returnFalse} - styleProvider={DefaultStyleProvider} - layerProvider={undefined} - rootSelected={returnTrue} - bringToFront={emptyFunction} - select={emptyFunction} - isAnyChildContentActive={returnFalse} - isContentActive={emptyFunction} - isSelected={returnFalse} - docViewPath={returnEmptyDoclist} - moveDocument={this.moveButtonDoc} - CollectionView={undefined} - addDocument={this.addButtonDoc} - addDocTab={this.addDocTabFunc} - pinToPres={emptyFunction} - removeDocument={this.remButtonDoc} - ScreenToLocalTransform={this.buttonBarXf} - PanelWidth={this.leftMenuFlyoutWidth} - PanelHeight={this.leftMenuFlyoutHeight} - renderDepth={0} - focus={DocUtils.DefaultFocus} - whenChildContentsActiveChanged={emptyFunction} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} /> - {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{this.userDoc?.presentationMode}</div>: <></>} + return true; + } + + @computed get mainInnerContent() { + const leftMenuFlyoutWidth = this._leftMenuFlyoutWidth + this.leftMenuWidth(); + const width = this.propertiesWidth() + leftMenuFlyoutWidth; + return <> + {this.leftMenuPanel} + <div key="inner" className={`mainView-innerContent${this.colorScheme}`}> + {this.flyout} + <div className="mainView-libraryHandle" style={{ left: leftMenuFlyoutWidth - 10 /* ~half width of handle */, display: !this._leftMenuFlyoutWidth ? "none" : undefined }} onPointerDown={this.onFlyoutPointerDown} > + <FontAwesomeIcon icon="chevron-left" color={this.colorScheme === ColorScheme.Dark ? "white" : "black"} style={{ opacity: "50%" }} size="sm" /> + </div> + <div className="mainView-innerContainer" style={{ width: `calc(100% - ${width}px)` }}> + + {this.dockingContent} + + <div className="mainView-propertiesDragger" key="props" onPointerDown={this.onPropertiesPointerDown} style={{ right: this.propertiesWidth() - 1 }}> + <FontAwesomeIcon icon={this.propertiesWidth() < 10 ? "chevron-left" : "chevron-right"} color={this.colorScheme === ColorScheme.Dark ? Colors.WHITE : Colors.BLACK} size="sm" /> + </div> + <div className="properties-container" style={{ width: this.propertiesWidth() }}> + {this.propertiesWidth() < 10 ? (null) : <PropertiesView styleProvider={DefaultStyleProvider} addDocTab={this.addDocTabFunc} width={this.propertiesWidth()} height={this.propertiesHeight()} />} + </div> + </div> + </div> + </>; + } + + @computed get mainDashboardArea() { + return !this.userDoc ? (null) : + <div className="mainView-dashboardArea" ref={r => { + r && new _global.ResizeObserver(action(() => { + this._dashUIWidth = r.getBoundingClientRect().width; + this._dashUIHeight = r.getBoundingClientRect().height; + })).observe(r); + }} style={{ + color: this.colorScheme === ColorScheme.Dark ? "rgb(205,205,205)" : "black", + height: `calc(100% - ${this.topOfDashUI + this.topMenuHeight()}px)`, + width: "100%", + }} > + {this.mainInnerContent} + </div>; + } + + + expandFlyout = action((button: Doc) => { + // bcz: What's going on here!? + // Chrome(not firefox) seems to have a bug when the flyout expands and there's a zoomed freeform tab. All of the div below the CollectionFreeFormView's main div + // generate the wrong value from getClientRectangle() -- specifically they return an 'x' that is the flyout's width greater than it should be. + // interactively adjusting the flyout fixes the problem. So does programmatically changing the value after a timeout to something *fractionally* different (ie, 1.5, not 1);) + this._leftMenuFlyoutWidth = (this._leftMenuFlyoutWidth || 250); + setTimeout(action(() => this._leftMenuFlyoutWidth += 0.5), 0); + + this._sidebarContent.proto = button.target as any; + this.LastButton = button; + }); + + closeFlyout = action(() => { + this.LastButton = undefined; + this._panelContent = "none"; + this._sidebarContent.proto = undefined; + this._leftMenuFlyoutWidth = 0; + }); + + remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true); + moveButtonDoc = (doc: Doc | Doc[], targetCollection: Doc | undefined, addDocument: (document: Doc | Doc[]) => boolean) => this.remButtonDoc(doc) && addDocument(doc); + addButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.AddDocToList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true); + + buttonBarXf = () => { + if (!this._docBtnRef.current) return Transform.Identity(); + const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current); + return new Transform(-translateX, -translateY, 1 / scale); + } + + @computed get docButtons() { + return !(this.userDoc.dockedBtns instanceof Doc) ? (null) : + <div className="mainView-docButtons" ref={this._docBtnRef} style={{ height: !this.userDoc.dockedBtns.linearViewIsExpanded ? "42px" : undefined }} > + <CollectionLinearView + Document={this.userDoc.dockedBtns} + DataDoc={undefined} + fieldKey={"data"} + dropAction={"alias"} + setHeight={returnFalse} + styleProvider={DefaultStyleProvider} + layerProvider={undefined} + rootSelected={returnTrue} + bringToFront={emptyFunction} + select={emptyFunction} + isAnyChildContentActive={returnFalse} + isContentActive={emptyFunction} + isSelected={returnFalse} + docViewPath={returnEmptyDoclist} + moveDocument={this.moveButtonDoc} + CollectionView={undefined} + addDocument={this.addButtonDoc} + addDocTab={this.addDocTabFunc} + pinToPres={emptyFunction} + removeDocument={this.remButtonDoc} + ScreenToLocalTransform={this.buttonBarXf} + PanelWidth={this.leftMenuFlyoutWidth} + PanelHeight={this.leftMenuFlyoutHeight} + renderDepth={0} + focus={DocUtils.DefaultFocus} + whenChildContentsActiveChanged={emptyFunction} + docFilters={returnEmptyFilter} + docRangeFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} /> + {['watching', 'recording'].includes(String(this.userDoc?.presentationMode) ?? '') ? <div style={{ border: '.5rem solid green', padding: '5px' }}>{this.userDoc?.presentationMode}</div> : <></>} + </div>; + } + @computed get snapLines() { + return !this.userDoc.showSnapLines ? (null) : <div className="mainView-snapLines"> + <svg style={{ width: "100%", height: "100%" }}> + {SnappingManager.horizSnapLines().map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)} + {SnappingManager.vertSnapLines().map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)} + </svg> </div>; - } - @computed get snapLines() { - return !this.userDoc.showSnapLines ? (null) : <div className="mainView-snapLines"> - <svg style={{ width: "100%", height: "100%" }}> - {SnappingManager.horizSnapLines().map(l => <line x1="0" y1={l} x2="2000" y2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)} - {SnappingManager.vertSnapLines().map(l => <line y1="0" x1={l} y2="2000" x2={l} stroke="black" opacity={0.3} strokeWidth={0.5} strokeDasharray={"1 1"} />)} - </svg> - </div>; - } - - @computed get inkResources() { - return <svg width={0} height={0}> - <defs> - <filter id="inkSelectionHalo"> - <feColorMatrix type="matrix" - result="color" - values="1 0 0 0 0 + } + + @computed get inkResources() { + return <svg width={0} height={0}> + <defs> + <filter id="inkSelectionHalo"> + <feColorMatrix type="matrix" + result="color" + values="1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0"> - </feColorMatrix> - <feGaussianBlur in="color" stdDeviation="4" result="blur"></feGaussianBlur> - <feOffset in="blur" dx="0" dy="0" result="offset"></feOffset> - <feMerge> - <feMergeNode in="bg"></feMergeNode> - <feMergeNode in="offset"></feMergeNode> - <feMergeNode in="SourceGraphic"></feMergeNode> - </feMerge> - </filter> - </defs> - </svg>; - } - - @computed get topbar() { - TraceMobx(); - return <div className="mainView-topbar"> - <TopBar /> - </div>; - } - - @computed get invisibleWebBox() { // see note under the makeLink method in HypothesisUtils.ts - return !DocumentLinksButton.invisibleWebDoc ? null : - <div className="mainView-invisibleWebRef" ref={DocumentLinksButton.invisibleWebRef}> - <WebBox - fieldKey={"data"} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - Document={DocumentLinksButton.invisibleWebDoc} - dropAction={"move"} - layerProvider={undefined} - styleProvider={undefined} - isSelected={returnFalse} - select={returnFalse} - setHeight={returnFalse} - rootSelected={returnFalse} - renderDepth={0} - addDocTab={returnFalse} - pinToPres={returnFalse} - ScreenToLocalTransform={Transform.Identity} - bringToFront={returnFalse} - isContentActive={emptyFunction} - whenChildContentsActiveChanged={returnFalse} - focus={returnFalse} - docViewPath={returnEmptyDoclist} - PanelWidth={() => 500} - PanelHeight={() => 800} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - /> + </feColorMatrix> + <feGaussianBlur in="color" stdDeviation="4" result="blur"></feGaussianBlur> + <feOffset in="blur" dx="0" dy="0" result="offset"></feOffset> + <feMerge> + <feMergeNode in="bg"></feMergeNode> + <feMergeNode in="offset"></feMergeNode> + <feMergeNode in="SourceGraphic"></feMergeNode> + </feMerge> + </filter> + </defs> + </svg>; + } + + @computed get topbar() { + TraceMobx(); + return <div className="mainView-topbar"> + <TopBar /> </div>; - } - - render() { - return (<div className={`mainView-container${this.colorScheme}`} - onScroll={() => ((ele) => ele.scrollTop = ele.scrollLeft = 0)(document.getElementById("root")!)} - ref={r => { - r && new _global.ResizeObserver(action(() => { this._windowWidth = r.getBoundingClientRect().width; this._windowHeight = r.getBoundingClientRect().height; })).observe(r); - }}> - {this.inkResources} - <DictationOverlay /> - <SharingManager /> - <SettingsManager /> - <CaptureManager /> - <GroupManager /> - <GoogleAuthenticationManager /> - <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} /> - <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} /> - {this.topbar} - {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null} - {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)} - {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)} - <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 2001 }} > - <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} /> - </div> - <GestureOverlay > - {this.mainDashboardArea} - </GestureOverlay> - <PreviewCursor /> - <TaskCompletionBox /> - <ContextMenu /> - <RadialMenu /> - <AnchorMenu /> - <MarqueeOptionsMenu /> - <OverlayView /> - <TimelineMenu /> - <RichTextMenu /> - {this.snapLines} - <div className="mainView-webRef" ref={this.makeWebRef} /> - <LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} /> - </div >); - } - - makeWebRef = (ele: HTMLDivElement) => { - reaction(() => DocumentLinksButton.invisibleWebDoc, - invisibleDoc => { - ReactDOM.unmountComponentAtNode(ele); - invisibleDoc && ReactDOM.render(<span title="Drag as document" className="invisible-webbox" > - <div className="mainView-webRef" ref={DocumentLinksButton.invisibleWebRef}> + } + + @computed get invisibleWebBox() { // see note under the makeLink method in HypothesisUtils.ts + return !DocumentLinksButton.invisibleWebDoc ? null : + <div className="mainView-invisibleWebRef" ref={DocumentLinksButton.invisibleWebRef}> <WebBox - fieldKey={"data"} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - Document={invisibleDoc} - dropAction={"move"} - isSelected={returnFalse} - docViewPath={returnEmptyDoclist} - select={returnFalse} - rootSelected={returnFalse} - renderDepth={0} - setHeight={returnFalse} - layerProvider={undefined} - styleProvider={undefined} - addDocTab={returnFalse} - pinToPres={returnFalse} - ScreenToLocalTransform={Transform.Identity} - bringToFront={returnFalse} - isContentActive={emptyFunction} - whenChildContentsActiveChanged={returnFalse} - focus={returnFalse} - PanelWidth={() => 500} - PanelHeight={() => 800} - docFilters={returnEmptyFilter} - docRangeFilters={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} + fieldKey={"data"} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + Document={DocumentLinksButton.invisibleWebDoc} + dropAction={"move"} + layerProvider={undefined} + styleProvider={undefined} + isSelected={returnFalse} + select={returnFalse} + setHeight={returnFalse} + rootSelected={returnFalse} + renderDepth={0} + addDocTab={returnFalse} + pinToPres={returnFalse} + ScreenToLocalTransform={Transform.Identity} + bringToFront={returnFalse} + isContentActive={emptyFunction} + whenChildContentsActiveChanged={returnFalse} + focus={returnFalse} + docViewPath={returnEmptyDoclist} + PanelWidth={() => 500} + PanelHeight={() => 800} + docFilters={returnEmptyFilter} + docRangeFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} /> - </div>; - </span>, ele); - - let success = false; - const onSuccess = () => { - success = true; - clearTimeout(interval); - document.removeEventListener("editSuccess", onSuccess); - }; - - // For some reason, Hypothes.is annotations don't load until a click is registered on the page, - // so we keep simulating clicks until annotations have loaded and editing is successful - const interval = setInterval(() => !success && simulateMouseClick(ele, 50, 50, 50, 50), 500); - setTimeout(() => !success && clearInterval(interval), 10000); // give up if no success after 10s - document.addEventListener("editSuccess", onSuccess); - }); - } + </div>; + } + + render() { + return (<div className={`mainView-container${this.colorScheme}`} + onScroll={() => ((ele) => ele.scrollTop = ele.scrollLeft = 0)(document.getElementById("root")!)} + ref={r => { + r && new _global.ResizeObserver(action(() => { this._windowWidth = r.getBoundingClientRect().width; this._windowHeight = r.getBoundingClientRect().height; })).observe(r); + }}> + {this.inkResources} + <DictationOverlay /> + <SharingManager /> + <SettingsManager /> + <CaptureManager /> + <GroupManager /> + <GoogleAuthenticationManager /> + <DocumentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDoc} PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} /> + <ComponentDecorations boundsLeft={this.leftScreenOffsetOfMainDocView} boundsTop={this.topOfMainDocContent} /> + {this.topbar} + {LinkDescriptionPopup.descriptionPopup ? <LinkDescriptionPopup /> : null} + {DocumentLinksButton.LinkEditorDocView ? <LinkMenu clearLinkEditor={action(() => DocumentLinksButton.LinkEditorDocView = undefined)} docView={DocumentLinksButton.LinkEditorDocView} /> : (null)} + {LinkDocPreview.LinkInfo ? <LinkDocPreview {...LinkDocPreview.LinkInfo} /> : (null)} + <div style={{ position: "relative", display: LightboxView.LightboxDoc ? "none" : undefined, zIndex: 2001 }} > + <CollectionMenu panelWidth={this.topMenuWidth} panelHeight={this.topMenuHeight} /> + </div> + <GestureOverlay > + {this.mainDashboardArea} + </GestureOverlay> + <PreviewCursor /> + <TaskCompletionBox /> + <ContextMenu /> + <RadialMenu /> + <AnchorMenu /> + <MarqueeOptionsMenu /> + <OverlayView /> + <TimelineMenu /> + <RichTextMenu /> + {this.snapLines} + <div className="mainView-webRef" ref={this.makeWebRef} /> + <LightboxView PanelWidth={this._windowWidth} PanelHeight={this._windowHeight} maxBorder={[200, 50]} /> + </div >); + } + + makeWebRef = (ele: HTMLDivElement) => { + reaction(() => DocumentLinksButton.invisibleWebDoc, + invisibleDoc => { + ReactDOM.unmountComponentAtNode(ele); + invisibleDoc && ReactDOM.render(<span title="Drag as document" className="invisible-webbox" > + <div className="mainView-webRef" ref={DocumentLinksButton.invisibleWebRef}> + <WebBox + fieldKey={"data"} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + Document={invisibleDoc} + dropAction={"move"} + isSelected={returnFalse} + docViewPath={returnEmptyDoclist} + select={returnFalse} + rootSelected={returnFalse} + renderDepth={0} + setHeight={returnFalse} + layerProvider={undefined} + styleProvider={undefined} + addDocTab={returnFalse} + pinToPres={returnFalse} + ScreenToLocalTransform={Transform.Identity} + bringToFront={returnFalse} + isContentActive={emptyFunction} + whenChildContentsActiveChanged={returnFalse} + focus={returnFalse} + PanelWidth={() => 500} + PanelHeight={() => 800} + docFilters={returnEmptyFilter} + docRangeFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + /> + </div>; + </span>, ele); + + let success = false; + const onSuccess = () => { + success = true; + clearTimeout(interval); + document.removeEventListener("editSuccess", onSuccess); + }; + + // For some reason, Hypothes.is annotations don't load until a click is registered on the page, + // so we keep simulating clicks until annotations have loaded and editing is successful + const interval = setInterval(() => !success && simulateMouseClick(ele, 50, 50, 50, 50), 500); + setTimeout(() => !success && clearInterval(interval), 10000); // give up if no success after 10s + document.addEventListener("editSuccess", onSuccess); + }); + } } ScriptingGlobals.add(function selectMainMenu(doc: Doc, title: string) { MainView.Instance.selectMenu(doc); });
\ No newline at end of file diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx index 6aed07c60..a2a08f5b8 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -67,23 +67,13 @@ export function RecordingView(props: IRecordingViewProps) { if (finished) { props.setDuration(recordingTimer * 100) let allVideoChunks: any = [] - console.log(videos) videos.forEach((vid) => { console.log(vid.videoChunks) allVideoChunks = allVideoChunks.concat(vid.videoChunks) }) - console.log(allVideoChunks) const videoFile = new File(allVideoChunks, "video.mkv", { type: allVideoChunks[0].type, lastModified: Date.now() }); - // const uploadVideo = async () => { - // const [{ result }] = await Networking.UploadFilesToServer(videoFile); - // console.log("upload result", result); - // if (!(result instanceof Error)) { - // setResult(result) - // } - // } - Networking.UploadFilesToServer(videoFile) .then((data) => { const result = data[0].result @@ -93,13 +83,9 @@ export function RecordingView(props: IRecordingViewProps) { alert("video conversion failed"); } }) - // uploadVideo() // this.dataDoc[this.fieldKey + "-duration"] = (new Date().getTime() - this.recordingStart!) / 1000; - - - // change to one recording box } @@ -190,7 +176,6 @@ export function RecordingView(props: IRecordingViewProps) { // recording paused videoRecorder.current.onpause = (event: any) => { // append the current portion to the video pieces - console.log(videoChunks) setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current }]) // reset the temporary chunks @@ -199,7 +184,6 @@ export function RecordingView(props: IRecordingViewProps) { } videoRecorder.current.onresume = async (event: any) => { - console.log(event) await startShowingStream(); setRecording(true); } @@ -226,7 +210,6 @@ export function RecordingView(props: IRecordingViewProps) { } const startOrResume = () => { - console.log('[RecordingView.tsx] startOrResume') if (!videoRecorder.current || videoRecorder.current.state === "inactive") { record(); } else if (videoRecorder.current.state === "paused") { @@ -234,22 +217,6 @@ export function RecordingView(props: IRecordingViewProps) { } } - // const playSegment = (idx: number) => { - // console.log(idx) - // let currentChunks = videos[idx].chunks - // console.log(currentChunks) - - // const blob = new Blob(currentChunks, { - // type: 'video/webm' - // }) - // const blobUrl = URL.createObjectURL(blob) - // console.log(blobUrl) - - // videoElementRef.current!.srcObject = null - // videoElementRef.current!.src = blobUrl - // videoElementRef.current!.muted = false - // } - const clearPrevious = () => { const numVideos = videos.length setRecordingTimer(numVideos == 1 ? 0 : videos[numVideos - 2].endTime) @@ -284,14 +251,6 @@ export function RecordingView(props: IRecordingViewProps) { return toTwoDigit(minutes) + " : " + toTwoDigit(seconds); } - - - - useEffect(() => { - console.log(videos.map((elt) => elt.endTime / MAXTIME * 100)) - console.log(videos) - }, [videos]) - return ( <div className="recording-container"> <div className="video-wrapper"> diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index ca13590de..71b165618 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -30,540 +30,540 @@ import { WebBox } from '../WebBox'; import { FontIconBadge } from './FontIconBadge'; import './FontIconBox.scss'; const FontIconSchema = createSchema({ - icon: "string", + icon: "string", }); export enum ButtonType { - TextButton = "textBtn", - MenuButton = "menuBtn", - DropdownList = "drpdownList", - DropdownButton = "drpdownBtn", - ClickButton = "clickBtn", - DoubleButton = "dblBtn", - ToggleButton = "tglBtn", - ColorButton = "colorBtn", - ToolButton = "toolBtn", - NumberButton = "numBtn", - EditableText = "editableText" + TextButton = "textBtn", + MenuButton = "menuBtn", + DropdownList = "drpdownList", + DropdownButton = "drpdownBtn", + ClickButton = "clickBtn", + DoubleButton = "dblBtn", + ToggleButton = "tglBtn", + ColorButton = "colorBtn", + ToolButton = "toolBtn", + NumberButton = "numBtn", + EditableText = "editableText" } export enum NumButtonType { - Slider = "slider", - DropdownOptions = "list", - Inline = "inline" + Slider = "slider", + DropdownOptions = "list", + Inline = "inline" } export interface ButtonProps extends FieldViewProps { - type?: ButtonType; + type?: ButtonType; } @observer export class FontIconBox extends DocComponent<ButtonProps>() { - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); } - showTemplate = (): void => { - const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null); - dragFactory && this.props.addDocTab(dragFactory, "add:right"); - } - dragAsTemplate = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); }; - useAsPrototype = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'); }; - - specificContextMenu = (): void => { - if (!Doc.UserDoc().noviceMode) { - const cm = ContextMenu.Instance; - cm.addItem({ description: "Show Template", event: this.showTemplate, icon: "tag" }); - cm.addItem({ description: "Use as Render Template", event: this.dragAsTemplate, icon: "tag" }); - cm.addItem({ description: "Use as Prototype", event: this.useAsPrototype, icon: "tag" }); - } - } - - // Determining UI Specs - @observable private label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title)); - @observable private icon = StrCast(this.dataDoc.icon, "user") as any; - @observable private dropdown: boolean = BoolCast(this.rootDoc.dropDownOpen); - @observable private buttonList: string[] = StrListCast(this.rootDoc.btnList); - @observable private type = StrCast(this.rootDoc.btnType); - - /** - * Types of buttons in dash: - * - Main menu button (LHS) - * - Tool button - * - Expandable button (CollectionLinearView) - * - Button inside of CollectionLinearView vs. outside of CollectionLinearView - * - Action button - * - Dropdown button - * - Color button - * - Dropdown list - * - Number button - **/ - - _batch: UndoManager.Batch | undefined = undefined; - /** - * Number button - */ - @computed get numberButton() { - const numBtnType: string = StrCast(this.rootDoc.numBtnType); - const numScript = ScriptCast(this.rootDoc.script); - const setValue = (value: number) => numScript?.script.run({ value, _readOnly_: false }); - - // Script for checking the outcome of the toggle - const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0; - - if (numBtnType === NumButtonType.Slider) { - const dropdown = - <div - className="menuButton-dropdownBox" - onPointerDown={e => e.stopPropagation()} - > - <input type="range" step="1" min={NumCast(this.rootDoc.numBtnMin, 0)} max={NumCast(this.rootDoc.numBtnMax, 100)} value={checkResult} - className={"menu-slider"} id="slider" - onPointerDown={() => this._batch = UndoManager.StartBatch("presDuration")} - onPointerUp={() => this._batch?.end()} - onChange={e => { e.stopPropagation(); setValue(Number(e.target.value)); }} - /> - </div>; - return ( - <div - className={`menuButton ${this.type} ${numBtnType}`} - onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)} - > - {checkResult} - {this.rootDoc.dropDownOpen ? dropdown : null} - </div> - ); - } else if (numBtnType === NumButtonType.DropdownOptions) { - const items: number[] = []; - for (let i = 0; i < 100; i++) { - if (i % 2 === 0) { - items.push(i); - } + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); } + showTemplate = (): void => { + const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null); + dragFactory && this.props.addDocTab(dragFactory, "add:right"); + } + dragAsTemplate = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('getCopy(this.dragFactory, true)'); }; + useAsPrototype = (): void => { this.layoutDoc.onDragStart = ScriptField.MakeFunction('makeDelegate(this.dragFactory, true)'); }; + + specificContextMenu = (): void => { + if (!Doc.UserDoc().noviceMode) { + const cm = ContextMenu.Instance; + cm.addItem({ description: "Show Template", event: this.showTemplate, icon: "tag" }); + cm.addItem({ description: "Use as Render Template", event: this.dragAsTemplate, icon: "tag" }); + cm.addItem({ description: "Use as Prototype", event: this.useAsPrototype, icon: "tag" }); } - const list = items.map((value) => { - return <div className="list-item" key={`${value}`} - style={{ - backgroundColor: value === checkResult ? Colors.LIGHT_BLUE : undefined - }} - onClick={() => setValue(value)}> - {value} - </div>; - }); - return ( - <div - className={`menuButton ${this.type} ${numBtnType}`} - > - <div className={`button`} onClick={action((e) => setValue(Number(checkResult) - 1))}> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"minus"} /> - </div> - <div - className={`button ${'number'}`} - onPointerDown={(e) => { - e.stopPropagation(); - e.preventDefault(); - }} - onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)} - > - <input - style={{ width: 30 }} - className="button-input" - type="number" - value={checkResult} - onChange={action((e) => setValue(Number(e.target.value)))} - /> - </div> - <div className={`button`} onClick={action((e) => setValue(Number(checkResult) + 1))}> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"plus"} /> - </div> - - {this.rootDoc.dropDownOpen ? + } + + // Determining UI Specs + @observable private label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title)); + @observable private icon = StrCast(this.dataDoc.icon, "user") as any; + @observable private dropdown: boolean = BoolCast(this.rootDoc.dropDownOpen); + @observable private buttonList: string[] = StrListCast(this.rootDoc.btnList); + @observable private type = StrCast(this.rootDoc.btnType); + + /** + * Types of buttons in dash: + * - Main menu button (LHS) + * - Tool button + * - Expandable button (CollectionLinearView) + * - Button inside of CollectionLinearView vs. outside of CollectionLinearView + * - Action button + * - Dropdown button + * - Color button + * - Dropdown list + * - Number button + **/ + + _batch: UndoManager.Batch | undefined = undefined; + /** + * Number button + */ + @computed get numberButton() { + const numBtnType: string = StrCast(this.rootDoc.numBtnType); + const numScript = ScriptCast(this.rootDoc.script); + const setValue = (value: number) => numScript?.script.run({ value, _readOnly_: false }); + + // Script for checking the outcome of the toggle + const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0; + + if (numBtnType === NumButtonType.Slider) { + const dropdown = + <div + className="menuButton-dropdownBox" + onPointerDown={e => e.stopPropagation()} + > + <input type="range" step="1" min={NumCast(this.rootDoc.numBtnMin, 0)} max={NumCast(this.rootDoc.numBtnMax, 100)} value={checkResult} + className={"menu-slider"} id="slider" + onPointerDown={() => this._batch = UndoManager.StartBatch("presDuration")} + onPointerUp={() => this._batch?.end()} + onChange={e => { e.stopPropagation(); setValue(Number(e.target.value)); }} + /> + </div>; + return ( + <div + className={`menuButton ${this.type} ${numBtnType}`} + onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)} + > + {checkResult} + {this.rootDoc.dropDownOpen ? dropdown : null} + </div> + ); + } else if (numBtnType === NumButtonType.DropdownOptions) { + const items: number[] = []; + for (let i = 0; i < 100; i++) { + if (i % 2 === 0) { + items.push(i); + } + } + const list = items.map((value) => { + return <div className="list-item" key={`${value}`} + style={{ + backgroundColor: value === checkResult ? Colors.LIGHT_BLUE : undefined + }} + onClick={() => setValue(value)}> + {value} + </div>; + }); + return ( + <div + className={`menuButton ${this.type} ${numBtnType}`} + > + <div className={`button`} onClick={action((e) => setValue(Number(checkResult) - 1))}> + <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"minus"} /> + </div> + <div + className={`button ${'number'}`} + onPointerDown={(e) => { + e.stopPropagation(); + e.preventDefault(); + }} + onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)} + > + <input + style={{ width: 30 }} + className="button-input" + type="number" + value={checkResult} + onChange={action((e) => setValue(Number(e.target.value)))} + /> + </div> + <div className={`button`} onClick={action((e) => setValue(Number(checkResult) + 1))}> + <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"plus"} /> + </div> + + {this.rootDoc.dropDownOpen ? + <div> + <div className="menuButton-dropdownList" + style={{ left: "25%" }}> + {list} + </div> + <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} /> + </div> : null} + + </div> + ); + } else { + return ( <div> - <div className="menuButton-dropdownList" - style={{ left: "25%" }}> - {list} - </div> - <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} /> - </div> : null} - - </div> - ); - } else { - return ( - <div> - </div> - ); - } - - - } - - /** - * Dropdown button - */ - @computed get dropdownButton() { - const active: string = StrCast(this.rootDoc.dropDownOpen); - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - return ( - <div className={`menuButton ${this.type} ${active}`} - style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} - onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> - {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>} - <div - className="menuButton-dropdown" - style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}> - <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" /> - </div> - {this.rootDoc.dropDownOpen ? - <div className="menuButton-dropdownBox"> - {/* DROPDOWN BOX CONTENTS */} - </div> : null} - </div> - ); - } - - /** - * Dropdown list - */ - @computed get dropdownListButton() { - const active: string = StrCast(this.rootDoc.dropDownOpen); - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - - const script = ScriptCast(this.rootDoc.script); - - let noviceList: string[] = []; - let text: string | undefined; - let dropdown = true; - let icon: IconProp = "caret-down"; - try { - if (script.script.originalScript.startsWith('setView')) { - const selected = SelectionManager.Docs().lastElement(); - if (selected) { - if (StrCast(selected.type) === DocumentType.COL) { - text = StrCast(selected._viewType); - } else { - dropdown = false; - text = selected.type === DocumentType.RTF ? "Text" : StrCast(selected.type); - icon = Doc.toIcon(selected); - } - } else { - dropdown = false; - icon = "globe-asia"; - text = "User Default"; - } - noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking]; - } else if (script.script.originalScript.startsWith('setFont')) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); - noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia", - "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]; - } - } catch (e) { - console.log(e); - } - - // Get items to place into the list - const list = this.buttonList.map((value) => { - if (Doc.UserDoc().noviceMode && !noviceList.includes(value)) { - return; + </div> + ); } - return <div className="list-item" key={`${value}`} - style={{ - fontFamily: script.script.originalScript.startsWith('setFont') ? value : undefined, - backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined - }} - onClick={() => script.script.run({ value }).result}> - {value[0].toUpperCase() + value.slice(1)} - </div>; - }); - const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : - <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}> - {this.label} - </div>; - return ( - <div className={`menuButton ${this.type} ${active}`} - style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : "flex" }} - onClick={dropdown ? () => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}> - {dropdown ? (null) : <FontAwesomeIcon style={{ marginLeft: 5 }} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />} - <div className="menuButton-dropdown-header"> - {text && text[0].toUpperCase() + text.slice(1)} - </div> - {label} - {!dropdown ? (null) : <div className="menuButton-dropDown"> - <FontAwesomeIcon icon={icon} color={color} size="sm" /> - </div>} - {this.rootDoc.dropDownOpen ? - <div> - <div className="menuButton-dropdownList" - style={{ left: 0 }}> - {list} - </div> - <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} /> - </div> - : null} - </div> - ); - } - - @observable colorPickerClosed: boolean = true; - @computed get colorScript() { return ScriptCast(this.rootDoc.script); } - - colorPicker = (curColor: string) => { - const change = (value: ColorState) => { - const s = this.colorScript; - s && undoBatch(() => s.script.run({ value: Utils.colorString(value), _readOnly_: false }).result)(); - }; - const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', - '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', - '#FFFFFF', '#f1efeb', "transparent"]; - return <SketchPicker - onChange={change} - color={curColor} - presetColors={presets} />; - } - /** - * Color button - */ - @computed get colorButton() { - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent"; - - const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : - <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}> - {this.label} - </div>; + } - const dropdownCaret = <div - className="menuButton-dropDown" - style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}> - <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" /> - </div>; - setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView - return ( - <div className={`menuButton ${this.type} ${this.colorPickerClosed}`} - style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} - onClick={action(() => this.colorPickerClosed = !this.colorPickerClosed)} - onPointerDown={e => e.stopPropagation()}> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> - <div className="colorButton-color" style={{ backgroundColor: curColor }} /> - {label} - {/* {dropdownCaret} */} - {this.colorPickerClosed ? (null) : - <div> - <div className="menuButton-dropdownBox" - onPointerDown={e => e.stopPropagation()} - onClick={e => e.stopPropagation()}> - {this.colorPicker(curColor)} + /** + * Dropdown button + */ + @computed get dropdownButton() { + const active: string = StrCast(this.rootDoc.dropDownOpen); + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + return ( + <div className={`menuButton ${this.type} ${active}`} + style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} + onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}> + <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> + {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>} + <div + className="menuButton-dropdown" + style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}> + <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" /> </div> - <div className="dropbox-background" onClick={action((e) => { - e.stopPropagation(); this.colorPickerClosed = true; - })} /> - </div>} - </div> - ); - } - - @computed get toggleButton() { - // Determine the type of toggle button - const switchToggle: boolean = BoolCast(this.rootDoc.switchToggle); - const buttonText: string = StrCast(this.rootDoc.buttonText); - // Colors - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - - // Button label - const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : - <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}> - {this.label} - </div>; + {this.rootDoc.dropDownOpen ? + <div className="menuButton-dropdownBox"> + {/* DROPDOWN BOX CONTENTS */} + </div> : null} + </div> + ); + } + + /** + * Dropdown list + */ + @computed get dropdownListButton() { + const active: string = StrCast(this.rootDoc.dropDownOpen); + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + + const script = ScriptCast(this.rootDoc.script); + + let noviceList: string[] = []; + let text: string | undefined; + let dropdown = true; + let icon: IconProp = "caret-down"; + try { + if (script.script.originalScript.startsWith('setView')) { + const selected = SelectionManager.Docs().lastElement(); + if (selected) { + if (StrCast(selected.type) === DocumentType.COL) { + text = StrCast(selected._viewType); + } else { + dropdown = false; + text = selected.type === DocumentType.RTF ? "Text" : StrCast(selected.type); + icon = Doc.toIcon(selected); + } + } else { + dropdown = false; + icon = "globe-asia"; + text = "User Default"; + } + noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking]; + } else if (script.script.originalScript.startsWith('setFont')) { + const editorView = RichTextMenu.Instance?.TextView?.EditorView; + text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); + noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia", + "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]; + } + } catch (e) { + console.log(e); + } + + // Get items to place into the list + const list = this.buttonList.map((value) => { + if (Doc.UserDoc().noviceMode && !noviceList.includes(value)) { + return; + } + return <div className="list-item" key={`${value}`} + style={{ + fontFamily: script.script.originalScript.startsWith('setFont') ? value : undefined, + backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined + }} + onClick={() => script.script.run({ value }).result}> + {value[0].toUpperCase() + value.slice(1)} + </div>; + }); + + const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : + <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor, position: "absolute" }}> + {this.label} + </div>; - if (switchToggle) { return ( - <div className={`menuButton ${this.type} ${'switch'}`}> - {buttonText ? buttonText : null} - <label className="switch"> - <input type="checkbox" - checked={backgroundColor === Colors.MEDIUM_BLUE} - /> - <span className="slider round"></span> - </label> - </div> + <div className={`menuButton ${this.type} ${active}`} + style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : "flex" }} + onClick={dropdown ? () => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}> + {dropdown ? (null) : <FontAwesomeIcon style={{ marginLeft: 5 }} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />} + <div className="menuButton-dropdown-header"> + {text && text[0].toUpperCase() + text.slice(1)} + </div> + {label} + {!dropdown ? (null) : <div className="menuButton-dropDown"> + <FontAwesomeIcon icon={icon} color={color} size="sm" /> + </div>} + {this.rootDoc.dropDownOpen ? + <div> + <div className="menuButton-dropdownList" + style={{ left: 0 }}> + {list} + </div> + <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} /> + </div> + : null} + </div> ); - } else { + } + + @observable colorPickerClosed: boolean = true; + @computed get colorScript() { return ScriptCast(this.rootDoc.script); } + + colorPicker = (curColor: string) => { + const change = (value: ColorState) => { + const s = this.colorScript; + s && undoBatch(() => s.script.run({ value: Utils.colorString(value), _readOnly_: false }).result)(); + }; + const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', + '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', + '#FFFFFF', '#f1efeb', "transparent"]; + return <SketchPicker + onChange={change} + color={curColor} + presetColors={presets} />; + } + /** + * Color button + */ + @computed get colorButton() { + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent"; + + const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : + <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}> + {this.label} + </div>; + + const dropdownCaret = <div + className="menuButton-dropDown" + style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}> + <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" /> + </div>; + setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView return ( - <div className={`menuButton ${this.type}`} - style={{ opacity: 1, backgroundColor, color }}> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> - {label} - </div> + <div className={`menuButton ${this.type} ${this.colorPickerClosed}`} + style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} + onClick={action(() => this.colorPickerClosed = !this.colorPickerClosed)} + onPointerDown={e => e.stopPropagation()}> + <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> + <div className="colorButton-color" style={{ backgroundColor: curColor }} /> + {label} + {/* {dropdownCaret} */} + {this.colorPickerClosed ? (null) : + <div> + <div className="menuButton-dropdownBox" + onPointerDown={e => e.stopPropagation()} + onClick={e => e.stopPropagation()}> + {this.colorPicker(curColor)} + </div> + <div className="dropbox-background" onClick={action((e) => { + e.stopPropagation(); this.colorPickerClosed = true; + })} /> + </div>} + </div> ); - } - } - - - - /** - * Default - */ - @computed get defaultButton() { - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const active: string = StrCast(this.rootDoc.dropDownOpen); - return ( - <div className={`menuButton ${this.type}`} onContextMenu={this.specificContextMenu} - style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}> - <div className="menuButton-wrap"> - <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={"black"} size={"sm"} /> - {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>} - </div> - </div> - ); - } - - @computed get editableText() { - // Script for running the toggle - const script = ScriptCast(this.rootDoc.script); - // Function to run the script - const checkResult = script?.script.run({ value: "", _readOnly_: true }).result; - - const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ value, _readOnly_: false }).result; - return ( - <div className="menuButton editableText"> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} /> - <div style={{ width: "calc(100% - .875em)", paddingLeft: "4px" }}> - <EditableView GetValue={() => script?.script.run({ value: "", _readOnly_: true }).result} SetValue={setValue} contents={checkResult} /> - </div> - </div> - ); - } - - render() { - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : - <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}> - {this.label} - </div>; + } + + @computed get toggleButton() { + // Determine the type of toggle button + const switchToggle: boolean = BoolCast(this.rootDoc.switchToggle); + const buttonText: string = StrCast(this.rootDoc.buttonText); + // Colors + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + + // Button label + const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : + <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}> + {this.label} + </div>; + + if (switchToggle) { + return ( + <div className={`menuButton ${this.type} ${'switch'}`}> + {buttonText ? buttonText : null} + <label className="switch"> + <input type="checkbox" + checked={backgroundColor === Colors.MEDIUM_BLUE} + /> + <span className="slider round"></span> + </label> + </div> + ); + } else { + return ( + <div className={`menuButton ${this.type}`} + style={{ opacity: 1, backgroundColor, color }}> + <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> + {label} + </div> + ); + } + } - const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) : - <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}> - {this.label} - </div>; - const buttonText = StrCast(this.rootDoc.buttonText); - // TODO:glr Add label of button type - let button = this.defaultButton; + /** + * Default + */ + @computed get defaultButton() { + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + const active: string = StrCast(this.rootDoc.dropDownOpen); + return ( + <div className={`menuButton ${this.type}`} onContextMenu={this.specificContextMenu} + style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}> + <div className="menuButton-wrap"> + <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={"black"} size={"sm"} /> + {!this.label || !Doc.UserDoc()._showLabel ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>} + </div> + </div> + ); + } - switch (this.type) { - case ButtonType.TextButton: - button = ( - <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> - {buttonText ? - <div className="button-text"> - {buttonText} - </div> - : null} - {label} - </div> - ); - // button = <TextButton {...buttonProps}></TextButton> - break; - case ButtonType.EditableText: - button = this.editableText; - break; - case ButtonType.NumberButton: - button = this.numberButton; - break; - case ButtonType.DropdownButton: - button = this.dropdownButton; - break; - case ButtonType.DropdownList: - button = this.dropdownListButton; - break; - case ButtonType.ColorButton: - button = this.colorButton; - break; - case ButtonType.ToolButton: - button = ( - <div className={`menuButton ${this.type}`} style={{ opacity: 1, backgroundColor, color }}> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> - {label} - </div> - ); - break; - case ButtonType.ToggleButton: - button = this.toggleButton; - // button = <ToggleButton {...buttonProps}></ToggleButton> - break; - case ButtonType.ClickButton: - button = ( - <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1 }}> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> - {label} - </div> - ); - break; - case ButtonType.MenuButton: - const trailsIcon = <img src={`/assets/${"presTrails.png"}`} - style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})` }} />; - button = ( - <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}> - {this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />} - {menuLabel} - <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} /> - </div > - ); - break; - default: - break; - } - - return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? button : - <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}> - {button} - </Tooltip>; - } + @computed get editableText() { + // Script for running the toggle + const script = ScriptCast(this.rootDoc.script); + // Function to run the script + const checkResult = script?.script.run({ value: "", _readOnly_: true }).result; + + const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ value, _readOnly_: false }).result; + return ( + <div className="menuButton editableText"> + <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} /> + <div style={{ width: "calc(100% - .875em)", paddingLeft: "4px" }}> + <EditableView GetValue={() => script?.script.run({ value: "", _readOnly_: true }).result} SetValue={setValue} contents={checkResult} /> + </div> + </div> + ); + } + + render() { + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : + <div className="fontIconBox-label" style={{ color, backgroundColor, position: "absolute" }}> + {this.label} + </div>; + + const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) : + <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}> + {this.label} + </div>; + + const buttonText = StrCast(this.rootDoc.buttonText); + + // TODO:glr Add label of button type + let button = this.defaultButton; + + switch (this.type) { + case ButtonType.TextButton: + button = ( + <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}> + <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> + {buttonText ? + <div className="button-text"> + {buttonText} + </div> + : null} + {label} + </div> + ); + // button = <TextButton {...buttonProps}></TextButton> + break; + case ButtonType.EditableText: + button = this.editableText; + break; + case ButtonType.NumberButton: + button = this.numberButton; + break; + case ButtonType.DropdownButton: + button = this.dropdownButton; + break; + case ButtonType.DropdownList: + button = this.dropdownListButton; + break; + case ButtonType.ColorButton: + button = this.colorButton; + break; + case ButtonType.ToolButton: + button = ( + <div className={`menuButton ${this.type}`} style={{ opacity: 1, backgroundColor, color }}> + <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> + {label} + </div> + ); + break; + case ButtonType.ToggleButton: + button = this.toggleButton; + // button = <ToggleButton {...buttonProps}></ToggleButton> + break; + case ButtonType.ClickButton: + button = ( + <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1 }}> + <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> + {label} + </div> + ); + break; + case ButtonType.MenuButton: + const trailsIcon = <img src={`/assets/${"presTrails.png"}`} + style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})` }} />; + button = ( + <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}> + {this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />} + {menuLabel} + <FontIconBadge collection={Cast(this.rootDoc.watchedDocuments, Doc, null)} /> + </div > + ); + break; + default: + break; + } + + return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? button : + <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}> + {button} + </Tooltip>; + } } // toggle: Set overlay status of selected document ScriptingGlobals.add(function setView(view: string) { - const selected = SelectionManager.Docs().lastElement(); - selected ? selected._viewType = view : console.log("[FontIconBox.tsx] changeView failed"); + const selected = SelectionManager.Docs().lastElement(); + selected ? selected._viewType = view : console.log("[FontIconBox.tsx] changeView failed"); }); // toggle: Set overlay status of selected document ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) { - const selected = SelectionManager.Docs().lastElement(); - if (checkResult) { - return selected?._backgroundColor ?? "transparent"; - } - if (selected) selected._backgroundColor = color; + const selected = SelectionManager.Docs().lastElement(); + if (checkResult) { + return selected?._backgroundColor ?? "transparent"; + } + if (selected) selected._backgroundColor = color; }); // toggle: Set overlay status of selected document ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boolean) { - if (checkResult) { - return Doc.SharingDoc().userColor; - } - Doc.SharingDoc().userColor = undefined; - Doc.GetProto(Doc.SharingDoc()).userColor = color; - Doc.UserDoc().showTitle = color === "transparent" ? undefined : StrCast(Doc.UserDoc().showTitle, "creationDate"); + if (checkResult) { + return Doc.SharingDoc().userColor; + } + Doc.SharingDoc().userColor = undefined; + Doc.GetProto(Doc.SharingDoc()).userColor = color; + Doc.UserDoc().showTitle = color === "transparent" ? undefined : StrCast(Doc.UserDoc().showTitle, "creationDate"); }); // toggle: Set overlay status of selected document ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { - const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; - if (checkResult && selected) { - if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE; - return "transparent"; - } - selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed"); + const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; + if (checkResult && selected) { + if (NumCast(selected.Document.z) >= 1) return Colors.MEDIUM_BLUE; + return "transparent"; + } + selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed"); }); /** TEXT @@ -580,115 +580,115 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { // toggle: Set overlay status of selected document ScriptingGlobals.add(function setFont(font: string, checkResult?: boolean) { - SelectionManager.Docs().map(doc => doc._fontFamily = font); - const editorView = RichTextMenu.Instance.TextView?.EditorView; - if (checkResult) { - return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); - } - if (editorView) RichTextMenu.Instance.setFontFamily(font); - else Doc.UserDoc().fontFamily = font; + SelectionManager.Docs().map(doc => doc._fontFamily = font); + const editorView = RichTextMenu.Instance.TextView?.EditorView; + if (checkResult) { + return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); + } + if (editorView) RichTextMenu.Instance.setFontFamily(font); + else Doc.UserDoc().fontFamily = font; }); ScriptingGlobals.add(function getActiveTextInfo(info: "family" | "size" | "color" | "highlight") { - const editorView = RichTextMenu.Instance.TextView?.EditorView; - const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection(); - switch (info) { - case "family": return style?.activeFamilies[0]; - case "size": return style?.activeSizes[0]; - case "color": return style?.activeColors[0]; - case "highlight": return style?.activeHighlights[0]; - } + const editorView = RichTextMenu.Instance.TextView?.EditorView; + const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection(); + switch (info) { + case "family": return style?.activeFamilies[0]; + case "size": return style?.activeSizes[0]; + case "color": return style?.activeColors[0]; + case "highlight": return style?.activeHighlights[0]; + } }); ScriptingGlobals.add(function setAlignment(align: "left" | "right" | "center", checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - if (checkResult) { - return (editorView ? RichTextMenu.Instance.textAlign : Doc.UserDoc().textAlign) === align ? Colors.MEDIUM_BLUE : "transparent"; - } - if (editorView?.state) RichTextMenu.Instance.align(editorView, editorView.dispatch, align); - else Doc.UserDoc().textAlign = align; + const editorView = RichTextMenu.Instance?.TextView?.EditorView; + if (checkResult) { + return (editorView ? RichTextMenu.Instance.textAlign : Doc.UserDoc().textAlign) === align ? Colors.MEDIUM_BLUE : "transparent"; + } + if (editorView?.state) RichTextMenu.Instance.align(editorView, editorView.dispatch, align); + else Doc.UserDoc().textAlign = align; }); ScriptingGlobals.add(function setBulletList(mapStyle: "bullet" | "decimal", checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - if (checkResult) { - const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle(); - if (active === mapStyle) return Colors.MEDIUM_BLUE; - return "transparent"; - } - if (editorView) { - const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle(); - editorView?.state && RichTextMenu.Instance.changeListType( - editorView.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? "" : mapStyle })); - } + const editorView = RichTextMenu.Instance?.TextView?.EditorView; + if (checkResult) { + const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle(); + if (active === mapStyle) return Colors.MEDIUM_BLUE; + return "transparent"; + } + if (editorView) { + const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle(); + editorView?.state && RichTextMenu.Instance.changeListType( + editorView.state.schema.nodes.ordered_list.create({ mapStyle: active === mapStyle ? "" : mapStyle })); + } }); // toggle: Set overlay status of selected document ScriptingGlobals.add(function setFontColor(color?: string, checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; + const editorView = RichTextMenu.Instance?.TextView?.EditorView; - if (checkResult) { - return editorView ? RichTextMenu.Instance.fontColor : Doc.UserDoc().fontColor; - } + if (checkResult) { + return editorView ? RichTextMenu.Instance.fontColor : Doc.UserDoc().fontColor; + } - if (editorView) color && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch); - else Doc.UserDoc().fontColor = color; + if (editorView) color && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch); + else Doc.UserDoc().fontColor = color; }); // toggle: Set overlay status of selected document ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boolean) { - const selected = SelectionManager.Docs().lastElement(); - const editorView = RichTextMenu.Instance.TextView?.EditorView; - - if (checkResult) { - return (selected ?? Doc.UserDoc())._fontHighlight; - } - if (selected) { - selected._fontColor = color; - if (color) { - editorView?.state && RichTextMenu.Instance.setHighlight(color, editorView, editorView?.dispatch); - } - } - Doc.UserDoc()._fontHighlight = color; + const selected = SelectionManager.Docs().lastElement(); + const editorView = RichTextMenu.Instance.TextView?.EditorView; + + if (checkResult) { + return (selected ?? Doc.UserDoc())._fontHighlight; + } + if (selected) { + selected._fontColor = color; + if (color) { + editorView?.state && RichTextMenu.Instance.setHighlight(color, editorView, editorView?.dispatch); + } + } + Doc.UserDoc()._fontHighlight = color; }); // toggle: Set overlay status of selected document ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - if (checkResult) { - return (editorView ? RichTextMenu.Instance.fontSize : StrCast(Doc.UserDoc().fontSize, "10px")).replace("px", ""); - } - if (typeof size === "number") size = size.toString(); - if (size && Number(size).toString() === size) size += "px"; - if (editorView) RichTextMenu.Instance.setFontSize(size); - else Doc.UserDoc()._fontSize = size; + const editorView = RichTextMenu.Instance?.TextView?.EditorView; + if (checkResult) { + return (editorView ? RichTextMenu.Instance.fontSize : StrCast(Doc.UserDoc().fontSize, "10px")).replace("px", ""); + } + if (typeof size === "number") size = size.toString(); + if (size && Number(size).toString() === size) size += "px"; + if (editorView) RichTextMenu.Instance.setFontSize(size); + else Doc.UserDoc()._fontSize = size; }); ScriptingGlobals.add(function toggleBold(checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - if (checkResult) { - return (editorView ? RichTextMenu.Instance.bold : Doc.UserDoc().fontWeight === "bold") ? Colors.MEDIUM_BLUE : "transparent"; - } - if (editorView) RichTextMenu.Instance?.toggleBold(); - else Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === "bold" ? undefined : "bold"; + const editorView = RichTextMenu.Instance?.TextView?.EditorView; + if (checkResult) { + return (editorView ? RichTextMenu.Instance.bold : Doc.UserDoc().fontWeight === "bold") ? Colors.MEDIUM_BLUE : "transparent"; + } + if (editorView) RichTextMenu.Instance?.toggleBold(); + else Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === "bold" ? undefined : "bold"; }); ScriptingGlobals.add(function toggleUnderline(checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - if (checkResult) { - return (editorView ? RichTextMenu.Instance.underline : Doc.UserDoc().textDecoration === "underline") ? Colors.MEDIUM_BLUE : "transparent"; - } - if (editorView) RichTextMenu.Instance?.toggleUnderline(); - else Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === "underline" ? undefined : "underline"; + const editorView = RichTextMenu.Instance?.TextView?.EditorView; + if (checkResult) { + return (editorView ? RichTextMenu.Instance.underline : Doc.UserDoc().textDecoration === "underline") ? Colors.MEDIUM_BLUE : "transparent"; + } + if (editorView) RichTextMenu.Instance?.toggleUnderline(); + else Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === "underline" ? undefined : "underline"; }); ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - if (checkResult) { - return (editorView ? RichTextMenu.Instance.italics : Doc.UserDoc().fontStyle === "italics") ? Colors.MEDIUM_BLUE : "transparent"; - } - if (editorView) RichTextMenu.Instance?.toggleItalics(); - else Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === "italics" ? undefined : "italics"; + const editorView = RichTextMenu.Instance?.TextView?.EditorView; + if (checkResult) { + return (editorView ? RichTextMenu.Instance.italics : Doc.UserDoc().fontStyle === "italics") ? Colors.MEDIUM_BLUE : "transparent"; + } + if (editorView) RichTextMenu.Instance?.toggleItalics(); + else Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === "italics" ? undefined : "italics"; }); @@ -701,66 +701,66 @@ ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) { **/ ScriptingGlobals.add(function setActiveInkTool(tool: string, checkResult?: boolean) { - if (checkResult) { - return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ? - Colors.MEDIUM_BLUE : "transparent"; - } - if (["circle", "square", "line"].includes(tool)) { - if (GestureOverlay.Instance.InkShape === tool) { - Doc.UserDoc().activeInkTool = InkTool.None; - GestureOverlay.Instance.InkShape = InkTool.None; - } else { - Doc.UserDoc().activeInkTool = InkTool.Pen; - GestureOverlay.Instance.InkShape = tool; - } - } else if (tool) { // pen or eraser - if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) { + if (checkResult) { + return ((Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ? + Colors.MEDIUM_BLUE : "transparent"; + } + if (["circle", "square", "line"].includes(tool)) { + if (GestureOverlay.Instance.InkShape === tool) { + Doc.UserDoc().activeInkTool = InkTool.None; + GestureOverlay.Instance.InkShape = InkTool.None; + } else { + Doc.UserDoc().activeInkTool = InkTool.Pen; + GestureOverlay.Instance.InkShape = tool; + } + } else if (tool) { // pen or eraser + if (Doc.UserDoc().activeInkTool === tool && !GestureOverlay.Instance.InkShape) { + Doc.UserDoc().activeInkTool = InkTool.None; + } else { + Doc.UserDoc().activeInkTool = tool; + GestureOverlay.Instance.InkShape = ""; + } + } else { Doc.UserDoc().activeInkTool = InkTool.None; - } else { - Doc.UserDoc().activeInkTool = tool; - GestureOverlay.Instance.InkShape = ""; - } - } else { - Doc.UserDoc().activeInkTool = InkTool.None; - } + } }); // toggle: Set overlay status of selected document ScriptingGlobals.add(function setFillColor(color?: string, checkResult?: boolean) { - const selected = SelectionManager.Docs().lastElement(); - if (checkResult) { - if (selected?.type === DocumentType.INK) { - return StrCast(selected.fillColor); - } - return ActiveFillColor(); - } - SetActiveFillColor(StrCast(color)); - SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.fillColor = color); + const selected = SelectionManager.Docs().lastElement(); + if (checkResult) { + if (selected?.type === DocumentType.INK) { + return StrCast(selected.fillColor); + } + return ActiveFillColor(); + } + SetActiveFillColor(StrCast(color)); + SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.fillColor = color); }); ScriptingGlobals.add(function setStrokeWidth(width: number, checkResult?: boolean) { - if (checkResult) { - const selected = SelectionManager.Docs().lastElement(); - if (selected?.type === DocumentType.INK) { - return NumCast(selected.strokeWidth); - } - return ActiveInkWidth(); - } - SetActiveInkWidth(width.toString()); - SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.strokeWidth = Number(width)); + if (checkResult) { + const selected = SelectionManager.Docs().lastElement(); + if (selected?.type === DocumentType.INK) { + return NumCast(selected.strokeWidth); + } + return ActiveInkWidth(); + } + SetActiveInkWidth(width.toString()); + SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.strokeWidth = Number(width)); }); // toggle: Set overlay status of selected document ScriptingGlobals.add(function setStrokeColor(color?: string, checkResult?: boolean) { - if (checkResult) { - const selected = SelectionManager.Docs().lastElement(); - if (selected?.type === DocumentType.INK) { - return StrCast(selected.color); - } - return ActiveInkColor(); - } - SetActiveInkColor(StrCast(color)); - SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.color = String(color)); + if (checkResult) { + const selected = SelectionManager.Docs().lastElement(); + if (selected?.type === DocumentType.INK) { + return StrCast(selected.color); + } + return ActiveInkColor(); + } + SetActiveInkColor(StrCast(color)); + SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.color = String(color)); }); @@ -768,28 +768,28 @@ ScriptingGlobals.add(function setStrokeColor(color?: string, checkResult?: boole * webSetURL **/ ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) { - const selected = SelectionManager.Views().lastElement(); - if (selected?.rootDoc.type === DocumentType.WEB) { - if (checkResult) { - return StrCast(selected.rootDoc.data, Cast(selected.rootDoc.data, WebField, null)?.url?.href); - } - (selected.ComponentView as WebBox).submitURL(url); - //selected.rootDoc.data = new WebField(url); - } + const selected = SelectionManager.Views().lastElement(); + if (selected?.rootDoc.type === DocumentType.WEB) { + if (checkResult) { + return StrCast(selected.rootDoc.data, Cast(selected.rootDoc.data, WebField, null)?.url?.href); + } + (selected.ComponentView as WebBox).submitURL(url); + //selected.rootDoc.data = new WebField(url); + } }); ScriptingGlobals.add(function webForward(checkResult?: boolean) { - const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox); - if (checkResult) { - return selected?.forward(checkResult) ? undefined : "lightGray"; - } - selected?.forward(); + const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox); + if (checkResult) { + return selected?.forward(checkResult) ? undefined : "lightGray"; + } + selected?.forward(); }); ScriptingGlobals.add(function webBack(checkResult?: boolean) { - const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox); - if (checkResult) { - return selected?.back(checkResult) ? undefined : "lightGray"; - } - selected?.back(); + const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox); + if (checkResult) { + return selected?.back(checkResult) ? undefined : "lightGray"; + } + selected?.back(); }); @@ -797,30 +797,30 @@ ScriptingGlobals.add(function webBack(checkResult?: boolean) { * toggleSchemaPreview **/ ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) { - const selected = SelectionManager.Docs().lastElement(); - if (checkResult && selected) { - const result: boolean = NumCast(selected.schemaPreviewWidth) > 0; - if (result) return Colors.MEDIUM_BLUE; - else return "transparent"; - } - else if (selected) { - if (NumCast(selected.schemaPreviewWidth) > 0) { - selected.schemaPreviewWidth = 200; - } else { - selected.schemaPreviewWidth = 0; - } - } + const selected = SelectionManager.Docs().lastElement(); + if (checkResult && selected) { + const result: boolean = NumCast(selected.schemaPreviewWidth) > 0; + if (result) return Colors.MEDIUM_BLUE; + else return "transparent"; + } + else if (selected) { + if (NumCast(selected.schemaPreviewWidth) > 0) { + selected.schemaPreviewWidth = 200; + } else { + selected.schemaPreviewWidth = 0; + } + } }); /** STACK * groupBy */ ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) { - SelectionManager.Docs().map(doc => doc._fontFamily = key); - const editorView = RichTextMenu.Instance.TextView?.EditorView; - if (checkResult) { - return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); - } - if (editorView) RichTextMenu.Instance.setFontFamily(key); - else Doc.UserDoc().fontFamily = key; + SelectionManager.Docs().map(doc => doc._fontFamily = key); + const editorView = RichTextMenu.Instance.TextView?.EditorView; + if (checkResult) { + return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); + } + if (editorView) RichTextMenu.Instance.setFontFamily(key); + else Doc.UserDoc().fontFamily = key; });
\ No newline at end of file diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index f4f33482e..bc1cea505 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -43,6 +43,7 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { @computed get presBox() { return Cast(this.lookupField("presBox"), Doc, null); } @computed get targetDoc() { return Cast(this.rootDoc.presentationTargetDoc, Doc, null) || this.rootDoc; } + @observable isShowingVideo = false; @action setIsShowingVideo(shown: boolean) { this.isShowingVideo = shown @@ -271,10 +272,31 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { } } + @computed get recordingIsInOverlay() { + let isInOverlay = false + DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => { + if (doc.slides === this.rootDoc) { + isInOverlay = true + return + } + }) + return isInOverlay + } + @undoBatch @action - startRecording = (targetDoc: Doc, activeItem: Doc) => { + hideRecording = (targetDoc: Doc, activeItem: Doc) => { + console.log("hide recording") + DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => { + if (doc.slides === activeItem) { + Doc.RemoveDocFromList((Doc.UserDoc().myOverlayDocs as Doc), undefined, doc); + } + }) + } + @undoBatch + @action + startRecording = (targetDoc: Doc, activeItem: Doc) => { // Remove every recording that already exists in overlay view DocListCast((Doc.UserDoc().myOverlayDocs as Doc).data).forEach((doc) => { if (doc.slides !== null) { @@ -284,21 +306,24 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { if (activeItem.recording) { // if we already have an existing recording + console.log("recording exists") Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, Cast(activeItem.recording, Doc, null)); } else { + console.log("creating new recording") // if we dont have any recording const recording = Docs.Create.WebCamDocument("", { _width: 400, _height: 200, title: "recording", system: true, cloneFieldFilter: new List<string>(["system"]) }); + // attach the recording to the slide, and attach the slide to the recording recording.slides = activeItem activeItem.recording = recording + // TODO: Figure out exactly where we want the video to appear const pt = this.props.ScreenToLocalTransform().inverse().transformPoint(0, 0); recording.x = pt[0]; recording.y = pt[1]; Doc.AddDocToList((Doc.UserDoc().myOverlayDocs as Doc), undefined, recording); } - } @computed @@ -375,13 +400,25 @@ export class PresElementBox extends ViewBoxBaseComponent<FieldViewProps>() { onClick={() => this.updateView(targetDoc, activeItem)} style={{ fontWeight: 700, display: activeItem.presPinView ? "flex" : "none" }}>V</div> </Tooltip> - <Tooltip title={<><div className="dash-tooltip">{"Start recording"}</div></>}> - <div className="slideButton" - onClick={() => this.startRecording(targetDoc, activeItem)} - style={{ fontWeight: 700 }}> - <FontAwesomeIcon icon={"video"} onPointerDown={e => e.stopPropagation()} /> - </div> - </Tooltip> + + {this.recordingIsInOverlay ? + <Tooltip title={<><div className="dash-tooltip">{"Hide Recording"}</div></>}> + <div className="slideButton" + onClick={() => this.hideRecording(targetDoc, activeItem)} + style={{ fontWeight: 700 }}> + <FontAwesomeIcon icon={"video-slash"} onPointerDown={e => e.stopPropagation()} /> + </div> + </Tooltip> : + <Tooltip title={<><div className="dash-tooltip">{"Start recording"}</div></>}> + <div className="slideButton" + onClick={() => this.startRecording(targetDoc, activeItem)} + style={{ fontWeight: 700 }}> + <FontAwesomeIcon icon={"video"} onPointerDown={e => e.stopPropagation()} /> + </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} |