From dced40cd975732c2c98f230a6c750af54ffdabd4 Mon Sep 17 00:00:00 2001 From: Mehek Jethani Date: Sat, 17 Jul 2021 15:04:27 -0400 Subject: basic trim ui complete - switching laptops --- src/client/views/nodes/AudioBox.scss | 334 ++++++------ src/client/views/nodes/AudioBox.tsx | 990 +++++++++++++++++++++++------------ 2 files changed, 816 insertions(+), 508 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/AudioBox.scss b/src/client/views/nodes/AudioBox.scss index 3fcb024df..0fb0dc70e 100644 --- a/src/client/views/nodes/AudioBox.scss +++ b/src/client/views/nodes/AudioBox.scss @@ -1,188 +1,200 @@ .audiobox-container, .audiobox-container-interactive { + width: 100%; + height: 100%; + position: inherit; + display: flex; + position: relative; + cursor: default; + + .audiobox-buttons { + display: flex; width: 100%; + align-items: center; height: 100%; - position: inherit; - display: flex; - position: relative; - cursor: default; - .audiobox-buttons { - display: flex; - width: 100%; - align-items: center; - height: 100%; - - .audiobox-dictation { - position: relative; - width: 30px; - height: 100%; - align-items: center; - display: inherit; - background: dimgray; - left: 0px; - &:hover { - color: white; - cursor: pointer; - } - } + .audiobox-dictation { + position: relative; + width: 30px; + height: 100%; + align-items: center; + display: inherit; + background: dimgray; + left: 0px; + &:hover { + color: white; + cursor: pointer; + } } + } - .audiobox-control, - .audiobox-control-interactive { - top: 0; - max-height: 32px; - width: 100%; - display: inline-block; - pointer-events: none; - } + .audiobox-control, + .audiobox-control-interactive { + top: 0; + max-height: 32px; + width: 100%; + display: inline-block; + pointer-events: none; + } + + .audiobox-control-interactive { + pointer-events: all; + } + + .audiobox-record-interactive, + .audiobox-record { + pointer-events: all; + width: 100%; + height: 100%; + position: relative; + } - .audiobox-control-interactive { - pointer-events: all; + .audiobox-record { + pointer-events: none; + } + + .recording { + margin-top: auto; + margin-bottom: auto; + width: 100%; + height: 100%; + position: relative; + padding-right: 5px; + display: flex; + background-color: red; + + .time { + position: relative; + height: 100%; + width: 100%; + font-size: 20; + text-align: center; + top: 5; } - .audiobox-record-interactive, - .audiobox-record { - pointer-events: all; - width: 100%; - height: 100%; - position: relative; + .buttons { + position: relative; + margin-top: auto; + margin-bottom: auto; + width: 25px; + padding: 5px; + &:hover { + background-color: crimson; + } } + } + + .audiobox-controls { + width: 100%; + height: 100%; + position: relative; + display: flex; + padding-left: 2px; + background: black; - .audiobox-record { - pointer-events: none; + .audiobox-dictation { + position: absolute; + width: 32px; + height: 100%; + align-items: center; + display: inherit; + background: dimgray; + left: 0px; } - .recording { + .audiobox-player { + margin-top: auto; + margin-bottom: auto; + width: 100%; + position: relative; + padding-right: 5px; + display: flex; + flex-direction: column; + + .audiobox-playhead { + position: relative; margin-top: auto; margin-bottom: auto; - width: 100%; - height: 100%; - position: relative; - padding-right: 5px; + margin-right: 2px; + height: 25px; + border-radius: 50%; + background-color: black; + color: white; display: flex; - background-color: red; - - .time { - position: relative; - height: 100%; - width: 100%; - font-size: 20; - text-align: center; - top: 5; + align-items: center; + justify-content: center; + &:hover { + background-color: grey; + color: lightgrey; } - .buttons { - position: relative; - margin-top: auto; - margin-bottom: auto; - width: 25px; - padding: 5px; - &:hover{ - background-color: crimson; - } + svg { + width: 100%; + position: absolute; + border-width: "thin"; + border-color: "white"; } - } + } - .audiobox-controls { - width: 100%; - height: 100%; + .audiobox-dictation { position: relative; - display: flex; - padding-left: 2px; - background: black; - - .audiobox-dictation { - position: absolute; - width: 30px; - height: 100%; - align-items: center; - display: inherit; - background: dimgray; - left: 0px; - } + margin-top: auto; + margin-bottom: auto; + width: 25px; + padding: 2px; + align-items: center; + display: inherit; + background: dimgray; + } - .audiobox-player { - margin-top: auto; - margin-bottom: auto; - width: 100%; - position: relative; - padding-right: 5px; - display: flex; - - .audiobox-playhead { - position: relative; - margin-top: auto; - margin-bottom: auto; - margin-right: 2px; - height: 25px; - padding: 2px; - border-radius: 50%; - background-color: black; - color: white; - &:hover { - background-color: grey; - color: lightgrey; - } - } - - .audiobox-dictation { - position: relative; - margin-top: auto; - margin-bottom: auto; - width: 25px; - padding: 2px; - align-items: center; - display: inherit; - background: dimgray; - } - - .audiobox-timeline { - position: absolute; - width: 100%; - border: gray solid 1px; - border-radius: 3px; - z-index: 1000; - overflow: hidden; - } - - .audioBox-total-time, - .audioBox-current-time { - position: absolute; - font-size: 8; - top: 100%; - color: white; - } - .audioBox-current-time { - left: 30px; - } - - .audioBox-total-time { - right: 2px; - } - } + .audiobox-timeline { + position: absolute; + width: 100%; + border: gray solid 1px; + border-radius: 3px; + z-index: 1000; + overflow: hidden; + } + + .audioBox-total-time, + .audioBox-current-time { + position: absolute; + font-size: 8; + top: 100%; + color: white; + } + .audioBox-current-time { + left: 30px; + } + + .audioBox-total-time { + right: 2px; + } } + } } - @media only screen and (max-device-width: 480px) { - .audiobox-dictation { - font-size: 5em; - display: flex; - width: 100; - justify-content: center; - flex-direction: column; - align-items: center; - } - - .audiobox-container .audiobox-record, - .audiobox-container-interactive .audiobox-record { - font-size: 3em; - } - - .audiobox-container .audiobox-controls .audiobox-player .audiobox-playhead, - .audiobox-container .audiobox-controls .audiobox-player .audiobox-dictation, - .audiobox-container-interactive .audiobox-controls .audiobox-player .audiobox-playhead { - width: 70px; - } -} \ No newline at end of file + .audiobox-dictation { + font-size: 5em; + display: flex; + width: 100; + justify-content: center; + flex-direction: column; + align-items: center; + } + + .audiobox-container .audiobox-record, + .audiobox-container-interactive .audiobox-record { + font-size: 3em; + } + + .audiobox-container .audiobox-controls .audiobox-player .audiobox-playhead, + .audiobox-container .audiobox-controls .audiobox-player .audiobox-dictation, + .audiobox-container-interactive + .audiobox-controls + .audiobox-player + .audiobox-playhead { + width: 70px; + } +} diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index a2e36f12e..99960e934 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -1,6 +1,13 @@ import React = require("react"); import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; +import { + action, + computed, + IReactionDisposer, + observable, + reaction, + runInAction, +} from "mobx"; import { observer } from "mobx-react"; import { DateField } from "../../../fields/DateField"; import { Doc, DocListCast, Opt } from "../../../fields/Doc"; @@ -17,376 +24,665 @@ import { SnappingManager } from "../../util/SnappingManager"; import { CollectionStackedTimeline } from "../collections/CollectionStackedTimeline"; import { ContextMenu } from "../ContextMenu"; import { ContextMenuProps } from "../ContextMenuItem"; -import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from "../DocComponent"; +import { + ViewBoxAnnotatableComponent, + ViewBoxAnnotatableProps, +} from "../DocComponent"; import "./AudioBox.scss"; -import { FieldView, FieldViewProps } from './FieldView'; +import { FieldView, FieldViewProps } from "./FieldView"; import { LinkDocPreview } from "./LinkDocPreview"; declare class MediaRecorder { - constructor(e: any); // whatever MediaRecorder has + constructor(e: any); // whatever MediaRecorder has } type AudioDocument = makeInterface<[typeof documentSchema]>; const AudioDocument = makeInterface(documentSchema); @observer -export class AudioBox extends ViewBoxAnnotatableComponent(AudioDocument) { - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(AudioBox, fieldKey); } - public static Enabled = false; - static playheadWidth = 30; // width of playhead - static heightPercent = 80; // height of timeline in percent of height of audioBox. - static Instance: AudioBox; - - _disposers: { [name: string]: IReactionDisposer } = {}; - _ele: HTMLAudioElement | null = null; - _stackedTimeline = React.createRef(); - _recorder: any; - _recordStart = 0; - _pauseStart = 0; - _pauseEnd = 0; - _pausedTime = 0; - _stream: MediaStream | undefined; - _start: number = 0; - _play: any = null; - - @observable static _scrubTime = 0; - @observable _markerEnd: number = 0; - @observable _position: number = 0; - @observable _waveHeight: Opt = this.layoutDoc._height; - @observable _paused: boolean = false; - @computed get mediaState(): undefined | "pendingRecording" | "recording" | "paused" | "playing" { return this.dataDoc.mediaState as (undefined | "pendingRecording" | "recording" | "paused" | "playing"); } - set mediaState(value) { this.dataDoc.mediaState = value; } - public static SetScrubTime = action((timeInMillisFrom1970: number) => { AudioBox._scrubTime = 0; AudioBox._scrubTime = timeInMillisFrom1970; }); - @computed get recordingStart() { return Cast(this.dataDoc[this.props.fieldKey + "-recordingStart"], DateField)?.date.getTime(); } - @computed get duration() { return NumCast(this.dataDoc[`${this.fieldKey}-duration`]); } - @computed get anchorDocs() { return DocListCast(this.dataDoc[this.annotationKey]); } - @computed get links() { return DocListCast(this.dataDoc.links); } - @computed get pauseTime() { return this._pauseEnd - this._pauseStart; } // total time paused to update the correct time - @computed get heightPercent() { return AudioBox.heightPercent; } - - constructor(props: Readonly) { - super(props); - AudioBox.Instance = this; - - if (this.duration === undefined) { - runInAction(() => this.Document[this.fieldKey + "-duration"] = this.Document.duration); - } - } - - getLinkData(l: Doc) { - let la1 = l.anchor1 as Doc; - let la2 = l.anchor2 as Doc; - const linkTime = this._stackedTimeline.current?.anchorStart(la2) || this._stackedTimeline.current?.anchorStart(la1) || 0; - if (Doc.AreProtosEqual(la1, this.dataDoc)) { - la1 = l.anchor2 as Doc; - la2 = l.anchor1 as Doc; - } - return { la1, la2, linkTime }; - } - - getAnchor = () => { - return CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.annotationKey, - "_timecodeToShow" /* audioStart */, "_timecodeToHide" /* audioEnd */, this._ele?.currentTime || - Cast(this.props.Document._currentTimecode, "number", null) || (this.mediaState === "recording" ? (Date.now() - (this.recordingStart || 0)) / 1000 : undefined)) - || this.rootDoc; +export class AudioBox extends ViewBoxAnnotatableComponent< + ViewBoxAnnotatableProps & FieldViewProps, + AudioDocument +>(AudioDocument) { + public static LayoutString(fieldKey: string) { + return FieldView.LayoutString(AudioBox, fieldKey); + } + public static Enabled = false; + static playheadWidth = 30; // width of playhead + static heightPercent = 80; // height of timeline in percent of height of audioBox. + static Instance: AudioBox; + + _disposers: { [name: string]: IReactionDisposer } = {}; + _ele: HTMLAudioElement | null = null; + _stackedTimeline = React.createRef(); + _recorder: any; + _recordStart = 0; + _pauseStart = 0; + _pauseEnd = 0; + _pausedTime = 0; + _stream: MediaStream | undefined; + _start: number = 0; + _play: any = null; + + @observable static _scrubTime = 0; + @observable _markerEnd: number = 0; + @observable _position: number = 0; + @observable _waveHeight: Opt = this.layoutDoc._height; + @observable _paused: boolean = false; + @observable _trimming: boolean = false; + @observable _trimBounds: { start: number; end: number } = { + start: this.dataDoc.clipStart ? this.dataDoc.clipStart : 0, + end: this.dataDoc.clipEnd + ? this.dataDoc.clipEnd + : this.duration + ? this.duration + : undefined, + }; + @computed get mediaState(): + | undefined + | "pendingRecording" + | "recording" + | "paused" + | "playing" { + return this.dataDoc.mediaState as + | undefined + | "pendingRecording" + | "recording" + | "paused" + | "playing"; + } + set mediaState(value) { + this.dataDoc.mediaState = value; + } + public static SetScrubTime = action((timeInMillisFrom1970: number) => { + AudioBox._scrubTime = 0; + AudioBox._scrubTime = timeInMillisFrom1970; + }); + @computed get recordingStart() { + return Cast( + this.dataDoc[this.props.fieldKey + "-recordingStart"], + DateField + )?.date.getTime(); + } + @computed get duration() { + return NumCast(this.dataDoc[`${this.fieldKey}-duration`]); + } + @computed get anchorDocs() { + return DocListCast(this.dataDoc[this.annotationKey]); + } + @computed get links() { + return DocListCast(this.dataDoc.links); + } + @computed get pauseTime() { + return this._pauseEnd - this._pauseStart; + } // total time paused to update the correct time + @computed get heightPercent() { + return AudioBox.heightPercent; + } + + constructor(props: Readonly) { + super(props); + AudioBox.Instance = this; + + if (this.duration === undefined) { + runInAction( + () => + (this.Document[this.fieldKey + "-duration"] = this.Document.duration) + ); } - - componentWillUnmount() { - Object.values(this._disposers).forEach(disposer => disposer?.()); - const ind = DocUtils.ActiveRecordings.indexOf(this); - ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1)); + } + + getLinkData(l: Doc) { + let la1 = l.anchor1 as Doc; + let la2 = l.anchor2 as Doc; + const linkTime = + this._stackedTimeline.current?.anchorStart(la2) || + this._stackedTimeline.current?.anchorStart(la1) || + 0; + if (Doc.AreProtosEqual(la1, this.dataDoc)) { + la1 = l.anchor2 as Doc; + la2 = l.anchor1 as Doc; } - - @action - componentDidMount() { - this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. - - this.mediaState = this.path ? "paused" : undefined; - - this._disposers.triggerAudio = reaction( - () => !LinkDocPreview.LinkInfo && this.props.renderDepth !== -1 ? NumCast(this.Document._triggerAudio, null) : undefined, - start => start !== undefined && setTimeout(() => { - this.playFrom(start); - setTimeout(() => { - this.Document._currentTimecode = start; - this.Document._triggerAudio = undefined; - }, 10); - }), // wait for mainCont and try again to play - { fireImmediately: true } - ); - - this._disposers.audioStop = reaction( - () => this.props.renderDepth !== -1 && !LinkDocPreview.LinkInfo ? Cast(this.Document._audioStop, "number", null) : undefined, - audioStop => audioStop !== undefined && setTimeout(() => { - this.Pause(); - setTimeout(() => this.Document._audioStop = undefined, 10); - }), // wait for mainCont and try again to play - { fireImmediately: true } + return { la1, la2, linkTime }; + } + + getAnchor = () => { + return ( + CollectionStackedTimeline.createAnchor( + this.rootDoc, + this.dataDoc, + this.annotationKey, + "_timecodeToShow" /* audioStart */, + "_timecodeToHide" /* audioEnd */, + this._ele?.currentTime || + Cast(this.props.Document._currentTimecode, "number", null) || + (this.mediaState === "recording" + ? (Date.now() - (this.recordingStart || 0)) / 1000 + : undefined) + ) || this.rootDoc + ); + }; + + componentWillUnmount() { + Object.values(this._disposers).forEach((disposer) => disposer?.()); + const ind = DocUtils.ActiveRecordings.indexOf(this); + ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1); + } + + @action + componentDidMount() { + this.props.setContentView?.(this); // this tells the DocumentView that this AudioBox is the "content" of the document. this allows the DocumentView to indirectly call getAnchor() on the AudioBox when making a link. + + this.mediaState = this.path ? "paused" : undefined; + + this._disposers.triggerAudio = reaction( + () => + !LinkDocPreview.LinkInfo && this.props.renderDepth !== -1 + ? NumCast(this.Document._triggerAudio, null) + : undefined, + (start) => + start !== undefined && + setTimeout(() => { + this.playFrom(start); + setTimeout(() => { + this.Document._currentTimecode = start; + this.Document._triggerAudio = undefined; + }, 10); + }), // wait for mainCont and try again to play + { fireImmediately: true } + ); + + this._disposers.audioStop = reaction( + () => + this.props.renderDepth !== -1 && !LinkDocPreview.LinkInfo + ? Cast(this.Document._audioStop, "number", null) + : undefined, + (audioStop) => + audioStop !== undefined && + setTimeout(() => { + this.Pause(); + setTimeout(() => (this.Document._audioStop = undefined), 10); + }), // wait for mainCont and try again to play + { fireImmediately: true } + ); + } + + // for updating the timecode + timecodeChanged = () => { + const htmlEle = this._ele; + if (this.mediaState !== "recording" && htmlEle) { + htmlEle.duration && + htmlEle.duration !== Infinity && + runInAction( + () => (this.dataDoc[this.fieldKey + "-duration"] = htmlEle.duration) ); + this.links + .map((l) => this.getLinkData(l)) + .forEach(({ la1, la2, linkTime }) => { + if ( + linkTime > NumCast(this.layoutDoc._currentTimecode) && + linkTime < htmlEle.currentTime + ) { + Doc.linkFollowHighlight(la1); + } + }); + this.layoutDoc._currentTimecode = htmlEle.currentTime; } - - // for updating the timecode - timecodeChanged = () => { - const htmlEle = this._ele; - if (this.mediaState !== "recording" && htmlEle) { - htmlEle.duration && htmlEle.duration !== Infinity && runInAction(() => this.dataDoc[this.fieldKey + "-duration"] = htmlEle.duration); - this.links.map(l => this.getLinkData(l)).forEach(({ la1, la2, linkTime }) => { - if (linkTime > NumCast(this.layoutDoc._currentTimecode) && linkTime < htmlEle.currentTime) { - Doc.linkFollowHighlight(la1); - } - }); - this.layoutDoc._currentTimecode = htmlEle.currentTime; - } - } - - // pause play back - Pause = action(() => { - this._ele!.pause(); - this.mediaState = "paused"; - }); - - // play audio for documents created during recording - playFromTime = (absoluteTime: number) => { - this.recordingStart && this.playFrom((absoluteTime - this.recordingStart) / 1000); - } - - // play back the audio from time - @action - playFrom = (seekTimeInSeconds: number, endTime: number = this.duration) => { - clearTimeout(this._play); - if (Number.isNaN(this._ele?.duration)) { - setTimeout(() => this.playFrom(seekTimeInSeconds, endTime), 500); - } else if (this._ele && AudioBox.Enabled) { - if (seekTimeInSeconds < 0) { - if (seekTimeInSeconds > -1) { - setTimeout(() => this.playFrom(0), -seekTimeInSeconds * 1000); - } else { - this.Pause(); - } - } else if (seekTimeInSeconds <= this._ele.duration) { - this._ele.currentTime = seekTimeInSeconds; - this._ele.play(); - runInAction(() => this.mediaState = "playing"); - if (endTime !== this.duration) { - this._play = setTimeout(() => this.Pause(), (endTime - seekTimeInSeconds) * 1000); // use setTimeout to play a specific duration - } - } else { - this.Pause(); - } + }; + + // pause play back + Pause = action(() => { + this._ele!.pause(); + this.mediaState = "paused"; + }); + + // play audio for documents created during recording + playFromTime = (absoluteTime: number) => { + this.recordingStart && + this.playFrom((absoluteTime - this.recordingStart) / 1000); + }; + + // play back the audio from time + @action + playFrom = (seekTimeInSeconds: number, endTime: number = this.duration) => { + clearTimeout(this._play); + if (Number.isNaN(this._ele?.duration)) { + setTimeout(() => this.playFrom(seekTimeInSeconds, endTime), 500); + } else if (this._ele && AudioBox.Enabled) { + if (seekTimeInSeconds < 0) { + if (seekTimeInSeconds > -1) { + setTimeout(() => this.playFrom(0), -seekTimeInSeconds * 1000); + } else { + this.Pause(); } - } - - // update the recording time - updateRecordTime = () => { - if (this.mediaState === "recording") { - setTimeout(this.updateRecordTime, 30); - if (this._paused) { - this._pausedTime += (new Date().getTime() - this._recordStart) / 1000; - } else { - this.layoutDoc._currentTimecode = (new Date().getTime() - this._recordStart - this.pauseTime) / 1000; - } + } else if (seekTimeInSeconds <= this._ele.duration) { + this._ele.currentTime = seekTimeInSeconds; + this._ele.play(); + runInAction(() => (this.mediaState = "playing")); + if (endTime !== this.duration) { + this._play = setTimeout( + () => this.Pause(), + (endTime - seekTimeInSeconds) * 1000 + ); // use setTimeout to play a specific duration } + } else { + this.Pause(); + } } - - // starts recording - recordAudioAnnotation = async () => { - this._stream = await navigator.mediaDevices.getUserMedia({ audio: true }); - this._recorder = new MediaRecorder(this._stream); - this.dataDoc[this.props.fieldKey + "-recordingStart"] = new DateField(new Date()); - DocUtils.ActiveRecordings.push(this); - this._recorder.ondataavailable = async (e: any) => { - const [{ result }] = await Networking.UploadFilesToServer(e.data); - if (!(result instanceof Error)) { - this.props.Document[this.props.fieldKey] = new AudioField(Utils.prepend(result.accessPaths.agnostic.client)); - } - }; - this._recordStart = new Date().getTime(); - runInAction(() => this.mediaState = "recording"); - setTimeout(this.updateRecordTime, 0); - this._recorder.start(); - setTimeout(() => this._recorder && this.stopRecording(), 60 * 60 * 1000); // stop after an hour - } - - // context menu - specificContextMenu = (e: React.MouseEvent): void => { - const funcs: ContextMenuProps[] = []; - funcs.push({ description: (this.layoutDoc.hideAnchors ? "Don't hide" : "Hide") + " anchors", event: () => this.layoutDoc.hideAnchors = !this.layoutDoc.hideAnchors, icon: "expand-arrows-alt" }); - funcs.push({ description: (this.layoutDoc.dontAutoPlayFollowedLinks ? "" : "Don't") + " play when link is selected", event: () => this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc.dontAutoPlayFollowedLinks, icon: "expand-arrows-alt" }); - funcs.push({ description: (this.layoutDoc.autoPlayAnchors ? "Don't auto play" : "Auto play") + " anchors onClick", event: () => this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors, icon: "expand-arrows-alt" }); - ContextMenu.Instance?.addItem({ description: "Options...", subitems: funcs, icon: "asterisk" }); + }; + + // update the recording time + updateRecordTime = () => { + if (this.mediaState === "recording") { + setTimeout(this.updateRecordTime, 30); + if (this._paused) { + this._pausedTime += (new Date().getTime() - this._recordStart) / 1000; + } else { + this.layoutDoc._currentTimecode = + (new Date().getTime() - this._recordStart - this.pauseTime) / 1000; + } } - - // stops the recording - stopRecording = action(() => { - this._recorder.stop(); - this._recorder = undefined; - this.dataDoc[this.fieldKey + "-duration"] = (new Date().getTime() - this._recordStart - this.pauseTime) / 1000; - this.mediaState = "paused"; - this._stream?.getAudioTracks()[0].stop(); - const ind = DocUtils.ActiveRecordings.indexOf(this); - ind !== -1 && (DocUtils.ActiveRecordings.splice(ind, 1)); + }; + + // starts recording + recordAudioAnnotation = async () => { + this._stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + this._recorder = new MediaRecorder(this._stream); + this.dataDoc[this.props.fieldKey + "-recordingStart"] = new DateField( + new Date() + ); + DocUtils.ActiveRecordings.push(this); + this._recorder.ondataavailable = async (e: any) => { + const [{ result }] = await Networking.UploadFilesToServer(e.data); + if (!(result instanceof Error)) { + this.props.Document[this.props.fieldKey] = new AudioField( + Utils.prepend(result.accessPaths.agnostic.client) + ); + } + }; + this._recordStart = new Date().getTime(); + runInAction(() => (this.mediaState = "recording")); + setTimeout(this.updateRecordTime, 0); + this._recorder.start(); + setTimeout(() => this._recorder && this.stopRecording(), 60 * 60 * 1000); // stop after an hour + }; + + // context menu + specificContextMenu = (e: React.MouseEvent): void => { + const funcs: ContextMenuProps[] = []; + funcs.push({ + description: + (this.layoutDoc.hideAnchors ? "Don't hide" : "Hide") + " anchors", + event: () => (this.layoutDoc.hideAnchors = !this.layoutDoc.hideAnchors), + icon: "expand-arrows-alt", }); - - // button for starting and stopping the recording - recordClick = (e: React.MouseEvent) => { - if (e.button === 0 && !e.ctrlKey) { - this._recorder ? this.stopRecording() : this.recordAudioAnnotation(); - e.stopPropagation(); - } - } - - // for play button - Play = (e?: any) => { - this.playFrom(this._ele!.paused ? this._ele!.currentTime : -1); - e?.stopPropagation?.(); - } - - // creates a text document for dictation - onFile = (e: any) => { - const newDoc = CurrentUserUtils.GetNewTextDoc("", NumCast(this.props.Document.x), NumCast(this.props.Document.y) + NumCast(this.props.Document._height) + 10, - NumCast(this.props.Document._width), 2 * NumCast(this.props.Document._height)); - Doc.GetProto(newDoc).recordingSource = this.dataDoc; - Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction(`self.recordingSource["${this.props.fieldKey}-recordingStart"]`); - Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction("self.recordingSource.mediaState"); - this.props.addDocument?.(newDoc); - e.stopPropagation(); - } - - // ref for updating time - setRef = (e: HTMLAudioElement | null) => { - e?.addEventListener("timeupdate", this.timecodeChanged); - e?.addEventListener("ended", this.Pause); - this._ele = e; + funcs.push({ + description: + (this.layoutDoc.dontAutoPlayFollowedLinks ? "" : "Don't") + + " play when link is selected", + event: () => + (this.layoutDoc.dontAutoPlayFollowedLinks = !this.layoutDoc + .dontAutoPlayFollowedLinks), + icon: "expand-arrows-alt", + }); + funcs.push({ + description: + (this.layoutDoc.autoPlayAnchors ? "Don't auto play" : "Auto play") + + " anchors onClick", + event: () => + (this.layoutDoc.autoPlayAnchors = !this.layoutDoc.autoPlayAnchors), + icon: "expand-arrows-alt", + }); + ContextMenu.Instance?.addItem({ + description: "Options...", + subitems: funcs, + icon: "asterisk", + }); + }; + + // stops the recording + stopRecording = action(() => { + this._recorder.stop(); + this._recorder = undefined; + this.dataDoc[this.fieldKey + "-duration"] = + (new Date().getTime() - this._recordStart - this.pauseTime) / 1000; + this.mediaState = "paused"; + this._trimBounds.end = this.duration; + this.dataDoc.clipStart = 0; + this.dataDoc.clipEnd = this.duration; + this._stream?.getAudioTracks()[0].stop(); + const ind = DocUtils.ActiveRecordings.indexOf(this); + ind !== -1 && DocUtils.ActiveRecordings.splice(ind, 1); + }); + + // button for starting and stopping the recording + recordClick = (e: React.MouseEvent) => { + if (e.button === 0 && !e.ctrlKey) { + this._recorder ? this.stopRecording() : this.recordAudioAnnotation(); + e.stopPropagation(); } - - // returns the path of the audio file - @computed get path() { - const field = Cast(this.props.Document[this.props.fieldKey], AudioField); - const path = (field instanceof AudioField) ? field.url.href : ""; - return path === nullAudio ? "" : path; + }; + + // for play button + Play = (e?: any) => { + if (this._trimming) { + let end = this._trimBounds.end; + let start = + this._ele!.currentTime > end || + this._ele!.currentTime < this._trimBounds.start + ? -1 + : this._trimBounds.start; + this.playFrom(this._ele!.paused ? this._ele!.currentTime : start, end); + } else { + this.playFrom(this._ele!.paused ? this._ele!.currentTime : -1); } - - // returns the html audio element - @computed get audio() { - return ; + e?.stopPropagation?.(); + }; + + // creates a text document for dictation + onFile = (e: any) => { + const newDoc = CurrentUserUtils.GetNewTextDoc( + "", + NumCast(this.props.Document.x), + NumCast(this.props.Document.y) + + NumCast(this.props.Document._height) + + 10, + NumCast(this.props.Document._width), + 2 * NumCast(this.props.Document._height) + ); + Doc.GetProto(newDoc).recordingSource = this.dataDoc; + Doc.GetProto(newDoc).recordingStart = ComputedField.MakeFunction( + `self.recordingSource["${this.props.fieldKey}-recordingStart"]` + ); + Doc.GetProto(newDoc).mediaState = ComputedField.MakeFunction( + "self.recordingSource.mediaState" + ); + this.props.addDocument?.(newDoc); + e.stopPropagation(); + }; + + // ref for updating time + setRef = (e: HTMLAudioElement | null) => { + e?.addEventListener("timeupdate", this.timecodeChanged); + e?.addEventListener("ended", this.Pause); + this._ele = e; + }; + + // returns the path of the audio file + @computed get path() { + const field = Cast(this.props.Document[this.props.fieldKey], AudioField); + const path = field instanceof AudioField ? field.url.href : ""; + return path === nullAudio ? "" : path; + } + + // returns the html audio element + @computed get audio() { + return ( + + ); + } + + // pause the time during recording phase + @action + recordPause = (e: React.MouseEvent) => { + this._pauseStart = new Date().getTime(); + this._paused = true; + this._recorder.pause(); + e.stopPropagation(); + }; + + // continue the recording + @action + recordPlay = (e: React.MouseEvent) => { + this._pauseEnd = new Date().getTime(); + this._paused = false; + this._recorder.resume(); + e.stopPropagation(); + }; + + playing = () => this.mediaState === "playing"; + playLink = (link: Doc) => { + const stack = this._stackedTimeline.current; + if (link.annotationOn === this.rootDoc) { + if (!this.layoutDoc.dontAutoPlayFollowedLinks) + this.playFrom(stack?.anchorStart(link) || 0, stack?.anchorEnd(link)); + else + this._ele!.currentTime = this.layoutDoc._currentTimecode = + stack?.anchorStart(link) || 0; + } else { + this.links + .filter((l) => l.anchor1 === link || l.anchor2 === link) + .forEach((l) => { + const { la1, la2 } = this.getLinkData(l); + const startTime = stack?.anchorStart(la1) || stack?.anchorStart(la2); + const endTime = stack?.anchorEnd(la1) || stack?.anchorEnd(la2); + if (startTime !== undefined) { + if (!this.layoutDoc.dontAutoPlayFollowedLinks) + endTime + ? this.playFrom(startTime, endTime) + : this.playFrom(startTime); + else + this._ele!.currentTime = this.layoutDoc._currentTimecode = startTime; + } + }); } + }; - // pause the time during recording phase - @action - recordPause = (e: React.MouseEvent) => { - this._pauseStart = new Date().getTime(); - this._paused = true; - this._recorder.pause(); - e.stopPropagation(); + // shows trim controls + @action + startTrim = () => { + if (this.mediaState === "playing") { + this.Pause(); } - - // continue the recording - @action - recordPlay = (e: React.MouseEvent) => { - this._pauseEnd = new Date().getTime(); - this._paused = false; - this._recorder.resume(); - e.stopPropagation(); + this._trimming = true; + }; + + // hides trim controls and displays new clip + @action + finishTrim = () => { + if (this.mediaState === "playing") { + this.Pause(); } - - playing = () => this.mediaState === "playing"; - playLink = (link: Doc) => { - const stack = this._stackedTimeline.current; - if (link.annotationOn === this.rootDoc) { - if (!this.layoutDoc.dontAutoPlayFollowedLinks) this.playFrom(stack?.anchorStart(link) || 0, stack?.anchorEnd(link)); - else this._ele!.currentTime = this.layoutDoc._currentTimecode = (stack?.anchorStart(link) || 0); + this.dataDoc.clipStart = this._trimBounds.start; + this.dataDoc.clipEnd = this._trimBounds.end; + this._trimming = false; + }; + + isActiveChild = () => this._isAnyChildContentActive; + timelineWhenChildContentsActiveChanged = (isActive: boolean) => + this.props.whenChildContentsActiveChanged( + runInAction(() => (this._isAnyChildContentActive = isActive)) + ); + timelineScreenToLocal = () => + this.props + .ScreenToLocalTransform() + .translate( + -AudioBox.playheadWidth, + (-(100 - this.heightPercent) / 200) * this.props.PanelHeight() + ); + setAnchorTime = (time: number) => + (this._ele!.currentTime = this.layoutDoc._currentTimecode = time); + timelineHeight = () => + (((this.props.PanelHeight() * this.heightPercent) / 100) * + this.heightPercent) / + 100; // panelHeight * heightPercent is player height. * heightPercent is timeline height (as per css inline) + timelineWidth = () => this.props.PanelWidth() - AudioBox.playheadWidth; + @computed get renderTimeline() { + return ( + l.anchor1 === link || l.anchor2 === link).forEach(l => { - const { la1, la2 } = this.getLinkData(l); - const startTime = stack?.anchorStart(la1) || stack?.anchorStart(la2); - const endTime = stack?.anchorEnd(la1) || stack?.anchorEnd(la2); - if (startTime !== undefined) { - if (!this.layoutDoc.dontAutoPlayFollowedLinks) endTime ? this.playFrom(startTime, endTime) : this.playFrom(startTime); - else this._ele!.currentTime = this.layoutDoc._currentTimecode = startTime; - } - }); + removeDocument={this.removeDocument} + ScreenToLocalTransform={this.timelineScreenToLocal} + Play={this.Play} + Pause={this.Pause} + isContentActive={this.isContentActive} + playLink={this.playLink} + PanelWidth={this.timelineWidth} + PanelHeight={this.timelineHeight} + trimming={this._trimming} + trimBounds={this._trimBounds} + /> + ); + } + + render() { + const interactive = + SnappingManager.GetIsDragging() || this.isContentActive() + ? "-interactive" + : ""; + return ( +
this._isAnyChildContentActive; - timelineWhenChildContentsActiveChanged = (isActive: boolean) => this.props.whenChildContentsActiveChanged(runInAction(() => this._isAnyChildContentActive = isActive)); - timelineScreenToLocal = () => this.props.ScreenToLocalTransform().translate(-AudioBox.playheadWidth, -(100 - this.heightPercent) / 200 * this.props.PanelHeight()); - setAnchorTime = (time: number) => this._ele!.currentTime = this.layoutDoc._currentTimecode = time; - timelineHeight = () => this.props.PanelHeight() * this.heightPercent / 100 * this.heightPercent / 100; // panelHeight * heightPercent is player height. * heightPercent is timeline height (as per css inline) - timelineWidth = () => this.props.PanelWidth() - AudioBox.playheadWidth; - @computed get renderTimeline() { - return ; - } - - render() { - const interactive = SnappingManager.GetIsDragging() || this.isContentActive() ? "-interactive" : ""; - return
- {!this.path ? -
-
- -
- {this.mediaState === "recording" || this.mediaState === "paused" ? -
e.stopPropagation()}> -
- -
-
- -
-
{formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))}
-
- : - } -
: -
-
-
-
-
- {this.renderTimeline} -
- {this.audio} -
- {formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode)))} -
-
- {formatTime(Math.round(this.duration))} -
-
+ style={{ + pointerEvents: + this.props.layerProvider?.(this.layoutDoc) === false + ? "none" + : undefined, + }} + > + {!this.path ? ( +
+
+ +
+ {this.mediaState === "recording" || this.mediaState === "paused" ? ( +
e.stopPropagation()}> +
+
- } -
; - } -} \ No newline at end of file +
+ +
+
+ {formatTime( + Math.round(NumCast(this.layoutDoc._currentTimecode)) + )} +
+
+ ) : ( + + )} +
+ ) : ( +
+
+
+
+ {" "} + +
+
+ +
+
+ {this.renderTimeline} +
+ {this.audio} +
+ {formatTime( + Math.round(NumCast(this.layoutDoc._currentTimecode)) + )} +
+
+ {formatTime(Math.round(this.duration))} +
+
+
+ )} +
+ ); + } +} -- cgit v1.2.3-70-g09d2 From 136b900ed0b939c2a10b601470f764dcb50809ad Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 12 Aug 2021 17:07:41 -0400 Subject: fixed textboxes which were generally a line too short when opened in contentfitting view (e.g. stackingView or pivotView) --- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 9bba15b28..1058070f8 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1444,12 +1444,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp const margins = 2 * NumCast(this.layoutDoc._yMargin, this.props.yPadding || 0); const children = this.ProseRef?.children.length ? Array.from(this.ProseRef.children[0].children) : undefined; if (children) { - const proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); - const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); + var proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); + var scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); if (scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation const setScrollHeight = () => this.rootDoc[this.fieldKey + "-scrollHeight"] = scrollHeight; if (this.rootDoc === this.layoutDoc.doc || this.layoutDoc.resolvedDataDoc) { setScrollHeight(); + setTimeout(() => { + proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); + scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); + setScrollHeight(); + }, 10); } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... } } -- cgit v1.2.3-70-g09d2 From f5c126ba41bb15837c3527e588ba6fb3c79f3e89 Mon Sep 17 00:00:00 2001 From: geireann Date: Sat, 14 Aug 2021 13:29:46 -0400 Subject: code reorganising and updates --- package-lock.json | 5 +- src/client/documents/Documents.ts | 15 +- src/client/util/CurrentUserUtils.ts | 171 ++- src/client/util/tempCurrentUserUtils.ts | 1389 ++++++++++++++++++++ .../views/collections/CollectionLinearView.scss | 139 -- .../views/collections/CollectionLinearView.tsx | 193 --- .../views/collections/collectionFreeForm/index.ts | 7 + .../views/collections/collectionGrid/index.ts | 2 + .../collectionLinearView/CollectionLinearView.scss | 139 ++ .../collectionLinearView/CollectionLinearView.tsx | 192 +++ .../collections/collectionLinearView/index.ts | 1 + src/client/views/nodes/DocumentContentsView.tsx | 2 +- src/client/views/nodes/FontIconBox.scss | 103 -- src/client/views/nodes/FontIconBox.tsx | 96 -- src/client/views/nodes/button/FontIconBadge.tsx | 37 + src/client/views/nodes/button/FontIconBox.scss | 103 ++ src/client/views/nodes/button/FontIconBox.tsx | 295 +++++ 17 files changed, 2328 insertions(+), 561 deletions(-) create mode 100644 src/client/util/tempCurrentUserUtils.ts delete mode 100644 src/client/views/collections/CollectionLinearView.scss delete mode 100644 src/client/views/collections/CollectionLinearView.tsx create mode 100644 src/client/views/collections/collectionFreeForm/index.ts create mode 100644 src/client/views/collections/collectionGrid/index.ts create mode 100644 src/client/views/collections/collectionLinearView/CollectionLinearView.scss create mode 100644 src/client/views/collections/collectionLinearView/CollectionLinearView.tsx create mode 100644 src/client/views/collections/collectionLinearView/index.ts delete mode 100644 src/client/views/nodes/FontIconBox.scss delete mode 100644 src/client/views/nodes/FontIconBox.tsx create mode 100644 src/client/views/nodes/button/FontIconBadge.tsx create mode 100644 src/client/views/nodes/button/FontIconBox.scss create mode 100644 src/client/views/nodes/button/FontIconBox.tsx (limited to 'src/client/views/nodes') diff --git a/package-lock.json b/package-lock.json index 58cdf8805..59ae898bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7694,14 +7694,13 @@ "resolved": "https://registry.npmjs.org/image-size-stream/-/image-size-stream-1.1.0.tgz", "integrity": "sha1-Ivou2mbG31AQh0bacUkmSy0l+Gs=", "requires": { - "image-size": "image-size@github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1", "readable-stream": "^1.0.33", "tryit": "^1.0.1" }, "dependencies": { "image-size": { - "version": "git+ssh://git@github.com/netroy/image-size.git#da2c863807a3e9602617bdd357b0de3ab4a064c1", - "from": "image-size@github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1" + "version": "git+https://github.com/netroy/image-size.git#da2c863807a3e9602617bdd357b0de3ab4a064c1", + "from": "git+https://github.com/netroy/image-size.git#da2c863807a3e9602617bdd357b0de3ab4a064c1" }, "isarray": { "version": "0.0.1", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 142c37ea4..a15b017bd 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -37,7 +37,7 @@ import { ColorBox } from "../views/nodes/ColorBox"; import { ComparisonBox } from "../views/nodes/ComparisonBox"; import { DocFocusOptions } from "../views/nodes/DocumentView"; import { FilterBox } from "../views/nodes/FilterBox"; -import { FontIconBox } from "../views/nodes/FontIconBox"; +import { FontIconBox } from "../views/nodes/button/FontIconBox"; import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; import { ImageBox } from "../views/nodes/ImageBox"; import { KeyValueBox } from "../views/nodes/KeyValueBox"; @@ -215,7 +215,19 @@ export class DocumentOptions { annotationOn?: Doc; isPushpin?: boolean; _removeDropProperties?: List; // list of properties that should be removed from a document when it is dropped. e.g., a creator button may be forceActive to allow it be dragged, but the forceActive property can be removed from the dropped document + + //BUTTONS iconShape?: string; // shapes of the fonticon border + btnType?: string; + btnList?: List; + docColorBtn?: string; + userColorBtn?: string; + canClick?: string; + + //LINEAR VIEW + linearViewIsExpanded?: boolean; // is linear view expanded + linearViewExpandable?: boolean; // can linear view be expanded + layout_linkView?: Doc; // view template for a link document layout_keyValue?: string; // view tempalte for key value docs linkRelationship?: string; // type of relatinoship a link represents @@ -266,7 +278,6 @@ export class DocumentOptions { selectedIndex?: number; // which item in a linear view has been selected using the "thumb doc" ui clipboard?: Doc; searchQuery?: string; // for quersyBox - linearViewIsExpanded?: boolean; // is linear view expanded useLinkSmallAnchor?: boolean; // whether links to this document should use a miniature linkAnchorBox border?: string; //for searchbox hoverBackgroundColor?: string; // background color of a label when hovered diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 66f9d060f..ecce573a1 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -2,13 +2,14 @@ import { computed, observable, reaction } from "mobx"; import * as rp from 'request-promise'; import { DataSym, Doc, DocListCast, DocListCastAsync } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; +import { InkTool } from "../../fields/InkField"; import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; import { ComputedField, ScriptField } from "../../fields/ScriptField"; -import { BoolCast, Cast, NumCast, PromiseValue, StrCast, DateCast } from "../../fields/Types"; +import { BoolCast, Cast, DateCast, NumCast, PromiseValue, StrCast } from "../../fields/Types"; import { nullAudio } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; import { Utils } from "../../Utils"; @@ -19,6 +20,7 @@ import { Networking } from "../Network"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; +import { Colors } from "../views/global/globalEnums"; import { MainView } from "../views/MainView"; import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; import { LabelBox } from "../views/nodes/LabelBox"; @@ -31,13 +33,11 @@ import { LinkManager } from "./LinkManager"; import { Scripting } from "./Scripting"; import { SearchUtil } from "./SearchUtil"; import { SelectionManager } from "./SelectionManager"; -import { UndoManager } from "./UndoManager"; -import { SnappingManager } from "./SnappingManager"; -import { InkTool } from "../../fields/InkField"; -import { SharingManager } from "./SharingManager"; -import { computedFn } from "mobx-utils"; import { ColorScheme } from "./SettingsManager"; -import { Colors } from "../views/global/globalEnums"; +import { SharingManager } from "./SharingManager"; +import { SnappingManager } from "./SnappingManager"; +import { UndoManager } from "./UndoManager"; +import { ButtonType } from "../views/nodes/button/FontIconBox"; export let resolvedPorts: { server: number, socket: number }; @@ -68,13 +68,14 @@ export class CurrentUserUtils { [this.ficon({ ignoreClick: true, icon: "mobile", + btnType: ButtonType.ClickButton, backgroundColor: "transparent" }), this.mobileTextContainer({}, [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])]); doc["template-mobile-button"] = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(queryTemplate) as any as Doc, title: "mobile button", icon: "mobile" + dragFactory: new PrefetchProxy(queryTemplate) as any as Doc, title: "mobile button", icon: "mobile", btnType: ButtonType.ClickButton, }); } @@ -89,7 +90,8 @@ export class CurrentUserUtils { slideTemplate.isTemplateDoc = makeTemplate(slideTemplate); doc["template-button-slides"] = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(slideTemplate) as any as Doc, title: "presentation slide", icon: "address-card" + dragFactory: new PrefetchProxy(slideTemplate) as any as Doc, title: "presentation slide", icon: "address-card", + btnType: ButtonType.ClickButton }); } @@ -135,7 +137,8 @@ export class CurrentUserUtils { doc["template-button-link"] = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(linkTemplate) as any as Doc, title: "link view", icon: "window-maximize", system: true + dragFactory: new PrefetchProxy(linkTemplate) as any as Doc, title: "link view", icon: "window-maximize", system: true, + btnType: ButtonType.ClickButton }); } @@ -166,7 +169,8 @@ export class CurrentUserUtils { doc["template-button-switch"] = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", icon: "toggle-on", system: true + dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", icon: "toggle-on", system: true, + btnType: ButtonType.ClickButton }); } @@ -215,7 +219,11 @@ export class CurrentUserUtils { doc["template-button-detail"] = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(detailView) as any as Doc, title: "detailView", icon: "window-maximize", system: true + dragFactory: new PrefetchProxy(detailView) as any as Doc, + title: "detailView", + icon: "window-maximize", + system: true, + btnType: ButtonType.ClickButton, }); } @@ -501,6 +509,7 @@ export class CurrentUserUtils { icon, title, toolTip, + btnType: ButtonType.ClickButton, ignoreClick, _dropAction: "alias", onDragStart: drag ? ScriptField.MakeFunction(drag) : undefined, @@ -556,7 +565,7 @@ export class CurrentUserUtils { const menuBtns = (await CurrentUserUtils.menuBtnDescriptions(doc)).map(({ title, target, icon, click, watchedDocuments }) => Docs.Create.FontIconDocument({ icon, - iconShape: "square", + btnType: ButtonType.MenuButton, _stayInCollection: true, _hideContextMenu: true, system: true, @@ -577,7 +586,6 @@ export class CurrentUserUtils { title: "menuItemPanel", childDropAction: "alias", _chromeHidden: true, - backgroundColor: Colors.DARK_GRAY, boxShadow: "rgba(0,0,0,0)", dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), ignoreClick: true, @@ -637,7 +645,7 @@ export class CurrentUserUtils { onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, backgroundColor: data.backgroundColor, system: true }, - [this.ficon({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", system: true }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])]) + [this.ficon({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", system: true, btnType: ButtonType.ClickButton, }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])]) ); } @@ -747,8 +755,8 @@ export class CurrentUserUtils { if (doc.myTools === undefined) { const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], { - title: "My Tools", _width: 500, _yMargin: 20, ignoreClick: true, _lockedPosition: true, _forceActive: true, - system: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, + title: "My Tools", _showTitle: "title", _width: 500, _yMargin: 20, ignoreClick: true, _lockedPosition: true, _forceActive: true, + system: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, boxShadow: "0 0", })) as any as Doc; doc.myTools = toolsStack; @@ -869,9 +877,9 @@ export class CurrentUserUtils { } static blist = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, { - ...opts, _gridGap: 5, _xMargin: 5, _yMargin: 5, _height: 42, _width: 100, boxShadow: "0 0", _forceActive: true, + ...opts, _gridGap: 5, _xMargin: 5, _yMargin: 5, _width: 100, boxShadow: "0 0", _forceActive: true, dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), - backgroundColor: "black", _lockedPosition: true, linearViewIsExpanded: true, system: true + _lockedPosition: true, linearViewIsExpanded: true, system: true })) as any as Doc static ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({ @@ -881,18 +889,133 @@ export class CurrentUserUtils { /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window static setupDockedButtons(doc: Doc) { if (doc["dockedBtn-undo"] === undefined) { - doc["dockedBtn-undo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), dontUndo: true, _stayInCollection: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "click to undo", title: "undo", icon: "undo-alt", system: true }); + doc["dockedBtn-undo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), dontUndo: true, _stayInCollection: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to undo", title: "undo", icon: "undo-alt", system: true }); } if (doc["dockedBtn-redo"] === undefined) { - doc["dockedBtn-redo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), dontUndo: true, _stayInCollection: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "click to redo", title: "redo", icon: "redo-alt", system: true }); + doc["dockedBtn-redo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), dontUndo: true, _stayInCollection: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to redo", title: "redo", icon: "redo-alt", system: true }); } if (doc.dockedBtns === undefined) { - doc.dockedBtns = CurrentUserUtils.blist({ title: "docked buttons", ignoreClick: true }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]); + doc.dockedBtns = CurrentUserUtils.blist({ title: "docked buttons", linearViewExpandable: true, ignoreClick: true }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]); } (doc["dockedBtn-undo"] as Doc).dontUndo = true; (doc["dockedBtn-redo"] as Doc).dontUndo = true; } + static textTools(doc: Doc) { + return [ + { + title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, + list: ["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", + "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"], + scriptDoc: Doc.UserDoc(), toggle: 'userDoc._fontFamily' + }, + { + title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', + scriptDoc: Doc.UserDoc(), + toggle: 'userDoc._boldActive' + }, + { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', scriptDoc: Doc.UserDoc(), toggle: 'userDoc._italicsActive' }, + { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', scriptDoc: Doc.UserDoc(), toggle: 'userDoc._underlineActive' }, + // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()', toggle: 'userDoc._underlineActive' }, + // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()', toggle: 'userDoc._underlineActive' }, + // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()', toggle: 'userDoc._underlineActive' }, + { title: "Highlight", toolTip: "Highlight", btnType: ButtonType.ColorButton, icon: "highlighter", ignoreClick: true, scriptDoc: Doc.UserDoc(), userColorBtn: 'userDoc._highlightColor' }, + { title: "Text color", toolTip: "Text color", btnType: ButtonType.ColorButton, icon: "fill-drip", ignoreClick: true, scriptDoc: Doc.UserDoc(), userColorBtn: 'userDoc._textColor' }, + // { title: "Link", tooltip: "Link", btnType: ButtonType.DropdownButton, icon: "link", click: '', ignoreClick: true }, + ]; + } + + static async contextMenuBtnDescriptions(doc: Doc) { + return [ + // { title: "Perspective", tooltip: "Change document's perspective", type: "btn", btnType: ButtonType.DropdownButton, ignoreClick: true, icon: "desktop", click: '' }, + { + title: "Perspective", toolTip: "Perspective", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, + list: [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Tree, + CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Multicolumn, + CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel, + CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, + CollectionViewType.Grid], + scriptDoc: 'selectedDoc', + toggle: 'selectedDoc._viewType' + }, + { + title: "Background", toolTip: "Background", btnType: ButtonType.ColorButton, scriptDoc: 'selectedDoc', + docColorBtn: 'selectedDoc.backgroundColor', width: 60, ignoreClick: true, icon: "fill-drip", + canClick: 'numSelected > 0' + }, + { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', toggle: 'selectedDoc.z', canClick: 'numSelected > 0' }, + { title: "Text Tools", type: "TextMenu", icon: "font" }, + // { title: "Ink Tools", type: "LinearMenu", icon: "pen-nib" }, + // { title: "GFX Tools", type: "LinearMenu", icon: "shapes" }, + // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy" }, + ]; + } + + // Default context menu buttons + static async setupContextMenuButtons(doc: Doc) { + const docList: Doc[] = []; + + const contextMenuBtns = (await CurrentUserUtils.contextMenuBtnDescriptions(doc)).map(({ title, width, toolTip, ignoreClick, icon, type, btnType, click, toggle, scriptDoc, canClick, docColorBtn }) => { + const textDocList: Doc[] = []; + if (type === "TextMenu") { + const textBtns = (CurrentUserUtils.textTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, btnType, click, toggle, scriptDoc, userColorBtn }) => { + textDocList.push(Docs.Create.FontIconDocument({ + _nativeWidth: width ? width : 25, + _nativeHeight: 25, + _width: width ? width : 25, + _height: 25, + icon, + toolTip, + userColorBtn, + + // testToggle: toggle ? ScriptField.MakeScript(toggle, { this: scriptDoc, scriptContext: "any" }) : undefined, + // toggle: toggle, + btnType: btnType, + btnList: new List(list), + ignoreClick: ignoreClick, + _stayInCollection: true, + _hideContextMenu: true, + system: true, + dontUndo: true, + title, + backgroundColor: "black", + _dropAction: "alias", + _removeDropProperties: new List(["dropAction", "_stayInCollection"]), + onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined + })); + }); + docList.push(CurrentUserUtils.blist({ ignoreClick: true, linearViewExpandable: true, _height: 30, backgroundColor: "transparent" }, textDocList)); + } else { + docList.push(Docs.Create.FontIconDocument({ + _nativeWidth: width ? width : 30, + _nativeHeight: 30, + _width: width ? width : 30, + _height: 30, + icon, + toolTip, + // testToggle: toggle ? ScriptField.MakeScript(toggle, { scriptContext: "any" }) : undefined, + // toggle: toggle, + docColorBtn, + canClick: canClick, + btnType: btnType, + ignoreClick: ignoreClick, + _stayInCollection: true, + _hideContextMenu: true, + system: true, + dontUndo: true, + title, + backgroundColor: "black", + _dropAction: "alias", + _removeDropProperties: new List(["dropAction", "_stayInCollection"]), + onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined + })); + } + }); + + if (doc.contextMenuBtns === undefined) { + doc.contextMenuBtns = CurrentUserUtils.blist({ title: "menu buttons", ignoreClick: true, linearViewExpandable: false, _height: 35 }, docList); + } + } // sets up the default set of documents to be shown in the Overlay layer static setupOverlays(doc: Doc) { if (doc.myOverlayDocs === undefined) { @@ -1052,8 +1175,8 @@ export class CurrentUserUtils { setTimeout(() => this.setupDefaultPresentation(doc), 0); // presentation that's initially triggered // setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet - doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); - doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); + // doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); + // doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); // uncomment this to setup a default note style that uses the custom header layout // PromiseValue(doc.emptyHeader).then(factory => { diff --git a/src/client/util/tempCurrentUserUtils.ts b/src/client/util/tempCurrentUserUtils.ts new file mode 100644 index 000000000..3fba672e6 --- /dev/null +++ b/src/client/util/tempCurrentUserUtils.ts @@ -0,0 +1,1389 @@ +import { computed, observable, reaction, action } from "mobx"; +import * as rp from 'request-promise'; +import { DataSym, Doc, DocListCast, DocListCastAsync, AclReadonly } from "../../fields/Doc"; +import { Id } from "../../fields/FieldSymbols"; +import { List } from "../../fields/List"; +import { PrefetchProxy } from "../../fields/Proxy"; +import { RichTextField } from "../../fields/RichTextField"; +import { listSpec } from "../../fields/Schema"; +import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; +import { ComputedField, ScriptField } from "../../fields/ScriptField"; +import { BoolCast, Cast, NumCast, PromiseValue, StrCast, DateCast } from "../../fields/Types"; +import { nullAudio } from "../../fields/URLField"; +import { SharingPermissions } from "../../fields/util"; +import { Utils } from "../../Utils"; +import { DocServer } from "../DocServer"; +import { Docs, DocumentOptions, DocUtils } from "../documents/Documents"; +import { DocumentType } from "../documents/DocumentTypes"; +import { Networking } from "../Network"; +import { CollectionDockingView } from "../views/collections/collectionDocking/CollectionDockingView"; +import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; +import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; +import { MainView } from "../views/MainView"; +import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; +import { LabelBox } from "../views/nodes/LabelBox"; +import { OverlayView } from "../views/OverlayView"; +import { DocumentManager } from "./DocumentManager"; +import { DragManager } from "./DragManager"; +import { makeTemplate } from "./DropConverter"; +import { HistoryUtil } from "./History"; +import { LinkManager } from "./LinkManager"; +import { Scripting } from "./Scripting"; +import { SearchUtil } from "./SearchUtil"; +import { SelectionManager } from "./SelectionManager"; +import { SnappingManager } from "./SnappingManager"; +import { InkTool } from "../../fields/InkField"; +import { ButtonType } from "../views/nodes/FontIconBox"; + + +export let resolvedPorts: { server: number, socket: number }; +const headerViewVersion = "0.1"; +export class CurrentUserUtils { + private static curr_id: string; + //TODO tfs: these should be temporary... + private static mainDocId: string | undefined; + + public static get id() { return this.curr_id; } + public static get MainDocId() { return this.mainDocId; } + public static set MainDocId(id: string | undefined) { this.mainDocId = id; } + @computed public static get UserDocument() { return Doc.UserDoc(); } + + @observable public static GuestTarget: Doc | undefined; + @observable public static GuestDashboard: Doc | undefined; + @observable public static GuestMobile: Doc | undefined; + @observable public static propertiesWidth: number = 0; + + // sets up the default User Templates - slideView, headerView + static setupUserTemplateButtons(doc: Doc) { + // Prototype for mobile button (not sure if 'Advanced Item Prototypes' is ideal location) + if (doc["template-mobile-button"] === undefined) { + const queryTemplate = this.mobileButton({ + title: "NEW MOBILE BUTTON", + onClick: undefined, + }, + [this.ficon({ + ignoreClick: true, + icon: "mobile", + btnType: ButtonType.ClickButton, + backgroundColor: "transparent" + }), + this.mobileTextContainer({}, + [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])]); + doc["template-mobile-button"] = CurrentUserUtils.ficon({ + onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), + dragFactory: new PrefetchProxy(queryTemplate) as any as Doc, title: "mobile button", + btnType: ButtonType.ClickButton, + icon: "mobile" + }); + } + + if (doc["template-button-slides"] === undefined) { + const slideTemplate = Docs.Create.MultirowDocument( + [ + Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }), + Docs.Create.TextDocument("", { title: "text", _height: 100, system: true }) + ], + { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true } + ); + slideTemplate.isTemplateDoc = makeTemplate(slideTemplate); + doc["template-button-slides"] = CurrentUserUtils.ficon({ + onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), + dragFactory: new PrefetchProxy(slideTemplate) as any as Doc, title: "presentation slide", + icon: "address-card", + btnType: ButtonType.ClickButton + }); + } + + if (doc["template-button-link"] === undefined) { // set _backgroundColor to transparent to prevent link dot from obscuring document it's attached to. + const linkTemplate = Doc.MakeDelegate(Docs.Create.TextDocument(" ", { title: "header", _autoHeight: true, system: true }, "header")); // text needs to be a space to allow templateText to be created + linkTemplate.system = true; + Doc.GetProto(linkTemplate).layout = + "
" + + " " + + " " + + "
"; + (linkTemplate.proto as Doc).isTemplateDoc = makeTemplate(linkTemplate.proto as Doc, true, "linkView"); + + const rtf2 = { + doc: { + type: "doc", content: [ + { + type: "paragraph", + content: [{ + type: "dashField", + attrs: { + fieldKey: "src", + hideKey: false + } + }] + }, + { type: "paragraph" }, + { + type: "paragraph", + content: [{ + type: "dashField", + attrs: { + fieldKey: "dst", + hideKey: false + } + }] + }] + }, + selection: { type: "text", anchor: 1, head: 1 }, + storedMarks: [] + }; + linkTemplate.header = new RichTextField(JSON.stringify(rtf2), ""); + + doc["template-button-link"] = CurrentUserUtils.ficon({ + onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), + dragFactory: new PrefetchProxy(linkTemplate) as any as Doc, title: "link view", + btnType: ButtonType.ClickButton, + icon: "window-maximize", system: true + }); + } + + if (doc["template-button-switch"] === undefined) { + const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create; + + const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40, system: true }); + const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1, system: true }); + const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, system: true }); + const labelTemplate = { + doc: { + type: "doc", content: [{ + type: "paragraph", + content: [{ type: "dashField", attrs: { fieldKey: "PARAMS", hideKey: true } }] + }] + }, + selection: { type: "text", anchor: 1, head: 1 }, + storedMarks: [] + }; + Doc.GetProto(name).text = new RichTextField(JSON.stringify(labelTemplate), "PARAMS"); + Doc.GetProto(yes).backgroundColor = ComputedField.MakeFunction("self[this.PARAMS] ? 'green':'red'"); + // Doc.GetProto(no).backgroundColor = ComputedField.MakeFunction("!self[this.PARAMS] ? 'red':'white'"); + // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = true"); + Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = !self[this.PARAMS]"); + // Doc.GetProto(no).onClick = ScriptField.MakeScript("self[this.PARAMS] = false"); + const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, system: true }); + box.isTemplateDoc = makeTemplate(box, true, "switch"); + + doc["template-button-switch"] = CurrentUserUtils.ficon({ + onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), + dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", + btnType: ButtonType.ClickButton, + icon: "toggle-on", system: true + }); + } + + if (doc["template-button-detail"] === undefined) { + const { TextDocument, MasonryDocument, CarouselDocument } = Docs.Create; + + const openInTarget = ScriptField.MakeScript("openOnRight(self.doubleClickView)"); + const carousel = CarouselDocument([], { + title: "data", _height: 350, _itemIndex: 0, "_carousel-caption-xMargin": 10, "_carousel-caption-yMargin": 10, + onChildDoubleClick: openInTarget, backgroundColor: "#9b9b9b3F", system: true + }); + + const details = TextDocument("", { title: "details", _height: 200, _autoHeight: true, system: true }); + const short = TextDocument("", { title: "shortDescription", treeViewOpen: true, treeViewExpandedView: "layout", _height: 75, _autoHeight: true, system: true }); + const long = TextDocument("", { title: "longDescription", treeViewOpen: false, treeViewExpandedView: "layout", _height: 150, _autoHeight: true, system: true }); + + const buxtonFieldKeys = ["year", "originalPrice", "degreesOfFreedom", "company", "attribute", "primaryKey", "secondaryKey", "dimensions"]; + const detailedTemplate = { + doc: { + type: "doc", content: buxtonFieldKeys.map(fieldKey => ({ + type: "paragraph", + content: [{ type: "dashField", attrs: { fieldKey } }] + })) + }, + selection: { type: "text", anchor: 1, head: 1 }, + storedMarks: [] + }; + details.text = new RichTextField(JSON.stringify(detailedTemplate), buxtonFieldKeys.join(" ")); + + const shared = { _autoHeight: true, _xMargin: 0 }; + const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12px" }; + const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTitle: true, _pivotField: "title", system: true }; + + const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts }); + descriptionWrapper._columnHeaders = new List([ + new SchemaHeaderField("[A Short Description]", "dimGray", undefined, undefined, undefined, false), + new SchemaHeaderField("[Long Description]", "dimGray", undefined, undefined, undefined, true), + new SchemaHeaderField("[Details]", "dimGray", undefined, undefined, undefined, true), + ]); + const detailView = Docs.Create.StackingDocument([carousel, descriptionWrapper], { ...shared, ...detailViewOpts, _chromeHidden: true, system: true }); + detailView.isTemplateDoc = makeTemplate(detailView); + + details.title = "Details"; + short.title = "A Short Description"; + long.title = "Long Description"; + + doc["template-button-detail"] = CurrentUserUtils.ficon({ + onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), + dragFactory: new PrefetchProxy(detailView) as any as Doc, title: "detailView", + btnType: ButtonType.ClickButton, + icon: "window-maximize", system: true + }); + } + + const requiredTypes = [ + doc["template-button-slides"] as Doc, + doc["template-mobile-button"] as Doc, + doc["template-button-detail"] as Doc, + doc["template-button-link"] as Doc, + //doc["template-button-switch"] as Doc] + ]; + if (doc["template-buttons"] === undefined) { + doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument(requiredTypes, { + title: "Advanced Item Prototypes", _xMargin: 0, _showTitle: "title", _chromeHidden: true, + hidden: ComputedField.MakeFunction("IsNoviceMode()") as any, + _stayInCollection: true, _hideContextMenu: true, + _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true + })); + } else { + const curButnTypes = Cast(doc["template-buttons"], Doc, null); + DocListCastAsync(curButnTypes.data).then(async curBtns => { + curBtns && await Promise.all(curBtns); + requiredTypes.map(btype => Doc.AddDocToList(curButnTypes, "data", btype)); + }); + } + return doc["template-buttons"] as Doc; + } + + // setup the different note type skins + static setupNoteTemplates(doc: Doc) { + if (doc["template-note-Note"] === undefined) { + const noteView = Docs.Create.TextDocument("", { title: "text", isTemplateDoc: true, backgroundColor: "yellow", system: true }); + noteView.isTemplateDoc = makeTemplate(noteView, true, "Note"); + doc["template-note-Note"] = new PrefetchProxy(noteView); + } + if (doc["template-note-Idea"] === undefined) { + const noteView = Docs.Create.TextDocument("", { title: "text", backgroundColor: "pink", system: true }); + noteView.isTemplateDoc = makeTemplate(noteView, true, "Idea"); + doc["template-note-Idea"] = new PrefetchProxy(noteView); + } + if (doc["template-note-Topic"] === undefined) { + const noteView = Docs.Create.TextDocument("", { title: "text", backgroundColor: "lightblue", system: true }); + noteView.isTemplateDoc = makeTemplate(noteView, true, "Topic"); + doc["template-note-Topic"] = new PrefetchProxy(noteView); + } + if (doc["template-note-Todo"] === undefined) { + const noteView = Docs.Create.TextDocument("", { + title: "text", backgroundColor: "orange", _autoHeight: false, _height: 100, _showCaption: "caption", + layout: FormattedTextBox.LayoutString("Todo"), caption: RichTextField.DashField("taskStatus"), system: true + }); + noteView.isTemplateDoc = makeTemplate(noteView, true, "Todo"); + doc["template-note-Todo"] = new PrefetchProxy(noteView); + } + const taskStatusValues = [ + { title: "todo", _backgroundColor: "blue", color: "white", system: true }, + { title: "in progress", _backgroundColor: "yellow", color: "black", system: true }, + { title: "completed", _backgroundColor: "green", color: "white", system: true } + ]; + if (doc.fieldTypes === undefined) { + doc.fieldTypes = Docs.Create.TreeDocument([], { title: "field enumerations", system: true }); + DocUtils.addFieldEnumerations(Doc.GetProto(doc["template-note-Todo"] as any as Doc), "taskStatus", taskStatusValues); + } + + if (doc["template-notes"] === undefined) { + doc["template-notes"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-note-Note"] as any as Doc, doc["template-note-Idea"] as any as Doc, doc["template-note-Topic"] as any as Doc], // doc["template-note-Todo"] as any as Doc], + { title: "Note Layouts", _height: 75, system: true })); + } else { + const curNoteTypes = Cast(doc["template-notes"], Doc, null); + const requiredTypes = [doc["template-note-Note"] as any as Doc, doc["template-note-Idea"] as any as Doc, doc["template-note-Topic"] as any as Doc];//, doc["template-note-Todo"] as any as Doc]; + DocListCastAsync(curNoteTypes.data).then(async curNotes => { + curNotes && await Promise.all(curNotes); + requiredTypes.map(ntype => Doc.AddDocToList(curNoteTypes, "data", ntype)); + }); + } + + return doc["template-notes"] as Doc; + } + + // creates Note templates, and initial "user" templates + static setupDocTemplates(doc: Doc) { + const noteTemplates = CurrentUserUtils.setupNoteTemplates(doc); + const userTemplateBtns = CurrentUserUtils.setupUserTemplateButtons(doc); + const clickTemplates = CurrentUserUtils.setupClickEditorTemplates(doc); + if (doc.templateDocs === undefined) { + doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([noteTemplates, userTemplateBtns, clickTemplates], { + title: "template layouts", _xPadding: 0, system: true, + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }) + })); + } + } + + // setup templates for different document types when they are iconified from Document Decorations + static setupDefaultIconTemplates(doc: Doc) { + if (doc["template-icon-view"] === undefined) { + const iconView = Docs.Create.LabelDocument({ + title: "icon", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("title"), _backgroundColor: "dimGray", + _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true + }); + // Docs.Create.TextDocument("", { + // title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") + // }); + // Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', ""); + iconView.isTemplateDoc = makeTemplate(iconView); + doc["template-icon-view"] = new PrefetchProxy(iconView); + } + if (doc["template-icon-view-rtf"] === undefined) { + const iconRtfView = Docs.Create.LabelDocument({ + title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"), + _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true + }); + iconRtfView.isTemplateDoc = makeTemplate(iconRtfView, true, "icon_" + DocumentType.RTF); + doc["template-icon-view-rtf"] = new PrefetchProxy(iconRtfView); + } + if (doc["template-icon-view-button"] === undefined) { + const iconBtnView = Docs.Create.FontIconDocument({ + title: "icon_" + DocumentType.BUTTON, _nativeHeight: 30, _nativeWidth: 30, + _width: 30, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true + }); + iconBtnView.isTemplateDoc = makeTemplate(iconBtnView, true, "icon_" + DocumentType.BUTTON); + doc["template-icon-view-button"] = new PrefetchProxy(iconBtnView); + } + if (doc["template-icon-view-img"] === undefined) { + const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { + title: "data", _width: 50, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true + }); + iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG); + doc["template-icon-view-img"] = new PrefetchProxy(iconImageView); + } + if (doc["template-icon-view-col"] === undefined) { + const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true }); + iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL); + doc["template-icon-view-col"] = new PrefetchProxy(iconColView); + } + if (doc["template-icons"] === undefined) { + doc["template-icons"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, + doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc, doc["template-icon-view-pdf"] as Doc], { title: "icon templates", _height: 75, system: true })); + } else { + const templateIconsDoc = Cast(doc["template-icons"], Doc, null); + const requiredTypes = [doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, + doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc]; + DocListCastAsync(templateIconsDoc.data).then(async curIcons => { + curIcons && await Promise.all(curIcons); + requiredTypes.map(ntype => Doc.AddDocToList(templateIconsDoc, "data", ntype)); + }); + } + return doc["template-icons"] as Doc; + } + + static creatorBtnDescriptors(doc: Doc): { + title: string, toolTip: string, icon: string, drag?: string, ignoreClick?: boolean, + click?: string, backgroundColor?: string, dragFactory?: Doc, noviceMode?: boolean, clickFactory?: Doc + }[] { + if (doc.emptyPresentation === undefined) { + doc.emptyPresentation = Docs.Create.PresDocument(new List(), + { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0", system: true, cloneFieldFilter: new List(["system"]) }); + ((doc.emptyPresentation as Doc).proto as Doc)["dragFactory-count"] = 0; + } + if (doc.emptyCollection === undefined) { + doc.emptyCollection = Docs.Create.FreeformDocument([], + { _nativeWidth: undefined, _nativeHeight: undefined, _fitWidth: true, _width: 150, _height: 100, title: "freeform", system: true, cloneFieldFilter: new List(["system"]) }); + ((doc.emptyCollection as Doc).proto as Doc)["dragFactory-count"] = 0; + } + if (doc.emptyPane === undefined) { + doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _width: 500, _height: 800, title: "Untitled Tab", system: true, cloneFieldFilter: new List(["system"]) }); + ((doc.emptyPane as Doc).proto as Doc)["dragFactory-count"] = 0; + } + if (doc.emptySlide === undefined) { + const textDoc = Docs.Create.TreeDocument([], { title: "Slide", _viewType: CollectionViewType.Tree, _fontSize: "20px", treeViewType: "outline", _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "transparent", system: true, cloneFieldFilter: new List(["system"]) }); + Doc.GetProto(textDoc).title = ComputedField.MakeFunction('self.text?.Text'); + FormattedTextBox.SelectOnLoad = textDoc[Id]; + doc.emptySlide = textDoc; + } + if ((doc.emptyHeader as Doc)?.version !== headerViewVersion) { + const json = { + doc: { + type: "doc", + content: [ + { + type: "paragraph", attrs: {}, content: [{ + type: "dashField", + attrs: { fieldKey: "author", docid: "", hideKey: false }, + marks: [{ type: "strong" }] + }, { + type: "dashField", + attrs: { fieldKey: "creationDate", docid: "", hideKey: false }, + marks: [{ type: "strong" }] + }] + }] + }, + selection: { type: "text", anchor: 1, head: 1 }, + storedMarks: [] + }; + const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { + title: "text", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all", + _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, _fitWidth: true, + cloneFieldFilter: new List(["system"]) + }, "header"); + const headerBtnHgt = 10; + headerTemplate[DataSym].layout = + "" + + ` ` + + " " + + ` Metadata` + + ""; + + // "
" + + // " " + + // " " + + // "
"; + (headerTemplate.proto as Doc).isTemplateDoc = makeTemplate(headerTemplate.proto as Doc, true, "headerView"); + doc.emptyHeader = headerTemplate; + ((doc.emptyHeader as Doc).proto as Doc)["dragFactory-count"] = 0; + } + if (doc.emptyComparison === undefined) { + doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300, system: true, cloneFieldFilter: new List(["system"]) }); + } + if (doc.emptyScript === undefined) { + doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250, title: "script", system: true, cloneFieldFilter: new List(["system"]) }); + ((doc.emptyScript as Doc).proto as Doc)["dragFactory-count"] = 0; + } + if (doc.emptyScreenshot === undefined) { + doc.emptyScreenshot = Docs.Create.ScreenshotDocument("empty screenshot", { _fitWidth: true, _width: 400, _height: 200, system: true, cloneFieldFilter: new List(["system"]) }); + } + if (doc.emptyWall === undefined) { + doc.emptyWall = Docs.Create.ScreenshotDocument("", { _fitWidth: true, _width: 400, _height: 200, title: "screen snapshot", system: true, cloneFieldFilter: new List(["system"]) }); + (doc.emptyWall as Doc).videoWall = true; + } + if (doc.emptyAudio === undefined) { + doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { _width: 200, title: "audio recording", system: true, cloneFieldFilter: new List(["system"]) }); + ((doc.emptyAudio as Doc).proto as Doc)["dragFactory-count"] = 0; + } + if (doc.emptyNote === undefined) { + doc.emptyNote = Docs.Create.TextDocument("", { _width: 200, title: "text note", _autoHeight: true, system: true, cloneFieldFilter: new List(["system"]) }); + ((doc.emptyNote as Doc).proto as Doc)["dragFactory-count"] = 0; + } + if (doc.emptyImage === undefined) { + doc.emptyImage = Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth: 250, title: "an image of a cat", system: true }); + } + if (doc.emptyButton === undefined) { + doc.emptyButton = Docs.Create.ButtonDocument({ _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, title: "Button", system: true, cloneFieldFilter: new List(["system"]) }); + ((doc.emptyButton as Doc).proto as Doc)["dragFactory-count"] = 0; + } + if (doc.emptyWebpage === undefined) { + doc.emptyWebpage = Docs.Create.WebDocument("", { title: "webpage", _nativeWidth: 850, isTemplateDoc: true, _height: 512, _width: 400, useCors: true, system: true, cloneFieldFilter: new List(["system"]) }); + } + if (doc.activeMobileMenu === undefined) { + this.setupActiveMobileMenu(doc); + } + return [ + { toolTip: "Tap to create a note in a new pane, drag for a note", title: "Note", icon: "sticky-note", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyNote as Doc, noviceMode: true, clickFactory: doc.emptyNote as Doc, }, + { toolTip: "Tap to create a collection in a new pane, drag for a collection", title: "Col", icon: "folder", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyCollection as Doc, noviceMode: true, clickFactory: doc.emptyPane as Doc, }, + { toolTip: "Tap to create a webpage in a new pane, drag for a webpage", title: "Web", icon: "globe-asia", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWebpage as Doc, noviceMode: true }, + { toolTip: "Tap to create a progressive slide", title: "Slide", icon: "file", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptySlide as Doc, noviceMode: true }, + { toolTip: "Tap to create a cat image in a new pane, drag for a cat image", title: "Image", icon: "cat", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyImage as Doc }, + { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyComparison as Doc, noviceMode: true }, + { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc, noviceMode: true }, + { toolTip: "Tap to create a videoWall", title: "Wall", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWall as Doc }, + { toolTip: "Tap to create an audio recorder in a new pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true }, + { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc }, + // { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true }, + { toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScript as Doc }, + { toolTip: "Tap to create a mobile view in a new pane, drag for a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc }, + { toolTip: "Tap to create a custom header note document, drag for a custom header note", title: "Custom", icon: "window-maximize", click: 'openOnRight(delegateDragFactory(this.dragFactory))', drag: 'delegateDragFactory(this.dragFactory)', dragFactory: doc.emptyHeader as Doc }, + { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' }, + ]; + + } + + // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools + static async setupCreatorButtons(doc: Doc) { + let alreadyCreatedButtons: string[] = []; + const dragCreatorSet = await Cast(doc.myItemCreators, Doc, null); + if (dragCreatorSet) { + const dragCreators = await Cast(dragCreatorSet.data, listSpec(Doc)); + if (dragCreators) { + const dragDocs = await Promise.all(dragCreators); + alreadyCreatedButtons = dragDocs.map(d => StrCast(d.title)); + } + } + const buttons = CurrentUserUtils.creatorBtnDescriptors(doc).filter(d => !alreadyCreatedButtons?.includes(d.title)); + const creatorBtns = buttons.map(({ title, toolTip, icon, ignoreClick, drag, click, backgroundColor, dragFactory, noviceMode, clickFactory }) => Docs.Create.FontIconDocument({ + _nativeWidth: 50, _nativeHeight: 50, _width: 30, _height: 25, + icon, + title, + toolTip, + btnType: ButtonType.ClickButton, + ignoreClick, + _dropAction: "alias", + onDragStart: drag ? ScriptField.MakeFunction(drag) : undefined, + onClick: click ? ScriptField.MakeScript(click) : undefined, + backgroundColor, + _hideContextMenu: true, + _removeDropProperties: new List(["_stayInCollection"]), + _stayInCollection: true, + dragFactory, + clickFactory, + hidden: !noviceMode ? ComputedField.MakeFunction("IsNoviceMode()") as any : undefined, + system: true, + })); + + if (dragCreatorSet === undefined) { + doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, { + title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, + _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true + })); + } else { + creatorBtns.forEach(nb => Doc.AddDocToList(doc.myItemCreators as Doc, "data", nb)); + } + return doc.myItemCreators as Doc; + } + + static async menuBtnDescriptions(doc: Doc) { + return [ + { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' }, + { title: "My Files", target: Cast(doc.myFilesystem, Doc, null), icon: "file", click: 'selectMainMenu(self)' }, + { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)' }, + { title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' }, + { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' }, + { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc }, + // { title: "Filter", target: Cast(doc.currentFilter, Doc, null), icon: "filter", click: 'selectMainMenu(self)' }, + { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' }, + // { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' }, + // { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' }, + { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' }, + ]; + } + + static setupSearchPanel(doc: Doc) { + if (doc.mySearchPanelDoc === undefined) { + doc.mySearchPanelDoc = new PrefetchProxy(Docs.Create.SearchDocument({ + _width: 500, _height: 300, backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true, + childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "sidebar search stack", system: true + })) as any as Doc; + } + } + static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { + if (doc.menuStack === undefined) { + await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId); // sets up the right sidebar collection for mobile upload documents and sharing + const menuBtns = (await CurrentUserUtils.menuBtnDescriptions(doc)).map(({ title, target, icon, click, watchedDocuments }) => + Docs.Create.FontIconDocument({ + icon, + btnType: ButtonType.MenuButton, + _stayInCollection: true, + _hideContextMenu: true, + system: true, + dontUndo: true, + title, + target, + _dropAction: "alias", + _removeDropProperties: new List(["dropAction", "_stayInCollection"]), + _width: 60, + _height: 60, + watchedDocuments, + onClick: ScriptField.MakeScript(click, { scriptContext: "any" }) + })); + // hack -- last button is assumed to be the userDoc + menuBtns[menuBtns.length - 1].hidden = ComputedField.MakeFunction("IsNoviceMode()"); + + doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument(menuBtns, { + title: "menuItemPanel", + childDropAction: "alias", + _chromeHidden: true, + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), + ignoreClick: true, + _gridGap: 0, + _yMargin: 0, + _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true + })); + } + // this resets all sidebar buttons to being deactivated + PromiseValue(Cast(doc.menuStack, Doc)).then(stack => { + stack && PromiseValue(stack.data).then(btns => { + DocListCastAsync(btns).then(bts => bts?.forEach(btn => { + btn.dontUndo = true; + btn.system = true; + if (btn.title === "Catalog" || btn.title === "My Files") { // migration from Catalog to My Files + btn.target = Doc.UserDoc().myFilesystem; + btn.title = "My Files"; + } + })); + }); + }); + return doc.menuStack as Doc; + } + + + // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu + static setupActiveMobileMenu(doc: Doc) { + if (doc.activeMobileMenu === undefined) { + doc.activeMobileMenu = this.setupMobileMenu(); + } + return doc.activeMobileMenu as Doc; + } + + // Sets up mobileMenu stacking document + static setupMobileMenu() { + const menu = new PrefetchProxy(Docs.Create.StackingDocument(this.setupMobileButtons(), { + _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, system: true, _chromeHidden: true, + })); + return menu; + } + + // SEts up mobile buttons for inside mobile menu + static setupMobileButtons(doc?: Doc, buttons?: string[]) { + const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, activePen?: Doc, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [ + { title: "DASHBOARDS", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Dashboards from your mobile, and navigate through all of your documents. " }, + { title: "UPLOAD", icon: "upload", click: 'openMobileUploads()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." }, + { title: "MOBILE UPLOAD", icon: "mobile", click: 'switchToMobileUploadCollection()', backgroundColor: "lightgrey", info: "Access the collection of your mobile uploads." }, + { title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "lightgrey", info: "Use your phone to record, dictate and then upload audio onto Dash Web." }, + { title: "PRESENTATION", icon: "desktop", click: 'switchToMobilePresentation()', backgroundColor: "lightgrey", info: "Use your phone as a remote for you presentation." }, + { title: "SETTINGS", icon: "cog", click: 'openMobileSettings()', backgroundColor: "lightgrey", info: "Change your password, log out, or manage your account security." } + ]; + // returns a list of mobile buttons + return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data => + this.mobileButton({ + title: data.title, + _lockedPosition: true, + onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, + backgroundColor: data.backgroundColor, system: true + }, + [this.ficon({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", btnType: ButtonType.ClickButton, system: true }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])]) + ); + } + + // sets up the main document for the mobile button + static mobileButton = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MulticolumnDocument(docs, { + ...opts, + _removeDropProperties: new List(["dropAction"]), _nativeWidth: 900, _nativeHeight: 250, _width: 900, _height: 250, _yMargin: 15, + borderRounding: "5px", boxShadow: "0 0", system: true + }) as any as Doc + + // sets up the text container for the information contained within the mobile button + static mobileTextContainer = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MultirowDocument(docs, { + ...opts, + _removeDropProperties: new List(["dropAction"]), _nativeWidth: 450, _nativeHeight: 250, _width: 450, _height: 250, _yMargin: 25, + backgroundColor: "rgba(0,0,0,0)", borderRounding: "0", boxShadow: "0 0", ignoreClick: true, system: true + }) as any as Doc + + // Sets up the title of the button + static mobileButtonText = (opts: DocumentOptions, buttonTitle: string) => Docs.Create.TextDocument(buttonTitle, { + ...opts, + title: buttonTitle, _fontSize: "37px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", system: true + }) as any as Doc + + // Sets up the description of the button + static mobileButtonInfo = (opts: DocumentOptions, buttonInfo: string) => Docs.Create.TextDocument(buttonInfo, { + ...opts, + title: "info", _fontSize: "25px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, system: true + }) as any as Doc + + + static setupThumbButtons(doc: Doc) { + const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, clipboard?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ + { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue" }, + { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow" }, + { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, system: true }), backgroundColor: "orange" }, + { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: "orange" }, + { title: "ignore gestures", icon: "signature", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: "green" }, + ]; + return docProtoData.map(data => Docs.Create.FontIconDocument({ + _nativeWidth: 10, _nativeHeight: 10, _width: 10, _height: 10, title: data.title, icon: data.icon, + _dropAction: data.pointerDown ? "copy" : undefined, ignoreClick: data.ignoreClick, + onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, + clipboard: data.clipboard, + onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined, + backgroundColor: data.backgroundColor, + _removeDropProperties: new List(["dropAction"]), dragFactory: data.dragFactory, system: true + })); + } + + static setupThumbDoc(userDoc: Doc) { + if (!userDoc.thumbDoc) { + const thumbDoc = Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), { + _width: 100, _height: 50, ignoreClick: true, _lockedPosition: true, title: "buttons", + _autoHeight: true, _yMargin: 5, linearViewIsExpanded: true, backgroundColor: "white", system: true + }); + thumbDoc.inkToTextDoc = Docs.Create.LinearDocument([], { + _width: 300, _height: 25, _autoHeight: true, linearViewIsExpanded: true, flexDirection: "column", system: true + }); + userDoc.thumbDoc = thumbDoc; + } + return Cast(userDoc.thumbDoc, Doc); + } + + static setupMobileInkingDoc(userDoc: Doc) { + return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white", system: true }); + } + + static setupMobileUploadDoc(userDoc: Doc) { + // const addButton = Docs.Create.FontIconDocument({ onDragStart: ScriptField.MakeScript('addWebToMobileUpload()'), title: "Add Web Doc to Upload Collection", icon: "plus", backgroundColor: "black" }) + const webDoc = Docs.Create.WebDocument("https://www.britannica.com/biography/Miles-Davis", { + title: "Upload Images From the Web", _lockedPosition: true, system: true + }); + const uploadDoc = Docs.Create.StackingDocument([], { + title: "Mobile Upload Collection", backgroundColor: "white", _lockedPosition: true, system: true, _chromeHidden: true, + }); + return Docs.Create.StackingDocument([webDoc, uploadDoc], { + _width: screen.width, _lockedPosition: true, title: "Upload", _autoHeight: true, _yMargin: 80, backgroundColor: "lightgray", system: true, _chromeHidden: true, + }); + } + + static setupLibrary(userDoc: Doc) { + return CurrentUserUtils.setupDashboards(userDoc); + } + + // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. + // when clicked, this panel will be displayed in the target container (ie, sidebarContainer) + static async setupToolsBtnPanel(doc: Doc) { + // setup a masonry view of all he creators + const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc); + const templateBtns = CurrentUserUtils.setupUserTemplateButtons(doc); + + doc["tabs-button-tools"] = undefined; + + if (doc.myCreators === undefined) { + doc.myCreators = new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], { + title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, _fitWidth: true, + _width: 500, _height: 300, ignoreClick: true, _lockedPosition: true, system: true, _chromeHidden: true, + })); + } + // setup a color picker + if (doc.myColorPicker === undefined) { + const color = Docs.Create.ColorDocument({ + title: "color picker", ignoreClick: true, _width: 220, _dropAction: "alias", _hideContextMenu: true, _stayInCollection: true, _forceActive: true, _removeDropProperties: new List(["dropAction", "_stayInCollection", "_hideContextMenu", "forceActive"]), system: true + }); + doc.myColorPicker = new PrefetchProxy(color); + } + + if (doc.myTools === undefined) { + const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], { + title: "My Tools", _showTitle: "title", _width: 500, _yMargin: 20, ignoreClick: true, _lockedPosition: true, _forceActive: true, + system: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, boxShadow: "0 0", + })) as any as Doc; + + doc.myTools = toolsStack; + } + } + + static async setupDashboards(doc: Doc) { + // setup dashboards library item + await doc.myDashboards; + if (doc.myDashboards === undefined) { + doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], { + title: "My Dashboards", _showTitle: "title", _height: 400, childHideLinkButton: true, + treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", + treeViewTruncateTitleWidth: 150, ignoreClick: true, + _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true + })); + const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`); + (doc.myDashboards as any as Doc).contextMenuScripts = new List([newDashboard!]); + (doc.myDashboards as any as Doc).contextMenuLabels = new List(["Create New Dashboard"]); + } + return doc.myDashboards as any as Doc; + } + + static async setupPresentations(doc: Doc) { + await doc.myPresentations; + if (doc.myPresentations === undefined) { + doc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], { + title: "My Trails", _showTitle: "title", _height: 100, + treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", + treeViewTruncateTitleWidth: 150, ignoreClick: true, + _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true + })); + const newPresentations = ScriptField.MakeScript(`createNewPresentation()`); + (doc.myPresentations as any as Doc).contextMenuScripts = new List([newPresentations!]); + (doc.myPresentations as any as Doc).contextMenuLabels = new List(["Create New Presentation"]); + const presentations = doc.myPresentations as any as Doc; + } + return doc.myPresentations as any as Doc; + } + + static async setupFilesystem(doc: Doc) { + await doc.myFilesystem; + if (doc.myFilesystem === undefined) { + doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true }); + doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true }); + doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileRoot as Doc, doc.myFileOrphans as Doc], { + title: "My Documents", _showTitle: "title", _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 + })); + } + return doc.myFilesystem as any as Doc; + } + + static setupRecentlyClosedDocs(doc: Doc) { + // setup Recently Closed library item + if (doc.myRecentlyClosedDocs === undefined) { + doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], { + title: "Recently Closed", _showTitle: "title", treeViewShowClearButton: true, childHideLinkButton: true, + treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", + treeViewTruncateTitleWidth: 150, ignoreClick: true, + _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true + })); + const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`); + (doc.myRecentlyClosedDocs as any as Doc).contextMenuScripts = new List([clearAll!]); + (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List(["Clear All"]); + } + } + static setupFilterDocs(doc: Doc) { + // setup Filter item + if (doc.currentFilter === undefined) { + doc.currentFilter = Docs.Create.FilterDocument({ + title: "unnamed filter", _height: 150, + treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "none", + treeViewTruncateTitleWidth: 150, ignoreClick: true, + _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true, _autoHeight: true, _fitWidth: true + }); + const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`); + (doc.currentFilter as Doc).contextMenuScripts = new List([clearAll!]); + (doc.currentFilter as Doc).contextMenuLabels = new List(["Clear All"]); + (doc.currentFilter as Doc).filterBoolean = "AND"; + } + } + + static setupUserDoc(doc: Doc) { + if (doc.myUserDoc === undefined) { + doc.treeViewOpen = true; + doc.treeViewExpandedView = "fields"; + doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], { + treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, title: "My UserDoc", _showTitle: "title", + treeViewTruncateTitleWidth: 150, ignoreClick: true, + _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true + })) as any as Doc; + } + } + + static setupSidebarContainer(doc: Doc) { + if (doc.sidebar === undefined) { + const sidebarContainer = new Doc(); + sidebarContainer.system = true; + doc.sidebar = new PrefetchProxy(sidebarContainer); + } + return doc.sidebar as Doc; + } + + // setup the list of sidebar mode buttons which determine what is displayed in the sidebar + static async setupSidebarButtons(doc: Doc) { + CurrentUserUtils.setupSidebarContainer(doc); + await CurrentUserUtils.setupToolsBtnPanel(doc); + CurrentUserUtils.setupImportSidebar(doc); + CurrentUserUtils.setupDashboards(doc); + CurrentUserUtils.setupPresentations(doc); + CurrentUserUtils.setupFilesystem(doc); + CurrentUserUtils.setupRecentlyClosedDocs(doc); + // CurrentUserUtils.setupFilterDocs(doc); + CurrentUserUtils.setupUserDoc(doc); + } + + static blist = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, { + ...opts, _gridGap: 5, _xMargin: 5, _yMargin: 5, _width: 100, boxShadow: "0 0", _forceActive: true, + dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), + _lockedPosition: true, linearViewIsExpanded: true, system: true + })) as any as Doc + + static ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({ + ...opts, _dropAction: "alias", _removeDropProperties: new List(["_dropAction", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true + })) as any as Doc + + /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window + static setupDockedButtons(doc: Doc) { + if (doc["dockedBtn-undo"] === undefined) { + doc["dockedBtn-undo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), btnType: ButtonType.ClickButton, dontUndo: true, _stayInCollection: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to undo", title: "Undo", icon: "undo-alt", system: true, canClick: 'canUndo' }); + } + if (doc["dockedBtn-redo"] === undefined) { + doc["dockedBtn-redo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), btnType: ButtonType.ClickButton, dontUndo: true, _stayInCollection: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to redo", title: "Redo", icon: "redo-alt", system: true, canClick: 'canRedo' }); + } + if (doc.dockedBtns === undefined) { + doc.dockedBtns = CurrentUserUtils.blist({ title: "docked buttons", ignoreClick: true, linearViewExpandable: true, _height: 42 }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]); + } + (doc["dockedBtn-undo"] as Doc).dontUndo = true; + (doc["dockedBtn-redo"] as Doc).dontUndo = true; + } + + static textTools(doc: Doc) { + return [ + { + title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, + list: ["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", + "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"], + scriptDoc: Doc.UserDoc(), toggle: 'userDoc._fontFamily' + }, + { + title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', + scriptDoc: Doc.UserDoc(), + toggle: 'userDoc._boldActive' + }, + { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', scriptDoc: Doc.UserDoc(), toggle: 'userDoc._italicsActive' }, + { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', scriptDoc: Doc.UserDoc(), toggle: 'userDoc._underlineActive' }, + // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()', toggle: 'userDoc._underlineActive' }, + // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()', toggle: 'userDoc._underlineActive' }, + // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()', toggle: 'userDoc._underlineActive' }, + { title: "Highlight", toolTip: "Highlight", btnType: ButtonType.ColorButton, icon: "highlighter", ignoreClick: true, scriptDoc: Doc.UserDoc(), userColorBtn: 'userDoc._highlightColor' }, + { title: "Text color", toolTip: "Text color", btnType: ButtonType.ColorButton, icon: "fill-drip", ignoreClick: true, scriptDoc: Doc.UserDoc(), userColorBtn: 'userDoc._textColor' }, + // { title: "Link", tooltip: "Link", btnType: ButtonType.DropdownButton, icon: "link", click: '', ignoreClick: true }, + ]; + } + + static async contextMenuBtnDescriptions(doc: Doc) { + return [ + // { title: "Perspective", tooltip: "Change document's perspective", type: "btn", btnType: ButtonType.DropdownButton, ignoreClick: true, icon: "desktop", click: '' }, + { + title: "Perspective", toolTip: "Perspective", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, + list: [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Tree, + CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Multicolumn, + CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel, + CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, + CollectionViewType.Grid], + scriptDoc: 'selectedDoc', + toggle: 'selectedDoc._viewType' + }, + { + title: "Background", toolTip: "Background", btnType: ButtonType.ColorButton, scriptDoc: 'selectedDoc', + docColorBtn: 'selectedDoc.backgroundColor', width: 60, ignoreClick: true, icon: "fill-drip", + canClick: 'numSelected > 0' + }, + { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', toggle: 'selectedDoc.z', canClick: 'numSelected > 0' }, + { title: "Text Tools", type: "TextMenu", icon: "font" }, + // { title: "Ink Tools", type: "LinearMenu", icon: "pen-nib" }, + // { title: "GFX Tools", type: "LinearMenu", icon: "shapes" }, + // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy" }, + ]; + } + + // Default context menu buttons + static async setupContextMenuButtons(doc: Doc) { + const docList: Doc[] = []; + + const contextMenuBtns = (await CurrentUserUtils.contextMenuBtnDescriptions(doc)).map(({ title, width, toolTip, ignoreClick, icon, type, btnType, click, toggle, scriptDoc, canClick, docColorBtn }) => { + const textDocList: Doc[] = []; + if (type === "TextMenu") { + const textBtns = (CurrentUserUtils.textTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, btnType, click, toggle, scriptDoc, userColorBtn }) => { + textDocList.push(Docs.Create.FontIconDocument({ + _nativeWidth: width ? width : 25, + _nativeHeight: 25, + _width: width ? width : 25, + _height: 25, + icon, + toolTip, + userColorBtn, + // testToggle: toggle ? ScriptField.MakeScript(toggle, { this: scriptDoc, scriptContext: "any" }) : undefined, + // toggle: toggle, + btnType: btnType, + btnList: new List(list), + ignoreClick: ignoreClick, + _stayInCollection: true, + _hideContextMenu: true, + system: true, + dontUndo: true, + title, + backgroundColor: "black", + _dropAction: "alias", + _removeDropProperties: new List(["dropAction", "_stayInCollection"]), + onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined + })); + }); + docList.push(CurrentUserUtils.blist({ ignoreClick: true, linearViewExpandable: true, _height: 30, backgroundColor: "transparent" }, textDocList)); + } else { + docList.push(Docs.Create.FontIconDocument({ + _nativeWidth: width ? width : 30, + _nativeHeight: 30, + _width: width ? width : 30, + _height: 30, + icon, + toolTip, + // testToggle: toggle ? ScriptField.MakeScript(toggle, { scriptContext: "any" }) : undefined, + // toggle: toggle, + docColorBtn, + canClick: canClick, + btnType: btnType, + ignoreClick: ignoreClick, + _stayInCollection: true, + _hideContextMenu: true, + system: true, + dontUndo: true, + title, + backgroundColor: "black", + _dropAction: "alias", + _removeDropProperties: new List(["dropAction", "_stayInCollection"]), + onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined + })); + } + }); + + if (doc.contextMenuBtns === undefined) { + doc.contextMenuBtns = CurrentUserUtils.blist({ title: "menu buttons", ignoreClick: true, linearViewExpandable: false, _height: 35 }, docList); + } + } + + // sets up the default set of documents to be shown in the Overlay layer + static setupOverlays(doc: Doc) { + if (doc.myOverlayDocs === undefined) { + doc.myOverlayDocs = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "overlay documents", backgroundColor: "#aca3a6", system: true })); + } + } + + // the initial presentation Doc to use + static setupDefaultPresentation(doc: Doc) { + if (doc["template-presentation"] === undefined) { + doc["template-presentation"] = new PrefetchProxy(Docs.Create.PresElementBoxDocument({ + title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _fitWidth: true, _height: 46, isTemplateDoc: true, isTemplateForField: "data", system: true + })); + } + } + + // Sharing sidebar is where shared documents are contained + static async setupSharingSidebar(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { + if (doc.myLinkDatabase === undefined) { + let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId); + if (!linkDocs) { + linkDocs = new Doc(linkDatabaseId, true); + (linkDocs as Doc).author = Doc.CurrentUserEmail; + (linkDocs as Doc).data = new List([]); + (linkDocs as Doc)["acl-Public"] = SharingPermissions.Add; + } + doc.myLinkDatabase = new PrefetchProxy(linkDocs); + } + if (doc.mySharedDocs === undefined) { + let sharedDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(sharingDocumentId + "outer"); + if (!sharedDocs) { + sharedDocs = Docs.Create.StackingDocument([], { + title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, + _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Add, "_acl-Public": SharingPermissions.Add, + _chromeHidden: true, boxShadow: "0 0", + }, sharingDocumentId + "outer", sharingDocumentId); + (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Add; + } + if (sharedDocs instanceof Doc) { + Doc.GetProto(sharedDocs).userColor = sharedDocs.userColor || "rgb(202, 202, 202)"; + } + doc.mySharedDocs = new PrefetchProxy(sharedDocs); + } + } + + // Import sidebar is where shared documents are contained + static setupImportSidebar(doc: Doc) { + if (doc.myImportDocs === undefined) { + doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { + title: "My ImportDocuments", _forceActive: true, ignoreClick: true, _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, + childDropAction: "alias", _autoHeight: true, _yMargin: 50, _gridGap: 15, _lockedPosition: true, system: true, _chromeHidden: true, + })); + } + if (doc.myImportPanel === undefined) { + const uploads = Cast(doc.myImportDocs, Doc, null); + const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), btnType: ButtonType.ClickButton, toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", icon: "upload", system: true }); + doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, _showTitle: "title", ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true, boxShadow: "0 0" })); + } + } + + static setupClickEditorTemplates(doc: Doc) { + if (doc["clickFuncs-child"] === undefined) { + // to use this function, select it from the context menu of a collection. then edit the onChildClick script. Add two Doc variables: 'target' and 'thisContainer', then assign 'target' to some target collection. After that, clicking on any document in the initial collection will open it in the target + const openInTarget = Docs.Create.ScriptingDocument(ScriptField.MakeScript( + "docCast(thisContainer.target).then((target) => target && (target.proto.data = new List([self]))) ", + { thisContainer: Doc.name }), { + title: "Click to open in target", _width: 300, _height: 200, + targetScriptKey: "onChildClick", system: true + }); + + const openDetail = Docs.Create.ScriptingDocument(ScriptField.MakeScript( + "openOnRight(self.doubleClickView)", + {}), { title: "Double click to open doubleClickView", _width: 300, _height: 200, targetScriptKey: "onChildDoubleClick", system: true }); + + doc["clickFuncs-child"] = Docs.Create.TreeDocument([openInTarget, openDetail], { title: "on Child Click function templates", system: true }); + } + // this is equivalent to using PrefetchProxies to make sure all the childClickFuncs have been retrieved. + PromiseValue(Cast(doc["clickFuncs-child"], Doc)).then(func => func && PromiseValue(func.data).then(DocListCast)); + + if (doc.clickFuncs === undefined) { + const onClick = Docs.Create.ScriptingDocument(undefined, { + title: "onClick", "onClick-rawScript": "console.log('click')", + isTemplateDoc: true, isTemplateForField: "onClick", _width: 300, _height: 200, system: true + }, "onClick"); + const onChildClick = Docs.Create.ScriptingDocument(undefined, { + title: "onChildClick", "onChildClick-rawScript": "console.log('child click')", + isTemplateDoc: true, isTemplateForField: "onChildClick", _width: 300, _height: 200, system: true + }, "onChildClick"); + const onDoubleClick = Docs.Create.ScriptingDocument(undefined, { + title: "onDoubleClick", "onDoubleClick-rawScript": "console.log('double click')", + isTemplateDoc: true, isTemplateForField: "onDoubleClick", _width: 300, _height: 200, system: true + }, "onDoubleClick"); + const onChildDoubleClick = Docs.Create.ScriptingDocument(undefined, { + title: "onChildDoubleClick", "onChildDoubleClick-rawScript": "console.log('child double click')", + isTemplateDoc: true, isTemplateForField: "onChildDoubleClick", _width: 300, _height: 200, system: true + }, "onChildDoubleClick"); + const onCheckedClick = Docs.Create.ScriptingDocument(undefined, { + title: "onCheckedClick", "onCheckedClick-rawScript": "console.log(heading + checked + containingTreeView)", + "onCheckedClick-params": new List(["heading", "checked", "containingTreeView"]), isTemplateDoc: true, + isTemplateForField: "onCheckedClick", _width: 300, _height: 200, system: true + }, "onCheckedClick"); + doc.clickFuncs = Docs.Create.TreeDocument([onClick, onChildClick, onDoubleClick, onCheckedClick], { title: "onClick funcs", system: true }); + } + PromiseValue(Cast(doc.clickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast)); + + return doc.clickFuncs as Doc; + } + + static async updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { + if (!doc.globalGroupDatabase) doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument(); + const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data); + reaction(() => DateCast((doc.globalGroupDatabase as Doc)["data-lastModified"]), + async () => { + const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data); + const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || []; + SnappingManager.SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]); + }, { fireImmediately: true }); + // Document properties on load + doc.system = true; + doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode; + doc.title = Doc.CurrentUserEmail; + doc._raiseWhenDragged = true; + doc._showLabel = false; + doc._showMenuLabel = true; + doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)"); + doc.activeInkWidth = StrCast(doc.activeInkWidth, "1"); + doc.activeInkBezier = StrCast(doc.activeInkBezier, "0"); + doc.activeFillColor = StrCast(doc.activeFillColor, ""); + doc.activeArrowStart = StrCast(doc.activeArrowStart, ""); + doc.activeArrowEnd = StrCast(doc.activeArrowEnd, ""); + doc.activeDash = StrCast(doc.activeDash, "0"); + doc.fontSize = StrCast(doc.fontSize, "12px"); + doc.fontFamily = StrCast(doc.fontFamily, "Arial"); + doc.fontColor = StrCast(doc.fontColor, "black"); + doc.fontHighlight = StrCast(doc.fontHighlight, ""); + doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, true); + doc.activeCollectionBackground = StrCast(doc.activeCollectionBackground, "white"); + doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null); + doc.noviceMode = BoolCast(doc.noviceMode, true); + doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); // + doc["constants-dragThreshold"] = NumCast(doc["constants-dragThreshold"], 4); // + Utils.DRAG_THRESHOLD = NumCast(doc["constants-dragThreshold"]); + doc.savedFilters = new List(); + doc.filterDocCount = 0; + this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon + this.setupDocTemplates(doc); // sets up the template menu of templates + this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile + this.setupSearchPanel(doc); + this.setupOverlays(doc); // documents in overlay layer + this.setupDockedButtons(doc); // the bottom bar of font icons + this.setupContextMenuButtons(doc); //buttons for context menu + await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels + await this.setupMenuPanel(doc, sharingDocumentId, linkDatabaseId); + if (!doc.globalScriptDatabase) doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument(); + + setTimeout(() => this.setupDefaultPresentation(doc), 0); // presentation that's initially triggered + + // setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet + // doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); + // doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); + + // uncomment this to setup a default note style that uses the custom header layout + // PromiseValue(doc.emptyHeader).then(factory => { + // if (Cast(doc.defaultTextLayout, Doc, null)?.version !== headerViewVersion) { + // const deleg = Doc.delegateDragFactory(factory as Doc); + // deleg.title = "header"; + // doc.defaultTextLayout = new PrefetchProxy(deleg); + // Doc.AddDocToList(Cast(doc["template-notes"], Doc, null), "data", deleg); + // } + // }); + setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); + doc.fieldInfos = await Docs.setupFieldInfos(); + return doc; + } + + public static async loadCurrentUser() { + return rp.get(Utils.prepend("/getCurrentUser")).then(async response => { + if (response) { + const result: { id: string, email: string, cacheDocumentIds: string } = JSON.parse(response); + Doc.CurrentUserEmail = result.email; + resolvedPorts = JSON.parse(await Networking.FetchFromServer("/resolvedPorts")); + DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, result.email); + result.cacheDocumentIds && (await DocServer.GetRefFields(result.cacheDocumentIds.split(";"))); + return result; + } else { + throw new Error("There should be a user! Why does Dash think there isn't one?"); + } + }); + } + + public static async loadUserDocument(id: string) { + this.curr_id = id; + await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => { + const { userDocumentId, sharingDocumentId, linkDatabaseId } = JSON.parse(ids); + if (userDocumentId !== "guest") { + return DocServer.GetRefField(userDocumentId).then(async field => { + Docs.newAccount = !(field instanceof Doc); + await Docs.Prototypes.initialize(); + const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc; + const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId); + (await DocListCastAsync(Cast(Doc.UserDoc().myLinkDatabase, Doc, null)?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager + const a1 = await Cast(link?.anchor1, Doc, null); + const a2 = await Cast(link?.anchor2, Doc, null); + }); + return updated; + }); + } else { + throw new Error("There should be a user id! Why does Dash think there isn't one?"); + } + }); + } + + public static _urlState: HistoryUtil.DocUrl; + + public static openDashboard = (userDoc: Doc, doc: Doc, fromHistory = false) => { + CurrentUserUtils.MainDocId = doc[Id]; + + if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest dashboard + !("presentationView" in doc) && (doc.presentationView = new List([Docs.Create.TreeDocument([], { title: "Presentation" })])); + userDoc ? (userDoc.activeDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc); + } + const state = CurrentUserUtils._urlState; + if (state.sharing === true && !userDoc) { + DocServer.Control.makeReadOnly(); + } else { + fromHistory || HistoryUtil.pushState({ + type: "doc", + docId: doc[Id], + readonly: state.readonly, + nro: state.nro, + sharing: false, + }); + if (state.readonly === true || state.readonly === null) { + DocServer.Control.makeReadOnly(); + } else if (state.safe) { + if (!state.nro) { + DocServer.Control.makeReadOnly(); + } + CollectionView.SetSafeMode(true); + } else if (state.nro || state.nro === null || state.readonly === false) { + } else if (doc.readOnly) { + DocServer.Control.makeReadOnly(); + } else { + DocServer.Control.makeEditable(); + } + } + + return true; + } + + public static importDocument = () => { + const input = document.createElement("input"); + input.type = "file"; + input.multiple = true; + input.accept = ".zip, application/pdf, video/*, image/*, audio/*"; + input.onchange = async _e => { + const upload = Utils.prepend("/uploadDoc"); + const formData = new FormData(); + const file = input.files && input.files[0]; + if (file && file.type === 'application/zip') { + formData.append('file', file); + formData.append('remap', "true"); + const response = await fetch(upload, { method: "POST", body: formData }); + const json = await response.json(); + if (json !== "error") { + const doc = Docs.newAccount ? undefined : await DocServer.GetRefField(json); + if (doc instanceof Doc) { + setTimeout(() => SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => + docs.docs.forEach(d => LinkManager.Instance.addLink(d))), 2000); // need to give solr some time to update so that this query will find any link docs we've added. + } + } + } else if (input.files && input.files.length !== 0) { + const importDocs = Cast(Doc.UserDoc().myImportDocs, Doc, null); + const disposer = OverlayView.ShowSpinner(); + DocListCastAsync(importDocs.data).then(async list => { + const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {}); + if (results.length !== input.files?.length) { + alert("Error uploading files - possibly due to unsupported file types"); + } + list?.splice(0, 0, ...results); + disposer(); + }); + } else { + console.log("No file selected"); + } + }; + input.click(); + } + + public static async snapshotDashboard(userDoc: Doc) { + const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); + Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy); + CurrentUserUtils.openDashboard(userDoc, copy); + } + + public static createNewDashboard = async (userDoc: Doc, id?: string) => { + const myPresentations = await userDoc.myPresentations as Doc; + const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true); + const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc; + const dashboardCount = DocListCast(dashboards.data).length + 1; + const emptyPane = Cast(userDoc.emptyPane, Doc, null); + emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; + const freeformOptions: DocumentOptions = { + x: 0, + y: 400, + _width: 1500, + _height: 1000, + _fitWidth: true, + title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`, + }; + const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); + const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row"); + Doc.AddDocToList(myPresentations, "data", presentation); + userDoc.activePresentation = presentation; + const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`); + const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); + const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`); + const createDashboard = ScriptField.MakeScript(`createNewDashboard()`); + dashboardDoc.contextMenuScripts = new List([toggleTheme!, toggleComic!, snapshotDashboard!, createDashboard!]); + dashboardDoc.contextMenuLabels = new List(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard"]); + + Doc.AddDocToList(dashboards, "data", dashboardDoc); + CurrentUserUtils.openDashboard(userDoc, dashboardDoc); + } + + public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number) { + const tbox = Docs.Create.TextDocument("", { + _xMargin: noMargins ? 0 : undefined, _yMargin: noMargins ? 0 : undefined, annotationOn, docMaxAutoHeight: maxHeight, + _width: width || 200, _height: height || 100, x: x, y: y, _fitWidth: true, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize), + _fontFamily: StrCast(Doc.UserDoc().fontFamily), title + }); + const template = Doc.UserDoc().defaultTextLayout; + if (template instanceof Doc) { + tbox._width = NumCast(template._width); + tbox.layoutKey = "layout_" + StrCast(template.title); + Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template; + } + return tbox; + } + + public static get MySearchPanelDoc() { return Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null); } + public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); } + public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); } + public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); } + public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); } + public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); } + public static get OverlayDocs() { return DocListCast((Doc.UserDoc().myOverlayDocs as Doc)?.data); } + public static set SelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; } + @computed public static get SelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; } +} + +Scripting.addGlobal(function openDragFactory(dragFactory: Doc) { + const copy = Doc.copyDragFactory(dragFactory); + if (copy) { + CollectionDockingView.AddSplit(copy, "right"); + const view = DocumentManager.Instance.getFirstDocumentView(copy); + view && SelectionManager.SelectView(view, false); + } +}); +Scripting.addGlobal(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, + "is Dash in novice mode"); +Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, + "creates a snapshot copy of a dashboard"); +Scripting.addGlobal(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, + "creates a new dashboard when called"); +Scripting.addGlobal(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, + "creates a new presentation when called"); +Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, + "returns all the links to the document or its annotations", "(doc: any)"); +Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); }, + "imports files from device directly into the import sidebar"); \ No newline at end of file diff --git a/src/client/views/collections/CollectionLinearView.scss b/src/client/views/collections/CollectionLinearView.scss deleted file mode 100644 index 46e40489b..000000000 --- a/src/client/views/collections/CollectionLinearView.scss +++ /dev/null @@ -1,139 +0,0 @@ -@import "../global/globalCssVariables"; -@import "../_nodeModuleOverrides"; - -.collectionLinearView-outer { - overflow: visible; - height: 100%; - pointer-events: none; - - .collectionLinearView { - display: flex; - height: 100%; - align-items: center; - - >span { - background: $dark-gray; - color: $white; - border-radius: 18px; - margin-right: 6px; - cursor: pointer; - } - - .bottomPopup-background { - background: $medium-blue; - display: flex; - border-radius: 10px; - height: 35; - transform: translate3d(6px, 0px, 0px); - align-content: center; - justify-content: center; - align-items: center; - } - - .bottomPopup-text { - color: $white; - display: inline; - white-space: nowrap; - padding-left: 8px; - padding-right: 20px; - vertical-align: middle; - font-size: 12.5px; - } - - .bottomPopup-descriptions { - cursor:pointer; - display: inline; - white-space: nowrap; - padding-left: 8px; - padding-right: 8px; - vertical-align: middle; - background-color: $light-gray; - border-radius: 3px; - color: black; - margin-right: 5px; - } - - .bottomPopup-exit { - cursor:pointer; - display: inline; - white-space: nowrap; - margin-right: 10px; - padding-left: 8px; - padding-right: 8px; - vertical-align: middle; - background-color: $close-red; - border-radius: 3px; - color: black; - } - - >label { - margin-top: "auto"; - margin-bottom: "auto"; - background: $dark-gray; - color: $white; - display: inline-block; - border-radius: 18px; - font-size: 12.5px; - width: 18px; - height: 18px; - margin-top: auto; - margin-bottom: auto; - margin-right: 3px; - cursor: pointer; - transition: transform 0.2s; - } - - label p { - padding-left: 5px; - } - - label:hover { - background: $medium-gray; - transform: scale(1.15); - } - - >input { - display: none; - } - - >input:not(:checked)~.collectionLinearView-content { - display: none; - } - - >input:checked~label { - transform: rotate(45deg); - transition: transform 0.5s; - cursor: pointer; - } - - .collectionLinearView-content { - display: flex; - opacity: 1; - position: relative; - margin-top: auto; - - .collectionLinearView-docBtn, - .collectionLinearView-docBtn-scalable { - position: relative; - margin: auto; - margin-left: 3px; - transform-origin: center 80%; - } - - .collectionLinearView-docBtn-scalable:hover { - transform: scale(1.15); - } - - .collectionLinearView-round-button { - width: 18px; - height: 18px; - border-radius: 18px; - font-size: 15px; - } - - .collectionLinearView-round-button:hover { - transform: scale(1.15); - } - } - } -} \ No newline at end of file diff --git a/src/client/views/collections/CollectionLinearView.tsx b/src/client/views/collections/CollectionLinearView.tsx deleted file mode 100644 index 52c836556..000000000 --- a/src/client/views/collections/CollectionLinearView.tsx +++ /dev/null @@ -1,193 +0,0 @@ -import { Tooltip } from '@material-ui/core'; -import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { Doc, HeightSym, WidthSym } from '../../../fields/Doc'; -import { documentSchema } from '../../../fields/documentSchemas'; -import { Id } from '../../../fields/FieldSymbols'; -import { makeInterface } from '../../../fields/Schema'; -import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; -import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, Utils } from '../../../Utils'; -import { DragManager } from '../../util/DragManager'; -import { Transform } from '../../util/Transform'; -import { DocumentLinksButton } from '../nodes/DocumentLinksButton'; -import { DocumentView } from '../nodes/DocumentView'; -import { LinkDescriptionPopup } from '../nodes/LinkDescriptionPopup'; -import { StyleProp } from '../StyleProvider'; -import "./CollectionLinearView.scss"; -import { CollectionSubView } from './CollectionSubView'; -import { CollectionViewType } from './CollectionView'; - - -type LinearDocument = makeInterface<[typeof documentSchema,]>; -const LinearDocument = makeInterface(documentSchema); - -@observer -export class CollectionLinearView extends CollectionSubView(LinearDocument) { - @observable public addMenuToggle = React.createRef(); - @observable private _selectedIndex = -1; - private _dropDisposer?: DragManager.DragDropDisposer; - private _widthDisposer?: IReactionDisposer; - private _selectedDisposer?: IReactionDisposer; - - componentWillUnmount() { - this._dropDisposer?.(); - this._widthDisposer?.(); - this._selectedDisposer?.(); - this.childLayoutPairs.map((pair, ind) => ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log)); - } - - componentDidMount() { - this._widthDisposer = reaction(() => 5 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * (this.rootDoc[HeightSym]()) : 10), - width => this.childDocs.length && (this.layoutDoc._width = width), - { fireImmediately: true } - ); - - this._selectedDisposer = reaction( - () => NumCast(this.layoutDoc.selectedIndex), - (i) => runInAction(() => { - this._selectedIndex = i; - let selected: any = undefined; - this.childLayoutPairs.map(async (pair, ind) => { - const isSelected = this._selectedIndex === ind; - if (isSelected) { - selected = pair; - } - else { - ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log); - } - }); - if (selected && selected.layout) { - ScriptCast(selected.layout.proto?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log); - } - }), - { fireImmediately: true } - ); - } - protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view - this._dropDisposer && this._dropDisposer(); - if (ele) { - this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); - } - } - - dimension = () => NumCast(this.rootDoc._height); // 2 * the padding - getTransform = (ele: React.RefObject) => () => { - if (!ele.current) return Transform.Identity(); - const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current); - return new Transform(-translateX, -translateY, 1); - } - - @action - exitLongLinks = () => { - if (DocumentLinksButton.StartLink) { - if (DocumentLinksButton.StartLink.Document) { - action((e: React.PointerEvent) => { - Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc); - }); - } - } - DocumentLinksButton.StartLink = undefined; - DocumentLinksButton.StartLinkView = undefined; - } - - @action - changeDescriptionSetting = () => { - if (LinkDescriptionPopup.showDescriptions) { - if (LinkDescriptionPopup.showDescriptions === "ON") { - LinkDescriptionPopup.showDescriptions = "OFF"; - LinkDescriptionPopup.descriptionPopup = false; - } else { - LinkDescriptionPopup.showDescriptions = "ON"; - } - } else { - LinkDescriptionPopup.showDescriptions = "OFF"; - LinkDescriptionPopup.descriptionPopup = false; - } - } - - render() { - const guid = Utils.GenerateGuid(); - const flexDir: any = StrCast(this.Document.flexDirection); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - - const menuOpener = ; - - return
-
-
{BoolCast(this.layoutDoc.linearViewIsExpanded) ? "Close menu" : "Open menu"}
} placement="top"> - {menuOpener} -
- this.layoutDoc.linearViewIsExpanded = this.addMenuToggle.current!.checked)} /> - -
- {this.childLayoutPairs.map((pair, ind) => { - const nested = pair.layout._viewType === CollectionViewType.Linear; - const dref = React.createRef(); - const scalable = pair.layout.onClick || pair.layout.onDragStart; - return
- -
; - })} -
- {DocumentLinksButton.StartLink ? e.stopPropagation()} > - - Creating link from: {DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'} - - -
{"Toggle description pop-up"}
} placement="top"> - - Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"} - -
- -
Exit linking mode
} placement="top"> - - Stop - -
- -
: null} -
-
; - } -} \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/index.ts b/src/client/views/collections/collectionFreeForm/index.ts new file mode 100644 index 000000000..702dc8d42 --- /dev/null +++ b/src/client/views/collections/collectionFreeForm/index.ts @@ -0,0 +1,7 @@ +export * from "./CollectionFreeFormLayoutEngines"; +export * from "./CollectionFreeFormLinkView"; +export * from "./CollectionFreeFormLinksView"; +export * from "./CollectionFreeFormRemoteCursors"; +export * from "./CollectionFreeFormView"; +export * from "./MarqueeOptionsMenu"; +export * from "./MarqueeView"; \ No newline at end of file diff --git a/src/client/views/collections/collectionGrid/index.ts b/src/client/views/collections/collectionGrid/index.ts new file mode 100644 index 000000000..be5d5667a --- /dev/null +++ b/src/client/views/collections/collectionGrid/index.ts @@ -0,0 +1,2 @@ +export * from "./Grid"; +export * from "./CollectionGridView"; \ No newline at end of file diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss new file mode 100644 index 000000000..a10d43917 --- /dev/null +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss @@ -0,0 +1,139 @@ +@import "../../global/globalCssVariables"; +@import "../../_nodeModuleOverrides"; + +.collectionLinearView-outer { + overflow: visible; + height: 100%; + pointer-events: none; + + .collectionLinearView { + display: flex; + height: 100%; + align-items: center; + + >span { + background: $dark-gray; + color: $white; + border-radius: 18px; + margin-right: 6px; + cursor: pointer; + } + + .bottomPopup-background { + background: $medium-blue; + display: flex; + border-radius: 10px; + height: 35; + transform: translate3d(6px, 0px, 0px); + align-content: center; + justify-content: center; + align-items: center; + } + + .bottomPopup-text { + color: $white; + display: inline; + white-space: nowrap; + padding-left: 8px; + padding-right: 20px; + vertical-align: middle; + font-size: 12.5px; + } + + .bottomPopup-descriptions { + cursor:pointer; + display: inline; + white-space: nowrap; + padding-left: 8px; + padding-right: 8px; + vertical-align: middle; + background-color: $light-gray; + border-radius: 3px; + color: black; + margin-right: 5px; + } + + .bottomPopup-exit { + cursor:pointer; + display: inline; + white-space: nowrap; + margin-right: 10px; + padding-left: 8px; + padding-right: 8px; + vertical-align: middle; + background-color: $close-red; + border-radius: 3px; + color: black; + } + + >label { + margin-top: "auto"; + margin-bottom: "auto"; + background: $dark-gray; + color: $white; + display: inline-block; + border-radius: 18px; + font-size: 12.5px; + width: 18px; + height: 18px; + margin-top: auto; + margin-bottom: auto; + margin-right: 3px; + cursor: pointer; + transition: transform 0.2s; + } + + label p { + padding-left: 5px; + } + + label:hover { + background: $medium-gray; + transform: scale(1.15); + } + + >input { + display: none; + } + + >input:not(:checked)~.collectionLinearView-content { + display: none; + } + + >input:checked~label { + transform: rotate(45deg); + transition: transform 0.5s; + cursor: pointer; + } + + .collectionLinearView-content { + display: flex; + opacity: 1; + position: relative; + margin-top: auto; + + .collectionLinearView-docBtn, + .collectionLinearView-docBtn-scalable { + position: relative; + margin: auto; + margin-left: 3px; + transform-origin: center 80%; + } + + .collectionLinearView-docBtn-scalable:hover { + transform: scale(1.15); + } + + .collectionLinearView-round-button { + width: 18px; + height: 18px; + border-radius: 18px; + font-size: 15px; + } + + .collectionLinearView-round-button:hover { + transform: scale(1.15); + } + } + } +} \ No newline at end of file diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx new file mode 100644 index 000000000..8e2ba2275 --- /dev/null +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx @@ -0,0 +1,192 @@ +import { Tooltip } from '@material-ui/core'; +import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { makeInterface } from '../../../../fields/Schema'; +import { documentSchema } from '../../../../fields/documentSchemas'; +import { CollectionSubView } from '../CollectionSubView'; +import { DragManager } from '../../../util/DragManager'; +import { ScriptCast, NumCast, StrCast, BoolCast } from '../../../../fields/Types'; +import { HeightSym, Doc, WidthSym } from '../../../../fields/Doc'; +import { Utils, returnFalse, returnTrue, emptyFunction, returnEmptyDoclist } from '../../../../Utils'; +import { DocumentLinksButton } from '../../nodes/DocumentLinksButton'; +import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup'; +import { StyleProp } from '../../StyleProvider'; +import { CollectionViewType } from '../CollectionView'; +import { Id } from '../../../../fields/FieldSymbols'; +import { DocumentView } from '../../nodes/DocumentView'; +import { Transform } from '../../../util/Transform'; + + +type LinearDocument = makeInterface<[typeof documentSchema,]>; +const LinearDocument = makeInterface(documentSchema); + +@observer +export class CollectionLinearView extends CollectionSubView(LinearDocument) { + @observable public addMenuToggle = React.createRef(); + @observable private _selectedIndex = -1; + private _dropDisposer?: DragManager.DragDropDisposer; + private _widthDisposer?: IReactionDisposer; + private _selectedDisposer?: IReactionDisposer; + + componentWillUnmount() { + this._dropDisposer?.(); + this._widthDisposer?.(); + this._selectedDisposer?.(); + this.childLayoutPairs.map((pair, ind) => ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log)); + } + + componentDidMount() { + this._widthDisposer = reaction(() => 5 + (this.layoutDoc.linearViewIsExpanded ? this.childDocs.length * (this.rootDoc[HeightSym]()) : 10), + width => this.childDocs.length && (this.layoutDoc._width = width), + { fireImmediately: true } + ); + + this._selectedDisposer = reaction( + () => NumCast(this.layoutDoc.selectedIndex), + (i) => runInAction(() => { + this._selectedIndex = i; + let selected: any = undefined; + this.childLayoutPairs.map(async (pair, ind) => { + const isSelected = this._selectedIndex === ind; + if (isSelected) { + selected = pair; + } + else { + ScriptCast(pair.layout.proto?.onPointerUp)?.script.run({ this: pair.layout.proto }, console.log); + } + }); + if (selected && selected.layout) { + ScriptCast(selected.layout.proto?.onPointerDown)?.script.run({ this: selected.layout.proto }, console.log); + } + }), + { fireImmediately: true } + ); + } + protected createDashEventsTarget = (ele: HTMLDivElement) => { //used for stacking and masonry view + this._dropDisposer && this._dropDisposer(); + if (ele) { + this._dropDisposer = DragManager.MakeDropTarget(ele, this.onInternalDrop.bind(this), this.layoutDoc); + } + } + + dimension = () => NumCast(this.rootDoc._height); // 2 * the padding + getTransform = (ele: React.RefObject) => () => { + if (!ele.current) return Transform.Identity(); + const { scale, translateX, translateY } = Utils.GetScreenTransform(ele.current); + return new Transform(-translateX, -translateY, 1); + } + + @action + exitLongLinks = () => { + if (DocumentLinksButton.StartLink) { + if (DocumentLinksButton.StartLink.Document) { + action((e: React.PointerEvent) => { + Doc.UnBrushDoc(DocumentLinksButton.StartLink?.Document as Doc); + }); + } + } + DocumentLinksButton.StartLink = undefined; + DocumentLinksButton.StartLinkView = undefined; + } + + @action + changeDescriptionSetting = () => { + if (LinkDescriptionPopup.showDescriptions) { + if (LinkDescriptionPopup.showDescriptions === "ON") { + LinkDescriptionPopup.showDescriptions = "OFF"; + LinkDescriptionPopup.descriptionPopup = false; + } else { + LinkDescriptionPopup.showDescriptions = "ON"; + } + } else { + LinkDescriptionPopup.showDescriptions = "OFF"; + LinkDescriptionPopup.descriptionPopup = false; + } + } + + render() { + const guid = Utils.GenerateGuid(); + const flexDir: any = StrCast(this.Document.flexDirection); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + + const menuOpener = ; + + return
+
+
{BoolCast(this.layoutDoc.linearViewIsExpanded) ? "Close menu" : "Open menu"}
} placement="top"> + {menuOpener} +
+ this.layoutDoc.linearViewIsExpanded = this.addMenuToggle.current!.checked)} /> + +
+ {this.childLayoutPairs.map((pair, ind) => { + const nested = pair.layout._viewType === CollectionViewType.Linear; + const dref = React.createRef(); + const scalable = pair.layout.onClick || pair.layout.onDragStart; + return
+ +
; + })} +
+ {DocumentLinksButton.StartLink ? e.stopPropagation()} > + + Creating link from: {DocumentLinksButton.AnnotationId ? "Annotation in " : " "} {StrCast(DocumentLinksButton.StartLink.title).length < 51 ? DocumentLinksButton.StartLink.title : StrCast(DocumentLinksButton.StartLink.title).slice(0, 50) + '...'} + + +
{"Toggle description pop-up"}
} placement="top"> + + Labels: {LinkDescriptionPopup.showDescriptions ? LinkDescriptionPopup.showDescriptions : "ON"} + +
+ +
Exit linking mode
} placement="top"> + + Stop + +
+ +
: null} +
+
; + } +} \ No newline at end of file diff --git a/src/client/views/collections/collectionLinearView/index.ts b/src/client/views/collections/collectionLinearView/index.ts new file mode 100644 index 000000000..ff73e14ae --- /dev/null +++ b/src/client/views/collections/collectionLinearView/index.ts @@ -0,0 +1 @@ +export * from "./CollectionLinearView"; \ No newline at end of file diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 3d2cdf5a4..544125ede 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -23,7 +23,7 @@ import "./DocumentView.scss"; import { EquationBox } from "./EquationBox"; import { FieldView, FieldViewProps } from "./FieldView"; import { FilterBox } from "./FilterBox"; -import { FontIconBox } from "./FontIconBox"; +import { FontIconBox } from "./button/FontIconBox"; import { FormattedTextBox, FormattedTextBoxProps } from "./formattedText/FormattedTextBox"; import { FunctionPlotBox } from "./FunctionPlotBox"; import { ImageBox } from "./ImageBox"; diff --git a/src/client/views/nodes/FontIconBox.scss b/src/client/views/nodes/FontIconBox.scss deleted file mode 100644 index 718af2c16..000000000 --- a/src/client/views/nodes/FontIconBox.scss +++ /dev/null @@ -1,103 +0,0 @@ -@import "../global/globalCssVariables"; - -.fontIconBox-label { - color: $white; - margin-right: 4px; - margin-top: 1px; - position: relative; - text-align: center; - font-size: 7px; - letter-spacing: normal; - background-color: inherit; - border-radius: 8px; - margin-top: -8px; - padding: 0; - width: 100%; -} - -.fontIconBadge-container { - position:absolute; - z-index: 1000; - top: 12px; - - .fontIconBadge { - position: absolute; - top: -10px; - right: -10px; - color: $white; - background: $pink; - font-weight: 300; - border-radius: 100%; - width: 25px; - height: 25px; - text-align: center; - padding-top: 4px; - font-size: 12px; - } -} - -.menuButton-circle, -.menuButton-round { - border-radius: 100%; - background-color: $dark-gray; - padding: 0; - - .fontIconBox-label { - //margin-left: -10px; // button padding is 10px; - bottom: 0; - position: absolute; - } - - &:hover { - background-color: $light-gray; - } -} - -.menuButton-square { - padding-top: 3px; - padding-bottom: 3px; - background-color: $dark-gray; - - .fontIconBox-label { - border-radius: 0px; - margin-top: 0px; - border-radius: "inherit"; - } -} - -.menuButton, -.menuButton-circle, -.menuButton-round, -.menuButton-square { - margin-left: -5%; - width: 110%; - height: 100%; - pointer-events: all; - touch-action: none; - - .menuButton-wrap { - touch-action: none; - border-radius: 8px; - width: 100%; - } - - .menuButton-icon-square { - width: auto; - height: 29px; - padding: 4px; - } - - svg { - width: 95% !important; - height: 95%; - } -} -.menuButton-round { - width: 100%; - svg { - width: 50% !important; - height: 50%; - position: relative; - bottom: 2px; - } -} \ No newline at end of file diff --git a/src/client/views/nodes/FontIconBox.tsx b/src/client/views/nodes/FontIconBox.tsx deleted file mode 100644 index 0d415e238..000000000 --- a/src/client/views/nodes/FontIconBox.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { AclPrivate, Doc, DocListCast } from '../../../fields/Doc'; -import { createSchema, makeInterface } from '../../../fields/Schema'; -import { ScriptField } from '../../../fields/ScriptField'; -import { Cast, StrCast } from '../../../fields/Types'; -import { GetEffectiveAcl } from '../../../fields/util'; -import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../Utils"; -import { DragManager } from '../../util/DragManager'; -import { ContextMenu } from '../ContextMenu'; -import { DocComponent } from '../DocComponent'; -import { StyleProp } from '../StyleProvider'; -import { FieldView, FieldViewProps } from './FieldView'; -import './FontIconBox.scss'; -import { Colors } from '../global/globalEnums'; -const FontIconSchema = createSchema({ - icon: "string", -}); - -type FontIconDocument = makeInterface<[typeof FontIconSchema]>; -const FontIconDocument = makeInterface(FontIconSchema); -@observer -export class FontIconBox extends DocComponent(FontIconDocument) { - 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" }); - } - } - - render() { - const label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title)); - const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const shape = StrCast(this.layoutDoc.iconShape, label ? "round" : "circle"); - const icon = StrCast(this.dataDoc.icon, "user") as any; - const presSize = shape === 'round' ? 25 : 30; - const presTrailsIcon = ; - const button = ; - return !this.layoutDoc.toolTip ? button : - {StrCast(this.layoutDoc.toolTip)}
}> - {button} - ; - } -} - -interface FontIconBadgeProps { - collection: Doc | undefined; -} - -@observer -export class FontIconBadge extends React.Component { - _notifsRef = React.createRef(); - - onPointerDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, - (e: PointerEvent) => { - const dragData = new DragManager.DocumentDragData([this.props.collection!]); - DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y); - return true; - }, - returnFalse, emptyFunction, false); - } - - render() { - if (!(this.props.collection instanceof Doc)) return (null); - const length = DocListCast(this.props.collection.data).filter(d => GetEffectiveAcl(d) !== AclPrivate).length; // Object.keys(d).length).length; // filter out any documents that we can't read - return
-
0 ? { "display": "initial" } : { "display": "none" }} - onPointerDown={this.onPointerDown} > - {length} -
-
; - } -} \ No newline at end of file diff --git a/src/client/views/nodes/button/FontIconBadge.tsx b/src/client/views/nodes/button/FontIconBadge.tsx new file mode 100644 index 000000000..3be723052 --- /dev/null +++ b/src/client/views/nodes/button/FontIconBadge.tsx @@ -0,0 +1,37 @@ + +import { AclPrivate, Doc, DocListCast } from '../../../../fields/Doc'; +import { GetEffectiveAcl } from '../../../../fields/util'; +import { emptyFunction, returnFalse, setupMoveUpEvents } from "../../../../Utils"; +import { DragManager } from '../../../util/DragManager'; +import './FontIconBox.scss'; +import { observer } from 'mobx-react'; +import React from 'react'; +interface FontIconBadgeProps { + collection: Doc | undefined; +} + +@observer +export class FontIconBadge extends React.Component { + _notifsRef = React.createRef(); + + onPointerDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, + (e: PointerEvent) => { + const dragData = new DragManager.DocumentDragData([this.props.collection!]); + DragManager.StartDocumentDrag([this._notifsRef.current!], dragData, e.x, e.y); + return true; + }, + returnFalse, emptyFunction, false); + } + + render() { + if (!(this.props.collection instanceof Doc)) return (null); + const length = DocListCast(this.props.collection.data).filter(d => GetEffectiveAcl(d) !== AclPrivate).length; // Object.keys(d).length).length; // filter out any documents that we can't read + return
+
0 ? { "display": "initial" } : { "display": "none" }} + onPointerDown={this.onPointerDown} > + {length} +
+
; + } +} \ No newline at end of file diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss new file mode 100644 index 000000000..d4f1b9379 --- /dev/null +++ b/src/client/views/nodes/button/FontIconBox.scss @@ -0,0 +1,103 @@ +@import "../../global/globalCssVariables"; + +.fontIconBox-label { + color: $white; + margin-right: 4px; + margin-top: 1px; + position: relative; + text-align: center; + font-size: 7px; + letter-spacing: normal; + background-color: inherit; + border-radius: 8px; + margin-top: -8px; + padding: 0; + width: 100%; +} + +.fontIconBadge-container { + position:absolute; + z-index: 1000; + top: 12px; + + .fontIconBadge { + position: absolute; + top: -10px; + right: -10px; + color: $white; + background: $pink; + font-weight: 300; + border-radius: 100%; + width: 25px; + height: 25px; + text-align: center; + padding-top: 4px; + font-size: 12px; + } +} + +.menuButton-circle, +.menuButton-round { + border-radius: 100%; + background-color: $dark-gray; + padding: 0; + + .fontIconBox-label { + //margin-left: -10px; // button padding is 10px; + bottom: 0; + position: absolute; + } + + &:hover { + background-color: $light-gray; + } +} + +.menuButton-square { + padding-top: 3px; + padding-bottom: 3px; + background-color: $dark-gray; + + .fontIconBox-label { + border-radius: 0px; + margin-top: 0px; + border-radius: "inherit"; + } +} + +.menuButton, +.menuButton-circle, +.menuButton-round, +.menuButton-square { + margin-left: -5%; + width: 110%; + height: 100%; + pointer-events: all; + touch-action: none; + + .menuButton-wrap { + touch-action: none; + border-radius: 8px; + width: 100%; + } + + .menuButton-icon-square { + width: auto; + height: 29px; + padding: 4px; + } + + svg { + width: 95% !important; + height: 95%; + } +} +.menuButton-round { + width: 100%; + svg { + width: 50% !important; + height: 50%; + position: relative; + bottom: 2px; + } +} \ No newline at end of file diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx new file mode 100644 index 000000000..f98d01dfc --- /dev/null +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -0,0 +1,295 @@ +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Tooltip } from '@material-ui/core'; +import { action, computed, observable } from 'mobx'; +import { observer } from 'mobx-react'; +import React from 'react'; +import { ColorState, SketchPicker } from 'react-color'; +import { Doc, StrListCast } from '../../../../fields/Doc'; +import { createSchema, makeInterface } from '../../../../fields/Schema'; +import { ScriptField } from '../../../../fields/ScriptField'; +import { BoolCast, Cast, StrCast } from '../../../../fields/Types'; +import { SelectionManager } from '../../../util/SelectionManager'; +import { ColorScheme } from '../../../util/SettingsManager'; +import { undoBatch, UndoManager } from '../../../util/UndoManager'; +import { ContextMenu } from '../../ContextMenu'; +import { DocComponent } from '../../DocComponent'; +import { StyleProp } from '../../StyleProvider'; +import { FieldView, FieldViewProps } from '../FieldView'; +import { FontIconBadge } from './FontIconBadge'; +import './FontIconBox.scss'; +const FontIconSchema = createSchema({ + icon: "string", +}); + +export enum ButtonType { + MenuButton = "menuBtn", + DropdownList = "drpdownList", + DropdownButton = "drpdownBtn", + ClickButton = "clickBtn", + DoubleButton = "dblBtn", + ToggleButton = "tglBtn", + ColorButton = "colorBtn" +} + +type FontIconDocument = makeInterface<[typeof FontIconSchema]>; +const FontIconDocument = makeInterface(FontIconSchema); +@observer +export class FontIconBox extends DocComponent(FontIconDocument) { + 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 color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + @observable private backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + @observable private icon = StrCast(this.dataDoc.icon, "user") as any; + @observable private dropdown: boolean = BoolCast(this.rootDoc.dropDownOpen); + @observable private dropdownDirection: string = StrCast(this.rootDoc.dropDownDirection); + @observable private buttonList: string[] = StrListCast(this.rootDoc.btnList); + @observable private activeFont: string = StrCast(Doc.UserDoc()._fontFamily); + @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 + **/ + + /** + * Dropdown button + */ + @computed get dropdownButton() { + const active: string = StrCast(this.rootDoc.dropDownOpen); + return ( +
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}> + + {!this.label || !Doc.UserDoc()._showLabel ? (null) :
{this.label}
} +
+ +
+ {this.rootDoc.dropDownOpen ? +
+ {/* DROPDOWN BOX CONTENTS */} +
: null} +
+ ); + } + + @undoBatch setColor = action((color: ColorState, docColor?: string, userColor?: string) => { + console.log(docColor, userColor); + if (docColor) { + const numSelected = SelectionManager.Views().length; + const selectedDoc = numSelected > 0 ? SelectionManager.Views()[0].Document : undefined; + eval(docColor); + } + else if (userColor) { + const userDoc = Doc.UserDoc(); + eval(userColor); + } + }); + + + + /** + * Default + */ + @computed get defaultButton() { + const active: string = StrCast(this.rootDoc.dropDownOpen); + return ( +
+
+ + {!this.label || !Doc.UserDoc()._showLabel ? (null) :
{this.label}
} + +
+
+ ) + } + + + render() { + // Variables called through eval (from button) + const canUndo: boolean = UndoManager.CanUndo(); + const canRedo: boolean = UndoManager.CanRedo(); + const numSelected = SelectionManager.Views().length; + const selectedDoc = numSelected > 0 ? SelectionManager.Views()[0].Document : undefined; + const userDoc = Doc.UserDoc(); + + // Toggle and canClick properties as determined from the variable passed into the button doc + // const toggle = this.rootDoc.toggle ? ScriptCast(this.rootDoc.toggle) : undefined; + const canClick: boolean = this.rootDoc.canClick ? eval(StrCast(this.rootDoc.canClick)) : false; + // if (toggle) { + // console.log(StrCast(this.rootDoc.title), toggle); + // toggle.script.run(); + // } + const dark: boolean = Doc.UserDoc().colorScheme === ColorScheme.Dark; + + const active: string = StrCast(this.rootDoc.dropDownOpen); + const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : +
+ {this.label} +
; + const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) : +
+ {this.label} +
; + const dropdownCaret =
+ +
; + const colorBox = (func: (color: ColorState) => void) => ; + const items = this.buttonList.map((value) => { + return
Doc.UserDoc()._fontFamily = value}> + {value} +
; + }); + + /** + * Menu Panel Button: menuBtn + * Dropdown Button: dropDownBtn + * doubleBtn + **/ + + // CODEDUMP: glr + // const presSize = type === ButtonType.MenuButton ? 30 : 25; + // const presTrailsIcon = ; + + // TODO:glr Add label of button type + let button = ( + <> + {this.defaultButton} + + ); + + switch (this.type) { + case ButtonType.DropdownButton: + button = ( + <> + {this.dropdownButton} + + ); + break; + case ButtonType.DropdownList: + button = ( +
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen}> + {/* {toggle} */} + {label} + {dropdownCaret} + {this.rootDoc.dropDownOpen ? + <> +
+ {items} +
+
{ e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} /> + + : null} +
+ ); + break; + case ButtonType.ColorButton: + button = ( +
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen} + onPointerDown={e => e.stopPropagation()}> + + {label} + {dropdownCaret} + {this.rootDoc.dropDownOpen ? + <> +
e.stopPropagation()} + onClick={e => e.stopPropagation()} + style={{ left: 0 }}> + {colorBox((color) => this.setColor(color, StrCast(this.rootDoc.docColor), StrCast(this.rootDoc.userColor)))} +
+
{ e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} /> + + : null} +
+ ); + break; + // case ButtonType.ToggleButton: + // button = ( + //
+ // + // {label} + //
+ // ); + // break; + case ButtonType.ClickButton: + button = ( +
+ + {label} +
+ ); + break; + case ButtonType.DoubleButton: + button = ( +
+ + {label} +
+ ); + break; + case ButtonType.MenuButton: + button = ( +
+ + {menuLabel} +
+ //
+ // + // {label} + //
+ ); + break; + default: + break; + } + + return !this.layoutDoc.toolTip ? button : + {StrCast(this.layoutDoc.toolTip)}
}> + {button} + ; + } +} -- cgit v1.2.3-70-g09d2 From bd41980b6d8849c5ae875fbe5325f88373b3640b Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 17 Aug 2021 11:58:21 -0400 Subject: fixed canEdit func in prosemirrorExampleTransfer to prevent error when hitting enter at end of uneditable doc. fixed scrollheight setting error in formattedtextbox caused by assigning null --- .../views/nodes/formattedText/FormattedTextBox.tsx | 6 +++--- .../nodes/formattedText/ProsemirrorExampleTransfer.ts | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 1058070f8..b0b8ce75d 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -535,7 +535,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" }); const min = Math.round(Date.now() / 1000 / 60); numberRange(10).map(i => addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-min-" + (min - i), { opacity: ((10 - i - 1) / 10).toString() })); - setTimeout(() => this.updateHighlights()); + setTimeout(this.updateHighlights); } if (FormattedTextBox._highlights.indexOf("By Recent Hour") !== -1) { addStyleSheetRule(FormattedTextBox._userStyleSheet, "UM-" + Doc.CurrentUserEmail.replace(".", "").replace("@", ""), { opacity: "0.1" }); @@ -882,7 +882,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } }, ); - if (this._recording) setTimeout(() => this.recordDictation()); + if (this._recording) setTimeout(this.recordDictation); } var quickScroll: string | undefined = ""; this._disposers.scroll = reaction(() => NumCast(this.layoutDoc._scrollTop), @@ -1453,7 +1453,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp setTimeout(() => { proseHeight = !this.ProseRef ? 0 : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace("px", "")), margins); scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); - setScrollHeight(); + scrollHeight && setScrollHeight(); }, 10); } else setTimeout(setScrollHeight, 10); // if we have a template that hasn't been resolved yet, we can't set the height or we'd be setting it on the unresolved template. So set a timeout and hope its arrived... } diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index 1f78b2204..eff400a98 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -7,7 +7,7 @@ import { splitListItem, wrapInList, } from "prosemirror-schema-list"; import { EditorState, Transaction, TextSelection } from "prosemirror-state"; import { SelectionManager } from "../../../util/SelectionManager"; import { NumCast, BoolCast, Cast, StrCast } from "../../../../fields/Types"; -import { Doc, DataSym, DocListCast, AclAugment } from "../../../../fields/Doc"; +import { Doc, DataSym, DocListCast, AclAugment, AclSelfEdit } from "../../../../fields/Doc"; import { FormattedTextBox } from "./FormattedTextBox"; import { Id } from "../../../../fields/FieldSymbols"; import { Docs } from "../../../documents/Documents"; @@ -72,13 +72,16 @@ export function buildKeymap>(schema: S, props: any, mapKey }; const canEdit = (state: any) => { - for (var i = state.selection.from; i < state.selection.to; i++) { - const node = state.doc.resolve(i); - if (node?.marks?.().some((mark: any) => mark.type === schema.marks.user_mark && - mark.attrs.userid !== Doc.CurrentUserEmail) && - GetEffectiveAcl(props.Document) === AclAugment) { - return false; - } + switch (GetEffectiveAcl(props.Document)) { + case AclAugment: return false; + case AclSelfEdit: + for (var i = state.selection.from; i < state.selection.to; i++) { + const marks = state.doc.resolve(i)?.marks?.(); + if (marks?.some((mark: any) => mark.type === schema.marks.user_mark && mark.attrs.userid !== Doc.CurrentUserEmail)) { + return false; + } + } + break; } return true; } -- cgit v1.2.3-70-g09d2 From 4d21696c1b49c2c383adcd01974c18461123b83c Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 17 Aug 2021 16:55:15 -0400 Subject: made dashboard context menu items context sensitive to whether they're called from MyDashboards, MySharedDocs or elsewhere. Made treeViewOpen a playground field so that users don't interfere with each other. Made Docking views not generate alias of their tabs when shared. --- src/client/util/CurrentUserUtils.ts | 35 +++++++++++----------- src/client/views/MainView.tsx | 2 +- .../views/collections/CollectionDockingView.tsx | 2 +- .../views/collections/CollectionTreeView.tsx | 13 ++++++-- src/client/views/collections/CollectionView.tsx | 24 +++++++++------ src/client/views/collections/TreeView.tsx | 25 +++++++++++----- src/client/views/nodes/DocumentView.tsx | 2 +- 7 files changed, 63 insertions(+), 40 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 34990e121..85762a73f 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -765,8 +765,13 @@ export class CurrentUserUtils { _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: "fileSystem", isFolder: true, system: true })); const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`); - (doc.myDashboards as any as Doc).contextMenuScripts = new List([newDashboard!]); - (doc.myDashboards as any as Doc).contextMenuLabels = new List(["Create New Dashboard"]); + const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`); + const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); + const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`); + const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`); + const removeDashboard = ScriptField.MakeScript('removeDashboard(self)'); + (doc.myDashboards as any as Doc).childContextMenuScripts = new List([newDashboard!, toggleTheme!, toggleComic!, snapshotDashboard!, shareDashboard!, removeDashboard!]); + (doc.myDashboards as any as Doc).childContextMenuLabels = new List(["Create New Dashboard", "Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]); } return doc.myDashboards as any as Doc; } @@ -933,6 +938,11 @@ export class CurrentUserUtils { } if (sharedDocs instanceof Doc) { Doc.GetProto(sharedDocs).userColor = sharedDocs.userColor || "rgb(202, 202, 202)"; + const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`); + const dashboardFilter = ScriptField.MakeFunction(`doc._viewType === '${CollectionViewType.Docking}'`, { doc: Doc.name }); + sharedDocs.childContextMenuFilters = new List([dashboardFilter!,]); + sharedDocs.childContextMenuScripts = new List([addToDashboards!,]); + sharedDocs.childContextMenuLabels = new List(["Add to Dashboards",]); } doc.mySharedDocs = new PrefetchProxy(sharedDocs); } @@ -1228,15 +1238,6 @@ export class CurrentUserUtils { Doc.AddDocToList(myPresentations, "data", presentation); userDoc.activePresentation = presentation; - const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`); - const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); - const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`); - const createDashboard = ScriptField.MakeScript(`createNewDashboard()`); - const shareDashboard = ScriptField.MakeScript(`shareDashboard(self)`); - const addToDashboards = ScriptField.MakeScript(`addToDashboards(self)`); - const removeDashboard = ScriptField.MakeScript('removeDashboard(self)'); - dashboardDoc.contextMenuScripts = new List([toggleTheme!, toggleComic!, snapshotDashboard!, createDashboard!, shareDashboard!, addToDashboards!, removeDashboard!]); - dashboardDoc.contextMenuLabels = new List(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard", "Share Dashboard", "Add to Dashboards", "Remove Dashboard"]); Doc.AddDocToList(dashboards, "data", dashboardDoc); CurrentUserUtils.openDashboard(userDoc, dashboardDoc); @@ -1306,17 +1307,15 @@ Scripting.addGlobal(async function addToDashboards(dashboard: Doc) { const allDocs = await DocListCastAsync(dashboard[DataSym]["data-all"]); // moves the data-all field from the datadoc to the layoutdoc, necessary for off screen docs tab to function properly - dashboard["data-all"] = new List(allDocs); - dashboardAlias["data-all"] = new List((allDocs || []).map(doc => Doc.MakeAlias(doc))); - - const dockingConfig = JSON.parse(StrCast(dashboardAlias.dockingConfig)); - dockingConfig.content = []; - dashboardAlias.dockingConfig = JSON.stringify(dockingConfig); + // dashboard["data-all"] = new List(allDocs); + // dashboardAlias["data-all"] = new List((allDocs || []).map(doc => Doc.MakeAlias(doc))); + // const dockingConfig = JSON.parse(StrCast(dashboardAlias.dockingConfig)); + // dashboardAlias.dockingConfig = JSON.stringify(dockingConfig); dashboardAlias.data = new List(DocListCast(dashboard.data).map(tabFolder => Doc.MakeAlias(tabFolder))); DocListCast(dashboardAlias.data).forEach(doc => doc.dashboard = dashboardAlias); - DocListCast(dashboardAlias.data)[0].data = new List(); + //new List(); DocListCast(dashboardAlias.data)[1].data = ComputedField.MakeFunction(`dynamicOffScreenDocs(self.dashboard)`) as any; Doc.AddDocToList(CurrentUserUtils.MyDashboards, "data", dashboardAlias); CurrentUserUtils.openDashboard(Doc.UserDoc(), dashboardAlias); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index b0b8d7f41..4d2a0a4c4 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -107,7 +107,7 @@ export class MainView extends React.Component { new InkStrokeProperties(); this._sidebarContent.proto = undefined; if (!MainView.Live) { - DocServer.setPlaygroundFields(["dataTransition", "autoHeight", "showSidebar", "sidebarWidthPercent", "viewTransition", + 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 } diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index c0d39b2a2..cae08e1f4 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -170,7 +170,7 @@ export class CollectionDockingView extends CollectionSubView(doc => doc) { const aliasDocList = DocListCast(alias["data-all"]); // if aliasDocList contains the alias, don't do anything // otherwise add the original or an alias depending on whether the doc you're looking at is the current doc or a different alias - !DocListCast(document.aliases).some(a => aliasDocList.includes(a)) && Doc.AddDocToList(alias, "data-all", alias !== instance.props.Document ? Doc.MakeAlias(document) : document); + !DocListCast(document.aliases).some(a => aliasDocList.includes(a)) && Doc.AddDocToList(alias, "data-all", document);//alias !== instance.props.Document ? Doc.MakeAlias(document) : document); }); } const docContentConfig = CollectionDockingView.makeDocumentConfig(document, panelName); diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index 3eece0086..c0553ca60 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -1,10 +1,10 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, reaction, IReactionDisposer, observable } from "mobx"; import { observer } from "mobx-react"; -import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { DataSym, Doc, DocListCast, HeightSym, Opt, WidthSym, StrListCast } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; -import { Document } from '../../../fields/Schema'; +import { Document, listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; @@ -215,6 +215,11 @@ export class CollectionTreeView extends CollectionSubView
; } + childContextMenuItems = () => { + const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []); + const filterScripts = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []); + return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: filterScripts[i], label })); + } @computed get treeViewElements() { TraceMobx(); const dropAction = StrCast(this.doc.childDropAction) as dropActionType; @@ -247,7 +252,9 @@ export class CollectionTreeView extends CollectionSubView boolean;// whether child documents can be dragged if collection can be dragged (eg., in a when a Pile document is in startburst mode) childFitWidth?: () => boolean; childOpacity?: () => number; + childContextMenuItems?: () => { script: ScriptField, label: string }[]; childHideTitle?: () => boolean; // whether to hide the documentdecorations title for children childHideDecorationTitle?: () => boolean; childLayoutTemplate?: () => (Doc | undefined);// specify a layout Doc template to use for children of the collection @@ -186,15 +187,20 @@ export class CollectionView extends ViewBoxAnnotatableComponent this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer, icon: "project-diagram" }); - optionItems.push({ - description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram" - }); - optionItems.push({ - description: "Pull Master", event: () => BranchTask(this.rootDoc, "pull"), icon: "project-diagram" - }); - optionItems.push({ - description: "Merge Branches", event: () => BranchTask(this.rootDoc, "merge"), icon: "project-diagram" - }); + if (!Doc.UserDoc().noviceMode) { + optionItems.push({ + description: "Create Branch", event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), "add:right"), icon: "project-diagram" + }); + optionItems.push({ + description: "Pull Master", event: () => BranchTask(this.rootDoc, "pull"), icon: "project-diagram" + }); + optionItems.push({ + description: "Merge Branches", event: () => BranchTask(this.rootDoc, "merge"), icon: "project-diagram" + }); + } + if (this.Document._viewType === CollectionViewType.Docking) { + optionItems.push({ description: "Create Dashboard", event: () => CurrentUserUtils.createNewDashboard(Doc.UserDoc()), icon: "project-diagram" }); + } !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "hand-point-right" }); diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 206e48aac..0598be49b 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -1,7 +1,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, IReactionDisposer, observable, reaction } from "mobx"; import { observer } from "mobx-react"; -import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; +import { DataSym, Doc, DocListCast, DocListCastOrNull, Field, HeightSym, Opt, WidthSym, StrListCast } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { RichTextField } from '../../../fields/RichTextField'; @@ -54,6 +54,7 @@ export interface TreeViewProps { indentDocument?: (editTitle: boolean) => void; outdentDocument?: (editTitle: boolean) => void; ScreenToLocalTransform: () => Transform; + contextMenuItems: { script: ScriptField, filter: ScriptField, label: string }[]; dontRegisterView?: boolean; styleProvider?: StyleProviderFunc | undefined; treeViewHideHeaderFields: () => boolean; @@ -336,7 +337,7 @@ export class TreeView extends React.Component { this.props.dropAction, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform, this.props.isContentActive, this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields, [...this.props.renderedIds, doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenChildContentsActiveChanged, - this.props.dontRegisterView, emptyFunction, emptyFunction); + this.props.dontRegisterView, emptyFunction, emptyFunction, this.childContextMenuItems()); } else { contentElement = { StrCast(this.doc.childDropAction, this.props.dropAction) as dropActionType, this.props.addDocTab, this.titleStyleProvider, this.props.ScreenToLocalTransform, this.props.isContentActive, this.props.panelWidth, this.props.renderDepth, this.props.treeViewHideHeaderFields, [...this.props.renderedIds, this.doc[Id]], this.props.onCheckedClick, this.props.onChildClick, this.props.skipFields, false, this.props.whenChildContentsActiveChanged, - this.props.dontRegisterView, emptyFunction, emptyFunction)} + this.props.dontRegisterView, emptyFunction, emptyFunction, this.childContextMenuItems())} ; } else if (this.treeViewExpandedView === "fields") { return
    @@ -507,11 +508,19 @@ export class TreeView extends React.Component { } contextMenuItems = () => { const makeFolder = { script: ScriptField.MakeFunction(`scriptContext.makeFolder()`, { scriptContext: "any" })!, label: "New Folder" }; - return this.doc.isFolder ? [makeFolder] : + const openAlias = { script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }; + const focusDoc = { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" }; + return [...this.props.contextMenuItems.filter(mi => !mi.filter ? true : mi.filter.script.run({ doc: this.doc })?.result), ... (this.doc.isFolder ? [makeFolder] : Doc.IsSystem(this.doc) ? [] : this.props.treeView.fileSysMode && this.doc === Doc.GetProto(this.doc) ? - [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }, makeFolder] : - [{ script: ScriptField.MakeFunction(`openOnRight(getAlias(self))`)!, label: "Open Alias" }, { script: ScriptField.MakeFunction(`DocFocusOrOpen(self)`)!, label: "Focus or Open" }]; + [openAlias, makeFolder] : + this.doc.viewType === CollectionViewType.Docking ? [] : + [openAlias, focusDoc])]; + } + childContextMenuItems = () => { + const customScripts = Cast(this.doc.childContextMenuScripts, listSpec(ScriptField), []); + const customFilters = Cast(this.doc.childContextMenuFilters, listSpec(ScriptField), []); + return StrListCast(this.doc.childContextMenuLabels).map((label, i) => ({ script: customScripts[i], filter: customFilters[i], label })); } onChildClick = () => this.props.onChildClick?.() ?? (this._editTitleScript?.() || ScriptCast(this.doc.treeChildClick)); @@ -817,7 +826,8 @@ export class TreeView extends React.Component { whenChildContentsActiveChanged: (isActive: boolean) => void, dontRegisterView: boolean | undefined, observerHeight: (ref: any) => void, - unobserveHeight: (ref: any) => void + unobserveHeight: (ref: any) => void, + contextMenuItems: ({ script: ScriptField, filter: ScriptField, label: string }[]) ) { const viewSpecScript = Cast(conainerCollection.viewSpecScript, ScriptField); if (viewSpecScript) { @@ -882,6 +892,7 @@ export class TreeView extends React.Component { parentTreeView={parentTreeView} observeHeight={observerHeight} unobserveHeight={unobserveHeight} + contextMenuItems={contextMenuItems} />; }); } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 745d58656..a29f2f662 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -664,7 +664,7 @@ export class DocumentViewInternal extends DocComponent + StrListCast(this.Document.contextMenuLabels).forEach((label, i) => cm.addItem({ description: label, event: () => customScripts[i]?.script.run({ documentView: this, this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: "sticky-note" })); this.props.contextMenuItems?.().forEach(item => item.label && cm.addItem({ description: item.label, event: () => item.script.script.run({ this: this.layoutDoc, scriptContext: this.props.scriptContext, self: this.rootDoc }), icon: "sticky-note" })); -- cgit v1.2.3-70-g09d2 From 9aa0e5e812dfbc67b0542c6740d43209313a37d6 Mon Sep 17 00:00:00 2001 From: geireann Date: Tue, 17 Aug 2021 23:50:00 -0400 Subject: more updates --- src/client/documents/Documents.ts | 19 +++++++++---------- src/client/views/linking/LinkPopup.scss | 2 +- src/client/views/linking/LinkPopup.tsx | 6 ++++-- src/client/views/nodes/FieldView.tsx | 5 ++--- src/client/views/pdf/AnchorMenu.tsx | 25 ++++++++++++++++--------- src/client/views/search/SearchBox.tsx | 28 ++++++++++++++++++++-------- 6 files changed, 52 insertions(+), 33 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 48886aa3b..b14a6f800 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,7 +1,7 @@ import { action, runInAction } from "mobx"; -import { basename, extname } from "path"; +import { basename } from "path"; import { DateField } from "../../fields/DateField"; -import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Opt, WidthSym, Initializing, updateCachedAcls } from "../../fields/Doc"; +import { Doc, DocListCast, DocListCastAsync, Field, HeightSym, Initializing, Opt, updateCachedAcls, WidthSym } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; import { HtmlField } from "../../fields/HtmlField"; import { InkField } from "../../fields/InkField"; @@ -13,20 +13,20 @@ import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { Cast, NumCast, StrCast } from "../../fields/Types"; import { AudioField, ImageField, PdfField, VideoField, WebField, YoutubeField } from "../../fields/URLField"; import { SharingPermissions } from "../../fields/util"; -import { MessageStore } from "../../server/Message"; import { Upload } from "../../server/SharedMediaTypes"; import { OmitKeys, Utils } from "../../Utils"; import { YoutubeBox } from "../apis/youtube/YoutubeBox"; import { DocServer } from "../DocServer"; import { Networking } from "../Network"; +import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { DocumentManager } from "../util/DocumentManager"; import { dropActionType } from "../util/DragManager"; import { DirectoryImportBox } from "../util/Import & Export/DirectoryImportBox"; import { LinkManager } from "../util/LinkManager"; import { Scripting } from "../util/Scripting"; import { undoBatch, UndoManager } from "../util/UndoManager"; -import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; import { CollectionDockingView } from "../views/collections/CollectionDockingView"; +import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; import { ContextMenu } from "../views/ContextMenu"; import { ContextMenuProps } from "../views/ContextMenuItem"; @@ -36,30 +36,29 @@ import { AudioBox } from "../views/nodes/AudioBox"; import { ColorBox } from "../views/nodes/ColorBox"; import { ComparisonBox } from "../views/nodes/ComparisonBox"; import { DocFocusOptions } from "../views/nodes/DocumentView"; +import { EquationBox } from "../views/nodes/EquationBox"; +import { FieldViewProps } from "../views/nodes/FieldView"; import { FilterBox } from "../views/nodes/FilterBox"; import { FontIconBox } from "../views/nodes/FontIconBox"; import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; +import { FunctionPlotBox } from "../views/nodes/FunctionPlotBox"; import { ImageBox } from "../views/nodes/ImageBox"; import { KeyValueBox } from "../views/nodes/KeyValueBox"; import { LabelBox } from "../views/nodes/LabelBox"; import { LinkBox } from "../views/nodes/LinkBox"; import { LinkDescriptionPopup } from "../views/nodes/LinkDescriptionPopup"; import { PDFBox } from "../views/nodes/PDFBox"; -import { PresBox } from "../views/nodes/trails/PresBox"; import { ScreenshotBox } from "../views/nodes/ScreenshotBox"; import { ScriptingBox } from "../views/nodes/ScriptingBox"; import { SliderBox } from "../views/nodes/SliderBox"; import { TaskCompletionBox } from "../views/nodes/TaskCompletedBox"; +import { PresBox } from "../views/nodes/trails/PresBox"; +import { PresElementBox } from "../views/nodes/trails/PresElementBox"; import { VideoBox } from "../views/nodes/VideoBox"; import { WebBox } from "../views/nodes/WebBox"; -import { PresElementBox } from "../views/nodes/trails/PresElementBox"; import { SearchBox } from "../views/search/SearchBox"; import { DashWebRTCVideo } from "../views/webcam/DashWebRTCVideo"; import { DocumentType } from "./DocumentTypes"; -import { EquationBox } from "../views/nodes/EquationBox"; -import { FunctionPlotBox } from "../views/nodes/FunctionPlotBox"; -import { CurrentUserUtils } from "../util/CurrentUserUtils"; -import { FieldViewProps } from "../views/nodes/FieldView"; const path = require('path'); const defaultNativeImageDim = Number(DFLT_IMAGE_NATIVE_DIM.replace("px", "")); diff --git a/src/client/views/linking/LinkPopup.scss b/src/client/views/linking/LinkPopup.scss index 8ae65158d..60c9ebfcd 100644 --- a/src/client/views/linking/LinkPopup.scss +++ b/src/client/views/linking/LinkPopup.scss @@ -5,7 +5,7 @@ height: 200px; width: 200px; position: absolute; - padding: 15px; + // padding: 15px; border-radius: 3px; input { diff --git a/src/client/views/linking/LinkPopup.tsx b/src/client/views/linking/LinkPopup.tsx index b5a29415f..85fd91d9f 100644 --- a/src/client/views/linking/LinkPopup.tsx +++ b/src/client/views/linking/LinkPopup.tsx @@ -50,7 +50,7 @@ export class LinkPopup extends React.Component { const popupVisibility = this.props.showPopup ? "block" : "none"; return (
    -
    + {/*
    @@ -58,7 +58,7 @@ export class LinkPopup extends React.Component {

    or

    -
    +
    */}
    {/* { { @observable public Status: "marquee" | "annotation" | "" = ""; public OnClick: (e: PointerEvent) => void = unimplementedFunction; + public OnLink: (e: PointerEvent) => void = unimplementedFunction; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; public Highlight: (color: string, isPushpin: boolean) => Opt = (color: string, isPushpin: boolean) => undefined; public GetAnchor: (savedAnnotations?: ObservableMap) => Opt = () => undefined; @@ -65,7 +66,14 @@ export class AnchorMenu extends AntimodeMenu { componentDidMount() { this._disposer = reaction(() => SelectionManager.Views(), - selected => AnchorMenu.Instance.fadeOut(true)); + selected => { + AnchorMenu.Instance.fadeOut(true) + this._showLinkPopup = false; + }); + } + + componentWillUnmount() { + console.log("unmount"); } pointerDown = (e: React.PointerEvent) => { @@ -145,14 +153,13 @@ export class AnchorMenu extends AntimodeMenu { , - - //NOTE: link popup is currently incomplete - // {"Link selected text to document or URL"}
    }> - // - // , - // + //NOTE: link popup is currently in progress + {"Link selected text to document or URL"}
    }> + + , + ] : [ {"Remove Link Anchor"}
    }> , - + ] : [ {"Remove Link Anchor"}
}>
} key="pin menu" placement="bottom"> - - ; const propIcon = CurrentUserUtils.propertiesWidth > 0 ? "angle-double-right" : "angle-double-left"; const propTitle = CurrentUserUtils.propertiesWidth > 0 ? "Close Properties Panel" : "Open Properties Panel"; const prop = {propTitle}} key="properties" placement="bottom"> - + ; - return this.getElement(!this.SelectedCollection ? [/*button*/] : - [, - prop, - /*button*/]); + // NEW BUTTONS + //dash col linear view buttons + const contMenuButtons = +
+ {this.contMenuButtons} + {prop} +
; + + return contMenuButtons; + + // const button = Pin Menu} key="pin menu" placement="bottom"> + // + // ; + + // OLD BUTTONS + // return this.getElement(!this.SelectedCollection ? [/*button*/] : + // [, + // prop, + // /*button*/]); } } diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss index a10d43917..db39e304b 100644 --- a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss @@ -5,12 +5,22 @@ overflow: visible; height: 100%; pointer-events: none; + // background-color: rgba(0, 0, 0, 0.2); + border-radius: 5px; + padding-left: 5px; + padding-right: 5px; + border-left: $standard-border; + border-right: $standard-border; .collectionLinearView { display: flex; height: 100%; align-items: center; + .collectionView{ + overflow: visible !important; + } + >span { background: $dark-gray; color: $white; @@ -67,29 +77,25 @@ } >label { - margin-top: "auto"; - margin-bottom: "auto"; background: $dark-gray; color: $white; - display: inline-block; + display: flex; border-radius: 18px; font-size: 12.5px; - width: 18px; - height: 18px; + font-weight:100; + width: fit-content; + height: 100%; margin-top: auto; margin-bottom: auto; margin-right: 3px; cursor: pointer; transition: transform 0.2s; - } - - label p { - padding-left: 5px; - } + align-items: center; + justify-content: center; - label:hover { - background: $medium-gray; - transform: scale(1.15); + &:hover { + background: $medium-gray; + } } >input { @@ -110,7 +116,6 @@ display: flex; opacity: 1; position: relative; - margin-top: auto; .collectionLinearView-docBtn, .collectionLinearView-docBtn-scalable { diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx index 8e2ba2275..e7970758a 100644 --- a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx @@ -2,20 +2,21 @@ import { Tooltip } from '@material-ui/core'; import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { makeInterface } from '../../../../fields/Schema'; +import { Doc, HeightSym, WidthSym } from '../../../../fields/Doc'; import { documentSchema } from '../../../../fields/documentSchemas'; -import { CollectionSubView } from '../CollectionSubView'; +import { Id } from '../../../../fields/FieldSymbols'; +import { makeInterface } from '../../../../fields/Schema'; +import { BoolCast, NumCast, ScriptCast, StrCast } from '../../../../fields/Types'; +import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, Utils } from '../../../../Utils'; import { DragManager } from '../../../util/DragManager'; -import { ScriptCast, NumCast, StrCast, BoolCast } from '../../../../fields/Types'; -import { HeightSym, Doc, WidthSym } from '../../../../fields/Doc'; -import { Utils, returnFalse, returnTrue, emptyFunction, returnEmptyDoclist } from '../../../../Utils'; +import { Transform } from '../../../util/Transform'; import { DocumentLinksButton } from '../../nodes/DocumentLinksButton'; +import { DocumentView } from '../../nodes/DocumentView'; import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup'; import { StyleProp } from '../../StyleProvider'; -import { CollectionViewType } from '../CollectionView'; -import { Id } from '../../../../fields/FieldSymbols'; -import { DocumentView } from '../../nodes/DocumentView'; -import { Transform } from '../../../util/Transform'; +import "./CollectionLinearView.scss"; +import { CollectionSubView } from '.././CollectionSubView'; +import { CollectionViewType } from '.././CollectionView'; type LinearDocument = makeInterface<[typeof documentSchema,]>; @@ -108,33 +109,37 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { render() { const guid = Utils.GenerateGuid(); const flexDir: any = StrCast(this.Document.flexDirection); + const expandable: boolean = BoolCast(this.props.Document.linearViewExpandable); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const icon: string = StrCast(this.props.Document.icon); const menuOpener = ; return
-
{BoolCast(this.layoutDoc.linearViewIsExpanded) ? "Close menu" : "Open menu"}
} placement="top"> + {!expandable ? (null) :
{BoolCast(this.props.Document.linearViewIsExpanded) ? "Close menu" : "Open menu"}
} placement="top"> {menuOpener} -
- this.layoutDoc.linearViewIsExpanded = this.addMenuToggle.current!.checked)} /> +
} + this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked)} />
{this.childLayoutPairs.map((pair, ind) => { const nested = pair.layout._viewType === CollectionViewType.Linear; const dref = React.createRef(); - const scalable = pair.layout.onClick || pair.layout.onDragStart; - return
{ LightboxView.SetCookie(StrCast(anchor["cookies-set"])); - // copying over VIEW fields immediately allows the view type to switch to create the right _componentView - Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => { - this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]); - }); + // copying over VIEW fields immediately allows the view type to switch to create the right _componentView + Array.from(Object.keys(Doc.GetProto(anchor))).filter(key => key.startsWith(ViewSpecPrefix)).forEach(spec => { + this.layoutDoc[spec.replace(ViewSpecPrefix, "")] = ((field) => field instanceof ObjectField ? ObjectField.MakeCopy(field) : field)(anchor[spec]); + }); // after a timeout, the right _componentView should have been created, so call it to update its view spec values setTimeout(() => this._componentView?.setViewSpec?.(anchor, LinkDocPreview.LinkInfo ? true : false)); - const focusSpeed = this._componentView?.scrollFocus?.(anchor, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here + const focusSpeed = this._componentView?.scrollFocus?.(anchor, !LinkDocPreview.LinkInfo); // bcz: smooth parameter should really be passed into focus() instead of inferred here const endFocus = focusSpeed === undefined ? options?.afterFocus : async (moved: boolean) => options?.afterFocus ? options?.afterFocus(true) : ViewAdjustment.doNothing; this.props.focus(options?.docTransform ? anchor : this.rootDoc, { ...options, afterFocus: (didFocus: boolean) => @@ -764,7 +765,7 @@ export class DocumentViewInternal extends DocComponent console.log(this.props.Document[DataSym]), icon: "hand-point-right" }); cm.addItem({ description: "Help...", noexpand: true, subitems: helpItems, icon: "question" }); } - + if (!this.topMost) e?.stopPropagation(); // DocumentViews should stop propagation of this event cm.displayMenu((e?.pageX || pageX || 0) - 15, (e?.pageY || pageY || 0) - 15); DocumentViewInternal.SelectAfterContextMenu && !this.props.isSelected(true) && setTimeout(() => SelectionManager.SelectView(this.props.DocumentView(), false), 300); // on a mac, the context menu is triggered on mouse down, but a YouTube video becaomes interactive when selected which means that the context menu won't show up. by delaying the selection until hopefully after the pointer up, the context menu will appear. @@ -949,12 +950,13 @@ export class DocumentViewInternal extends DocComponent { TraceMobx(); const xshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined); const yshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined); + const isButton: boolean = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear; return (
{!this.props.Document || !this.props.PanelWidth() ? (null) : (
; const FontIconDocument = makeInterface(FontIconSchema); @observer -export class FontIconBox extends DocComponent(FontIconDocument) { +export class FontIconBox extends DocComponent(FontIconDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); } showTemplate = (): void => { const dragFactory = Cast(this.layoutDoc.dragFactory, Doc, null); @@ -56,8 +59,6 @@ export class FontIconBox extends DocComponent( // Determining UI Specs @observable private label = StrCast(this.rootDoc.label, StrCast(this.rootDoc.title)); - @observable private color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - @observable private backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); @observable private icon = StrCast(this.dataDoc.icon, "user") as any; @observable private dropdown: boolean = BoolCast(this.rootDoc.dropDownOpen); @observable private dropdownDirection: string = StrCast(this.rootDoc.dropDownDirection); @@ -82,16 +83,18 @@ export class FontIconBox extends DocComponent( */ @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 (
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}> - - {!this.label || !Doc.UserDoc()._showLabel ? (null) :
{this.label}
} + + {!this.label || !Doc.UserDoc()._showLabel ? (null) :
{this.label}
}
- +
{this.rootDoc.dropDownOpen ?
( * 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 (
- {!this.label || !Doc.UserDoc()._showLabel ? (null) :
{this.label}
} - + {!this.label || !Doc.UserDoc()._showLabel ? (null) :
{this.label}
} + {/* */}
) @@ -136,6 +141,8 @@ export class FontIconBox extends DocComponent( render() { + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); // Variables called through eval (from button) const canUndo: boolean = UndoManager.CanUndo(); const canRedo: boolean = UndoManager.CanRedo(); @@ -144,7 +151,7 @@ export class FontIconBox extends DocComponent( const userDoc = Doc.UserDoc(); // Toggle and canClick properties as determined from the variable passed into the button doc - // const toggle = this.rootDoc.toggle ? ScriptCast(this.rootDoc.toggle) : undefined; + const toggle = this.rootDoc.toggle ? ScriptCast(this.rootDoc.toggle) : undefined; const canClick: boolean = this.rootDoc.canClick ? eval(StrCast(this.rootDoc.canClick)) : false; // if (toggle) { // console.log(StrCast(this.rootDoc.title), toggle); @@ -154,11 +161,11 @@ export class FontIconBox extends DocComponent( const active: string = StrCast(this.rootDoc.dropDownOpen); const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : -
+
{this.label}
; const menuLabel = !this.label || !Doc.UserDoc()._showMenuLabel ? (null) : -
+
{this.label}
; const dropdownCaret =
( style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
; - const colorBox = (func: (color: ColorState) => void) => void) => ; @@ -207,9 +214,9 @@ export class FontIconBox extends DocComponent( case ButtonType.DropdownList: button = (
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen}> - {/* {toggle} */} + {toggle} {label} {dropdownCaret} {this.rootDoc.dropDownOpen ? @@ -227,10 +234,10 @@ export class FontIconBox extends DocComponent( case ButtonType.ColorButton: button = (
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen} onPointerDown={e => e.stopPropagation()}> - + {label} {dropdownCaret} {this.rootDoc.dropDownOpen ? @@ -247,40 +254,36 @@ export class FontIconBox extends DocComponent(
); break; - // case ButtonType.ToggleButton: - // button = ( - //
- // - // {label} - //
- // ); - // break; + case ButtonType.ToggleButton: + button = ( +
+ + {label} +
+ ); + break; case ButtonType.ClickButton: button = ( -
- +
+ {label}
); break; case ButtonType.DoubleButton: button = ( -
- +
+ {label}
); break; case ButtonType.MenuButton: button = ( -
- +
+ {menuLabel}
- //
- // - // {label} - //
); break; default: -- cgit v1.2.3-70-g09d2 From 94cfa66db4d667cd0dae9c6ddbe152cbff27819f Mon Sep 17 00:00:00 2001 From: geireann <60007097+geireann@users.noreply.github.com> Date: Thu, 19 Aug 2021 11:24:54 -0400 Subject: updates --- package-lock.json | 5 +++-- src/client/util/CurrentUserUtils.ts | 17 +++++++++-------- src/client/views/nodes/button/FontIconBox.scss | 17 ++++++++++++++--- src/client/views/nodes/button/FontIconBox.tsx | 11 ++++++++++- 4 files changed, 36 insertions(+), 14 deletions(-) (limited to 'src/client/views/nodes') diff --git a/package-lock.json b/package-lock.json index 59ae898bf..7810e3120 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7694,13 +7694,14 @@ "resolved": "https://registry.npmjs.org/image-size-stream/-/image-size-stream-1.1.0.tgz", "integrity": "sha1-Ivou2mbG31AQh0bacUkmSy0l+Gs=", "requires": { + "image-size": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1", "readable-stream": "^1.0.33", "tryit": "^1.0.1" }, "dependencies": { "image-size": { - "version": "git+https://github.com/netroy/image-size.git#da2c863807a3e9602617bdd357b0de3ab4a064c1", - "from": "git+https://github.com/netroy/image-size.git#da2c863807a3e9602617bdd357b0de3ab4a064c1" + "version": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1", + "from": "github:netroy/image-size#da2c863807a3e9602617bdd357b0de3ab4a064c1" }, "isarray": { "version": "0.0.1", diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index cd4c217b5..d03ef4aca 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -70,14 +70,14 @@ export class CurrentUserUtils { [this.ficon({ ignoreClick: true, icon: "mobile", - btnType: ButtonType.ClickButton, + btnType: ButtonType.ToolButton, backgroundColor: "transparent" }), this.mobileTextContainer({}, [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])]); doc["template-mobile-button"] = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(queryTemplate) as any as Doc, title: "mobile button", icon: "mobile", btnType: ButtonType.ClickButton, + dragFactory: new PrefetchProxy(queryTemplate) as any as Doc, title: "mobile button", icon: "mobile", btnType: ButtonType.ToolButton, }); } @@ -93,7 +93,7 @@ export class CurrentUserUtils { doc["template-button-slides"] = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), dragFactory: new PrefetchProxy(slideTemplate) as any as Doc, title: "presentation slide", icon: "address-card", - btnType: ButtonType.ClickButton + btnType: ButtonType.ToolButton }); } @@ -140,7 +140,7 @@ export class CurrentUserUtils { doc["template-button-link"] = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), dragFactory: new PrefetchProxy(linkTemplate) as any as Doc, title: "link view", icon: "window-maximize", system: true, - btnType: ButtonType.ClickButton + btnType: ButtonType.ToolButton }); } @@ -172,7 +172,7 @@ export class CurrentUserUtils { doc["template-button-switch"] = CurrentUserUtils.ficon({ onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", icon: "toggle-on", system: true, - btnType: ButtonType.ClickButton + btnType: ButtonType.ToolButton }); } @@ -225,7 +225,7 @@ export class CurrentUserUtils { title: "detailView", icon: "window-maximize", system: true, - btnType: ButtonType.ClickButton, + btnType: ButtonType.ToolButton, }); } @@ -511,12 +511,13 @@ export class CurrentUserUtils { icon, title, toolTip, - btnType: ButtonType.ClickButton, + btnType: ButtonType.ToolButton, ignoreClick, _dropAction: "alias", onDragStart: drag ? ScriptField.MakeFunction(drag) : undefined, onClick: click ? ScriptField.MakeScript(click) : undefined, - backgroundColor, + backgroundColor: backgroundColor ? backgroundColor : Colors.DARK_GRAY, + color: Colors.WHITE, _hideContextMenu: true, _removeDropProperties: new List(["_stayInCollection"]), _stayInCollection: true, diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss index 46a499466..0c866988d 100644 --- a/src/client/views/nodes/button/FontIconBox.scss +++ b/src/client/views/nodes/button/FontIconBox.scss @@ -38,12 +38,24 @@ &.clickBtn { cursor: pointer; + width: 40px; } &.tglBtn { cursor: pointer; } + &.toolBtn { + cursor: pointer; + width: 40px; + border-radius: 100%; + + svg { + width: 60% !important; + height: 60%; + } + } + &.menuBtn { cursor: pointer; border-radius: 0px; @@ -109,7 +121,7 @@ } .list-item:hover { - background-color:lightgrey; + background-color: lightgrey; } } } @@ -201,5 +213,4 @@ // background:transparent; // position: fixed; // } -// } - +// } \ No newline at end of file diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 2c6369e9f..9e7608dc3 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -27,7 +27,8 @@ export enum ButtonType { ClickButton = "clickBtn", DoubleButton = "dblBtn", ToggleButton = "tglBtn", - ColorButton = "colorBtn" + ColorButton = "colorBtn", + ToolButton = "toolBtn" } export interface ButtonProps extends FieldViewProps { @@ -254,6 +255,14 @@ export class FontIconBox extends DocComponent(Fon
); break; + case ButtonType.ToolButton: + button = ( +
+ + {label} +
+ ); + break; case ButtonType.ToggleButton: button = (
-- cgit v1.2.3-70-g09d2 From 7cc9c998a904c8741dd57663c83f5ae64621ad44 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 19 Aug 2021 14:03:18 -0400 Subject: fixed typing ? to search bing. fixed scrolling to target for Web/PDF when doc was not previously displayed. --- src/client/views/collections/collectionFreeForm/MarqueeView.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 8 +++++--- src/client/views/pdf/PDFViewer.tsx | 7 ++++--- 3 files changed, 10 insertions(+), 7 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 19da7ea00..d0243850f 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -92,7 +92,7 @@ export class MarqueeView extends React.Component this.props.addDocTab( Docs.Create.WebDocument(`https://bing.com/search?q=${str}`, { _width: 400, x, y, _height: 512, _nativeWidth: 850, title: "bing", useCors: true }), "add:right")); - cm.displayMenu(this._downX, this._downY); + cm.displayMenu(this._downX, this._downY, undefined, true); e.stopPropagation(); } else if (e.key === "u" && this.props.ungroup) { diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index f3a4a46de..2ff41c73a 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -179,7 +179,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this._scrollHeight = Math.max(this.scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0)), 5000); - if (this._initialScroll !== undefined && this._outerRef.current) { - this._outerRef.current.scrollTop = this._initialScroll; + const initialScroll = this._initialScroll; + if (initialScroll !== undefined && this._outerRef.current) { + // bcz: not sure why this happens, but if the webpage isn't ready yet, it's scroll height seems to be limited. So we need to wait tp set scroll location to what we want. + setTimeout(() => this._outerRef.current!.scrollTop = initialScroll); this._initialScroll = undefined; } iframe.setAttribute("enable-annotation", "true"); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index e7911e8f8..ee553fd43 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -182,17 +182,18 @@ export class PDFViewer extends React.Component { scrollFocus = (doc: Doc, smooth: boolean) => { const mainCont = this._mainCont.current; let focusSpeed: Opt; - if (doc !== this.props.rootDoc && mainCont && this._pdfViewer) { + if (doc !== this.props.rootDoc && mainCont) { const windowHeight = this.props.PanelHeight() / (this.props.scaling?.() || 1); const scrollTo = Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, .1 * windowHeight); if (scrollTo !== undefined) { focusSpeed = 500; - if (smooth) smoothScroll(focusSpeed, mainCont, scrollTo); + if (!this._pdfViewer) this._initialScroll = scrollTo; + else if (smooth) smoothScroll(focusSpeed, mainCont, scrollTo); else this._mainCont.current?.scrollTo({ top: Math.abs(scrollTo || 0) }); } } else { - this._initialScroll = NumCast(doc.y); + this._initialScroll = NumCast(this.props.layoutDoc._scrollTop); } return focusSpeed; } -- cgit v1.2.3-70-g09d2 From d5841cda5aa838cf02b26a7ffbcc2b1713a66f36 Mon Sep 17 00:00:00 2001 From: geireann Date: Thu, 19 Aug 2021 17:43:33 -0400 Subject: menu nearing final updates --- src/client/documents/Documents.ts | 2 + src/client/util/CurrentUserUtils.ts | 115 +- src/client/util/tempCurrentUserUtils.ts | 1389 -------------------- src/client/views/DocumentButtonBar.tsx | 5 +- src/client/views/collections/CollectionMenu.tsx | 1 + .../collectionLinearView/CollectionLinearView.scss | 11 +- .../collectionLinearView/CollectionLinearView.tsx | 2 +- src/client/views/nodes/button/ButtonScripts.ts | 14 + src/client/views/nodes/button/FontIconBox.scss | 64 +- src/client/views/nodes/button/FontIconBox.tsx | 375 ++++-- 10 files changed, 414 insertions(+), 1564 deletions(-) delete mode 100644 src/client/util/tempCurrentUserUtils.ts create mode 100644 src/client/views/nodes/button/ButtonScripts.ts (limited to 'src/client/views/nodes') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 817fbb9d6..f6b2e0736 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -223,11 +223,13 @@ export class DocumentOptions { docColorBtn?: string; userColorBtn?: string; canClick?: string; + script?: string; //LINEAR VIEW linearViewIsExpanded?: boolean; // is linear view expanded linearViewExpandable?: boolean; // can linear view be expanded linearViewToggleButton?: string; // button to open close linear view group + linearViewSubMenu?: boolean; layout_linkView?: Doc; // view template for a link document layout_keyValue?: string; // view tempalte for key value docs diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index d03ef4aca..4ff0446ad 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -923,44 +923,58 @@ export class CurrentUserUtils { title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, list: ["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"], - scriptDoc: Doc.UserDoc(), toggle: 'userDoc._fontFamily' + script: 'changeFont' }, + { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', script: 'toggleBold' }, + { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', script: 'toggleItalic' }, + { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', script: 'toggleUnderline' }, + // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'}, + // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'}, + // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'}, + { title: "Highlight", toolTip: "Highlight", btnType: ButtonType.ColorButton, icon: "highlighter", ignoreClick: true, scriptDoc: Doc.UserDoc(), userColorBtn: 'userDoc._highlightColor' }, + { title: "Text color", toolTip: "Text color", btnType: ButtonType.ColorButton, icon: "fill-drip", ignoreClick: true, scriptDoc: Doc.UserDoc(), script:'console.log("test")', userColorBtn: 'userDoc._textColor' }, + ]; + } + + static inkTools(doc: Doc) { + return [ + { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'togglePen()', script: 'togglePen' }, + { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'toggleHighlighter()', script: 'toggleHighlighter' }, + { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'toggleCircle()', script: 'toggleCircle' }, + { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'toggleSquare()', script: 'toggleSquare' }, + { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "fill-drip", click: 'toggleLine()', script: 'toggleLine' }, + ]; + } + + static webTools(doc: Doc) { + return [ { - title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', - scriptDoc: Doc.UserDoc(), - toggle: 'userDoc._boldActive' + title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, + list: ["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", + "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"], + script: 'changeFont' }, - { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', scriptDoc: Doc.UserDoc(), toggle: 'userDoc._italicsActive' }, - { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', scriptDoc: Doc.UserDoc(), toggle: 'userDoc._underlineActive' }, - // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()', toggle: 'userDoc._underlineActive' }, - // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()', toggle: 'userDoc._underlineActive' }, - // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()', toggle: 'userDoc._underlineActive' }, - { title: "Highlight", toolTip: "Highlight", btnType: ButtonType.ColorButton, icon: "highlighter", ignoreClick: true, scriptDoc: Doc.UserDoc(), userColorBtn: 'userDoc._highlightColor' }, - { title: "Text color", toolTip: "Text color", btnType: ButtonType.ColorButton, icon: "fill-drip", ignoreClick: true, scriptDoc: Doc.UserDoc(), userColorBtn: 'userDoc._textColor' }, - // { title: "Link", tooltip: "Link", btnType: ButtonType.DropdownButton, icon: "link", click: '', ignoreClick: true }, ]; } static async contextMenuBtnDescriptions(doc: Doc) { return [ - // { title: "Perspective", tooltip: "Change document's perspective", type: "btn", btnType: ButtonType.DropdownButton, ignoreClick: true, icon: "desktop", click: '' }, { - title: "Perspective", toolTip: "Perspective", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, + title: "Perspective", toolTip: "View", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, list: [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Tree, - CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Multicolumn, - CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel, - CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, - CollectionViewType.Grid], - scriptDoc: 'selectedDoc', - toggle: 'selectedDoc._viewType' + CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Multicolumn, + CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel, + CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, + CollectionViewType.Grid], + script: 'changeView', }, { - title: "Background", toolTip: "Background", btnType: ButtonType.ColorButton, scriptDoc: 'selectedDoc', - docColorBtn: 'selectedDoc.backgroundColor', width: 60, ignoreClick: true, icon: "fill-drip", - canClick: 'numSelected > 0' + title: "Background", toolTip: "Background", btnType: ButtonType.ColorButton, width: 60, ignoreClick: true, icon: "fill-drip", + script: "changeBackgroundColor" }, - { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', toggle: 'selectedDoc.z', canClick: 'numSelected > 0' }, - { title: "Text Tools", type: "TextMenu", icon: "font" }, + { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()' }, + { title: "Text", type: "TextMenu" }, + { title: "Ink & GFX", type: "InkMenu" }, // { title: "Ink Tools", type: "LinearMenu", icon: "pen-nib" }, // { title: "GFX Tools", type: "LinearMenu", icon: "shapes" }, // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy" }, @@ -971,21 +985,44 @@ export class CurrentUserUtils { static async setupContextMenuButtons(doc: Doc) { const docList: Doc[] = []; - const contextMenuBtns = (await CurrentUserUtils.contextMenuBtnDescriptions(doc)).map(({ title, width, toolTip, ignoreClick, icon, type, btnType, click, toggle, scriptDoc, canClick, docColorBtn }) => { + const contextMenuBtns = (await CurrentUserUtils.contextMenuBtnDescriptions(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script }) => { const textDocList: Doc[] = []; if (type === "TextMenu") { - const textBtns = (CurrentUserUtils.textTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, btnType, click, toggle, scriptDoc, userColorBtn }) => { + const textBtns = (CurrentUserUtils.textTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, btnType, click, script, userColorBtn }) => { textDocList.push(Docs.Create.FontIconDocument({ - _nativeWidth: width ? width : 25, - _nativeHeight: 25, - _width: width ? width : 25, - _height: 25, + _nativeWidth: width ? width : 30, + _nativeHeight: 30, + _width: width ? width : 30, + _height: 30, icon, toolTip, userColorBtn, - - // testToggle: toggle ? ScriptField.MakeScript(toggle, { this: scriptDoc, scriptContext: "any" }) : undefined, - // toggle: toggle, + script, + btnType: btnType, + btnList: new List(list), + ignoreClick: ignoreClick, + _stayInCollection: true, + _hideContextMenu: true, + system: true, + dontUndo: true, + title, + backgroundColor: "black", + _dropAction: "alias", + _removeDropProperties: new List(["dropAction", "_stayInCollection"]), + onClick: click ? ScriptField.MakeScript(click, { doc: Doc.name }) : undefined + })); + }); + docList.push(CurrentUserUtils.blist({ linearViewSubMenu: true, ignoreClick: true, linearViewExpandable: true, icon:title, _height: 30, backgroundColor: "transparent" }, textDocList)); + } else if (type === "InkMenu") { + const inkBtns = (CurrentUserUtils.inkTools(doc)).map(({ title, toolTip, icon, btnType, click }) => { + textDocList.push(Docs.Create.FontIconDocument({ + _nativeWidth: width ? width : 30, + _nativeHeight: 30, + _width: width ? width : 30, + _height: 30, + icon, + toolTip, + script, btnType: btnType, btnList: new List(list), ignoreClick: ignoreClick, @@ -997,10 +1034,10 @@ export class CurrentUserUtils { backgroundColor: "black", _dropAction: "alias", _removeDropProperties: new List(["dropAction", "_stayInCollection"]), - onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined + onClick: click ? ScriptField.MakeScript(click, { doc: Doc.name }) : undefined })); }); - docList.push(CurrentUserUtils.blist({ ignoreClick: true, linearViewExpandable: true, icon:"Text", _height: 30, backgroundColor: "transparent" }, textDocList)); + docList.push(CurrentUserUtils.blist({ linearViewSubMenu: true, ignoreClick: true, linearViewExpandable: true, icon:title, _height: 30, backgroundColor: "transparent" }, textDocList)); } else { docList.push(Docs.Create.FontIconDocument({ _nativeWidth: width ? width : 30, @@ -1011,9 +1048,9 @@ export class CurrentUserUtils { toolTip, // testToggle: toggle ? ScriptField.MakeScript(toggle, { scriptContext: "any" }) : undefined, // toggle: toggle, - docColorBtn, - canClick: canClick, + script, btnType: btnType, + btnList: new List(list), ignoreClick: ignoreClick, _stayInCollection: true, _hideContextMenu: true, @@ -1093,7 +1130,7 @@ export class CurrentUserUtils { } if (doc.myImportPanel === undefined) { const uploads = Cast(doc.myImportDocs, Doc, null); - const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", icon: "upload", system: true }); + const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", type: ButtonType.ToolButton, icon: "upload", system: true }); doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, _showTitle: "title", ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true, boxShadow: "0 0" })); } } diff --git a/src/client/util/tempCurrentUserUtils.ts b/src/client/util/tempCurrentUserUtils.ts deleted file mode 100644 index 3fba672e6..000000000 --- a/src/client/util/tempCurrentUserUtils.ts +++ /dev/null @@ -1,1389 +0,0 @@ -import { computed, observable, reaction, action } from "mobx"; -import * as rp from 'request-promise'; -import { DataSym, Doc, DocListCast, DocListCastAsync, AclReadonly } from "../../fields/Doc"; -import { Id } from "../../fields/FieldSymbols"; -import { List } from "../../fields/List"; -import { PrefetchProxy } from "../../fields/Proxy"; -import { RichTextField } from "../../fields/RichTextField"; -import { listSpec } from "../../fields/Schema"; -import { SchemaHeaderField } from "../../fields/SchemaHeaderField"; -import { ComputedField, ScriptField } from "../../fields/ScriptField"; -import { BoolCast, Cast, NumCast, PromiseValue, StrCast, DateCast } from "../../fields/Types"; -import { nullAudio } from "../../fields/URLField"; -import { SharingPermissions } from "../../fields/util"; -import { Utils } from "../../Utils"; -import { DocServer } from "../DocServer"; -import { Docs, DocumentOptions, DocUtils } from "../documents/Documents"; -import { DocumentType } from "../documents/DocumentTypes"; -import { Networking } from "../Network"; -import { CollectionDockingView } from "../views/collections/collectionDocking/CollectionDockingView"; -import { DimUnit } from "../views/collections/collectionMulticolumn/CollectionMulticolumnView"; -import { CollectionView, CollectionViewType } from "../views/collections/CollectionView"; -import { MainView } from "../views/MainView"; -import { FormattedTextBox } from "../views/nodes/formattedText/FormattedTextBox"; -import { LabelBox } from "../views/nodes/LabelBox"; -import { OverlayView } from "../views/OverlayView"; -import { DocumentManager } from "./DocumentManager"; -import { DragManager } from "./DragManager"; -import { makeTemplate } from "./DropConverter"; -import { HistoryUtil } from "./History"; -import { LinkManager } from "./LinkManager"; -import { Scripting } from "./Scripting"; -import { SearchUtil } from "./SearchUtil"; -import { SelectionManager } from "./SelectionManager"; -import { SnappingManager } from "./SnappingManager"; -import { InkTool } from "../../fields/InkField"; -import { ButtonType } from "../views/nodes/FontIconBox"; - - -export let resolvedPorts: { server: number, socket: number }; -const headerViewVersion = "0.1"; -export class CurrentUserUtils { - private static curr_id: string; - //TODO tfs: these should be temporary... - private static mainDocId: string | undefined; - - public static get id() { return this.curr_id; } - public static get MainDocId() { return this.mainDocId; } - public static set MainDocId(id: string | undefined) { this.mainDocId = id; } - @computed public static get UserDocument() { return Doc.UserDoc(); } - - @observable public static GuestTarget: Doc | undefined; - @observable public static GuestDashboard: Doc | undefined; - @observable public static GuestMobile: Doc | undefined; - @observable public static propertiesWidth: number = 0; - - // sets up the default User Templates - slideView, headerView - static setupUserTemplateButtons(doc: Doc) { - // Prototype for mobile button (not sure if 'Advanced Item Prototypes' is ideal location) - if (doc["template-mobile-button"] === undefined) { - const queryTemplate = this.mobileButton({ - title: "NEW MOBILE BUTTON", - onClick: undefined, - }, - [this.ficon({ - ignoreClick: true, - icon: "mobile", - btnType: ButtonType.ClickButton, - backgroundColor: "transparent" - }), - this.mobileTextContainer({}, - [this.mobileButtonText({}, "NEW MOBILE BUTTON"), this.mobileButtonInfo({}, "You can customize this button and make it your own.")])]); - doc["template-mobile-button"] = CurrentUserUtils.ficon({ - onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(queryTemplate) as any as Doc, title: "mobile button", - btnType: ButtonType.ClickButton, - icon: "mobile" - }); - } - - if (doc["template-button-slides"] === undefined) { - const slideTemplate = Docs.Create.MultirowDocument( - [ - Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }), - Docs.Create.TextDocument("", { title: "text", _height: 100, system: true }) - ], - { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true } - ); - slideTemplate.isTemplateDoc = makeTemplate(slideTemplate); - doc["template-button-slides"] = CurrentUserUtils.ficon({ - onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(slideTemplate) as any as Doc, title: "presentation slide", - icon: "address-card", - btnType: ButtonType.ClickButton - }); - } - - if (doc["template-button-link"] === undefined) { // set _backgroundColor to transparent to prevent link dot from obscuring document it's attached to. - const linkTemplate = Doc.MakeDelegate(Docs.Create.TextDocument(" ", { title: "header", _autoHeight: true, system: true }, "header")); // text needs to be a space to allow templateText to be created - linkTemplate.system = true; - Doc.GetProto(linkTemplate).layout = - "
" + - " " + - " " + - "
"; - (linkTemplate.proto as Doc).isTemplateDoc = makeTemplate(linkTemplate.proto as Doc, true, "linkView"); - - const rtf2 = { - doc: { - type: "doc", content: [ - { - type: "paragraph", - content: [{ - type: "dashField", - attrs: { - fieldKey: "src", - hideKey: false - } - }] - }, - { type: "paragraph" }, - { - type: "paragraph", - content: [{ - type: "dashField", - attrs: { - fieldKey: "dst", - hideKey: false - } - }] - }] - }, - selection: { type: "text", anchor: 1, head: 1 }, - storedMarks: [] - }; - linkTemplate.header = new RichTextField(JSON.stringify(rtf2), ""); - - doc["template-button-link"] = CurrentUserUtils.ficon({ - onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(linkTemplate) as any as Doc, title: "link view", - btnType: ButtonType.ClickButton, - icon: "window-maximize", system: true - }); - } - - if (doc["template-button-switch"] === undefined) { - const { FreeformDocument, MulticolumnDocument, TextDocument } = Docs.Create; - - const yes = FreeformDocument([], { title: "yes", _height: 35, _width: 50, _dimUnit: DimUnit.Pixel, _dimMagnitude: 40, system: true }); - const name = TextDocument("name", { title: "name", _height: 35, _width: 70, _dimMagnitude: 1, system: true }); - const no = FreeformDocument([], { title: "no", _height: 100, _width: 100, system: true }); - const labelTemplate = { - doc: { - type: "doc", content: [{ - type: "paragraph", - content: [{ type: "dashField", attrs: { fieldKey: "PARAMS", hideKey: true } }] - }] - }, - selection: { type: "text", anchor: 1, head: 1 }, - storedMarks: [] - }; - Doc.GetProto(name).text = new RichTextField(JSON.stringify(labelTemplate), "PARAMS"); - Doc.GetProto(yes).backgroundColor = ComputedField.MakeFunction("self[this.PARAMS] ? 'green':'red'"); - // Doc.GetProto(no).backgroundColor = ComputedField.MakeFunction("!self[this.PARAMS] ? 'red':'white'"); - // Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = true"); - Doc.GetProto(yes).onClick = ScriptField.MakeScript("self[this.PARAMS] = !self[this.PARAMS]"); - // Doc.GetProto(no).onClick = ScriptField.MakeScript("self[this.PARAMS] = false"); - const box = MulticolumnDocument([/*no, */ yes, name], { title: "value", _width: 120, _height: 35, system: true }); - box.isTemplateDoc = makeTemplate(box, true, "switch"); - - doc["template-button-switch"] = CurrentUserUtils.ficon({ - onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(box) as any as Doc, title: "data switch", - btnType: ButtonType.ClickButton, - icon: "toggle-on", system: true - }); - } - - if (doc["template-button-detail"] === undefined) { - const { TextDocument, MasonryDocument, CarouselDocument } = Docs.Create; - - const openInTarget = ScriptField.MakeScript("openOnRight(self.doubleClickView)"); - const carousel = CarouselDocument([], { - title: "data", _height: 350, _itemIndex: 0, "_carousel-caption-xMargin": 10, "_carousel-caption-yMargin": 10, - onChildDoubleClick: openInTarget, backgroundColor: "#9b9b9b3F", system: true - }); - - const details = TextDocument("", { title: "details", _height: 200, _autoHeight: true, system: true }); - const short = TextDocument("", { title: "shortDescription", treeViewOpen: true, treeViewExpandedView: "layout", _height: 75, _autoHeight: true, system: true }); - const long = TextDocument("", { title: "longDescription", treeViewOpen: false, treeViewExpandedView: "layout", _height: 150, _autoHeight: true, system: true }); - - const buxtonFieldKeys = ["year", "originalPrice", "degreesOfFreedom", "company", "attribute", "primaryKey", "secondaryKey", "dimensions"]; - const detailedTemplate = { - doc: { - type: "doc", content: buxtonFieldKeys.map(fieldKey => ({ - type: "paragraph", - content: [{ type: "dashField", attrs: { fieldKey } }] - })) - }, - selection: { type: "text", anchor: 1, head: 1 }, - storedMarks: [] - }; - details.text = new RichTextField(JSON.stringify(detailedTemplate), buxtonFieldKeys.join(" ")); - - const shared = { _autoHeight: true, _xMargin: 0 }; - const detailViewOpts = { title: "detailView", _width: 300, _fontFamily: "Arial", _fontSize: "12px" }; - const descriptionWrapperOpts = { title: "descriptions", _height: 300, _columnWidth: -1, treeViewHideTitle: true, _pivotField: "title", system: true }; - - const descriptionWrapper = MasonryDocument([details, short, long], { ...shared, ...descriptionWrapperOpts }); - descriptionWrapper._columnHeaders = new List([ - new SchemaHeaderField("[A Short Description]", "dimGray", undefined, undefined, undefined, false), - new SchemaHeaderField("[Long Description]", "dimGray", undefined, undefined, undefined, true), - new SchemaHeaderField("[Details]", "dimGray", undefined, undefined, undefined, true), - ]); - const detailView = Docs.Create.StackingDocument([carousel, descriptionWrapper], { ...shared, ...detailViewOpts, _chromeHidden: true, system: true }); - detailView.isTemplateDoc = makeTemplate(detailView); - - details.title = "Details"; - short.title = "A Short Description"; - long.title = "Long Description"; - - doc["template-button-detail"] = CurrentUserUtils.ficon({ - onDragStart: ScriptField.MakeFunction('copyDragFactory(this.dragFactory)'), - dragFactory: new PrefetchProxy(detailView) as any as Doc, title: "detailView", - btnType: ButtonType.ClickButton, - icon: "window-maximize", system: true - }); - } - - const requiredTypes = [ - doc["template-button-slides"] as Doc, - doc["template-mobile-button"] as Doc, - doc["template-button-detail"] as Doc, - doc["template-button-link"] as Doc, - //doc["template-button-switch"] as Doc] - ]; - if (doc["template-buttons"] === undefined) { - doc["template-buttons"] = new PrefetchProxy(Docs.Create.MasonryDocument(requiredTypes, { - title: "Advanced Item Prototypes", _xMargin: 0, _showTitle: "title", _chromeHidden: true, - hidden: ComputedField.MakeFunction("IsNoviceMode()") as any, - _stayInCollection: true, _hideContextMenu: true, - _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true - })); - } else { - const curButnTypes = Cast(doc["template-buttons"], Doc, null); - DocListCastAsync(curButnTypes.data).then(async curBtns => { - curBtns && await Promise.all(curBtns); - requiredTypes.map(btype => Doc.AddDocToList(curButnTypes, "data", btype)); - }); - } - return doc["template-buttons"] as Doc; - } - - // setup the different note type skins - static setupNoteTemplates(doc: Doc) { - if (doc["template-note-Note"] === undefined) { - const noteView = Docs.Create.TextDocument("", { title: "text", isTemplateDoc: true, backgroundColor: "yellow", system: true }); - noteView.isTemplateDoc = makeTemplate(noteView, true, "Note"); - doc["template-note-Note"] = new PrefetchProxy(noteView); - } - if (doc["template-note-Idea"] === undefined) { - const noteView = Docs.Create.TextDocument("", { title: "text", backgroundColor: "pink", system: true }); - noteView.isTemplateDoc = makeTemplate(noteView, true, "Idea"); - doc["template-note-Idea"] = new PrefetchProxy(noteView); - } - if (doc["template-note-Topic"] === undefined) { - const noteView = Docs.Create.TextDocument("", { title: "text", backgroundColor: "lightblue", system: true }); - noteView.isTemplateDoc = makeTemplate(noteView, true, "Topic"); - doc["template-note-Topic"] = new PrefetchProxy(noteView); - } - if (doc["template-note-Todo"] === undefined) { - const noteView = Docs.Create.TextDocument("", { - title: "text", backgroundColor: "orange", _autoHeight: false, _height: 100, _showCaption: "caption", - layout: FormattedTextBox.LayoutString("Todo"), caption: RichTextField.DashField("taskStatus"), system: true - }); - noteView.isTemplateDoc = makeTemplate(noteView, true, "Todo"); - doc["template-note-Todo"] = new PrefetchProxy(noteView); - } - const taskStatusValues = [ - { title: "todo", _backgroundColor: "blue", color: "white", system: true }, - { title: "in progress", _backgroundColor: "yellow", color: "black", system: true }, - { title: "completed", _backgroundColor: "green", color: "white", system: true } - ]; - if (doc.fieldTypes === undefined) { - doc.fieldTypes = Docs.Create.TreeDocument([], { title: "field enumerations", system: true }); - DocUtils.addFieldEnumerations(Doc.GetProto(doc["template-note-Todo"] as any as Doc), "taskStatus", taskStatusValues); - } - - if (doc["template-notes"] === undefined) { - doc["template-notes"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-note-Note"] as any as Doc, doc["template-note-Idea"] as any as Doc, doc["template-note-Topic"] as any as Doc], // doc["template-note-Todo"] as any as Doc], - { title: "Note Layouts", _height: 75, system: true })); - } else { - const curNoteTypes = Cast(doc["template-notes"], Doc, null); - const requiredTypes = [doc["template-note-Note"] as any as Doc, doc["template-note-Idea"] as any as Doc, doc["template-note-Topic"] as any as Doc];//, doc["template-note-Todo"] as any as Doc]; - DocListCastAsync(curNoteTypes.data).then(async curNotes => { - curNotes && await Promise.all(curNotes); - requiredTypes.map(ntype => Doc.AddDocToList(curNoteTypes, "data", ntype)); - }); - } - - return doc["template-notes"] as Doc; - } - - // creates Note templates, and initial "user" templates - static setupDocTemplates(doc: Doc) { - const noteTemplates = CurrentUserUtils.setupNoteTemplates(doc); - const userTemplateBtns = CurrentUserUtils.setupUserTemplateButtons(doc); - const clickTemplates = CurrentUserUtils.setupClickEditorTemplates(doc); - if (doc.templateDocs === undefined) { - doc.templateDocs = new PrefetchProxy(Docs.Create.TreeDocument([noteTemplates, userTemplateBtns, clickTemplates], { - title: "template layouts", _xPadding: 0, system: true, - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }) - })); - } - } - - // setup templates for different document types when they are iconified from Document Decorations - static setupDefaultIconTemplates(doc: Doc) { - if (doc["template-icon-view"] === undefined) { - const iconView = Docs.Create.LabelDocument({ - title: "icon", textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("title"), _backgroundColor: "dimGray", - _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true - }); - // Docs.Create.TextDocument("", { - // title: "icon", _width: 150, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)") - // }); - // Doc.GetProto(iconView).icon = new RichTextField('{"doc":{"type":"doc","content":[{"type":"paragraph","attrs":{"align":null,"color":null,"id":null,"indent":null,"inset":null,"lineSpacing":null,"paddingBottom":null,"paddingTop":null},"content":[{"type":"dashField","attrs":{"fieldKey":"title","docid":""}}]}]},"selection":{"type":"text","anchor":2,"head":2},"storedMarks":[]}', ""); - iconView.isTemplateDoc = makeTemplate(iconView); - doc["template-icon-view"] = new PrefetchProxy(iconView); - } - if (doc["template-icon-view-rtf"] === undefined) { - const iconRtfView = Docs.Create.LabelDocument({ - title: "icon_" + DocumentType.RTF, textTransform: "unset", letterSpacing: "unset", layout: LabelBox.LayoutString("text"), - _width: 150, _height: 70, _xPadding: 10, _yPadding: 10, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true - }); - iconRtfView.isTemplateDoc = makeTemplate(iconRtfView, true, "icon_" + DocumentType.RTF); - doc["template-icon-view-rtf"] = new PrefetchProxy(iconRtfView); - } - if (doc["template-icon-view-button"] === undefined) { - const iconBtnView = Docs.Create.FontIconDocument({ - title: "icon_" + DocumentType.BUTTON, _nativeHeight: 30, _nativeWidth: 30, - _width: 30, _height: 30, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true - }); - iconBtnView.isTemplateDoc = makeTemplate(iconBtnView, true, "icon_" + DocumentType.BUTTON); - doc["template-icon-view-button"] = new PrefetchProxy(iconBtnView); - } - if (doc["template-icon-view-img"] === undefined) { - const iconImageView = Docs.Create.ImageDocument("http://www.cs.brown.edu/~bcz/face.gif", { - title: "data", _width: 50, isTemplateDoc: true, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true - }); - iconImageView.isTemplateDoc = makeTemplate(iconImageView, true, "icon_" + DocumentType.IMG); - doc["template-icon-view-img"] = new PrefetchProxy(iconImageView); - } - if (doc["template-icon-view-col"] === undefined) { - const iconColView = Docs.Create.TreeDocument([], { title: "data", _width: 180, _height: 80, onDoubleClick: ScriptField.MakeScript("deiconifyView(self)"), system: true }); - iconColView.isTemplateDoc = makeTemplate(iconColView, true, "icon_" + DocumentType.COL); - doc["template-icon-view-col"] = new PrefetchProxy(iconColView); - } - if (doc["template-icons"] === undefined) { - doc["template-icons"] = new PrefetchProxy(Docs.Create.TreeDocument([doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, - doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc, doc["template-icon-view-pdf"] as Doc], { title: "icon templates", _height: 75, system: true })); - } else { - const templateIconsDoc = Cast(doc["template-icons"], Doc, null); - const requiredTypes = [doc["template-icon-view"] as Doc, doc["template-icon-view-img"] as Doc, doc["template-icon-view-button"] as Doc, - doc["template-icon-view-col"] as Doc, doc["template-icon-view-rtf"] as Doc]; - DocListCastAsync(templateIconsDoc.data).then(async curIcons => { - curIcons && await Promise.all(curIcons); - requiredTypes.map(ntype => Doc.AddDocToList(templateIconsDoc, "data", ntype)); - }); - } - return doc["template-icons"] as Doc; - } - - static creatorBtnDescriptors(doc: Doc): { - title: string, toolTip: string, icon: string, drag?: string, ignoreClick?: boolean, - click?: string, backgroundColor?: string, dragFactory?: Doc, noviceMode?: boolean, clickFactory?: Doc - }[] { - if (doc.emptyPresentation === undefined) { - doc.emptyPresentation = Docs.Create.PresDocument(new List(), - { title: "Untitled Presentation", _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: "alias", _chromeHidden: true, boxShadow: "0 0", system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyPresentation as Doc).proto as Doc)["dragFactory-count"] = 0; - } - if (doc.emptyCollection === undefined) { - doc.emptyCollection = Docs.Create.FreeformDocument([], - { _nativeWidth: undefined, _nativeHeight: undefined, _fitWidth: true, _width: 150, _height: 100, title: "freeform", system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyCollection as Doc).proto as Doc)["dragFactory-count"] = 0; - } - if (doc.emptyPane === undefined) { - doc.emptyPane = Docs.Create.FreeformDocument([], { _nativeWidth: undefined, _nativeHeight: undefined, _width: 500, _height: 800, title: "Untitled Tab", system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyPane as Doc).proto as Doc)["dragFactory-count"] = 0; - } - if (doc.emptySlide === undefined) { - const textDoc = Docs.Create.TreeDocument([], { title: "Slide", _viewType: CollectionViewType.Tree, _fontSize: "20px", treeViewType: "outline", _xMargin: 0, _yMargin: 0, _width: 300, _height: 200, _singleLine: true, backgroundColor: "transparent", system: true, cloneFieldFilter: new List(["system"]) }); - Doc.GetProto(textDoc).title = ComputedField.MakeFunction('self.text?.Text'); - FormattedTextBox.SelectOnLoad = textDoc[Id]; - doc.emptySlide = textDoc; - } - if ((doc.emptyHeader as Doc)?.version !== headerViewVersion) { - const json = { - doc: { - type: "doc", - content: [ - { - type: "paragraph", attrs: {}, content: [{ - type: "dashField", - attrs: { fieldKey: "author", docid: "", hideKey: false }, - marks: [{ type: "strong" }] - }, { - type: "dashField", - attrs: { fieldKey: "creationDate", docid: "", hideKey: false }, - marks: [{ type: "strong" }] - }] - }] - }, - selection: { type: "text", anchor: 1, head: 1 }, - storedMarks: [] - }; - const headerTemplate = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { - title: "text", version: headerViewVersion, target: doc, _height: 70, _headerPointerEvents: "all", - _headerHeight: 12, _headerFontSize: 9, _autoHeight: true, system: true, _fitWidth: true, - cloneFieldFilter: new List(["system"]) - }, "header"); - const headerBtnHgt = 10; - headerTemplate[DataSym].layout = - "" + - ` ` + - " " + - ` Metadata` + - ""; - - // "
" + - // " " + - // " " + - // "
"; - (headerTemplate.proto as Doc).isTemplateDoc = makeTemplate(headerTemplate.proto as Doc, true, "headerView"); - doc.emptyHeader = headerTemplate; - ((doc.emptyHeader as Doc).proto as Doc)["dragFactory-count"] = 0; - } - if (doc.emptyComparison === undefined) { - doc.emptyComparison = Docs.Create.ComparisonDocument({ title: "compare", _width: 300, _height: 300, system: true, cloneFieldFilter: new List(["system"]) }); - } - if (doc.emptyScript === undefined) { - doc.emptyScript = Docs.Create.ScriptingDocument(undefined, { _width: 200, _height: 250, title: "script", system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyScript as Doc).proto as Doc)["dragFactory-count"] = 0; - } - if (doc.emptyScreenshot === undefined) { - doc.emptyScreenshot = Docs.Create.ScreenshotDocument("empty screenshot", { _fitWidth: true, _width: 400, _height: 200, system: true, cloneFieldFilter: new List(["system"]) }); - } - if (doc.emptyWall === undefined) { - doc.emptyWall = Docs.Create.ScreenshotDocument("", { _fitWidth: true, _width: 400, _height: 200, title: "screen snapshot", system: true, cloneFieldFilter: new List(["system"]) }); - (doc.emptyWall as Doc).videoWall = true; - } - if (doc.emptyAudio === undefined) { - doc.emptyAudio = Docs.Create.AudioDocument(nullAudio, { _width: 200, title: "audio recording", system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyAudio as Doc).proto as Doc)["dragFactory-count"] = 0; - } - if (doc.emptyNote === undefined) { - doc.emptyNote = Docs.Create.TextDocument("", { _width: 200, title: "text note", _autoHeight: true, system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyNote as Doc).proto as Doc)["dragFactory-count"] = 0; - } - if (doc.emptyImage === undefined) { - doc.emptyImage = Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", { _width: 250, _nativeWidth: 250, title: "an image of a cat", system: true }); - } - if (doc.emptyButton === undefined) { - doc.emptyButton = Docs.Create.ButtonDocument({ _width: 150, _height: 50, _xPadding: 10, _yPadding: 10, title: "Button", system: true, cloneFieldFilter: new List(["system"]) }); - ((doc.emptyButton as Doc).proto as Doc)["dragFactory-count"] = 0; - } - if (doc.emptyWebpage === undefined) { - doc.emptyWebpage = Docs.Create.WebDocument("", { title: "webpage", _nativeWidth: 850, isTemplateDoc: true, _height: 512, _width: 400, useCors: true, system: true, cloneFieldFilter: new List(["system"]) }); - } - if (doc.activeMobileMenu === undefined) { - this.setupActiveMobileMenu(doc); - } - return [ - { toolTip: "Tap to create a note in a new pane, drag for a note", title: "Note", icon: "sticky-note", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyNote as Doc, noviceMode: true, clickFactory: doc.emptyNote as Doc, }, - { toolTip: "Tap to create a collection in a new pane, drag for a collection", title: "Col", icon: "folder", click: 'openOnRight(copyDragFactory(this.clickFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyCollection as Doc, noviceMode: true, clickFactory: doc.emptyPane as Doc, }, - { toolTip: "Tap to create a webpage in a new pane, drag for a webpage", title: "Web", icon: "globe-asia", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWebpage as Doc, noviceMode: true }, - { toolTip: "Tap to create a progressive slide", title: "Slide", icon: "file", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptySlide as Doc, noviceMode: true }, - { toolTip: "Tap to create a cat image in a new pane, drag for a cat image", title: "Image", icon: "cat", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyImage as Doc }, - { toolTip: "Tap to create a comparison box in a new pane, drag for a comparison box", title: "Compare", icon: "columns", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyComparison as Doc, noviceMode: true }, - { toolTip: "Tap to create a screen grabber in a new pane, drag for a screen grabber", title: "Grab", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScreenshot as Doc, noviceMode: true }, - { toolTip: "Tap to create a videoWall", title: "Wall", icon: "photo-video", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyWall as Doc }, - { toolTip: "Tap to create an audio recorder in a new pane, drag for an audio recorder", title: "Audio", icon: "microphone", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyAudio as Doc, noviceMode: true }, - { toolTip: "Tap to create a button in a new pane, drag for a button", title: "Button", icon: "bolt", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyButton as Doc }, - // { toolTip: "Tap to create a presentation in a new pane, drag for a presentation", title: "Trails", icon: "pres-trail", click: 'openOnRight(Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory))', drag: `Doc.UserDoc().activePresentation = copyDragFactory(this.dragFactory)`, dragFactory: doc.emptyPresentation as Doc, noviceMode: true }, - { toolTip: "Tap to create a scripting box in a new pane, drag for a scripting box", title: "Script", icon: "terminal", click: 'openOnRight(copyDragFactory(this.dragFactory))', drag: 'copyDragFactory(this.dragFactory)', dragFactory: doc.emptyScript as Doc }, - { toolTip: "Tap to create a mobile view in a new pane, drag for a mobile view", title: "Phone", icon: "mobile", click: 'openOnRight(Doc.UserDoc().activeMobileMenu)', drag: 'this.dragFactory', dragFactory: doc.activeMobileMenu as Doc }, - { toolTip: "Tap to create a custom header note document, drag for a custom header note", title: "Custom", icon: "window-maximize", click: 'openOnRight(delegateDragFactory(this.dragFactory))', drag: 'delegateDragFactory(this.dragFactory)', dragFactory: doc.emptyHeader as Doc }, - { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", click: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' }, - ]; - - } - - // setup the "creator" buttons for the sidebar-- eg. the default set of draggable document creation tools - static async setupCreatorButtons(doc: Doc) { - let alreadyCreatedButtons: string[] = []; - const dragCreatorSet = await Cast(doc.myItemCreators, Doc, null); - if (dragCreatorSet) { - const dragCreators = await Cast(dragCreatorSet.data, listSpec(Doc)); - if (dragCreators) { - const dragDocs = await Promise.all(dragCreators); - alreadyCreatedButtons = dragDocs.map(d => StrCast(d.title)); - } - } - const buttons = CurrentUserUtils.creatorBtnDescriptors(doc).filter(d => !alreadyCreatedButtons?.includes(d.title)); - const creatorBtns = buttons.map(({ title, toolTip, icon, ignoreClick, drag, click, backgroundColor, dragFactory, noviceMode, clickFactory }) => Docs.Create.FontIconDocument({ - _nativeWidth: 50, _nativeHeight: 50, _width: 30, _height: 25, - icon, - title, - toolTip, - btnType: ButtonType.ClickButton, - ignoreClick, - _dropAction: "alias", - onDragStart: drag ? ScriptField.MakeFunction(drag) : undefined, - onClick: click ? ScriptField.MakeScript(click) : undefined, - backgroundColor, - _hideContextMenu: true, - _removeDropProperties: new List(["_stayInCollection"]), - _stayInCollection: true, - dragFactory, - clickFactory, - hidden: !noviceMode ? ComputedField.MakeFunction("IsNoviceMode()") as any : undefined, - system: true, - })); - - if (dragCreatorSet === undefined) { - doc.myItemCreators = new PrefetchProxy(Docs.Create.MasonryDocument(creatorBtns, { - title: "Basic Item Creators", _showTitle: "title", _xMargin: 0, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, - _autoHeight: true, _width: 500, _height: 300, _fitWidth: true, _columnWidth: 35, ignoreClick: true, _lockedPosition: true, - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), system: true - })); - } else { - creatorBtns.forEach(nb => Doc.AddDocToList(doc.myItemCreators as Doc, "data", nb)); - } - return doc.myItemCreators as Doc; - } - - static async menuBtnDescriptions(doc: Doc) { - return [ - { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' }, - { title: "My Files", target: Cast(doc.myFilesystem, Doc, null), icon: "file", click: 'selectMainMenu(self)' }, - { title: "Tools", target: Cast(doc.myTools, Doc, null), icon: "wrench", click: 'selectMainMenu(self)' }, - { title: "Import", target: Cast(doc.myImportPanel, Doc, null), icon: "upload", click: 'selectMainMenu(self)' }, - { title: "Recently Closed", target: Cast(doc.myRecentlyClosedDocs, Doc, null), icon: "archive", click: 'selectMainMenu(self)' }, - { title: "Sharing", target: Cast(doc.mySharedDocs, Doc, null), icon: "users", click: 'selectMainMenu(self)', watchedDocuments: doc.mySharedDocs as Doc }, - // { title: "Filter", target: Cast(doc.currentFilter, Doc, null), icon: "filter", click: 'selectMainMenu(self)' }, - { title: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", click: 'selectMainMenu(self)' }, - // { title: "Help", target: undefined as any, icon: "question-circle", click: 'selectMainMenu(self)' }, - // { title: "Settings", target: undefined as any, icon: "cog", click: 'selectMainMenu(self)' }, - { title: "User Doc", target: Cast(doc.myUserDoc, Doc, null), icon: "address-card", click: 'selectMainMenu(self)' }, - ]; - } - - static setupSearchPanel(doc: Doc) { - if (doc.mySearchPanelDoc === undefined) { - doc.mySearchPanelDoc = new PrefetchProxy(Docs.Create.SearchDocument({ - _width: 500, _height: 300, backgroundColor: "dimGray", ignoreClick: true, _searchDoc: true, - childDropAction: "alias", _lockedPosition: true, _viewType: CollectionViewType.Schema, title: "sidebar search stack", system: true - })) as any as Doc; - } - } - static async setupMenuPanel(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { - if (doc.menuStack === undefined) { - await this.setupSharingSidebar(doc, sharingDocumentId, linkDatabaseId); // sets up the right sidebar collection for mobile upload documents and sharing - const menuBtns = (await CurrentUserUtils.menuBtnDescriptions(doc)).map(({ title, target, icon, click, watchedDocuments }) => - Docs.Create.FontIconDocument({ - icon, - btnType: ButtonType.MenuButton, - _stayInCollection: true, - _hideContextMenu: true, - system: true, - dontUndo: true, - title, - target, - _dropAction: "alias", - _removeDropProperties: new List(["dropAction", "_stayInCollection"]), - _width: 60, - _height: 60, - watchedDocuments, - onClick: ScriptField.MakeScript(click, { scriptContext: "any" }) - })); - // hack -- last button is assumed to be the userDoc - menuBtns[menuBtns.length - 1].hidden = ComputedField.MakeFunction("IsNoviceMode()"); - - doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument(menuBtns, { - title: "menuItemPanel", - childDropAction: "alias", - _chromeHidden: true, - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), - ignoreClick: true, - _gridGap: 0, - _yMargin: 0, - _yPadding: 0, _xMargin: 0, _autoHeight: false, _width: 60, _columnWidth: 60, _lockedPosition: true, system: true - })); - } - // this resets all sidebar buttons to being deactivated - PromiseValue(Cast(doc.menuStack, Doc)).then(stack => { - stack && PromiseValue(stack.data).then(btns => { - DocListCastAsync(btns).then(bts => bts?.forEach(btn => { - btn.dontUndo = true; - btn.system = true; - if (btn.title === "Catalog" || btn.title === "My Files") { // migration from Catalog to My Files - btn.target = Doc.UserDoc().myFilesystem; - btn.title = "My Files"; - } - })); - }); - }); - return doc.menuStack as Doc; - } - - - // Sets up mobile menu if it is undefined creates a new one, otherwise returns existing menu - static setupActiveMobileMenu(doc: Doc) { - if (doc.activeMobileMenu === undefined) { - doc.activeMobileMenu = this.setupMobileMenu(); - } - return doc.activeMobileMenu as Doc; - } - - // Sets up mobileMenu stacking document - static setupMobileMenu() { - const menu = new PrefetchProxy(Docs.Create.StackingDocument(this.setupMobileButtons(), { - _width: 980, ignoreClick: true, _lockedPosition: false, title: "home", _yMargin: 100, system: true, _chromeHidden: true, - })); - return menu; - } - - // SEts up mobile buttons for inside mobile menu - static setupMobileButtons(doc?: Doc, buttons?: string[]) { - const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, click?: string, activePen?: Doc, backgroundColor?: string, info: string, dragFactory?: Doc }[] = [ - { title: "DASHBOARDS", icon: "bars", click: 'switchToMobileLibrary()', backgroundColor: "lightgrey", info: "Access your Dashboards from your mobile, and navigate through all of your documents. " }, - { title: "UPLOAD", icon: "upload", click: 'openMobileUploads()', backgroundColor: "lightgrey", info: "Upload files from your mobile device so they can be accessed on Dash Web." }, - { title: "MOBILE UPLOAD", icon: "mobile", click: 'switchToMobileUploadCollection()', backgroundColor: "lightgrey", info: "Access the collection of your mobile uploads." }, - { title: "RECORD", icon: "microphone", click: 'openMobileAudio()', backgroundColor: "lightgrey", info: "Use your phone to record, dictate and then upload audio onto Dash Web." }, - { title: "PRESENTATION", icon: "desktop", click: 'switchToMobilePresentation()', backgroundColor: "lightgrey", info: "Use your phone as a remote for you presentation." }, - { title: "SETTINGS", icon: "cog", click: 'openMobileSettings()', backgroundColor: "lightgrey", info: "Change your password, log out, or manage your account security." } - ]; - // returns a list of mobile buttons - return docProtoData.filter(d => !buttons || !buttons.includes(d.title)).map(data => - this.mobileButton({ - title: data.title, - _lockedPosition: true, - onClick: data.click ? ScriptField.MakeScript(data.click) : undefined, - backgroundColor: data.backgroundColor, system: true - }, - [this.ficon({ ignoreClick: true, icon: data.icon, backgroundColor: "rgba(0,0,0,0)", btnType: ButtonType.ClickButton, system: true }), this.mobileTextContainer({}, [this.mobileButtonText({}, data.title), this.mobileButtonInfo({}, data.info)])]) - ); - } - - // sets up the main document for the mobile button - static mobileButton = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MulticolumnDocument(docs, { - ...opts, - _removeDropProperties: new List(["dropAction"]), _nativeWidth: 900, _nativeHeight: 250, _width: 900, _height: 250, _yMargin: 15, - borderRounding: "5px", boxShadow: "0 0", system: true - }) as any as Doc - - // sets up the text container for the information contained within the mobile button - static mobileTextContainer = (opts: DocumentOptions, docs: Doc[]) => Docs.Create.MultirowDocument(docs, { - ...opts, - _removeDropProperties: new List(["dropAction"]), _nativeWidth: 450, _nativeHeight: 250, _width: 450, _height: 250, _yMargin: 25, - backgroundColor: "rgba(0,0,0,0)", borderRounding: "0", boxShadow: "0 0", ignoreClick: true, system: true - }) as any as Doc - - // Sets up the title of the button - static mobileButtonText = (opts: DocumentOptions, buttonTitle: string) => Docs.Create.TextDocument(buttonTitle, { - ...opts, - title: buttonTitle, _fontSize: "37px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", system: true - }) as any as Doc - - // Sets up the description of the button - static mobileButtonInfo = (opts: DocumentOptions, buttonInfo: string) => Docs.Create.TextDocument(buttonInfo, { - ...opts, - title: "info", _fontSize: "25px", _xMargin: 0, _yMargin: 0, ignoreClick: true, backgroundColor: "rgba(0,0,0,0)", _dimMagnitude: 2, system: true - }) as any as Doc - - - static setupThumbButtons(doc: Doc) { - const docProtoData: { title: string, icon: string, drag?: string, ignoreClick?: boolean, pointerDown?: string, pointerUp?: string, clipboard?: Doc, backgroundColor?: string, dragFactory?: Doc }[] = [ - { title: "use pen", icon: "pen-nib", pointerUp: "resetPen()", pointerDown: 'setPen(2, this.backgroundColor)', backgroundColor: "blue" }, - { title: "use highlighter", icon: "highlighter", pointerUp: "resetPen()", pointerDown: 'setPen(20, this.backgroundColor)', backgroundColor: "yellow" }, - { title: "notepad", icon: "clipboard", pointerUp: "GestureOverlay.Instance.closeFloatingDoc()", pointerDown: 'GestureOverlay.Instance.openFloatingDoc(this.clipboard)', clipboard: Docs.Create.FreeformDocument([], { _width: 300, _height: 300, system: true }), backgroundColor: "orange" }, - { title: "interpret text", icon: "font", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('inktotext')", backgroundColor: "orange" }, - { title: "ignore gestures", icon: "signature", pointerUp: "setToolglass('none')", pointerDown: "setToolglass('ignoregesture')", backgroundColor: "green" }, - ]; - return docProtoData.map(data => Docs.Create.FontIconDocument({ - _nativeWidth: 10, _nativeHeight: 10, _width: 10, _height: 10, title: data.title, icon: data.icon, - _dropAction: data.pointerDown ? "copy" : undefined, ignoreClick: data.ignoreClick, - onDragStart: data.drag ? ScriptField.MakeFunction(data.drag) : undefined, - clipboard: data.clipboard, - onPointerUp: data.pointerUp ? ScriptField.MakeScript(data.pointerUp) : undefined, onPointerDown: data.pointerDown ? ScriptField.MakeScript(data.pointerDown) : undefined, - backgroundColor: data.backgroundColor, - _removeDropProperties: new List(["dropAction"]), dragFactory: data.dragFactory, system: true - })); - } - - static setupThumbDoc(userDoc: Doc) { - if (!userDoc.thumbDoc) { - const thumbDoc = Docs.Create.LinearDocument(CurrentUserUtils.setupThumbButtons(userDoc), { - _width: 100, _height: 50, ignoreClick: true, _lockedPosition: true, title: "buttons", - _autoHeight: true, _yMargin: 5, linearViewIsExpanded: true, backgroundColor: "white", system: true - }); - thumbDoc.inkToTextDoc = Docs.Create.LinearDocument([], { - _width: 300, _height: 25, _autoHeight: true, linearViewIsExpanded: true, flexDirection: "column", system: true - }); - userDoc.thumbDoc = thumbDoc; - } - return Cast(userDoc.thumbDoc, Doc); - } - - static setupMobileInkingDoc(userDoc: Doc) { - return Docs.Create.FreeformDocument([], { title: "Mobile Inking", backgroundColor: "white", system: true }); - } - - static setupMobileUploadDoc(userDoc: Doc) { - // const addButton = Docs.Create.FontIconDocument({ onDragStart: ScriptField.MakeScript('addWebToMobileUpload()'), title: "Add Web Doc to Upload Collection", icon: "plus", backgroundColor: "black" }) - const webDoc = Docs.Create.WebDocument("https://www.britannica.com/biography/Miles-Davis", { - title: "Upload Images From the Web", _lockedPosition: true, system: true - }); - const uploadDoc = Docs.Create.StackingDocument([], { - title: "Mobile Upload Collection", backgroundColor: "white", _lockedPosition: true, system: true, _chromeHidden: true, - }); - return Docs.Create.StackingDocument([webDoc, uploadDoc], { - _width: screen.width, _lockedPosition: true, title: "Upload", _autoHeight: true, _yMargin: 80, backgroundColor: "lightgray", system: true, _chromeHidden: true, - }); - } - - static setupLibrary(userDoc: Doc) { - return CurrentUserUtils.setupDashboards(userDoc); - } - - // setup the Creator button which will display the creator panel. This panel will include the drag creators and the color picker. - // when clicked, this panel will be displayed in the target container (ie, sidebarContainer) - static async setupToolsBtnPanel(doc: Doc) { - // setup a masonry view of all he creators - const creatorBtns = await CurrentUserUtils.setupCreatorButtons(doc); - const templateBtns = CurrentUserUtils.setupUserTemplateButtons(doc); - - doc["tabs-button-tools"] = undefined; - - if (doc.myCreators === undefined) { - doc.myCreators = new PrefetchProxy(Docs.Create.StackingDocument([creatorBtns, templateBtns], { - title: "all Creators", _yMargin: 0, _autoHeight: true, _xMargin: 0, _fitWidth: true, - _width: 500, _height: 300, ignoreClick: true, _lockedPosition: true, system: true, _chromeHidden: true, - })); - } - // setup a color picker - if (doc.myColorPicker === undefined) { - const color = Docs.Create.ColorDocument({ - title: "color picker", ignoreClick: true, _width: 220, _dropAction: "alias", _hideContextMenu: true, _stayInCollection: true, _forceActive: true, _removeDropProperties: new List(["dropAction", "_stayInCollection", "_hideContextMenu", "forceActive"]), system: true - }); - doc.myColorPicker = new PrefetchProxy(color); - } - - if (doc.myTools === undefined) { - const toolsStack = new PrefetchProxy(Docs.Create.StackingDocument([doc.myCreators as Doc, doc.myColorPicker as Doc], { - title: "My Tools", _showTitle: "title", _width: 500, _yMargin: 20, ignoreClick: true, _lockedPosition: true, _forceActive: true, - system: true, _stayInCollection: true, _hideContextMenu: true, _chromeHidden: true, boxShadow: "0 0", - })) as any as Doc; - - doc.myTools = toolsStack; - } - } - - static async setupDashboards(doc: Doc) { - // setup dashboards library item - await doc.myDashboards; - if (doc.myDashboards === undefined) { - doc.myDashboards = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "My Dashboards", _showTitle: "title", _height: 400, childHideLinkButton: true, - treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, ignoreClick: true, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true - })); - const newDashboard = ScriptField.MakeScript(`createNewDashboard(Doc.UserDoc())`); - (doc.myDashboards as any as Doc).contextMenuScripts = new List([newDashboard!]); - (doc.myDashboards as any as Doc).contextMenuLabels = new List(["Create New Dashboard"]); - } - return doc.myDashboards as any as Doc; - } - - static async setupPresentations(doc: Doc) { - await doc.myPresentations; - if (doc.myPresentations === undefined) { - doc.myPresentations = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "My Trails", _showTitle: "title", _height: 100, - treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, ignoreClick: true, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true - })); - const newPresentations = ScriptField.MakeScript(`createNewPresentation()`); - (doc.myPresentations as any as Doc).contextMenuScripts = new List([newPresentations!]); - (doc.myPresentations as any as Doc).contextMenuLabels = new List(["Create New Presentation"]); - const presentations = doc.myPresentations as any as Doc; - } - return doc.myPresentations as any as Doc; - } - - static async setupFilesystem(doc: Doc) { - await doc.myFilesystem; - if (doc.myFilesystem === undefined) { - doc.myFileOrphans = Docs.Create.TreeDocument([], { title: "Unfiled", _stayInCollection: true, system: true, isFolder: true }); - doc.myFileRoot = Docs.Create.TreeDocument([], { title: "file root", _stayInCollection: true, system: true, isFolder: true }); - doc.myFilesystem = new PrefetchProxy(Docs.Create.TreeDocument([doc.myFileRoot as Doc, doc.myFileOrphans as Doc], { - title: "My Documents", _showTitle: "title", _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 - })); - } - return doc.myFilesystem as any as Doc; - } - - static setupRecentlyClosedDocs(doc: Doc) { - // setup Recently Closed library item - if (doc.myRecentlyClosedDocs === undefined) { - doc.myRecentlyClosedDocs = new PrefetchProxy(Docs.Create.TreeDocument([], { - title: "Recently Closed", _showTitle: "title", treeViewShowClearButton: true, childHideLinkButton: true, - treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "alias", - treeViewTruncateTitleWidth: 150, ignoreClick: true, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true - })); - const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`); - (doc.myRecentlyClosedDocs as any as Doc).contextMenuScripts = new List([clearAll!]); - (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List(["Clear All"]); - } - } - static setupFilterDocs(doc: Doc) { - // setup Filter item - if (doc.currentFilter === undefined) { - doc.currentFilter = Docs.Create.FilterDocument({ - title: "unnamed filter", _height: 150, - treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, childDropAction: "none", - treeViewTruncateTitleWidth: 150, ignoreClick: true, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true, _autoHeight: true, _fitWidth: true - }); - const clearAll = ScriptField.MakeScript(`getProto(self).data = new List([])`); - (doc.currentFilter as Doc).contextMenuScripts = new List([clearAll!]); - (doc.currentFilter as Doc).contextMenuLabels = new List(["Clear All"]); - (doc.currentFilter as Doc).filterBoolean = "AND"; - } - } - - static setupUserDoc(doc: Doc) { - if (doc.myUserDoc === undefined) { - doc.treeViewOpen = true; - doc.treeViewExpandedView = "fields"; - doc.myUserDoc = new PrefetchProxy(Docs.Create.TreeDocument([doc], { - treeViewHideTitle: true, _xMargin: 5, _yMargin: 5, _gridGap: 5, _forceActive: true, title: "My UserDoc", _showTitle: "title", - treeViewTruncateTitleWidth: 150, ignoreClick: true, - _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true - })) as any as Doc; - } - } - - static setupSidebarContainer(doc: Doc) { - if (doc.sidebar === undefined) { - const sidebarContainer = new Doc(); - sidebarContainer.system = true; - doc.sidebar = new PrefetchProxy(sidebarContainer); - } - return doc.sidebar as Doc; - } - - // setup the list of sidebar mode buttons which determine what is displayed in the sidebar - static async setupSidebarButtons(doc: Doc) { - CurrentUserUtils.setupSidebarContainer(doc); - await CurrentUserUtils.setupToolsBtnPanel(doc); - CurrentUserUtils.setupImportSidebar(doc); - CurrentUserUtils.setupDashboards(doc); - CurrentUserUtils.setupPresentations(doc); - CurrentUserUtils.setupFilesystem(doc); - CurrentUserUtils.setupRecentlyClosedDocs(doc); - // CurrentUserUtils.setupFilterDocs(doc); - CurrentUserUtils.setupUserDoc(doc); - } - - static blist = (opts: DocumentOptions, docs: Doc[]) => new PrefetchProxy(Docs.Create.LinearDocument(docs, { - ...opts, _gridGap: 5, _xMargin: 5, _yMargin: 5, _width: 100, boxShadow: "0 0", _forceActive: true, - dropConverter: ScriptField.MakeScript("convertToButtons(dragData)", { dragData: DragManager.DocumentDragData.name }), - _lockedPosition: true, linearViewIsExpanded: true, system: true - })) as any as Doc - - static ficon = (opts: DocumentOptions) => new PrefetchProxy(Docs.Create.FontIconDocument({ - ...opts, _dropAction: "alias", _removeDropProperties: new List(["_dropAction", "stayInCollection"]), _nativeWidth: 40, _nativeHeight: 40, _width: 40, _height: 40, system: true - })) as any as Doc - - /// sets up the default list of buttons to be shown in the expanding button menu at the bottom of the Dash window - static setupDockedButtons(doc: Doc) { - if (doc["dockedBtn-undo"] === undefined) { - doc["dockedBtn-undo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("undo()"), btnType: ButtonType.ClickButton, dontUndo: true, _stayInCollection: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to undo", title: "Undo", icon: "undo-alt", system: true, canClick: 'canUndo' }); - } - if (doc["dockedBtn-redo"] === undefined) { - doc["dockedBtn-redo"] = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("redo()"), btnType: ButtonType.ClickButton, dontUndo: true, _stayInCollection: true, _dropAction: "alias", _hideContextMenu: true, _removeDropProperties: new List(["dropAction", "_hideContextMenu", "stayInCollection"]), toolTip: "Click to redo", title: "Redo", icon: "redo-alt", system: true, canClick: 'canRedo' }); - } - if (doc.dockedBtns === undefined) { - doc.dockedBtns = CurrentUserUtils.blist({ title: "docked buttons", ignoreClick: true, linearViewExpandable: true, _height: 42 }, [doc["dockedBtn-undo"] as Doc, doc["dockedBtn-redo"] as Doc]); - } - (doc["dockedBtn-undo"] as Doc).dontUndo = true; - (doc["dockedBtn-redo"] as Doc).dontUndo = true; - } - - static textTools(doc: Doc) { - return [ - { - title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, - list: ["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", - "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"], - scriptDoc: Doc.UserDoc(), toggle: 'userDoc._fontFamily' - }, - { - title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', - scriptDoc: Doc.UserDoc(), - toggle: 'userDoc._boldActive' - }, - { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', scriptDoc: Doc.UserDoc(), toggle: 'userDoc._italicsActive' }, - { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', scriptDoc: Doc.UserDoc(), toggle: 'userDoc._underlineActive' }, - // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()', toggle: 'userDoc._underlineActive' }, - // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()', toggle: 'userDoc._underlineActive' }, - // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()', toggle: 'userDoc._underlineActive' }, - { title: "Highlight", toolTip: "Highlight", btnType: ButtonType.ColorButton, icon: "highlighter", ignoreClick: true, scriptDoc: Doc.UserDoc(), userColorBtn: 'userDoc._highlightColor' }, - { title: "Text color", toolTip: "Text color", btnType: ButtonType.ColorButton, icon: "fill-drip", ignoreClick: true, scriptDoc: Doc.UserDoc(), userColorBtn: 'userDoc._textColor' }, - // { title: "Link", tooltip: "Link", btnType: ButtonType.DropdownButton, icon: "link", click: '', ignoreClick: true }, - ]; - } - - static async contextMenuBtnDescriptions(doc: Doc) { - return [ - // { title: "Perspective", tooltip: "Change document's perspective", type: "btn", btnType: ButtonType.DropdownButton, ignoreClick: true, icon: "desktop", click: '' }, - { - title: "Perspective", toolTip: "Perspective", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, - list: [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Tree, - CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Multicolumn, - CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel, - CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, - CollectionViewType.Grid], - scriptDoc: 'selectedDoc', - toggle: 'selectedDoc._viewType' - }, - { - title: "Background", toolTip: "Background", btnType: ButtonType.ColorButton, scriptDoc: 'selectedDoc', - docColorBtn: 'selectedDoc.backgroundColor', width: 60, ignoreClick: true, icon: "fill-drip", - canClick: 'numSelected > 0' - }, - { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', toggle: 'selectedDoc.z', canClick: 'numSelected > 0' }, - { title: "Text Tools", type: "TextMenu", icon: "font" }, - // { title: "Ink Tools", type: "LinearMenu", icon: "pen-nib" }, - // { title: "GFX Tools", type: "LinearMenu", icon: "shapes" }, - // { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy" }, - ]; - } - - // Default context menu buttons - static async setupContextMenuButtons(doc: Doc) { - const docList: Doc[] = []; - - const contextMenuBtns = (await CurrentUserUtils.contextMenuBtnDescriptions(doc)).map(({ title, width, toolTip, ignoreClick, icon, type, btnType, click, toggle, scriptDoc, canClick, docColorBtn }) => { - const textDocList: Doc[] = []; - if (type === "TextMenu") { - const textBtns = (CurrentUserUtils.textTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, btnType, click, toggle, scriptDoc, userColorBtn }) => { - textDocList.push(Docs.Create.FontIconDocument({ - _nativeWidth: width ? width : 25, - _nativeHeight: 25, - _width: width ? width : 25, - _height: 25, - icon, - toolTip, - userColorBtn, - // testToggle: toggle ? ScriptField.MakeScript(toggle, { this: scriptDoc, scriptContext: "any" }) : undefined, - // toggle: toggle, - btnType: btnType, - btnList: new List(list), - ignoreClick: ignoreClick, - _stayInCollection: true, - _hideContextMenu: true, - system: true, - dontUndo: true, - title, - backgroundColor: "black", - _dropAction: "alias", - _removeDropProperties: new List(["dropAction", "_stayInCollection"]), - onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined - })); - }); - docList.push(CurrentUserUtils.blist({ ignoreClick: true, linearViewExpandable: true, _height: 30, backgroundColor: "transparent" }, textDocList)); - } else { - docList.push(Docs.Create.FontIconDocument({ - _nativeWidth: width ? width : 30, - _nativeHeight: 30, - _width: width ? width : 30, - _height: 30, - icon, - toolTip, - // testToggle: toggle ? ScriptField.MakeScript(toggle, { scriptContext: "any" }) : undefined, - // toggle: toggle, - docColorBtn, - canClick: canClick, - btnType: btnType, - ignoreClick: ignoreClick, - _stayInCollection: true, - _hideContextMenu: true, - system: true, - dontUndo: true, - title, - backgroundColor: "black", - _dropAction: "alias", - _removeDropProperties: new List(["dropAction", "_stayInCollection"]), - onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined - })); - } - }); - - if (doc.contextMenuBtns === undefined) { - doc.contextMenuBtns = CurrentUserUtils.blist({ title: "menu buttons", ignoreClick: true, linearViewExpandable: false, _height: 35 }, docList); - } - } - - // sets up the default set of documents to be shown in the Overlay layer - static setupOverlays(doc: Doc) { - if (doc.myOverlayDocs === undefined) { - doc.myOverlayDocs = new PrefetchProxy(Docs.Create.FreeformDocument([], { title: "overlay documents", backgroundColor: "#aca3a6", system: true })); - } - } - - // the initial presentation Doc to use - static setupDefaultPresentation(doc: Doc) { - if (doc["template-presentation"] === undefined) { - doc["template-presentation"] = new PrefetchProxy(Docs.Create.PresElementBoxDocument({ - title: "pres element template", backgroundColor: "transparent", _xMargin: 5, _fitWidth: true, _height: 46, isTemplateDoc: true, isTemplateForField: "data", system: true - })); - } - } - - // Sharing sidebar is where shared documents are contained - static async setupSharingSidebar(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { - if (doc.myLinkDatabase === undefined) { - let linkDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(linkDatabaseId); - if (!linkDocs) { - linkDocs = new Doc(linkDatabaseId, true); - (linkDocs as Doc).author = Doc.CurrentUserEmail; - (linkDocs as Doc).data = new List([]); - (linkDocs as Doc)["acl-Public"] = SharingPermissions.Add; - } - doc.myLinkDatabase = new PrefetchProxy(linkDocs); - } - if (doc.mySharedDocs === undefined) { - let sharedDocs = Docs.newAccount ? undefined : await DocServer.GetRefField(sharingDocumentId + "outer"); - if (!sharedDocs) { - sharedDocs = Docs.Create.StackingDocument([], { - title: "My SharedDocs", childDropAction: "alias", system: true, contentPointerEvents: "none", childLimitHeight: 0, _yMargin: 50, _gridGap: 15, - _showTitle: "title", ignoreClick: true, _lockedPosition: true, "acl-Public": SharingPermissions.Add, "_acl-Public": SharingPermissions.Add, - _chromeHidden: true, boxShadow: "0 0", - }, sharingDocumentId + "outer", sharingDocumentId); - (sharedDocs as Doc)["acl-Public"] = (sharedDocs as Doc)[DataSym]["acl-Public"] = SharingPermissions.Add; - } - if (sharedDocs instanceof Doc) { - Doc.GetProto(sharedDocs).userColor = sharedDocs.userColor || "rgb(202, 202, 202)"; - } - doc.mySharedDocs = new PrefetchProxy(sharedDocs); - } - } - - // Import sidebar is where shared documents are contained - static setupImportSidebar(doc: Doc) { - if (doc.myImportDocs === undefined) { - doc.myImportDocs = new PrefetchProxy(Docs.Create.StackingDocument([], { - title: "My ImportDocuments", _forceActive: true, ignoreClick: true, _stayInCollection: true, _hideContextMenu: true, childLimitHeight: 0, - childDropAction: "alias", _autoHeight: true, _yMargin: 50, _gridGap: 15, _lockedPosition: true, system: true, _chromeHidden: true, - })); - } - if (doc.myImportPanel === undefined) { - const uploads = Cast(doc.myImportDocs, Doc, null); - const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), btnType: ButtonType.ClickButton, toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", icon: "upload", system: true }); - doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, _showTitle: "title", ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true, boxShadow: "0 0" })); - } - } - - static setupClickEditorTemplates(doc: Doc) { - if (doc["clickFuncs-child"] === undefined) { - // to use this function, select it from the context menu of a collection. then edit the onChildClick script. Add two Doc variables: 'target' and 'thisContainer', then assign 'target' to some target collection. After that, clicking on any document in the initial collection will open it in the target - const openInTarget = Docs.Create.ScriptingDocument(ScriptField.MakeScript( - "docCast(thisContainer.target).then((target) => target && (target.proto.data = new List([self]))) ", - { thisContainer: Doc.name }), { - title: "Click to open in target", _width: 300, _height: 200, - targetScriptKey: "onChildClick", system: true - }); - - const openDetail = Docs.Create.ScriptingDocument(ScriptField.MakeScript( - "openOnRight(self.doubleClickView)", - {}), { title: "Double click to open doubleClickView", _width: 300, _height: 200, targetScriptKey: "onChildDoubleClick", system: true }); - - doc["clickFuncs-child"] = Docs.Create.TreeDocument([openInTarget, openDetail], { title: "on Child Click function templates", system: true }); - } - // this is equivalent to using PrefetchProxies to make sure all the childClickFuncs have been retrieved. - PromiseValue(Cast(doc["clickFuncs-child"], Doc)).then(func => func && PromiseValue(func.data).then(DocListCast)); - - if (doc.clickFuncs === undefined) { - const onClick = Docs.Create.ScriptingDocument(undefined, { - title: "onClick", "onClick-rawScript": "console.log('click')", - isTemplateDoc: true, isTemplateForField: "onClick", _width: 300, _height: 200, system: true - }, "onClick"); - const onChildClick = Docs.Create.ScriptingDocument(undefined, { - title: "onChildClick", "onChildClick-rawScript": "console.log('child click')", - isTemplateDoc: true, isTemplateForField: "onChildClick", _width: 300, _height: 200, system: true - }, "onChildClick"); - const onDoubleClick = Docs.Create.ScriptingDocument(undefined, { - title: "onDoubleClick", "onDoubleClick-rawScript": "console.log('double click')", - isTemplateDoc: true, isTemplateForField: "onDoubleClick", _width: 300, _height: 200, system: true - }, "onDoubleClick"); - const onChildDoubleClick = Docs.Create.ScriptingDocument(undefined, { - title: "onChildDoubleClick", "onChildDoubleClick-rawScript": "console.log('child double click')", - isTemplateDoc: true, isTemplateForField: "onChildDoubleClick", _width: 300, _height: 200, system: true - }, "onChildDoubleClick"); - const onCheckedClick = Docs.Create.ScriptingDocument(undefined, { - title: "onCheckedClick", "onCheckedClick-rawScript": "console.log(heading + checked + containingTreeView)", - "onCheckedClick-params": new List(["heading", "checked", "containingTreeView"]), isTemplateDoc: true, - isTemplateForField: "onCheckedClick", _width: 300, _height: 200, system: true - }, "onCheckedClick"); - doc.clickFuncs = Docs.Create.TreeDocument([onClick, onChildClick, onDoubleClick, onCheckedClick], { title: "onClick funcs", system: true }); - } - PromiseValue(Cast(doc.clickFuncs, Doc)).then(func => func && PromiseValue(func.data).then(DocListCast)); - - return doc.clickFuncs as Doc; - } - - static async updateUserDocument(doc: Doc, sharingDocumentId: string, linkDatabaseId: string) { - if (!doc.globalGroupDatabase) doc.globalGroupDatabase = Docs.Prototypes.MainGroupDocument(); - const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data); - reaction(() => DateCast((doc.globalGroupDatabase as Doc)["data-lastModified"]), - async () => { - const groups = await DocListCastAsync((doc.globalGroupDatabase as Doc).data); - const mygroups = groups?.filter(group => JSON.parse(StrCast(group.members)).includes(Doc.CurrentUserEmail)) || []; - SnappingManager.SetCachedGroups(["Public", ...mygroups?.map(g => StrCast(g.title))]); - }, { fireImmediately: true }); - // Document properties on load - doc.system = true; - doc.noviceMode = doc.noviceMode === undefined ? "true" : doc.noviceMode; - doc.title = Doc.CurrentUserEmail; - doc._raiseWhenDragged = true; - doc._showLabel = false; - doc._showMenuLabel = true; - doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)"); - doc.activeInkWidth = StrCast(doc.activeInkWidth, "1"); - doc.activeInkBezier = StrCast(doc.activeInkBezier, "0"); - doc.activeFillColor = StrCast(doc.activeFillColor, ""); - doc.activeArrowStart = StrCast(doc.activeArrowStart, ""); - doc.activeArrowEnd = StrCast(doc.activeArrowEnd, ""); - doc.activeDash = StrCast(doc.activeDash, "0"); - doc.fontSize = StrCast(doc.fontSize, "12px"); - doc.fontFamily = StrCast(doc.fontFamily, "Arial"); - doc.fontColor = StrCast(doc.fontColor, "black"); - doc.fontHighlight = StrCast(doc.fontHighlight, ""); - doc.defaultAclPrivate = BoolCast(doc.defaultAclPrivate, true); - doc.activeCollectionBackground = StrCast(doc.activeCollectionBackground, "white"); - doc.activeCollectionNestedBackground = Cast(doc.activeCollectionNestedBackground, "string", null); - doc.noviceMode = BoolCast(doc.noviceMode, true); - doc["constants-snapThreshold"] = NumCast(doc["constants-snapThreshold"], 10); // - doc["constants-dragThreshold"] = NumCast(doc["constants-dragThreshold"], 4); // - Utils.DRAG_THRESHOLD = NumCast(doc["constants-dragThreshold"]); - doc.savedFilters = new List(); - doc.filterDocCount = 0; - this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon - this.setupDocTemplates(doc); // sets up the template menu of templates - this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile - this.setupSearchPanel(doc); - this.setupOverlays(doc); // documents in overlay layer - this.setupDockedButtons(doc); // the bottom bar of font icons - this.setupContextMenuButtons(doc); //buttons for context menu - await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels - await this.setupMenuPanel(doc, sharingDocumentId, linkDatabaseId); - if (!doc.globalScriptDatabase) doc.globalScriptDatabase = Docs.Prototypes.MainScriptDocument(); - - setTimeout(() => this.setupDefaultPresentation(doc), 0); // presentation that's initially triggered - - // setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet - // doc["dockedBtn-undo"] && reaction(() => UndoManager.undoStack.slice(), () => Doc.GetProto(doc["dockedBtn-undo"] as Doc).opacity = UndoManager.CanUndo() ? 1 : 0.4, { fireImmediately: true }); - // doc["dockedBtn-redo"] && reaction(() => UndoManager.redoStack.slice(), () => Doc.GetProto(doc["dockedBtn-redo"] as Doc).opacity = UndoManager.CanRedo() ? 1 : 0.4, { fireImmediately: true }); - - // uncomment this to setup a default note style that uses the custom header layout - // PromiseValue(doc.emptyHeader).then(factory => { - // if (Cast(doc.defaultTextLayout, Doc, null)?.version !== headerViewVersion) { - // const deleg = Doc.delegateDragFactory(factory as Doc); - // deleg.title = "header"; - // doc.defaultTextLayout = new PrefetchProxy(deleg); - // Doc.AddDocToList(Cast(doc["template-notes"], Doc, null), "data", deleg); - // } - // }); - setTimeout(() => DocServer.UPDATE_SERVER_CACHE(), 2500); - doc.fieldInfos = await Docs.setupFieldInfos(); - return doc; - } - - public static async loadCurrentUser() { - return rp.get(Utils.prepend("/getCurrentUser")).then(async response => { - if (response) { - const result: { id: string, email: string, cacheDocumentIds: string } = JSON.parse(response); - Doc.CurrentUserEmail = result.email; - resolvedPorts = JSON.parse(await Networking.FetchFromServer("/resolvedPorts")); - DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, result.email); - result.cacheDocumentIds && (await DocServer.GetRefFields(result.cacheDocumentIds.split(";"))); - return result; - } else { - throw new Error("There should be a user! Why does Dash think there isn't one?"); - } - }); - } - - public static async loadUserDocument(id: string) { - this.curr_id = id; - await rp.get(Utils.prepend("/getUserDocumentIds")).then(ids => { - const { userDocumentId, sharingDocumentId, linkDatabaseId } = JSON.parse(ids); - if (userDocumentId !== "guest") { - return DocServer.GetRefField(userDocumentId).then(async field => { - Docs.newAccount = !(field instanceof Doc); - await Docs.Prototypes.initialize(); - const userDoc = Docs.newAccount ? new Doc(userDocumentId, true) : field as Doc; - const updated = this.updateUserDocument(Doc.SetUserDoc(userDoc), sharingDocumentId, linkDatabaseId); - (await DocListCastAsync(Cast(Doc.UserDoc().myLinkDatabase, Doc, null)?.data))?.forEach(async link => { // make sure anchors are loaded to avoid incremental updates to computedFn's in LinkManager - const a1 = await Cast(link?.anchor1, Doc, null); - const a2 = await Cast(link?.anchor2, Doc, null); - }); - return updated; - }); - } else { - throw new Error("There should be a user id! Why does Dash think there isn't one?"); - } - }); - } - - public static _urlState: HistoryUtil.DocUrl; - - public static openDashboard = (userDoc: Doc, doc: Doc, fromHistory = false) => { - CurrentUserUtils.MainDocId = doc[Id]; - - if (doc) { // this has the side-effect of setting the main container since we're assigning the active/guest dashboard - !("presentationView" in doc) && (doc.presentationView = new List([Docs.Create.TreeDocument([], { title: "Presentation" })])); - userDoc ? (userDoc.activeDashboard = doc) : (CurrentUserUtils.GuestDashboard = doc); - } - const state = CurrentUserUtils._urlState; - if (state.sharing === true && !userDoc) { - DocServer.Control.makeReadOnly(); - } else { - fromHistory || HistoryUtil.pushState({ - type: "doc", - docId: doc[Id], - readonly: state.readonly, - nro: state.nro, - sharing: false, - }); - if (state.readonly === true || state.readonly === null) { - DocServer.Control.makeReadOnly(); - } else if (state.safe) { - if (!state.nro) { - DocServer.Control.makeReadOnly(); - } - CollectionView.SetSafeMode(true); - } else if (state.nro || state.nro === null || state.readonly === false) { - } else if (doc.readOnly) { - DocServer.Control.makeReadOnly(); - } else { - DocServer.Control.makeEditable(); - } - } - - return true; - } - - public static importDocument = () => { - const input = document.createElement("input"); - input.type = "file"; - input.multiple = true; - input.accept = ".zip, application/pdf, video/*, image/*, audio/*"; - input.onchange = async _e => { - const upload = Utils.prepend("/uploadDoc"); - const formData = new FormData(); - const file = input.files && input.files[0]; - if (file && file.type === 'application/zip') { - formData.append('file', file); - formData.append('remap', "true"); - const response = await fetch(upload, { method: "POST", body: formData }); - const json = await response.json(); - if (json !== "error") { - const doc = Docs.newAccount ? undefined : await DocServer.GetRefField(json); - if (doc instanceof Doc) { - setTimeout(() => SearchUtil.Search(`{!join from=id to=proto_i}id:link*`, true, {}).then(docs => - docs.docs.forEach(d => LinkManager.Instance.addLink(d))), 2000); // need to give solr some time to update so that this query will find any link docs we've added. - } - } - } else if (input.files && input.files.length !== 0) { - const importDocs = Cast(Doc.UserDoc().myImportDocs, Doc, null); - const disposer = OverlayView.ShowSpinner(); - DocListCastAsync(importDocs.data).then(async list => { - const results = await DocUtils.uploadFilesToDocs(Array.from(input.files || []), {}); - if (results.length !== input.files?.length) { - alert("Error uploading files - possibly due to unsupported file types"); - } - list?.splice(0, 0, ...results); - disposer(); - }); - } else { - console.log("No file selected"); - } - }; - input.click(); - } - - public static async snapshotDashboard(userDoc: Doc) { - const copy = await CollectionDockingView.Copy(CurrentUserUtils.ActiveDashboard); - Doc.AddDocToList(Cast(userDoc.myDashboards, Doc, null), "data", copy); - CurrentUserUtils.openDashboard(userDoc, copy); - } - - public static createNewDashboard = async (userDoc: Doc, id?: string) => { - const myPresentations = await userDoc.myPresentations as Doc; - const presentation = Doc.MakeCopy(userDoc.emptyPresentation as Doc, true); - const dashboards = await Cast(userDoc.myDashboards, Doc) as Doc; - const dashboardCount = DocListCast(dashboards.data).length + 1; - const emptyPane = Cast(userDoc.emptyPane, Doc, null); - emptyPane["dragFactory-count"] = NumCast(emptyPane["dragFactory-count"]) + 1; - const freeformOptions: DocumentOptions = { - x: 0, - y: 400, - _width: 1500, - _height: 1000, - _fitWidth: true, - title: `Untitled Tab ${NumCast(emptyPane["dragFactory-count"])}`, - }; - const freeformDoc = CurrentUserUtils.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); - const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: `Dashboard ${dashboardCount}` }, id, "row"); - Doc.AddDocToList(myPresentations, "data", presentation); - userDoc.activePresentation = presentation; - const toggleTheme = ScriptField.MakeScript(`Doc.UserDoc().darkScheme = !Doc.UserDoc().darkScheme`); - const toggleComic = ScriptField.MakeScript(`toggleComicMode()`); - const snapshotDashboard = ScriptField.MakeScript(`snapshotDashboard()`); - const createDashboard = ScriptField.MakeScript(`createNewDashboard()`); - dashboardDoc.contextMenuScripts = new List([toggleTheme!, toggleComic!, snapshotDashboard!, createDashboard!]); - dashboardDoc.contextMenuLabels = new List(["Toggle Theme Colors", "Toggle Comic Mode", "Snapshot Dashboard", "Create Dashboard"]); - - Doc.AddDocToList(dashboards, "data", dashboardDoc); - CurrentUserUtils.openDashboard(userDoc, dashboardDoc); - } - - public static GetNewTextDoc(title: string, x: number, y: number, width?: number, height?: number, noMargins?: boolean, annotationOn?: Doc, maxHeight?: number) { - const tbox = Docs.Create.TextDocument("", { - _xMargin: noMargins ? 0 : undefined, _yMargin: noMargins ? 0 : undefined, annotationOn, docMaxAutoHeight: maxHeight, - _width: width || 200, _height: height || 100, x: x, y: y, _fitWidth: true, _autoHeight: true, _fontSize: StrCast(Doc.UserDoc().fontSize), - _fontFamily: StrCast(Doc.UserDoc().fontFamily), title - }); - const template = Doc.UserDoc().defaultTextLayout; - if (template instanceof Doc) { - tbox._width = NumCast(template._width); - tbox.layoutKey = "layout_" + StrCast(template.title); - Doc.GetProto(tbox)[StrCast(tbox.layoutKey)] = template; - } - return tbox; - } - - public static get MySearchPanelDoc() { return Cast(Doc.UserDoc().mySearchPanelDoc, Doc, null); } - public static get ActiveDashboard() { return Cast(Doc.UserDoc().activeDashboard, Doc, null); } - public static get ActivePresentation() { return Cast(Doc.UserDoc().activePresentation, Doc, null); } - public static get MyRecentlyClosed() { return Cast(Doc.UserDoc().myRecentlyClosedDocs, Doc, null); } - public static get MyDashboards() { return Cast(Doc.UserDoc().myDashboards, Doc, null); } - public static get EmptyPane() { return Cast(Doc.UserDoc().emptyPane, Doc, null); } - public static get OverlayDocs() { return DocListCast((Doc.UserDoc().myOverlayDocs as Doc)?.data); } - public static set SelectedTool(tool: InkTool) { Doc.UserDoc().activeInkTool = tool; } - @computed public static get SelectedTool(): InkTool { return StrCast(Doc.UserDoc().activeInkTool, InkTool.None) as InkTool; } -} - -Scripting.addGlobal(function openDragFactory(dragFactory: Doc) { - const copy = Doc.copyDragFactory(dragFactory); - if (copy) { - CollectionDockingView.AddSplit(copy, "right"); - const view = DocumentManager.Instance.getFirstDocumentView(copy); - view && SelectionManager.SelectView(view, false); - } -}); -Scripting.addGlobal(function IsNoviceMode() { return Doc.UserDoc().noviceMode; }, - "is Dash in novice mode"); -Scripting.addGlobal(function snapshotDashboard() { CurrentUserUtils.snapshotDashboard(Doc.UserDoc()); }, - "creates a snapshot copy of a dashboard"); -Scripting.addGlobal(function createNewDashboard() { return CurrentUserUtils.createNewDashboard(Doc.UserDoc()); }, - "creates a new dashboard when called"); -Scripting.addGlobal(function createNewPresentation() { return MainView.Instance.createNewPresentation(); }, - "creates a new presentation when called"); -Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, - "returns all the links to the document or its annotations", "(doc: any)"); -Scripting.addGlobal(function importDocument() { return CurrentUserUtils.importDocument(); }, - "imports files from device directly into the import sidebar"); \ No newline at end of file diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 5f09a322c..5640e5132 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -27,6 +27,7 @@ import React = require("react"); import { PresBox } from './nodes/trails/PresBox'; import { undoBatch } from '../util/UndoManager'; import { CollectionViewType } from './collections/CollectionView'; +import { Colors } from './global/globalEnums'; const higflyout = require("@hig/flyout"); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -187,9 +188,9 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV get followLinkButton() { const targetDoc = this.view0?.props.Document; return !targetDoc ? (null) : {"follow primary link on click"}
}> +
{"Set onClick to follow primary link"}
}>
this.props.views().map(view => view?.docView?.toggleFollowLink(undefined, false, false)))}>
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 55af4650f..1f36e94cf 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -1284,3 +1284,4 @@ Scripting.addGlobal(function gotoFrame(doc: any, newFrame: any) { CollectionFreeFormDocumentView.updateKeyframe(childDocs, currentFrame || 0); doc._currentFrame = newFrame === undefined ? 0 : Math.max(0, newFrame); }); + diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss index db39e304b..9c766e03f 100644 --- a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss @@ -6,11 +6,12 @@ height: 100%; pointer-events: none; // background-color: rgba(0, 0, 0, 0.2); - border-radius: 5px; - padding-left: 5px; - padding-right: 5px; - border-left: $standard-border; - border-right: $standard-border; + + &.true { + padding-left: 5px; + padding-right: 5px; + border-left: $standard-border; + } .collectionLinearView { display: flex; diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx index e7970758a..a8846fd45 100644 --- a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx @@ -119,7 +119,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {

{BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : "–" : icon ? icon : "+"}

; - return
+ return
{!expandable ? (null) :
{BoolCast(this.props.Document.linearViewIsExpanded) ? "Close menu" : "Open menu"}
} placement="top"> {menuOpener} diff --git a/src/client/views/nodes/button/ButtonScripts.ts b/src/client/views/nodes/button/ButtonScripts.ts new file mode 100644 index 000000000..bb4dd8bc9 --- /dev/null +++ b/src/client/views/nodes/button/ButtonScripts.ts @@ -0,0 +1,14 @@ +import { Scripting } from "../../../util/Scripting"; +import { SelectionManager } from "../../../util/SelectionManager"; + +// toggle: Set overlay status of selected document +Scripting.addGlobal(function changeView(view: string) { + const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; + selected ? selected.Document._viewType = view : console.log("[FontIconBox.tsx] changeView failed"); +}); + +// toggle: Set overlay status of selected document +Scripting.addGlobal(function toggleOverlay() { + const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; + selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("failed"); +}); \ No newline at end of file diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss index 0c866988d..72fab74d9 100644 --- a/src/client/views/nodes/button/FontIconBox.scss +++ b/src/client/views/nodes/button/FontIconBox.scss @@ -43,6 +43,11 @@ &.tglBtn { cursor: pointer; + + svg { + width: 50% !important; + height: 50%; + } } &.toolBtn { @@ -99,6 +104,8 @@ overflow: hidden; cursor: pointer; background: transparent; + align-content: center; + align-items: center; .menuButton-dropdownList { position: absolute; @@ -145,6 +152,39 @@ } } + .menuButton-dropdown { + display: flex; + justify-content: center; + align-items: center; + font-size: 15px; + /* background-color: #b9b9b9; */ + grid-column: 2; + border-radius: 0px 7px 7px 0px; + /* position: absolute; */ + width: 13px; + height: 100%; + right: 0; + } + + .menuButton-dropdown-header{ + width: 100%; + font-weight: 300; + overflow:hidden; + font-size: 12px; + white-space: nowrap; + text-overflow: ellipsis; + } + + .dropbox-background { + width: 100vw; + height: 100vh; + top: 0; + z-index: 20; + left: 0; + background:transparent; + position: fixed; + } + } @@ -186,31 +226,11 @@ // position: absolute; // } -// .menuButton-dropdown { -// display: flex; -// justify-content: center; -// align-items: center; -// font-size: 15px; -// /* background-color: #b9b9b9; */ -// grid-column: 2; -// border-radius: 0px 7px 7px 0px; -// /* position: absolute; */ -// width: 13px; -// height: 100%; -// right: 0; -// } + // &:hover { // background-color: $light-gray; // } -// .dropbox-background { -// width: 100vw; -// height: 100vh; -// top: 0; -// z-index: 20; -// left: 0; -// background:transparent; -// position: fixed; -// } + // } \ No newline at end of file diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 9e7608dc3..9a54579dc 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -56,15 +56,12 @@ export class FontIconBox extends DocComponent(Fon } } - - // 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 dropdownDirection: string = StrCast(this.rootDoc.dropDownDirection); @observable private buttonList: string[] = StrListCast(this.rootDoc.btnList); - @observable private activeFont: string = StrCast(Doc.UserDoc()._fontFamily); @observable private type = StrCast(this.rootDoc.btnType); /** @@ -106,18 +103,185 @@ export class FontIconBox extends DocComponent(Fon ); } - @undoBatch setColor = action((color: ColorState, docColor?: string, userColor?: string) => { - console.log(docColor, userColor); - if (docColor) { - const numSelected = SelectionManager.Views().length; - const selectedDoc = numSelected > 0 ? SelectionManager.Views()[0].Document : undefined; - eval(docColor); + /** + * Dropdown button + */ + @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: string = StrCast(this.rootDoc.script); + + let noviceList: string[] = []; + let text:string | undefined; + let dropdown = true; + let show = true; + let icon: IconProp = "caret-down"; + + + if (script == 'changeView'){ + const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; + if (selected && StrCast(selected.Document.type) == DocumentType.COL){ + text = StrCast(selected.Document._viewType); + } else if (selected) { + dropdown = false; + text = StrCast(selected.Document.type); + icon = Doc.toIcon(selected.Document); + } + noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking]; + } else if (script == 'changeFont') { + const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; + if (selected && StrCast(selected.Document.type) == DocumentType.RTF){ + text = StrCast(selected.Document._fontFamily); + } else { + text = StrCast(Doc.UserDoc()._fontFamily); + } + noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia", + "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]; + } else { + show = false; } - else if (userColor) { - const userDoc = Doc.UserDoc(); - eval(userColor); + + const items = this.buttonList.map((value) => { + // console.log(value); + if (Doc.UserDoc().noviceMode && !noviceList.includes(value)){ + return; + } + const click = () => { + const s = ScriptField.MakeScript(script+'("'+value+'")'); + if (s){ + // console.log(s.script); + s.script.run().result; + } + } + return
+ {value[0].toUpperCase() + value.slice(1)} +
; + }); + + const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : +
+ {this.label} +
; + + return ( +
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}> +
+ {text && text[0].toUpperCase() + text.slice(1)} +
+ {label} +
+ +
+ {this.rootDoc.dropDownOpen ? +
+
+ {items} +
+
{ e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} /> +
+ : null} +
+ ); + } + + + @computed get rangeButton() { + return ( +
+ +
+ ) + } + + /** + * Colour button + */ + @computed get colorButton() { + 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 numSelected = SelectionManager.Views().length; + const selectedDoc = numSelected > 0 ? SelectionManager.Views()[0].Document : undefined; + const colorBox = (func: (color: ColorState) => void) => ; + const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : +
+ {this.label} +
; + const dropdownCaret =
+ +
; + const script: string = StrCast(this.rootDoc.script); + const click = (value: ColorState) => { + const hex: string = value.hex; + const s = ScriptField.MakeScript(script+'("'+hex+'")'); + if (s){ + // console.log(s.script); + s.script.run().result; + } } - }); + return ( +
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen} + onPointerDown={e => e.stopPropagation()}> + + {label} + {dropdownCaret} + {this.rootDoc.dropDownOpen ? +
+
e.stopPropagation()} + onClick={e => e.stopPropagation()} + style={{ left: 0 }}> + {colorBox((color) => click(color))} +
+
{ e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} /> +
+ : null} +
+ ); + } + + @computed get toggleButton() { + const numSelected = SelectionManager.Views().length; + const selectedDoc = numSelected > 0 ? SelectionManager.Views()[0].Document : undefined; + + const script: string = StrCast(this.rootDoc.script)+"(true)"; + let toggleTrue: boolean | undefined = false; + if (script == 'toggleOverlay'){ + toggleTrue = selectedDoc && BoolCast(selectedDoc.z); + console.log('toggleOverlay'); + } + const boolResult = ScriptField.MakeScript(script)?.script.run().result; + // console.log(this.rootDoc.title, script, boolResult); + const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); + // const canClick: boolean = this.rootDoc.canClick ? eval(StrCast(this.rootDoc.canClick)) : false; + const label = !this.label || !Doc.UserDoc()._showLabel ? (null) : +
+ {this.label} +
; + return ( +
+ + {label} +
+ ) + } @@ -151,13 +315,7 @@ export class FontIconBox extends DocComponent(Fon const selectedDoc = numSelected > 0 ? SelectionManager.Views()[0].Document : undefined; const userDoc = Doc.UserDoc(); - // Toggle and canClick properties as determined from the variable passed into the button doc - const toggle = this.rootDoc.toggle ? ScriptCast(this.rootDoc.toggle) : undefined; - const canClick: boolean = this.rootDoc.canClick ? eval(StrCast(this.rootDoc.canClick)) : false; - // if (toggle) { - // console.log(StrCast(this.rootDoc.title), toggle); - // toggle.script.run(); - // } + const dark: boolean = Doc.UserDoc().colorScheme === ColorScheme.Dark; const active: string = StrCast(this.rootDoc.dropDownOpen); @@ -169,111 +327,34 @@ export class FontIconBox extends DocComponent(Fon
{this.label}
; - const dropdownCaret =
- -
; - const colorBox = (func: (color: ColorState) => void) => ; - const items = this.buttonList.map((value) => { - return
Doc.UserDoc()._fontFamily = value}> - {value} -
; - }); - - /** - * Menu Panel Button: menuBtn - * Dropdown Button: dropDownBtn - * doubleBtn - **/ - - // CODEDUMP: glr - // const presSize = type === ButtonType.MenuButton ? 30 : 25; - // const presTrailsIcon = ; - + // TODO:glr Add label of button type - let button = ( - <> - {this.defaultButton} - - ); + let button = this.defaultButton; switch (this.type) { case ButtonType.DropdownButton: - button = ( - <> - {this.dropdownButton} - - ); + button = this.dropdownButton; break; case ButtonType.DropdownList: - button = ( -
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen}> - {toggle} - {label} - {dropdownCaret} - {this.rootDoc.dropDownOpen ? - <> -
- {items} -
-
{ e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} /> - - : null} -
- ); + button = this.dropdownListButton; break; case ButtonType.ColorButton: - button = ( -
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen} - onPointerDown={e => e.stopPropagation()}> - - {label} - {dropdownCaret} - {this.rootDoc.dropDownOpen ? - <> -
e.stopPropagation()} - onClick={e => e.stopPropagation()} - style={{ left: 0 }}> - {colorBox((color) => this.setColor(color, StrCast(this.rootDoc.docColor), StrCast(this.rootDoc.userColor)))} -
-
{ e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} /> - - : null} -
- ); + button = this.colorButton; break; case ButtonType.ToolButton: button = ( -
+
{label}
); break; case ButtonType.ToggleButton: - button = ( -
- - {label} -
- ); + button = this.toggleButton; break; case ButtonType.ClickButton: button = ( -
+
{label}
@@ -299,9 +380,91 @@ export class FontIconBox extends DocComponent(Fon break; } - return !this.layoutDoc.toolTip ? button : + return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton ? button : {StrCast(this.layoutDoc.toolTip)}
}> {button} ; } } + +// SCRIPTING BUTTONS + +import { Scripting } from "../../../util/Scripting"; +import { CollectionViewType } from '../../collections/CollectionView'; +import { DocumentType } from '../../../documents/DocumentTypes'; +import { Colors } from '../../global/globalEnums'; +import { IconProp } from '@fortawesome/fontawesome-svg-core'; + +// toggle: Set overlay status of selected document +Scripting.addGlobal(function changeView(view: string) { + const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; + selected ? selected.Document._viewType = view : console.log("[FontIconBox.tsx] changeView failed"); +}); + +// toggle: Set overlay status of selected document +Scripting.addGlobal(function changeBackgroundColor(color?: string, checkResult?: boolean) { + const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; + if (checkResult){ + return selected && selected.Document._backgroundColor; + } + selected ? selected.Document._backgroundColor = color : console.log("[FontIconBox.tsx] changeBackgroundColor failed"); +}); + +// toggle: Set overlay status of selected document +Scripting.addGlobal(function toggleOverlay(checkResult?:boolean) { + const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; + if (checkResult){ + return selected && selected.Document.z == 1; + } + selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed"); +}); + +// toggle: Set overlay status of selected document +Scripting.addGlobal(function changeFont(font: string) { + // TODO: glr check if font selected and change selected font + SelectionManager.Views().map(dv => dv.props.Document._fontFamily = font); + console.log(font); + Doc.UserDoc()._fontFamily = font; + return Doc.UserDoc()._fontFamily; +}); + +// toggle: Set overlay status of selected document +Scripting.addGlobal(function changeFontColor(color: string) { + // TODO: glr check if font selected and change selected font + console.log(color); + Doc.UserDoc()._fontColor = color; +}); + +// toggle: Set overlay status of selected document +Scripting.addGlobal(function changeFontSize(size: string) { + // TODO: glr check if font selected and change selected font + console.log(size); + Doc.UserDoc()._fontSize = size; +}); + +Scripting.addGlobal(function toggleBold(checkResult?:boolean) { + if(checkResult) { + console.log("got here"); + return Doc.UserDoc().bold; + } + // TODO: glr check if font selected and change selected font + SelectionManager.Views().map(dv => dv.props.Document.bold = !dv.props.Document.bold); + Doc.UserDoc().bold = !Doc.UserDoc().bold; + return Doc.UserDoc().bold; +}); + +Scripting.addGlobal(function toggleUnderline(checkResult?:boolean) { + if(checkResult) return Doc.UserDoc().underline; + // TODO: glr check if font selected and change selected font + SelectionManager.Views().map(dv => dv.props.Document.underline = !dv.props.Document.underline); + Doc.UserDoc().bold = !Doc.UserDoc().underline; + return Doc.UserDoc().underline; +}); + +Scripting.addGlobal(function toggleItalic(checkResult?:boolean) { + if(checkResult) return Doc.UserDoc().italic; + // TODO: glr check if font selected and change selected font + SelectionManager.Views().map(dv => dv.props.Document.italic = !dv.props.Document.italic); + Doc.UserDoc().bold = !Doc.UserDoc().italic; + return Doc.UserDoc().italic; +}); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 3051d9a16dff8efbf4d32465812093cae7508c74 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 19 Aug 2021 18:42:46 -0400 Subject: fixed errors and warnings --- src/client/util/CurrentUserUtils.ts | 8 ++++---- src/client/util/LinkManager.ts | 4 ++-- src/client/util/SharingManager.tsx | 2 +- src/client/views/nodes/FilterBox.tsx | 4 ++-- src/client/views/nodes/ScreenshotBox.tsx | 2 +- .../views/nodes/formattedText/ProsemirrorExampleTransfer.ts | 2 +- src/fields/Doc.ts | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index e446e3752..1f37163d7 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -567,7 +567,7 @@ export class CurrentUserUtils { ); menuBtns.forEach(menuBtn => { - if (menuBtn.title == "Search") { + if (menuBtn.title === "Search") { this.searchBtn = menuBtn; } }); @@ -575,10 +575,10 @@ export class CurrentUserUtils { menuBtns[menuBtns.length - 1].hidden = ComputedField.MakeFunction("IsNoviceMode()"); menuBtns.forEach(menuBtn => { - if (menuBtn.title == "Search") { - doc.searchBtn = menuBtn + if (menuBtn.title === "Search") { + doc.searchBtn = menuBtn; } - }) + }); doc.menuStack = new PrefetchProxy(Docs.Create.StackingDocument(menuBtns, { title: "menuItemPanel", diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index c33dff8d1..3579083e4 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -47,7 +47,7 @@ export class LinkManager { Doc.GetProto(link)[DirectLinksSym].add(link); } })); - } + }; const remLinkFromDoc = (link: Doc) => { const a1 = link?.anchor1; const a2 = link?.anchor2; @@ -58,7 +58,7 @@ export class LinkManager { Doc.GetProto(link)[DirectLinksSym].delete(link); } })); - } + }; const watchUserLinkDB = (userLinkDBDoc: Doc) => { LinkManager.links.push(...DocListCast(userLinkDBDoc.data)); const toRealField = (field: Field) => field instanceof ProxyField ? field.value() : field; // see List.ts. data structure is not a simple list of Docs, but a list of ProxyField/Fields diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 6c4556250..6d7f7e8df 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -513,7 +513,7 @@ export class SharingManager extends React.Component<{}> { if (this.myDocAcls) { const newDocs: Doc[] = []; - SearchBox.foreachRecursiveDoc(docs, doc => newDocs.push(doc)); + SearchBox.foreachRecursiveDoc(docs, (depth, doc) => newDocs.push(doc)); docs = newDocs.filter(doc => GetEffectiveAcl(doc) === AclAdmin); } diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index c892a9f6c..beefc4a82 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -101,7 +101,7 @@ export class FilterBox extends ViewBoxBaseComponent(); const activeTabs = DocListCast(targetDoc.data); - SearchBox.foreachRecursiveDoc(activeTabs, (doc: Doc) => allDocs.add(doc)); + SearchBox.foreachRecursiveDoc(activeTabs, (depth, doc) => allDocs.add(doc)); setTimeout(action(() => this._allDocs = Array.from(allDocs))); } return this._allDocs; @@ -482,7 +482,7 @@ Scripting.addGlobal(function determineCheckedState(layoutDoc: Doc, facetHeader: Scripting.addGlobal(function readFacetData(layoutDoc: Doc, facetHeader: string) { const allCollectionDocs = new Set(); const activeTabs = DocListCast(layoutDoc.data); - SearchBox.foreachRecursiveDoc(activeTabs, (doc: Doc) => allCollectionDocs.add(doc)); + SearchBox.foreachRecursiveDoc(activeTabs, (depth: number, doc: Doc) => allCollectionDocs.add(doc)); const set = new Set(); if (facetHeader === "tags") allCollectionDocs.forEach(child => Field.toString(child[facetHeader] as Field).split(":").forEach(key => set.add(key))); else allCollectionDocs.forEach(child => set.add(Field.toString(child[facetHeader] as Field))); diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index 68ab3193b..f0db0b594 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -265,7 +265,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent { if (this.dataDoc[this.fieldKey + "-dictation"]) return; diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index eff400a98..76a5675de 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -84,7 +84,7 @@ export function buildKeymap>(schema: S, props: any, mapKey break; } return true; - } + }; const toggleEditableMark = (mark: any) => (state: EditorState, dispatch: (tx: Transaction) => void) => canEdit(state) && toggleMark(mark)(state, dispatch); diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 1eeadeedc..e4087cf43 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -257,7 +257,7 @@ export class Doc extends RefField { DocServer.GetRefField(this[Id], true); } }; - const writeMode = DocServer.getFieldWriteMode(fKey as string); + const writeMode = DocServer.getFieldWriteMode(fKey); if (fKey.startsWith("acl") || writeMode !== DocServer.WriteMode.Playground) { delete this[CachedUpdates][fKey]; await fn(); -- cgit v1.2.3-70-g09d2 From 077e4ba816afd35bba4622e53d4dca62b74bf292 Mon Sep 17 00:00:00 2001 From: geireann Date: Thu, 19 Aug 2021 21:56:57 -0400 Subject: menu UI updates --- src/Utils.ts | 48 ++++------------------ src/client/util/CurrentUserUtils.ts | 35 ++++++++-------- src/client/views/StyleProvider.tsx | 8 +--- src/client/views/collections/TabDocView.tsx | 2 + .../collectionLinearView/CollectionLinearView.scss | 28 +++++++------ .../collectionLinearView/CollectionLinearView.tsx | 4 +- src/client/views/nodes/button/FontIconBox.scss | 21 ++++++---- src/client/views/nodes/button/FontIconBox.tsx | 2 +- 8 files changed, 62 insertions(+), 86 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/Utils.ts b/src/Utils.ts index 194c38a6f..0887759ee 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -3,6 +3,8 @@ import v5 = require("uuid/v5"); import { ColorState } from 'react-color'; import { Socket } from 'socket.io'; import { Message } from './server/Message'; +import { Colors } from './client/views/global/globalEnums'; +import Color = require('color'); export namespace Utils { export let DRAG_THRESHOLD = 4; @@ -556,46 +558,12 @@ export function simulateMouseClick(element: Element | null | undefined, x: numbe } export function lightOrDark(color: any) { - - // Variables for red, green, blue values - var r, g, b, hsp; - - // Check the format of the color, HEX or RGB? - if (color.match(/^rgb/)) { - - // If RGB --> store the red, green, blue values in separate variables - color = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/); - - r = color[1]; - g = color[2]; - b = color[3]; - } - else { - - // If hex --> Convert it to RGB: http://gist.github.com/983661 - color = +("0x" + color.slice(1).replace( - color.length < 5 && /./g, '$&$&')); - - r = color >> 16; - g = color >> 8 & 255; - b = color & 255; - } - - // HSP (Highly Sensitive Poo) equation from http://alienryderflex.com/hsp.html - hsp = Math.sqrt( - 0.299 * (r * r) + - 0.587 * (g * g) + - 0.114 * (b * b) - ); - - // Using the HSP value, determine whether the color is light or dark - if (hsp > 127.5) { - return 'light'; - } - else { - - return 'dark'; - } + const nonAlphaColor = color.startsWith("#") ? (color as string).substring(0, 7) : + color.startsWith("rgba") ? color.replace(/,.[^,]*\)/, ")").replace("rgba", "rgb") : color; + const col = Color(nonAlphaColor).rgb(); + const colsum = (col.red() + col.green() + col.blue()); + if (colsum / col.alpha() > 400 || col.alpha() < 0.25) return Colors.DARK_GRAY; + else return Colors.WHITE; } diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 4ff0446ad..0440367de 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -990,10 +990,10 @@ export class CurrentUserUtils { if (type === "TextMenu") { const textBtns = (CurrentUserUtils.textTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, btnType, click, script, userColorBtn }) => { textDocList.push(Docs.Create.FontIconDocument({ - _nativeWidth: width ? width : 30, - _nativeHeight: 30, - _width: width ? width : 30, - _height: 30, + _nativeWidth: width ? width : 25, + _nativeHeight: 25, + _width: width ? width : 25, + _height: 25, icon, toolTip, userColorBtn, @@ -1006,7 +1006,8 @@ export class CurrentUserUtils { system: true, dontUndo: true, title, - backgroundColor: "black", + color: Colors.WHITE, + backgroundColor: "transparent", _dropAction: "alias", _removeDropProperties: new List(["dropAction", "_stayInCollection"]), onClick: click ? ScriptField.MakeScript(click, { doc: Doc.name }) : undefined @@ -1016,10 +1017,10 @@ export class CurrentUserUtils { } else if (type === "InkMenu") { const inkBtns = (CurrentUserUtils.inkTools(doc)).map(({ title, toolTip, icon, btnType, click }) => { textDocList.push(Docs.Create.FontIconDocument({ - _nativeWidth: width ? width : 30, - _nativeHeight: 30, - _width: width ? width : 30, - _height: 30, + _nativeWidth: width ? width : 25, + _nativeHeight: 25, + _width: width ? width : 25, + _height: 25, icon, toolTip, script, @@ -1031,7 +1032,8 @@ export class CurrentUserUtils { system: true, dontUndo: true, title, - backgroundColor: "black", + color: Colors.WHITE, + backgroundColor: "transparent", _dropAction: "alias", _removeDropProperties: new List(["dropAction", "_stayInCollection"]), onClick: click ? ScriptField.MakeScript(click, { doc: Doc.name }) : undefined @@ -1040,14 +1042,12 @@ export class CurrentUserUtils { docList.push(CurrentUserUtils.blist({ linearViewSubMenu: true, ignoreClick: true, linearViewExpandable: true, icon:title, _height: 30, backgroundColor: "transparent" }, textDocList)); } else { docList.push(Docs.Create.FontIconDocument({ - _nativeWidth: width ? width : 30, - _nativeHeight: 30, - _width: width ? width : 30, - _height: 30, + _nativeWidth: width ? width : 25, + _nativeHeight: 25, + _width: width ? width : 25, + _height: 25, icon, toolTip, - // testToggle: toggle ? ScriptField.MakeScript(toggle, { scriptContext: "any" }) : undefined, - // toggle: toggle, script, btnType: btnType, btnList: new List(list), @@ -1057,7 +1057,8 @@ export class CurrentUserUtils { system: true, dontUndo: true, title, - backgroundColor: "black", + color: Colors.WHITE, + backgroundColor: "transparent", _dropAction: "alias", _removeDropProperties: new List(["dropAction", "_stayInCollection"]), onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 85520f6b3..470ae7c77 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -21,6 +21,7 @@ import "./nodes/FilterBox.scss"; import "./StyleProvider.scss"; import React = require("react"); import Color = require('color'); +import { lightOrDark } from '../../Utils'; export enum StyleLayers { Background = "background" @@ -101,12 +102,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt 400 || col.alpha() < 0.25) return Colors.DARK_GRAY; - return Colors.WHITE; + return lightOrDark(backColor) case StyleProp.Hidden: return BoolCast(doc?._hidden); case StyleProp.BorderRounding: return StrCast(doc?.[fieldKey + "borderRounding"], doc?._viewType === CollectionViewType.Pile ? "50%" : ""); case StyleProp.TitleHeight: return 15; diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 1969d728c..9ff1a0f61 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -124,6 +124,8 @@ export class TabDocView extends React.Component { tab.element[0].prepend(iconWrap); tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }), ({ layer, color }) => { + console.log("TabDocView: " + this.tabColor); + console.log("lightOrDark: " + lightOrDark(this.tabColor)); const textColor = lightOrDark(this.tabColor); //not working with StyleProp.Color titleEle.style.color = textColor; titleEle.style.backgroundColor = "transparent"; diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss index 9c766e03f..b37233892 100644 --- a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss @@ -5,12 +5,16 @@ overflow: visible; height: 100%; pointer-events: none; - // background-color: rgba(0, 0, 0, 0.2); &.true { padding-left: 5px; padding-right: 5px; border-left: $standard-border; + background-color: #4476f780; + } + + >input:not(:checked)~&.true { + background-color: transparent; } .collectionLinearView { @@ -78,24 +82,25 @@ } >label { - background: $dark-gray; + pointer-events: all; + cursor: pointer; + background-color: $medium-blue; + padding: 5; + border-radius: 2px; + height: 25; + + margin: 0; color: $white; display: flex; - border-radius: 18px; - font-size: 12.5px; - font-weight:100; + font-weight: 100; width: fit-content; - height: 100%; - margin-top: auto; - margin-bottom: auto; - margin-right: 3px; - cursor: pointer; transition: transform 0.2s; align-items: center; justify-content: center; + transition:0.1s; &:hover { - background: $medium-gray; + transform: scale(1.05); } } @@ -122,7 +127,6 @@ .collectionLinearView-docBtn-scalable { position: relative; margin: auto; - margin-left: 3px; transform-origin: center 80%; } diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx index a8846fd45..5d89d82b4 100644 --- a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx @@ -114,12 +114,12 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const icon: string = StrCast(this.props.Document.icon); - const menuOpener = ; - return
+ return
{!expandable ? (null) :
{BoolCast(this.props.Document.linearViewIsExpanded) ? "Close menu" : "Open menu"}
} placement="top"> {menuOpener} diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss index 72fab74d9..8dfb66e30 100644 --- a/src/client/views/nodes/button/FontIconBox.scss +++ b/src/client/views/nodes/button/FontIconBox.scss @@ -6,7 +6,11 @@ justify-content: center; align-items: center; font-size: 80%; - border-radius: 7px; + border-radius: 2px; + + &:hover { + background-color: rgba(0,0,0,0.3) !important; + } .menuButton-wrap { grid-column: 1; @@ -94,8 +98,7 @@ width: 100px; display: grid; grid-auto-columns: 80px 20px; - justify-items: flex-start; - padding-left: 10px; + justify-items: center; font-family: 'Roboto'; white-space: nowrap; text-overflow: ellipsis; @@ -113,14 +116,15 @@ height: fit-content; top: 100%; z-index: 21; - background-color: #e3e3e3; - box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3); - border-radius: 3px; + background-color: $white; + box-shadow: 0px 3px 4px rgba(0,0,0,0.3); + padding: 1px; .list-item { - color: black; + color: $black; width: 100%; height: 25px; + font-weight: 400; display: flex; justify-content: left; align-items: center; @@ -169,7 +173,8 @@ .menuButton-dropdown-header{ width: 100%; font-weight: 300; - overflow:hidden; + padding: 5px; + overflow: hidden; font-size: 12px; white-space: nowrap; text-overflow: ellipsis; diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 9a54579dc..84ad03fa2 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -169,7 +169,7 @@ export class FontIconBox extends DocComponent(Fon return (
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}>
{text && text[0].toUpperCase() + text.slice(1)} -- cgit v1.2.3-70-g09d2 From 8f77323d6be4d4e3537d2bc2bbe815e9d578eccb Mon Sep 17 00:00:00 2001 From: dinhanhtruong <70963346+dinhanhtruong@users.noreply.github.com> Date: Fri, 20 Aug 2021 00:12:42 -0400 Subject: fixed bug where link colors reset on reload need to fix new mystery bug where any new links disappear after refreshing once but re-appear after a second refrsh --- src/client/util/LinkManager.ts | 14 +++++++------- .../collectionFreeForm/CollectionFreeFormLinkView.tsx | 3 ++- .../collectionFreeForm/CollectionFreeFormLinksView.tsx | 1 + src/client/views/linking/LinkEditor.tsx | 11 ++++++----- src/client/views/nodes/LinkDescriptionPopup.tsx | 4 ++-- src/client/views/pdf/AnchorMenu.tsx | 4 ++-- 6 files changed, 20 insertions(+), 17 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 175de0fa5..1fc0a7593 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -99,13 +99,13 @@ export class LinkManager { public createLinkrelationshipLists = () => { - const linkRelationshipList = new List(); - const linkColorList = new List(); - Doc.UserDoc().linkRelationshipList = linkRelationshipList; - Doc.UserDoc().linkColorList = linkColorList; - console.log(Doc.UserDoc().linkRelationshipList); - console.log(Doc.UserDoc().linkColorList); - // + //create new lists for link relations and their associated colors if the lists don't already exist + if (!Doc.UserDoc().linkRelationshipList && !Doc.UserDoc().linkColorList) { + const linkRelationshipList = new List(); + const linkColorList = new List(); + Doc.UserDoc().linkRelationshipList = linkRelationshipList; + Doc.UserDoc().linkColorList = linkColorList; + } } public addLink(linkDoc: Doc, checkExists = false) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index ba67bbcef..0c44df64c 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -180,7 +180,8 @@ export class CollectionFreeFormLinkView extends React.Component; const linkColorList = Doc.UserDoc().linkColorList as List; - const strokeColor = linkColorList[linkRelationshipList.indexOf(linkRelationship)]; //access stroke color using index of the relationship in the color list + //access stroke color using index of the relationship in the color list (default black) + const strokeColor = linkRelationshipList.indexOf(linkRelationship) == -1 ? "black" : linkColorList[linkRelationshipList.indexOf(linkRelationship)]; console.log(strokeColor); return !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<> { setRelationshipValue = action((value: string) => { if (LinkManager.currentLink) { LinkManager.currentLink.linkRelationship = value; - const linkRelationshipList = Doc.UserDoc().linkRelationshipList as List; - const linkColorList = Doc.UserDoc().linkColorList as List; + const linkRelationshipList = StrListCast(Doc.UserDoc().linkRelationshipList); + const linkColorList = StrListCast(Doc.UserDoc().linkColorList); // if the relationship does not exist in the list, add it and a corresponding unique randomly generated color if (linkRelationshipList && !linkRelationshipList.includes(value)) { linkRelationshipList.push(value); @@ -99,10 +99,11 @@ export class LinkEditor extends React.Component { style={{ width: "100%" }} id="input" value={this.relationship} - placeholder={"enter link label"} + placeholder={"Enter link relationship"} // color={"rgb(88, 88, 88)"} onKeyDown={this.onRelationshipKey} onChange={this.handleRelationshipChange} + onBlur={this.onRelationshipDown} >
{ style={{ width: "100%" }} id="input" value={this.description} - placeholder={"enter link label"} + placeholder={"Enter link description"} // color={"rgb(88, 88, 88)"} onKeyDown={this.onKey} onChange={this.handleChange} diff --git a/src/client/views/nodes/LinkDescriptionPopup.tsx b/src/client/views/nodes/LinkDescriptionPopup.tsx index 30b272a9a..b62a4dd56 100644 --- a/src/client/views/nodes/LinkDescriptionPopup.tsx +++ b/src/client/views/nodes/LinkDescriptionPopup.tsx @@ -54,7 +54,7 @@ export class LinkDescriptionPopup extends React.Component<{}> { }}> e.key === "Enter" && this.onDismiss(true)} - placeholder={"(optional) enter link label..."} + placeholder={"(Optional) Enter link description..."} onChange={(e) => this.descriptionChanged(e)}>
@@ -65,4 +65,4 @@ export class LinkDescriptionPopup extends React.Component<{}> {
; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 8d74b2ac4..75e3f81fb 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -152,12 +152,12 @@ export class AnchorMenu extends AntimodeMenu { , //NOTE: link popup is currently in progress - {"Link selected text to document or URL"}
}> + {"Link selected text to document"}
}> , - + ] : [ {"Remove Link Anchor"}
}>
} key="properties" placement="bottom"> -
0 ? Colors.MEDIUM_BLUE : undefined }} + key="properties" onPointerDown={this.toggleProperties}>
@@ -143,11 +146,11 @@ export class CollectionMenu extends AntimodeMenu { // NEW BUTTONS //dash col linear view buttons - const contMenuButtons = -
- {this.contMenuButtons} - {prop} -
; + const contMenuButtons = +
+ {this.contMenuButtons} + {prop} +
; return contMenuButtons; @@ -779,7 +782,7 @@ export class CollectionFreeFormViewChrome extends React.Component { SetActiveInkWidth(wid); this._widthBtn = false; this.editProperties(wid, "width"); })} style={{ backgroundColor: this._widthBtn ? "121212" : "", zIndex: 1001, fontSize: this._dotsize[i], padding: 0, textAlign: "center" }}> • - + )}
; } @@ -1050,7 +1053,7 @@ export class CollectionTreeViewChrome extends React.Component
Sort -
+
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss index 2b3f8f2c9..44752e034 100644 --- a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss @@ -5,12 +5,12 @@ overflow: visible; height: 100%; pointer-events: none; - + &.true { padding-left: 5px; padding-right: 5px; border-left: $standard-border; - background-color: #4476f73d; + background-color: $medium-blue-alt; } >input:not(:checked)~&.true { @@ -21,8 +21,9 @@ display: flex; height: 100%; align-items: center; + gap: 5px; - .collectionView{ + .collectionView { overflow: visible !important; } @@ -56,7 +57,7 @@ } .bottomPopup-descriptions { - cursor:pointer; + cursor: pointer; display: inline; white-space: nowrap; padding-left: 8px; @@ -69,7 +70,7 @@ } .bottomPopup-exit { - cursor:pointer; + cursor: pointer; display: inline; white-space: nowrap; margin-right: 10px; @@ -97,7 +98,7 @@ transition: transform 0.2s; align-items: center; justify-content: center; - transition:0.1s; + transition: 0.1s; &:hover { transform: scale(1.05); diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx index 3327bef36..713d93f97 100644 --- a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx @@ -108,12 +108,13 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { } render() { - const guid = Utils.GenerateGuid(); - const flexDir: any = StrCast(this.Document.flexDirection); - const expandable: boolean = BoolCast(this.props.Document.linearViewExpandable); + const guid = Utils.GenerateGuid(); // Generate a unique ID to use as the label + const flexDir: any = StrCast(this.Document.flexDirection); // Specify direction of linear view content + const flexGap: number = NumCast(this.Document.flexGap); // Specify the gap between linear view content + const expandable: boolean = BoolCast(this.props.Document.linearViewExpandable); // Specify whether it is expandable or not const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); - const icon: string = StrCast(this.props.Document.icon); + const icon: string = StrCast(this.props.Document.icon); // Menu opener toggle const menuOpener =
}> -
e.stopPropagation()} onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} > + {this._document.hideMinimap ? "Open minimap" : "Close minimap"}
}> +
e.stopPropagation()} + onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} >
diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss index 44752e034..24c9bc9cb 100644 --- a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss @@ -89,7 +89,7 @@ padding: 5; border-radius: 2px; height: 25; - + min-width: 25; margin: 0; color: $white; display: flex; diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx index 48f50cf2d..990c2e4b2 100644 --- a/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.tsx @@ -17,7 +17,7 @@ import { StyleProp } from '../../StyleProvider'; import "./CollectionLinearView.scss"; import { CollectionSubView } from '.././CollectionSubView'; import { CollectionViewType } from '.././CollectionView'; -import { Colors } from '../../global/globalEnums'; +import { Colors, Shadows } from '../../global/globalEnums'; type LinearDocument = makeInterface<[typeof documentSchema,]>; @@ -112,6 +112,8 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { const flexDir: any = StrCast(this.Document.flexDirection); // Specify direction of linear view content const flexGap: number = NumCast(this.Document.flexGap); // Specify the gap between linear view content const expandable: boolean = BoolCast(this.props.Document.linearViewExpandable); // Specify whether it is expandable or not + const floating: boolean = BoolCast(this.props.Document.linearViewFloating); // Specify whether it is expandable or not + const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const icon: string = StrCast(this.props.Document.icon); // Menu opener toggle @@ -121,7 +123,9 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) { backgroundColor: backgroundColor === color ? "black" : BoolCast(this.layoutDoc.linearViewIsExpanded) ? undefined : Colors.LIGHT_GRAY }} onPointerDown={e => e.stopPropagation()} > -
+
{BoolCast(this.layoutDoc.linearViewIsExpanded) ? icon ? icon : "–" : icon ? icon : "+"}
; @@ -181,7 +185,7 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
; })}
- {DocumentLinksButton.StartLink ? e.stopPropagation()} > diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss index a1baed7d9..2fd67a8dc 100644 --- a/src/client/views/global/globalCssVariables.scss +++ b/src/client/views/global/globalCssVariables.scss @@ -16,6 +16,7 @@ $close-red: #e48282; $drop-shadow: "#32323215"; + //padding $minimum-padding: 4px; $medium-padding: 16px; @@ -48,6 +49,10 @@ $standard-border: solid 1px #9f9f9f; // border radius $standard-border-radius: 3px; +// shadow +$standard-box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3); + + $searchpanel-height: 32px; $mainTextInput-zindex: 999; // then text input overlay so that it's context menu will appear over decorations, etc $docDecorations-zindex: 998; // then doc decorations appear over everything else diff --git a/src/client/views/global/globalEnums.tsx b/src/client/views/global/globalEnums.tsx index 074a3aab3..56779c37c 100644 --- a/src/client/views/global/globalEnums.tsx +++ b/src/client/views/global/globalEnums.tsx @@ -36,4 +36,8 @@ export enum IconSizes { export enum Borders { STANDARD = "solid 1px #9F9F9F" +} + +export enum Shadows { + STANDARD_SHADOW = "0px 3px 4px rgba(0, 0, 0, 0.3)" } \ No newline at end of file diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss index fef99ce83..c1f5282d0 100644 --- a/src/client/views/nodes/button/FontIconBox.scss +++ b/src/client/views/nodes/button/FontIconBox.scss @@ -38,11 +38,15 @@ &.clickBtn { cursor: pointer; - width: 40px; &:hover { background-color: rgba(0, 0, 0, 0.3) !important; } + + svg { + width: 50% !important; + height: 50%; + } } &.tglBtn { @@ -157,9 +161,58 @@ } } + &.numBtn { + cursor: pointer; + background: transparent; + + &.slider { + .numberBtn-slider {} + + .menuButton-dropdownBox { + position: absolute; + width: fit-content; + height: fit-content; + top: 100%; + z-index: 21; + background-color: #e3e3e3; + box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3); + border-radius: $standard-border-radius; + } + } + + &.dropdown { + .menuButton-dropdownList { + position: absolute; + width: fit-content; + height: fit-content; + top: 100%; + z-index: 21; + background-color: $white; + box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3); + padding: 1px; + + .list-item { + color: $black; + width: 100%; + height: 25px; + font-weight: 400; + display: flex; + justify-content: left; + align-items: center; + padding-left: 5px; + } + + .list-item:hover { + background-color: lightgrey; + } + } + } + } + &.drpDownBtn { cursor: pointer; background: transparent; + border: solid 0.5px grey; &.true { background: rgba(0, 0, 0, 0.3); @@ -172,7 +225,7 @@ top: 100%; background-color: #e3e3e3; box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.3); - border-radius: 3px; + border-radius: $standard-border-radius; } } @@ -208,4 +261,67 @@ position: fixed; } +} + + +//TOGGLE + +.switch { + position: relative; + display: inline-block; + width: 50px; + height: 25px; +} + +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: lightgrey; + -webkit-transition: .4s; + transition: .4s; +} + +.slider:before { + position: absolute; + content: ""; + height: 22px; + width: 22px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; +} + +input:checked+.slider { + background-color: $medium-blue; +} + +input:focus+.slider { + box-shadow: 0 0 1px $medium-blue; +} + +input:checked+.slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +/* Rounded sliders */ +.slider.round { + border-radius: $standard-border-radius; +} + +.slider.round:before { + border-radius: $standard-border-radius; } \ No newline at end of file diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index aa3a1ada4..46d92cd6e 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -3,9 +3,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; +import { StringifyOptions } from 'querystring'; import * as React from 'react'; import { ColorState, SketchPicker } from 'react-color'; import { Doc, StrListCast } from '../../../../fields/Doc'; +import { InkTool } from '../../../../fields/InkField'; import { createSchema, makeInterface } from '../../../../fields/Schema'; import { ScriptField } from '../../../../fields/ScriptField'; import { BoolCast, Cast, NumCast, StrCast } from '../../../../fields/Types'; @@ -17,9 +19,11 @@ import { UndoManager } from '../../../util/UndoManager'; import { CollectionViewType } from '../../collections/CollectionView'; import { ContextMenu } from '../../ContextMenu'; import { DocComponent } from '../../DocComponent'; +import { EditableView } from '../../EditableView'; import { Colors } from '../../global/globalEnums'; import { StyleProp } from '../../StyleProvider'; import { FieldView, FieldViewProps } from '.././FieldView'; +import { RichTextMenu } from '../formattedText/RichTextMenu'; import './FontIconBox.scss'; const FontIconSchema = createSchema({ icon: "string", @@ -34,7 +38,8 @@ export enum ButtonType { ToggleButton = "tglBtn", ColorButton = "colorBtn", ToolButton = "toolBtn", - NumberButton = "numBtn" + NumberButton = "numBtn", + EditableText = "editableTxt" } export enum NumButtonType { @@ -94,10 +99,15 @@ export class FontIconBox extends DocComponent(Fon @computed get numberButton() { const numType: string = StrCast(this.rootDoc.numButtonType); - return ( -
+ const dropdown = numType ? +
+ {/* DROPDOWN BOX CONTENTS */} +
: null; + return ( +
+ {this.rootDoc.dropDownOpen ? dropdown : null}
); } @@ -130,7 +140,7 @@ export class FontIconBox extends DocComponent(Fon } /** - * Dropdown button + * Dropdown list */ @computed get dropdownListButton() { const active: string = StrCast(this.rootDoc.dropDownOpen); @@ -221,7 +231,7 @@ export class FontIconBox extends DocComponent(Fon } /** - * Colour button + * Color button */ @computed get colorButton() { const active: string = StrCast(this.rootDoc.dropDownOpen); @@ -229,10 +239,18 @@ export class FontIconBox extends DocComponent(Fon const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); const numSelected = SelectionManager.Views().length; const selectedDoc = numSelected > 0 ? SelectionManager.Views()[0].Document : undefined; + + const script: string = StrCast(this.rootDoc.script); + const scriptCheck: string = script + "(undefined, true)"; + const boolResult = ScriptField.MakeScript(scriptCheck)?.script.run().result; + console.log("[boolResul]: ", boolResult); + + const colorOptions: string[] = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', + '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', + '#FFFFFF', '#f1efeb', 'transparent']; + const colorBox = (func: (color: ColorState) => void) => ; + presetColors={colorOptions} />; const label = !this.label || !Doc.UserDoc()._showLabel ? (null) :
{this.label} @@ -242,10 +260,9 @@ export class FontIconBox extends DocComponent(Fon style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}>
; - const script: string = StrCast(this.rootDoc.script); const click = (value: ColorState) => { const hex: string = value.hex; - const s = ScriptField.MakeScript(script + '("' + hex + '")'); + const s = ScriptField.MakeScript(script + '("' + hex + '", false)'); if (s) { s.script.run().result; } @@ -277,20 +294,47 @@ export class FontIconBox extends DocComponent(Fon } @computed get toggleButton() { - const script: string = StrCast(this.rootDoc.script) + "(true)"; - const boolResult = ScriptField.MakeScript(script)?.script.run().result; + // Determine the type of toggle button + const switchToggle: boolean = BoolCast(this.rootDoc.switchToggle); + + // Script for running the toggle + const script: string = StrCast(this.rootDoc.script) + "()"; + // Script for checking the outcome of the toggle + const checkScript: string = StrCast(this.rootDoc.script) + "(true)"; + // Function to run the script + const runScript = () => ScriptField.MakeScript(script)?.script.run(); + const checkResult = ScriptField.MakeScript(checkScript)?.script.run().result; + + // 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) :
{this.label}
; + return ( -
- - {label} -
+ switchToggle ? +
+ +
+ : +
+ + {label} +
); } @@ -314,6 +358,19 @@ export class FontIconBox extends DocComponent(Fon ); } + @computed get editableText() { + const setValue = (value: string, shiftDown?: boolean): boolean => { + console.log("setValue"); + return true; + }; + + return
+ TEST + ""} SetValue={setValue} contents="..."> + +
; + } + render() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); @@ -342,6 +399,8 @@ export class FontIconBox extends DocComponent(Fon let button = this.defaultButton; switch (this.type) { + case ButtonType.EditableText: + button = this.editableText; case ButtonType.NumberButton: button = this.numberButton; break; @@ -420,7 +479,6 @@ Scripting.addGlobal(function changeBackgroundColor(color?: string, checkResult?: Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) { const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; if (checkResult && selected) { - console.log("z: ", NumCast(selected.Document.z) === 1); return NumCast(selected.Document.z) === 1; } selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed"); @@ -429,17 +487,43 @@ Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) { // toggle: Set overlay status of selected document Scripting.addGlobal(function changeFont(font: string) { SelectionManager.Views().map(dv => dv.props.Document._fontFamily = font); - console.log(font); + console.log("[changeFont]: ", font); + console.log(RichTextMenu.Instance.getActiveMarksOnSelection()); Doc.UserDoc()._fontFamily = font; return Doc.UserDoc()._fontFamily; }); // toggle: Set overlay status of selected document -Scripting.addGlobal(function changeFontColor(color: string) { - console.log(color); +Scripting.addGlobal(function changeFontColor(color?: string, checkResult?: boolean) { + const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; + if (checkResult) { + if (selected) { + console.log("[Font color] (selected): " + StrCast(selected.Document._fontColor)); + return selected.Document._fontColor; + } else { + console.log("[Font color] (global): " + StrCast(Doc.UserDoc()._fontColor)); + return Doc.UserDoc()._fontColor; + } + } + Doc.UserDoc()._fontColor = color; +}); + +// toggle: Set overlay status of selected document +Scripting.addGlobal(function changeFontHighlight(color?: string, checkResult?: boolean) { + const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; + if (checkResult) { + if (selected) { + console.log("[Font color] (selected): " + StrCast(selected.Document._fontColor)); + return selected.Document._fontColor; + } else { + console.log("[Font color] (global): " + StrCast(Doc.UserDoc()._fontColor)); + return Doc.UserDoc()._fontColor; + } + } Doc.UserDoc()._fontColor = color; }); + // toggle: Set overlay status of selected document Scripting.addGlobal(function changeFontSize(size: string) { console.log(size); @@ -471,4 +555,15 @@ Scripting.addGlobal(function toggleItalic(checkResult?: boolean) { SelectionManager.Views().map(dv => dv.props.Document.italic = !dv.props.Document.italic); Doc.UserDoc().bold = !Doc.UserDoc().italic; return Doc.UserDoc().italic; +}); + +Scripting.addGlobal(function setActiveInkTool(checkResult?: boolean, tool?: InkTool) { + if (checkResult) { + return Doc.UserDoc().activeInkTool === tool; + } + if (tool) { + Doc.UserDoc().activeInkTool = tool; + } else { + Doc.UserDoc().activeInkTool = InkTool.None; + } }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 707038795f7129d30a1d4ed5f2311f18a282fecb Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 24 Aug 2021 02:30:06 -0400 Subject: fixed following link from ink stroke. made interacting with annotatable documents consistent when selections are cleared by clicking. fixed undo for webboxes. fixed limiting size of links button. --- src/client/documents/Documents.ts | 1 + src/client/views/DocComponent.tsx | 10 +++--- src/client/views/MarqueeAnnotator.tsx | 16 ++++----- src/client/views/PreviewCursor.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 10 ++++-- .../collections/collectionFreeForm/MarqueeView.tsx | 15 +++++--- src/client/views/nodes/DocumentView.tsx | 6 ++-- src/client/views/nodes/ImageBox.tsx | 25 ++++++++------ src/client/views/nodes/ScreenshotBox.tsx | 3 +- src/client/views/nodes/VideoBox.tsx | 19 +++++----- src/client/views/nodes/WebBox.tsx | 40 +++++++++------------- src/client/views/pdf/PDFViewer.tsx | 14 ++++---- 12 files changed, 82 insertions(+), 79 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 48886aa3b..f8e9e8702 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -703,6 +703,7 @@ export namespace Docs { I.data = new InkField(points); I["acl-Public"] = Doc.UserDoc()?.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; I["acl-Override"] = "None"; + I.links = ComputedField.MakeFunction("links(self)") as any; I[Initializing] = false; return I; } diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 14d32ef12..d9cc29bed 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -90,9 +90,9 @@ export interface ViewBoxAnnotatableProps { renderDepth: number; isAnnotationOverlay?: boolean; } -export function ViewBoxAnnotatableComponent

(schemaCtor: (doc: Doc) => T, _annotationKey: string = "annotations") { +export function ViewBoxAnnotatableComponent

(schemaCtor: (doc: Doc) => T) { class Component extends Touchable

{ - @observable _annotationKey: string = _annotationKey; + @observable _annotationKeySuffix = () => "annotations"; @observable _isAnyChildContentActive = false; //TODO This might be pretty inefficient if doc isn't observed, because computed doesn't cache then @@ -125,7 +125,7 @@ export function ViewBoxAnnotatableComponent

{ if ([AclAdmin, AclEdit].includes(GetEffectiveAcl(doc))) inheritParentAcls(CurrentUserUtils.ActiveDashboard, doc); doc.context = this.props.Document; - if (annotationKey ?? this._annotationKey) Doc.GetProto(doc).annotationOn = this.props.Document; + if (annotationKey ?? this._annotationKeySuffix()) Doc.GetProto(doc).annotationOn = this.props.Document; this.props.layerProvider?.(doc, true); Doc.AddDocToList(targetDataDoc, annotationKey ?? this.annotationKey, doc); }); @@ -214,7 +214,7 @@ export function ViewBoxAnnotatableComponent

{ @observable private _width: number = 0; @observable private _height: number = 0; - constructor(props: any) { - super(props); - runInAction(() => { - AnchorMenu.Instance.Status = "marquee"; - AnchorMenu.Instance.fadeOut(true); - // clear out old marquees and initialize menu for new selection - Array.from(this.props.savedAnnotations.values()).forEach(v => v.forEach(a => a.remove())); - this.props.savedAnnotations.clear(); - }); + @action + static clearAnnotations(savedAnnotations: ObservableMap) { + AnchorMenu.Instance.Status = "marquee"; + AnchorMenu.Instance.fadeOut(true); + // clear out old marquees and initialize menu for new selection + Array.from(savedAnnotations.values()).forEach(v => v.forEach(a => a.remove())); + savedAnnotations.clear(); } @action componentDidMount() { diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 679a4b81e..2b82ef475 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -158,7 +158,7 @@ export class PreviewCursor extends React.Component<{}> { } render() { return (!PreviewCursor._clickPoint || !PreviewCursor.Visible) ? (null) : -

e && e.focus()} +
e?.focus()} style={{ transform: `translate(${PreviewCursor._clickPoint[0]}px, ${PreviewCursor._clickPoint[1]}px)` }}> I
; diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index a82128e47..a821aeeea 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -36,6 +36,7 @@ import { CollectionTimeView } from './CollectionTimeView'; import { CollectionTreeView } from "./CollectionTreeView"; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import './CollectionView.scss'; +import { returnEmptyString } from '../../../Utils'; export const COLLECTION_BORDER_WIDTH = 2; const path = require('path'); @@ -62,7 +63,7 @@ export enum CollectionViewType { export interface CollectionViewProps extends FieldViewProps { isAnnotationOverlay?: boolean; // is the collection an annotation overlay (eg an overlay on an image/video/etc) layoutEngine?: () => string; - setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; + setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void; // property overrides for child documents children?: never | (() => JSX.Element[]) | React.ReactNode; @@ -84,7 +85,7 @@ export interface CollectionViewProps extends FieldViewProps { type CollectionDocument = makeInterface<[typeof documentSchema]>; const CollectionDocument = makeInterface(documentSchema); @observer -export class CollectionView extends ViewBoxAnnotatableComponent(CollectionDocument, "") { +export class CollectionView extends ViewBoxAnnotatableComponent(CollectionDocument) { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(CollectionView, fieldStr); } @observable private static _safeMode = false; @@ -92,6 +93,11 @@ export class CollectionView extends ViewBoxAnnotatableComponent this._annotationKeySuffix = returnEmptyString); + } + get collectionViewType(): CollectionViewType | undefined { const viewField = StrCast(this.layoutDoc._viewType); if (CollectionView._safeMode) { diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 437a3cba8..81f6307d1 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -41,7 +41,7 @@ interface MarqueeViewProps { trySelectCluster: (addToSel: boolean) => boolean; nudge?: (x: number, y: number, nudgeTime?: number) => boolean; ungroup?: () => void; - setPreviewCursor?: (func: (x: number, y: number, drag: boolean) => void) => void; + setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void; } @observer export class MarqueeView extends React.Component @@ -211,7 +211,7 @@ export class MarqueeView extends React.Component { - if (drag) { + setPreviewCursor = action((x: number, y: number, drag: boolean, hide: boolean) => { + if (hide) { + this._downX = this._lastX = x; + this._downY = this._lastY = y; + this._commandExecuted = false; + PreviewCursor.Visible = false; + } else if (drag) { this._downX = this._lastX = x; this._downY = this._lastY = y; this._commandExecuted = false; @@ -313,7 +318,7 @@ export class MarqueeView extends React.Component this.onClickHandler; setHeight = (height: number) => this.layoutDoc._height = height; setContentView = (view: { getAnchor?: () => Doc, forward?: () => boolean, back?: () => boolean }) => this._componentView = view; - isContentActive = (outsideReaction?: boolean) => this.props.isContentActive() ? true : false; + isContentActive = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || this.props.isContentActive() ? true : false; @computed get contents() { TraceMobx(); const audioView = !this.layoutDoc._showAudio ? (null) : @@ -792,7 +792,7 @@ export class DocumentViewInternal extends DocComponent; return
{this.layoutDoc.hideAllLinks ? (null) : this.allLinkEndpoints} {this.hideLinkButton ? (null) : -
+
} diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 2c0106960..c4e74ebd2 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -1,19 +1,21 @@ -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx'; import { observer } from "mobx-react"; import { DataSym, Doc, DocListCast, WidthSym } from '../../../fields/Doc'; import { documentSchema } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; +import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { createSchema, makeInterface } from '../../../fields/Schema'; import { ComputedField } from '../../../fields/ScriptField'; -import { Cast, NumCast, StrCast } from '../../../fields/Types'; +import { Cast, NumCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, OmitKeys, returnOne, Utils, returnFalse } from '../../../Utils'; +import { emptyFunction, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../Utils'; import { GooglePhotos } from '../../apis/google_docs/GooglePhotosClientUtils'; import { CognitiveServices, Confidence, Service, Tag } from '../../cognitive_services/CognitiveServices'; import { Networking } from '../../Network'; +import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { DragManager } from '../../util/DragManager'; import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from "../../views/ContextMenu"; @@ -21,15 +23,13 @@ import { CollectionFreeFormView } from '../collections/collectionFreeForm/Collec import { ContextMenuProps } from '../ContextMenuItem'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; import { MarqueeAnnotator } from '../MarqueeAnnotator'; +import { AnchorMenu } from '../pdf/AnchorMenu'; import { StyleProp } from '../StyleProvider'; import { FaceRectangles } from './FaceRectangles'; import { FieldView, FieldViewProps } from './FieldView'; import "./ImageBox.scss"; import React = require("react"); -import { InkTool } from '../../../fields/InkField'; -import { CurrentUserUtils } from '../../util/CurrentUserUtils'; -import { AnchorMenu } from '../pdf/AnchorMenu'; -import { Docs } from '../../documents/Documents'; +import { SnappingManager } from '../../util/SnappingManager'; const path = require('path'); export const pageSchema = createSchema({ @@ -294,7 +294,8 @@ export class ImageBox extends ViewBoxAnnotatableComponent {fadepath === srcpath ? (null) :
; } - @action marqueeDown = (e: React.PointerEvent) => { if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { - this._marqueeing = [e.clientX, e.clientY]; - e.stopPropagation(); + setupMoveUpEvents(this, e, action(e => { + MarqueeAnnotator.clearAnnotations(this._savedAnnotations) + this._marqueeing = [e.clientX, e.clientY]; + return true; + }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false); } } @action diff --git a/src/client/views/nodes/ScreenshotBox.tsx b/src/client/views/nodes/ScreenshotBox.tsx index f0db0b594..7ad96bf05 100644 --- a/src/client/views/nodes/ScreenshotBox.tsx +++ b/src/client/views/nodes/ScreenshotBox.tsx @@ -175,8 +175,7 @@ export class ScreenshotBox extends ViewBoxAnnotatableComponent { this._videoRef = r; setTimeout(() => { diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index fc97a6f4d..d4df30b48 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -9,7 +9,7 @@ import { InkTool } from "../../../fields/InkField"; import { makeInterface } from "../../../fields/Schema"; import { Cast, NumCast, StrCast } from "../../../fields/Types"; import { AudioField, nullAudio, VideoField } from "../../../fields/URLField"; -import { emptyFunction, formatTime, OmitKeys, returnOne, setupMoveUpEvents, Utils } from "../../../Utils"; +import { emptyFunction, formatTime, OmitKeys, returnOne, setupMoveUpEvents, Utils, returnFalse } from "../../../Utils"; import { Docs, DocUtils } from "../../documents/Documents"; import { Networking } from "../../Network"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; @@ -209,11 +209,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent this.props.isSelected(), - selected => !selected && setTimeout(() => { - Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove())); - this._savedAnnotations.clear(); - })); this._disposers.triggerVideo = reaction( () => !LinkDocPreview.LinkInfo && this.props.renderDepth !== -1 ? NumCast(this.Document._triggerVideo, null) : undefined, time => time !== undefined && setTimeout(() => { @@ -550,9 +545,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent; } - marqueeDown = action((e: React.PointerEvent) => { - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) this._marqueeing = [e.clientX, e.clientY]; - }); + marqueeDown = (e: React.PointerEvent) => { + if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { + setupMoveUpEvents(this, e, action(e => { + MarqueeAnnotator.clearAnnotations(this._savedAnnotations); + this._marqueeing = [e.clientX, e.clientY]; + return true; + }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false); + } + } finishMarquee = action(() => { this._marqueeing = undefined; diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 2ff41c73a..0c32a30db 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -44,7 +44,7 @@ const WebDocument = makeInterface(documentSchema); export class WebBox extends ViewBoxAnnotatableComponent(WebDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(WebBox, fieldKey); } public static openSidebarWidth = 250; - private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); + private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void); private _mainCont: React.RefObject = React.createRef(); private _outerRef: React.RefObject = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; @@ -52,16 +52,16 @@ export class WebBox extends ViewBoxAnnotatableComponent(); private _initialScroll: Opt; private _sidebarRef = React.createRef(); - @observable private _urlHash: string = ""; @observable private _scrollTimer: any; @observable private _overlayAnnoInfo: Opt; @observable private _marqueeing: number[] | undefined; - @observable private _url: string = "hello"; @observable private _isAnnotating = false; @observable private _iframeClick: HTMLIFrameElement | undefined = undefined; @observable private _iframe: HTMLIFrameElement | null = null; @observable private _savedAnnotations = new ObservableMap(); @observable private _scrollHeight = 1500; + @computed get _url() { return this.webField?.toString() || ""; } + @computed get _urlHash() { return this._url ? WebBox.urlHash(this._url) + "" : ""; } @computed get scrollHeight() { return this._scrollHeight; } @computed get allAnnotations() { return DocListCast(this.dataDoc[this.annotationKey]); } @computed get inlineTextAnnotations() { return this.allAnnotations.filter(a => a.textInlineAnnotations); } @@ -82,9 +82,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { - this._url = this.webField?.toString() || ""; - this._urlHash = WebBox.urlHash(this._url) + ""; - this._annotationKey = this._urlHash + "-annotations"; + this._annotationKeySuffix = () => this._urlHash + "-annotations"; // bcz: need to make sure that doc.data-annotations points to the currently active web page's annotations (this could/should be when the doc is created) this.dataDoc[this.fieldKey + "-annotations"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-annotations"`); this.dataDoc[this.fieldKey + "-sidebar"] = ComputedField.MakeFunction(`copyField(this["${this.fieldKey}-"+urlHash(this["${this.fieldKey}"]?.url?.toString())+"-sidebar"`); @@ -214,6 +212,8 @@ export class WebBox extends ViewBoxAnnotatableComponent([...history, this._url]); - this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = future.pop()!)); - this._urlHash = WebBox.urlHash(this._url) + ""; - this._annotationKey = this._urlHash + "-annotations"; + this.dataDoc[this.fieldKey] = new WebField(new URL(future.pop()!)); return true; } return false; @@ -321,9 +319,7 @@ export class WebBox extends ViewBoxAnnotatableComponent([this._url]); else this.dataDoc[this.fieldKey + "-future"] = new List([...future, this._url]); - this.dataDoc[this.fieldKey] = new WebField(new URL(this._url = history.pop()!)); - this._urlHash = WebBox.urlHash(this._url) + ""; - this._annotationKey = this._urlHash + "-annotations"; + this.dataDoc[this.fieldKey] = new WebField(new URL(history.pop()!)); return true; } return false; @@ -342,17 +338,10 @@ export class WebBox extends ViewBoxAnnotatableComponent([url]); - } else { - this.dataDoc[this.fieldKey + "-history"] = new List([...history, url]); - } + this.dataDoc[this.fieldKey + "-history"] = new List([...(history || []), url]); this.layoutDoc._scrollTop = 0; future && (future.length = 0); } - this._url = newUrl; - this._urlHash = WebBox.urlHash(this._url) + ""; - this._annotationKey = this._urlHash + "-annotations"; if (!preview) this.dataDoc[this.fieldKey] = new WebField(new URL(newUrl)); } catch (e) { console.log("WebBox URL error:" + this._url); @@ -413,15 +402,18 @@ export class WebBox extends ViewBoxAnnotatableComponent { if (!e.altKey && e.button === 0 && this.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { - this._marqueeing = [e.clientX, e.clientY]; - this.props.select(false); + setupMoveUpEvents(this, e, action(e => { + MarqueeAnnotator.clearAnnotations(this._savedAnnotations); + this._marqueeing = [e.clientX, e.clientY]; + return true; + }), returnFalse, () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations), false); } } @action finishMarquee = (x?: number, y?: number) => { this._marqueeing = undefined; this._isAnnotating = false; this._iframeClick = undefined; - x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false); + x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false); } @computed @@ -511,7 +503,7 @@ export class WebBox extends ViewBoxAnnotatableComponent) => this._overlayAnnoInfo = anno); - setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func; + setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func; panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1) - this.sidebarWidth(); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop)); diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index ee553fd43..41a60bedf 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -69,7 +69,7 @@ export class PDFViewer extends React.Component { private _pdfViewer: any; private _styleRule: any; // stylesheet rule for making hyperlinks clickable private _retries = 0; // number of times tried to create the PDF viewer - private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean) => void); + private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void); private _annotationLayer: React.RefObject = React.createRef(); private _disposers: { [name: string]: IReactionDisposer } = {}; private _viewer: React.RefObject = React.createRef(); @@ -371,10 +371,11 @@ export class PDFViewer extends React.Component { this._downY = e.clientY; if ((this.props.Document._viewScale || 1) !== 1) return; if ((e.button !== 0 || e.altKey) && this.props.isContentActive(true)) { - this._setPreviewCursor?.(e.clientX, e.clientY, true); + this._setPreviewCursor?.(e.clientX, e.clientY, true, false); } if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(CurrentUserUtils.SelectedTool)) { this.props.select(false); + MarqueeAnnotator.clearAnnotations(this._savedAnnotations); this._marqueeing = [e.clientX, e.clientY]; if (e.target && ((e.target as any).className.includes("endOfContent") || ((e.target as any).parentElement.className !== "textLayer"))) { this._textSelecting = false; @@ -382,10 +383,7 @@ export class PDFViewer extends React.Component { } else { // if textLayer is hit, then we select text instead of using a marquee so clear out the marquee. setTimeout(action(() => this._marqueeing = undefined), 100); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it. - // clear out old marquees and initialize menu for new selection - AnchorMenu.Instance.Status = "marquee"; - Array.from(this._savedAnnotations.values()).forEach(v => v.forEach(a => a.remove())); - this._savedAnnotations.clear(); + this._styleRule = addStyleSheetRule(PDFViewer._annotationStyle, "htmlAnnotation", { "pointer-events": "none" }); document.addEventListener("pointerup", this.onSelectEnd); document.addEventListener("pointermove", this.onSelectMove); @@ -454,12 +452,12 @@ export class PDFViewer extends React.Component { if (this._setPreviewCursor && e.button === 0 && Math.abs(e.clientX - this._downX) < Utils.DRAG_THRESHOLD && Math.abs(e.clientY - this._downY) < Utils.DRAG_THRESHOLD) { - this._setPreviewCursor(e.clientX, e.clientY, false); + this._setPreviewCursor(e.clientX, e.clientY, false, false); } // e.stopPropagation(); // bcz: not sure why this was here. We need to allow the DocumentView to get clicks to process doubleClicks } - setPreviewCursor = (func?: (x: number, y: number, drag: boolean) => void) => this._setPreviewCursor = func; + setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => this._setPreviewCursor = func; getCoverImage = () => { if (!this.props.Document[HeightSym]() || !Doc.NativeHeight(this.props.Document)) { -- cgit v1.2.3-70-g09d2 From 44a2517ca0f153e63a75bfdef5e624ac24acaa28 Mon Sep 17 00:00:00 2001 From: geireann <60007097+geireann@users.noreply.github.com> Date: Tue, 24 Aug 2021 08:19:42 -0400 Subject: icon updates --- src/client/util/CurrentUserUtils.ts | 2 +- src/client/views/nodes/button/FontIconBox.scss | 13 +++++++++++ src/client/views/nodes/button/FontIconBox.tsx | 30 ++++++++++++-------------- 3 files changed, 28 insertions(+), 17 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c7aa57ff0..c305a495c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -989,7 +989,7 @@ export class CurrentUserUtils { { title: "Back", toolTip: "Go back", btnType: ButtonType.ClickButton, icon: "arrow-left", click: 'webBack()' }, { title: "Forward", toolTip: "Go forward", btnType: ButtonType.ClickButton, icon: "arrow-right", click: 'webForward()' }, { title: "Reload", toolTip: "Reload webpage", btnType: ButtonType.ClickButton, icon: "redo-alt", click: 'webReload()' }, - { title: "URL", toolTip: "URL", width: 150, btnType: ButtonType.EditableText, icon: "lock", script: 'webSetURL' }, + { title: "URL", toolTip: "URL", width: 150, btnType: ButtonType.EditableText, icon: "lock", ignoreClick: true, script: 'webSetURL' }, ]; return tools; diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss index c1f5282d0..b6aa2ba60 100644 --- a/src/client/views/nodes/button/FontIconBox.scss +++ b/src/client/views/nodes/button/FontIconBox.scss @@ -209,6 +209,19 @@ } } + &.editableTxt { + cursor: pointer; + background: transparent; + width: 100%; + height: 100%; + + &:hover { + background-color: $close-red; + } + + + } + &.drpDownBtn { cursor: pointer; background: transparent; diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 46d92cd6e..054e55c56 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -152,7 +152,6 @@ export class FontIconBox extends DocComponent(Fon let noviceList: string[] = []; let text: string | undefined; let dropdown = true; - let show = true; let icon: IconProp = "caret-down"; @@ -164,6 +163,8 @@ export class FontIconBox extends DocComponent(Fon dropdown = false; text = StrCast(selected.Document.type); icon = Doc.toIcon(selected.Document); + } else { + text = "None selected"; } noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking]; } else if (script === 'changeFont') { @@ -175,8 +176,6 @@ export class FontIconBox extends DocComponent(Fon } noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]; - } else { - show = false; } const items = this.buttonList.map((value) => { @@ -206,7 +205,7 @@ export class FontIconBox extends DocComponent(Fon return (
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}>
{text && text[0].toUpperCase() + text.slice(1)} @@ -269,7 +268,7 @@ export class FontIconBox extends DocComponent(Fon }; return (
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen} onPointerDown={e => e.stopPropagation()}> @@ -315,9 +314,16 @@ export class FontIconBox extends DocComponent(Fon
{this.label}
; - + console.log("switchToggle"); return ( - switchToggle ? + !switchToggle ? +
+ + {label} +
:
- : -
- - {label} -
); } @@ -365,7 +363,7 @@ export class FontIconBox extends DocComponent(Fon }; return
- TEST + ""} SetValue={setValue} contents="...">
; -- cgit v1.2.3-70-g09d2 From 7bad6629a38f2e338be2150d86248d06a4fddda5 Mon Sep 17 00:00:00 2001 From: geireann <60007097+geireann@users.noreply.github.com> Date: Tue, 24 Aug 2021 09:31:32 -0400 Subject: create text document --- src/client/documents/Documents.ts | 2 + src/client/util/CurrentUserUtils.ts | 19 ++++--- src/client/views/EditableView.tsx | 1 + src/client/views/nodes/button/FontIconBox.tsx | 81 ++++++++++++++++++++++----- 4 files changed, 81 insertions(+), 22 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 57015e241..9ba31da3b 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1151,6 +1151,8 @@ export namespace DocUtils { description: ":" + StrCast(note.title), event: undoBatch((args: { x: number, y: number }) => { const textDoc = Docs.Create.TextDocument("", { + _fontFamily: StrCast(Doc.UserDoc()._fontFamily), + _fontSize: StrCast(Doc.UserDoc()._fontSize), _width: 200, x, y, _autoHeight: note._autoHeight !== false, title: StrCast(note.title) + "#" + (note.aliasCount = NumCast(note.aliasCount) + 1) }); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c305a495c..8abac9975 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -37,7 +37,7 @@ import { ColorScheme } from "./SettingsManager"; import { SharingManager } from "./SharingManager"; import { SnappingManager } from "./SnappingManager"; import { UndoManager } from "./UndoManager"; -import { ButtonType } from "../views/nodes/button/FontIconBox"; +import { ButtonType, NumButtonType } from "../views/nodes/button/FontIconBox"; import { IconName } from "@fortawesome/fontawesome-svg-core"; interface Button { @@ -46,6 +46,7 @@ interface Button { icon?: string; btnType?: ButtonType; click?: string; + numType?: NumButtonType; switchToggle?: boolean; script?: string; width?: number; @@ -939,7 +940,7 @@ export class CurrentUserUtils { "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"], script: 'changeFont' }, - { title: "Font size", toolTip: "Font size", btnType: ButtonType.NumberButton, script: 'changeFontSize'}, + { title: "Font size", toolTip: "Font size", btnType: ButtonType.NumberButton, numType: NumButtonType.DropdownOptions, script: 'changeFontSize'}, { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', script: 'toggleBold' }, { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', script: 'toggleItalic' }, { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', script: 'toggleUnderline' }, @@ -958,11 +959,12 @@ export class CurrentUserUtils { static inkTools(doc: Doc) { const tools:Button[] = [ - { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'togglePen()', script: 'togglePen' }, - { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setHighlighter()', script: 'toggleHighlighter' }, - { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'toggleCircle()', script: 'toggleCircle' }, - { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'toggleSquare()', script: 'toggleSquare' }, - { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "fill-drip", click: 'toggleLine()', script: 'toggleLine' }, + { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen")'}, + { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")' }, + { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")' }, + { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' }, + { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "fill-drip", click: 'setActiveInkTool("line")' }, + { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numType: NumButtonType.Slider, script: 'setLineWidth'}, ]; return tools; } @@ -1044,7 +1046,7 @@ export class CurrentUserUtils { default: break; } - tools.map(({ title, toolTip, icon, btnType, click, script, width, list, ignoreClick }) => { + tools.map(({ title, toolTip, icon, btnType, numType, click, script, width, list, ignoreClick }) => { menuDocList.push(Docs.Create.FontIconDocument({ _nativeWidth: width ? width : 25, _nativeHeight: 25, @@ -1052,6 +1054,7 @@ export class CurrentUserUtils { _height: 25, icon, toolTip, + numType, script, btnType: btnType, btnList: new List(list), diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index 03d9efff3..d5f8d3fcf 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -156,6 +156,7 @@ export class EditableView extends React.Component { } renderEditor() { + console.log("render editor", this.props.autosuggestProps); return this.props.autosuggestProps ? (Fon * - Number button **/ + _batch: UndoManager.Batch | undefined = undefined; /** * Number button */ @computed get numberButton() { - const numType: string = StrCast(this.rootDoc.numButtonType); + const numType: string = StrCast(this.rootDoc.numType); + const setValue = (value: number) => { + // Script for running the toggle + const script: string = StrCast(this.rootDoc.script) + "(" + value + ")"; + ScriptField.MakeScript(script)?.script.run(); + }; + + // Script for checking the outcome of the toggle + const checkScript: string = StrCast(this.rootDoc.script) + "(true)"; + const checkResult: number = ScriptField.MakeScript(checkScript)?.script.run().result; - const dropdown = numType ? -
+ const dropdown = +
{/* DROPDOWN BOX CONTENTS */} -
: null; +
; + + if (numType === NumButtonType.Slider) { + return ( +
+ { this._batch = UndoManager.StartBatch("presDuration"); }} + onPointerUp={() => { if (this._batch) this._batch.end(); }} + onChange={(e: React.ChangeEvent) => { e.stopPropagation(); setValue(Number(e.target.value)); }} + /> +
+ ); + } else if (numType === NumButtonType.DropdownOptions) { + return ( +
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)} + > +
+ +
+
+ setValue(Number(e.target.value)))} + /> +
+
+ +
+ {this.rootDoc.dropDownOpen ? dropdown : null} +
+ ); + } else { + return ( +
+ +
+ ); + } + - return ( -
- {this.rootDoc.dropDownOpen ? dropdown : null} -
- ); } /** @@ -172,7 +224,9 @@ export class FontIconBox extends DocComponent(Fon if (selected && StrCast(selected.Document.type) === DocumentType.RTF) { text = StrCast(selected.Document._fontFamily); } else { - text = StrCast(Doc.UserDoc()._fontFamily); + const fontFamily = StrCast(Doc.UserDoc()._fontFamily); + console.log(fontFamily); + text = fontFamily; } noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]; @@ -485,9 +539,8 @@ Scripting.addGlobal(function toggleOverlay(checkResult?: boolean) { // toggle: Set overlay status of selected document Scripting.addGlobal(function changeFont(font: string) { SelectionManager.Views().map(dv => dv.props.Document._fontFamily = font); - console.log("[changeFont]: ", font); - console.log(RichTextMenu.Instance.getActiveMarksOnSelection()); Doc.UserDoc()._fontFamily = font; + console.log(Doc.UserDoc()._fontFamily); return Doc.UserDoc()._fontFamily; }); @@ -555,7 +608,7 @@ Scripting.addGlobal(function toggleItalic(checkResult?: boolean) { return Doc.UserDoc().italic; }); -Scripting.addGlobal(function setActiveInkTool(checkResult?: boolean, tool?: InkTool) { +Scripting.addGlobal(function setActiveInkTool(tool: InkTool, checkResult?: boolean) { if (checkResult) { return Doc.UserDoc().activeInkTool === tool; } -- cgit v1.2.3-70-g09d2 From d1ea2f764d0207963788eb4238ede79bcffa23f6 Mon Sep 17 00:00:00 2001 From: geireann <60007097+geireann@users.noreply.github.com> Date: Tue, 24 Aug 2021 10:48:38 -0400 Subject: more bits and pieces (ahhhh) --- src/client/documents/Documents.ts | 3 +- src/client/util/CurrentUserUtils.ts | 22 ++++++--- src/client/util/SettingsManager.scss | 5 +- src/client/views/MainViewModal.scss | 4 +- src/client/views/global/globalCssVariables.scss | 1 + src/client/views/nodes/DocumentLinksButton.scss | 1 + src/client/views/nodes/DocumentLinksButton.tsx | 1 + src/client/views/nodes/button/FontIconBox.tsx | 66 ++++++++++++++++++------- 8 files changed, 71 insertions(+), 32 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9ba31da3b..63acd004f 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1151,8 +1151,7 @@ export namespace DocUtils { description: ":" + StrCast(note.title), event: undoBatch((args: { x: number, y: number }) => { const textDoc = Docs.Create.TextDocument("", { - _fontFamily: StrCast(Doc.UserDoc()._fontFamily), - _fontSize: StrCast(Doc.UserDoc()._fontSize), + _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), _width: 200, x, y, _autoHeight: note._autoHeight !== false, title: StrCast(note.title) + "#" + (note.aliasCount = NumCast(note.aliasCount) + 1) }); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 8abac9975..73a66332f 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -99,7 +99,7 @@ export class CurrentUserUtils { const slideTemplate = Docs.Create.MultirowDocument( [ Docs.Create.MulticolumnDocument([], { title: "data", _height: 200, system: true }), - Docs.Create.TextDocument("", { title: "text", _height: 100, system: true }) + Docs.Create.TextDocument("", { title: "text", _height: 100, system: true, _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize) }) ], { _width: 400, _height: 300, title: "slideView", _xMargin: 3, _yMargin: 3, system: true } ); @@ -271,24 +271,28 @@ export class CurrentUserUtils { // setup the different note type skins static setupNoteTemplates(doc: Doc) { if (doc["template-note-Note"] === undefined) { - const noteView = Docs.Create.TextDocument("", { title: "text", isTemplateDoc: true, backgroundColor: "yellow", system: true }); + const noteView = Docs.Create.TextDocument("", { title: "text", isTemplateDoc: true, backgroundColor: "yellow", system: true, + _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), }); noteView.isTemplateDoc = makeTemplate(noteView, true, "Note"); doc["template-note-Note"] = new PrefetchProxy(noteView); } if (doc["template-note-Idea"] === undefined) { - const noteView = Docs.Create.TextDocument("", { title: "text", backgroundColor: "pink", system: true }); + const noteView = Docs.Create.TextDocument("", { title: "text", backgroundColor: "pink", system: true, + _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), }); noteView.isTemplateDoc = makeTemplate(noteView, true, "Idea"); doc["template-note-Idea"] = new PrefetchProxy(noteView); } if (doc["template-note-Topic"] === undefined) { - const noteView = Docs.Create.TextDocument("", { title: "text", backgroundColor: "lightblue", system: true }); + const noteView = Docs.Create.TextDocument("", { title: "text", backgroundColor: "lightblue", system: true, + _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), }); noteView.isTemplateDoc = makeTemplate(noteView, true, "Topic"); doc["template-note-Topic"] = new PrefetchProxy(noteView); } if (doc["template-note-Todo"] === undefined) { const noteView = Docs.Create.TextDocument("", { title: "text", backgroundColor: "orange", _autoHeight: false, _height: 100, _showCaption: "caption", - layout: FormattedTextBox.LayoutString("Todo"), caption: RichTextField.DashField("taskStatus"), system: true + layout: FormattedTextBox.LayoutString("Todo"), caption: RichTextField.DashField("taskStatus"), system: true, + _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), }); noteView.isTemplateDoc = makeTemplate(noteView, true, "Todo"); doc["template-note-Todo"] = new PrefetchProxy(noteView); @@ -472,7 +476,9 @@ export class CurrentUserUtils { ((doc.emptyAudio as Doc).proto as Doc)["dragFactory-count"] = 0; } if (doc.emptyNote === undefined) { - doc.emptyNote = Docs.Create.TextDocument("", { _width: 200, title: "text note", _autoHeight: true, system: true, cloneFieldFilter: new List(["system"]) }); + doc.emptyNote = Docs.Create.TextDocument("", { _width: 200, title: "text note", _autoHeight: true, system: true, + _fontFamily: StrCast(Doc.UserDoc()._fontFamily), _fontSize: StrCast(Doc.UserDoc()._fontSize), + cloneFieldFilter: new List(["system"]) }); ((doc.emptyNote as Doc).proto as Doc)["dragFactory-count"] = 0; } if (doc.emptyImage === undefined) { @@ -940,7 +946,7 @@ export class CurrentUserUtils { "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"], script: 'changeFont' }, - { title: "Font size", toolTip: "Font size", btnType: ButtonType.NumberButton, numType: NumButtonType.DropdownOptions, script: 'changeFontSize'}, + { title: "Font size", toolTip: "Font size", btnType: ButtonType.NumberButton, numType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'changeFontSize'}, { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', script: 'toggleBold' }, { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', script: 'toggleItalic' }, { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', script: 'toggleUnderline' }, @@ -964,7 +970,7 @@ export class CurrentUserUtils { { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")' }, { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' }, { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "fill-drip", click: 'setActiveInkTool("line")' }, - { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numType: NumButtonType.Slider, script: 'setLineWidth'}, + { title: "Stroke width", toolTip: "Stroke width", width:75, btnType: ButtonType.NumberButton, numType: NumButtonType.Slider, ignoreClick: true, script: 'setLineWidth'}, ]; return tools; } diff --git a/src/client/util/SettingsManager.scss b/src/client/util/SettingsManager.scss index c9db94419..b7199f433 100644 --- a/src/client/util/SettingsManager.scss +++ b/src/client/util/SettingsManager.scss @@ -360,17 +360,18 @@ flex-direction: row; position: relative; min-height: 250px; + height: 100%; width: 100%; .settings-content { - background-color: #fdfdfd; + background-color: $off-white; } } .settings-panel { position: relative; min-width: 150px; - background-color: #e4e4e4; + background-color: $light-blue; .settings-user { position: absolute; diff --git a/src/client/views/MainViewModal.scss b/src/client/views/MainViewModal.scss index 5f19590b4..03cb5cc84 100644 --- a/src/client/views/MainViewModal.scss +++ b/src/client/views/MainViewModal.scss @@ -4,6 +4,7 @@ z-index: 10000; width: 100%; height: 100%; + .dialogue-box { position: absolute; z-index: 1000; @@ -11,11 +12,8 @@ justify-content: center; align-self: center; align-content: center; - padding: 20px; - // background: gainsboro; background: white; border-radius: 10px; - border: 0.5px solid black; box-shadow: #00000044 5px 5px 10px; transform: translate(-50%, -50%); top: 50%; diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss index 2fd67a8dc..caa9f4fe5 100644 --- a/src/client/views/global/globalCssVariables.scss +++ b/src/client/views/global/globalCssVariables.scss @@ -1,6 +1,7 @@ @import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap"); // colors $white: #ffffff; +$off-white: #fdfdfd; $light-gray: #dfdfdf; $medium-gray: #9f9f9f; $dark-gray: #323232; diff --git a/src/client/views/nodes/DocumentLinksButton.scss b/src/client/views/nodes/DocumentLinksButton.scss index b37b68249..228e1bdcb 100644 --- a/src/client/views/nodes/DocumentLinksButton.scss +++ b/src/client/views/nodes/DocumentLinksButton.scss @@ -50,6 +50,7 @@ width: 80%; height: 80%; font-size: 100%; + font-family: 'Roboto'; transition: 0.2s ease all; &:hover { diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 7648e866e..bf9499d1d 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -266,6 +266,7 @@ export class DocumentLinksButton extends React.Component diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 7ff3e388f..f78bb6842 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -106,21 +106,15 @@ export class FontIconBox extends DocComponent(Fon }; // Script for checking the outcome of the toggle - const checkScript: string = StrCast(this.rootDoc.script) + "(true)"; + const checkScript: string = StrCast(this.rootDoc.script) + "(0, true)"; const checkResult: number = ScriptField.MakeScript(checkScript)?.script.run().result; - const dropdown = -
- {/* DROPDOWN BOX CONTENTS */} -
; if (numType === NumButtonType.Slider) { - return ( + const dropdown =
(Fon onPointerUp={() => { if (this._batch) this._batch.end(); }} onChange={(e: React.ChangeEvent) => { e.stopPropagation(); setValue(Number(e.target.value)); }} /> +
; + return ( +
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)} + > + {checkResult} + {dropdown}
); } else if (numType === NumButtonType.DropdownOptions) { + const items: number[] = []; + for (let i = 0; i < 100; i++) { + if (i % 2 === 0) { + items.push(i); + } + } + items.map((value) => { + return
setValue(value)}> + {value} +
; + }); return (
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)} > -
+
setValue(checkResult - 1))}>
-
+
e.stopPropagation()} + onDoubleClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)} + > setValue(Number(e.target.value)))} />
-
+
setValue(checkResult + 1))}>
- {this.rootDoc.dropDownOpen ? dropdown : null} + {this.rootDoc.dropDownOpen ? +
+
+ {items} +
+
{ e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} /> +
: null} +
); } else { @@ -576,9 +604,13 @@ Scripting.addGlobal(function changeFontHighlight(color?: string, checkResult?: b // toggle: Set overlay status of selected document -Scripting.addGlobal(function changeFontSize(size: string) { +Scripting.addGlobal(function changeFontSize(size: string, checkResult?: boolean) { + if (checkResult) { + const size: number = parseInt(StrCast(Doc.UserDoc()._fontSize), 10); + return size; + } console.log(size); - Doc.UserDoc()._fontSize = size; + Doc.UserDoc()._fontSize = size + "px"; }); Scripting.addGlobal(function toggleBold(checkResult?: boolean) { -- cgit v1.2.3-70-g09d2 From addda2b98e450fcad1e2cb328788048ff2fe578f Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 24 Aug 2021 12:09:06 -0400 Subject: started to fix doc filters to work on any document --- src/client/views/PropertiesView.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 2 ++ src/client/views/nodes/FilterBox.tsx | 30 +++++++++++++++++++----------- 3 files changed, 22 insertions(+), 12 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index de437e1df..17b137c31 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -903,7 +903,7 @@ export class PropertiesView extends React.Component { * If it doesn't exist, it creates it. */ checkFilterDoc() { - if (this.selectedDoc.type === DocumentType.COL && !this.selectedDoc.currentFilter) CurrentUserUtils.setupFilterDocs(this.selectedDoc); + if (!this.selectedDoc.currentFilter) CurrentUserUtils.setupFilterDocs(this.selectedDoc); } /** diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 121d2d556..bb259da3e 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -88,6 +88,8 @@ export interface DocComponentView { setKeyFrameEditing?: (set: boolean) => void; // whether the document is in keyframe editing mode (if it is, then all hidden documents that are not active at the keyframe time will still be shown) playFrom?: (time: number, endTime?: number) => void; setFocus?: () => void; + fieldKey?: string; + annotationKey?: string; } export interface DocumentViewSharedProps { renderDepth: number; diff --git a/src/client/views/nodes/FilterBox.tsx b/src/client/views/nodes/FilterBox.tsx index beefc4a82..1e13d1b5a 100644 --- a/src/client/views/nodes/FilterBox.tsx +++ b/src/client/views/nodes/FilterBox.tsx @@ -79,10 +79,19 @@ export class FilterBox extends ViewBoxBaseComponent(); - const activeTabs = DocListCast(targetDoc.data); + const activeTabs = FilterBox.targetDocChildren; SearchBox.foreachRecursiveDoc(activeTabs, (depth, doc) => allDocs.add(doc)); setTimeout(action(() => this._allDocs = Array.from(allDocs))); } @@ -133,8 +142,7 @@ export class FilterBox extends ViewBoxBaseComponent StrCast(attribute.title)); } - gatherFieldValues(dashboard: Doc, facetKey: string) { - const childDocs = DocListCast(dashboard.data); + gatherFieldValues(childDocs: Doc[], facetKey: string) { const valueSet = new Set(); let rtFields = 0; childDocs.forEach((d) => { @@ -194,13 +202,13 @@ export class FilterBox extends ViewBoxBaseComponent { - const { targetDoc } = FilterBox; + const { targetDoc, targetDocChildren } = FilterBox; const found = this.activeAttributes.findIndex(doc => doc.title === facetHeader); if (found !== -1) { this.removeFilter(facetHeader); } else { - const allCollectionDocs = DocListCast((targetDoc.data as any)?.[0].data); - const facetValues = this.gatherFieldValues(targetDoc, facetHeader); + const allCollectionDocs = targetDocChildren; + const facetValues = this.gatherFieldValues(targetDocChildren, facetHeader); let nonNumbers = 0; let minVal = Number.MAX_VALUE, maxVal = -Number.MAX_VALUE; @@ -248,7 +256,7 @@ export class FilterBox extends ViewBoxBaseComponent(); - const activeTabs = DocListCast(layoutDoc.data); + const activeTabs = DocListCast(layoutDoc[childKey]); SearchBox.foreachRecursiveDoc(activeTabs, (depth: number, doc: Doc) => allCollectionDocs.add(doc)); const set = new Set(); if (facetHeader === "tags") allCollectionDocs.forEach(child => Field.toString(child[facetHeader] as Field).split(":").forEach(key => set.add(key))); -- cgit v1.2.3-70-g09d2 From 5867ca16f8a8beaf7daf754ee5c42a5cc249346f Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 24 Aug 2021 14:28:13 -0400 Subject: fixed undo for dragging docs. made separate layers for transparent and other annotations on pdfs/webs so that transparency will work better. --- src/Utils.ts | 10 ++++++++ src/client/util/DragManager.ts | 12 +++++----- src/client/views/nodes/WebBox.tsx | 48 +++++++++++++++++++++----------------- src/client/views/pdf/PDFViewer.tsx | 18 +++++++++----- src/fields/Doc.ts | 5 ++++ 5 files changed, 60 insertions(+), 33 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/Utils.ts b/src/Utils.ts index f251f776c..f90296121 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -115,6 +115,16 @@ export namespace Utils { return { r: r, g: g, b: b, a: a }; } + export function IsTransparentFilter() { + // bcz: isTransparent(__value__) is a hack. it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one + return ["backgroundColor:isTransparent(__value__):check"]; + } + export function IsOpaqueFilter() { + // bcz: isTransparent(__value__) is a hack. it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one + return ["backgroundColor:isTransparent(__value__):x"]; + } + + export function toRGBAstr(col: { r: number, g: number, b: number, a?: number }) { return "rgba(" + col.r + "," + col.g + "," + col.b + (col.a !== undefined ? "," + col.a : "") + ")"; } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 5e16de617..f7ef9ae6f 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -425,10 +425,10 @@ export namespace DragManager { AbortDrag = () => { options?.dragComplete?.(new DragCompleteEvent(true, dragData)); - endDrag(); + cleanupDrag(); }; - const endDrag = action(() => { + const cleanupDrag = action(() => { hideDragShowOriginalElements(false); document.removeEventListener("pointermove", moveHandler, true); document.removeEventListener("pointerup", upHandler); @@ -518,15 +518,14 @@ export namespace DragManager { `translate(${(xs[i] += moveVec.x) + (options?.offsetX || 0)}px, ${(ys[i] += moveVec.y) + (options?.offsetY || 0)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`) ); }; - const upHandler = async (e: PointerEvent) => { - dispatchDrag(document.elementFromPoint(e.x, e.y) || document.body, e, new DragCompleteEvent(false, dragData), snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom), finishDrag, options); - endDrag(); + const upHandler = (e: PointerEvent) => { + dispatchDrag(document.elementFromPoint(e.x, e.y) || document.body, e, new DragCompleteEvent(false, dragData), snapDrag(e, xFromLeft, yFromTop, xFromRight, yFromBottom), finishDrag, options, cleanupDrag); }; document.addEventListener("pointermove", moveHandler, true); document.addEventListener("pointerup", upHandler); } - async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number, y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions) { + async function dispatchDrag(target: Element, e: PointerEvent, complete: DragCompleteEvent, pos: { x: number, y: number }, finishDrag?: (e: DragCompleteEvent) => void, options?: DragOptions, endDrag?: () => void) { const dropArgs = { bubbles: true, detail: { @@ -543,5 +542,6 @@ export namespace DragManager { await finishDrag?.(complete); target.dispatchEvent(new CustomEvent("dashOnDrop", dropArgs)); options?.dragComplete?.(complete); + endDrag?.(); } } \ No newline at end of file diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 0c32a30db..94697464a 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -512,6 +512,29 @@ export class WebBox extends ViewBoxAnnotatableComponent string[]) => + ; return (
@@ -529,27 +552,10 @@ export class WebBox extends ViewBoxAnnotatableComponent
{this.content} - +
+ {renderAnnotations(Utils.IsTransparentFilter)} +
+ {renderAnnotations(Utils.IsOpaqueFilter)} {this.annotationLayer}
diff --git a/src/client/views/pdf/PDFViewer.tsx b/src/client/views/pdf/PDFViewer.tsx index 41a60bedf..2c00b005d 100644 --- a/src/client/views/pdf/PDFViewer.tsx +++ b/src/client/views/pdf/PDFViewer.tsx @@ -507,12 +507,7 @@ export class PDFViewer extends React.Component { panelWidth = () => this.props.PanelWidth() / (this.props.scaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); panelHeight = () => this.props.PanelHeight() / (this.props.scaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); @computed get overlayLayer() { - return
anno.mixBlendMode) ? "hard-light" : undefined, - transform: `scale(${this._zoomed})` - }}> + const renderAnnotations = (docFilters: () => string[]) => { select={emptyFunction} ContentScaling={this.contentZoom} bringToFront={emptyFunction} + docFilters={doFilters} CollectionView={undefined} ScreenToLocalTransform={this.overlayTransform} renderDepth={this.props.renderDepth + 1} childPointerEvents={true} /> + return
anno.mixBlendMode) ? "hard-light" : undefined, + transform: `scale(${this._zoomed})` + }}> +
+ {renderAnnotations(Utils.IsTransparentFilter)} +
+ {renderAnnotations(Utils.IsOpaqueFilter)}
; } @computed get pdfViewerDiv() { diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index e4087cf43..f6efefdf9 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -25,6 +25,7 @@ import { deleteProperty, GetEffectiveAcl, getField, getter, inheritParentAcls, m import JSZip = require("jszip"); import { CurrentUserUtils } from "../client/util/CurrentUserUtils"; import { IconProp } from "@fortawesome/fontawesome-svg-core"; +import Color = require("color"); export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { @@ -1087,6 +1088,10 @@ export namespace Doc { } export function matchFieldValue(doc: Doc, key: string, value: any): boolean { + if (value === "isTransparent(__value__)") { + const isTransparent = (color: string) => color !== "" && (Color(color).alpha() !== 1); + return isTransparent(StrCast(doc[key])); + } const fieldVal = doc[key]; if (Cast(fieldVal, listSpec("string"), []).length) { const vals = Cast(fieldVal, listSpec("string"), []); -- cgit v1.2.3-70-g09d2 From 66dd6788fbf46cd827d80eb97810e4d2af24eb26 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 24 Aug 2021 14:52:35 -0400 Subject: suppressed filter icon for annotations that are filtered based on transparency. fixed drawing ink on webbox's. --- src/client/views/collections/CollectionSubView.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index c7629acbe..55b1e4031 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -93,7 +93,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: childDocFilters = () => [...this.props.docFilters(), ...this.collectionFilters()]; childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()]; IsFiltered = () => this.collectionFilters().length || this.collectionRangeDocFilters().length ? "hasFilter" : - this.props.docFilters().length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined + this.props.docFilters().filter(f => !f.includes("__value__")).length || this.props.docRangeFilters().length ? "inheritsFilter" : undefined searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs); @computed.struct get childDocs() { TraceMobx(); diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 94697464a..cdc79678a 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -534,7 +534,7 @@ export class WebBox extends ViewBoxAnnotatableComponent; + pointerEvents={CurrentUserUtils.SelectedTool !== InkTool.None || this._isAnnotating || SnappingManager.GetIsDragging() ? "all" : "none"} />; return (
-- cgit v1.2.3-70-g09d2 From 325d3a8c931cb9ff7eadfa29603a3c7ca747911b Mon Sep 17 00:00:00 2001 From: geireann <60007097+geireann@users.noreply.github.com> Date: Tue, 24 Aug 2021 18:10:13 -0400 Subject: css changes for toggle button --- src/client/documents/Documents.ts | 4 + src/client/util/CurrentUserUtils.ts | 15 ++- src/client/views/nodes/button/FontIconBox.scss | 177 +++++++++++++++---------- src/client/views/nodes/button/FontIconBox.tsx | 85 +++++++----- 4 files changed, 176 insertions(+), 105 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 63acd004f..0b33943d6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -224,6 +224,10 @@ export class DocumentOptions { userColorBtn?: string; canClick?: string; script?: string; + numBtnType?: string; + numBtnMax?: number; + numBtnMin?: number; + switchToggle?: boolean; //LINEAR VIEW linearViewIsExpanded?: boolean; // is linear view expanded diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 73a66332f..8044f59f0 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -46,7 +46,9 @@ interface Button { icon?: string; btnType?: ButtonType; click?: string; - numType?: NumButtonType; + numBtnType?: NumButtonType; + numBtnMin?: number; + numBtnMax?: number; switchToggle?: boolean; script?: string; width?: number; @@ -946,7 +948,7 @@ export class CurrentUserUtils { "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"], script: 'changeFont' }, - { title: "Font size", toolTip: "Font size", btnType: ButtonType.NumberButton, numType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'changeFontSize'}, + { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'changeFontSize'}, { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', script: 'toggleBold' }, { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', script: 'toggleItalic' }, { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', script: 'toggleUnderline' }, @@ -970,7 +972,7 @@ export class CurrentUserUtils { { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")' }, { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' }, { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "fill-drip", click: 'setActiveInkTool("line")' }, - { title: "Stroke width", toolTip: "Stroke width", width:75, btnType: ButtonType.NumberButton, numType: NumButtonType.Slider, ignoreClick: true, script: 'setLineWidth'}, + { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, ignoreClick: true, script: 'setStrokeWidth'}, ]; return tools; } @@ -982,6 +984,7 @@ export class CurrentUserUtils { title: "Show preview", toolTip: "Show preview of selected document", btnType: ButtonType.ToggleButton, + width: 50, switchToggle: true, icon: "eye", click: 'toggleSchemaShow()', @@ -1050,9 +1053,10 @@ export class CurrentUserUtils { tools = CurrentUserUtils.textTools(doc); break; default: + tools = CurrentUserUtils.textTools(doc); break; } - tools.map(({ title, toolTip, icon, btnType, numType, click, script, width, list, ignoreClick }) => { + tools.map(({ title, toolTip, icon, btnType, numBtnType, click, script, width, list, ignoreClick, switchToggle }) => { menuDocList.push(Docs.Create.FontIconDocument({ _nativeWidth: width ? width : 25, _nativeHeight: 25, @@ -1060,7 +1064,7 @@ export class CurrentUserUtils { _height: 25, icon, toolTip, - numType, + numBtnType, script, btnType: btnType, btnList: new List(list), @@ -1070,6 +1074,7 @@ export class CurrentUserUtils { system: true, dontUndo: true, title, + switchToggle, color: Colors.WHITE, backgroundColor: "transparent", _dropAction: "alias", diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss index b6aa2ba60..7a6887d38 100644 --- a/src/client/views/nodes/button/FontIconBox.scss +++ b/src/client/views/nodes/button/FontIconBox.scss @@ -52,6 +52,71 @@ &.tglBtn { cursor: pointer; + &.switch { + //TOGGLE + + .switch { + position: relative; + display: inline-block; + width: 100%; + height: 25px; + margin: 0; + } + + .switch input { + opacity: 0; + width: 0; + height: 0; + } + + .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: lightgrey; + -webkit-transition: .4s; + transition: .4s; + } + + .slider:before { + position: absolute; + content: ""; + height: 21px; + width: 21px; + left: 2px; + bottom: 2px; + background-color: $white; + -webkit-transition: .4s; + transition: .4s; + } + + input:checked+.slider { + background-color: $medium-blue; + } + + input:focus+.slider { + box-shadow: 0 0 1px $medium-blue; + } + + input:checked+.slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); + } + + /* Rounded sliders */ + .slider.round { + border-radius: $standard-border-radius; + } + + .slider.round:before { + border-radius: $standard-border-radius; + } + } + svg { width: 50% !important; height: 50%; @@ -115,7 +180,7 @@ } &.drpdownList { - width: 100px; + width: 100%; display: grid; grid-auto-columns: 80px 20px; justify-items: center; @@ -165,7 +230,16 @@ cursor: pointer; background: transparent; + &:hover { + background-color: rgba(0, 0, 0, 0.3) !important; + } + &.slider { + color: $white; + cursor: pointer; + flex-direction: column; + background: transparent; + .numberBtn-slider {} .menuButton-dropdownBox { @@ -180,11 +254,43 @@ } } - &.dropdown { + .button { + width: 30%; + display: flex; + align-items: center; + justify-content: center; + + &.number { + width: 40%; + + .button-input { + background: none; + border: none; + text-align: right; + width: 100%; + color: black; + height: 100%; + text-align: center; + } + + .button-input:focus { + outline: none; + } + } + } + + &.list { + width: 100%; + justify-content: space-around; + border: $standard-border; + .menuButton-dropdownList { position: absolute; width: fit-content; height: fit-content; + min-width: 50px; + max-height: 50vh; + overflow: scroll; top: 100%; z-index: 21; background-color: $white; @@ -197,9 +303,8 @@ height: 25px; font-weight: 400; display: flex; - justify-content: left; + justify-content: center; align-items: center; - padding-left: 5px; } .list-item:hover { @@ -219,7 +324,6 @@ background-color: $close-red; } - } &.drpDownBtn { @@ -274,67 +378,4 @@ position: fixed; } -} - - -//TOGGLE - -.switch { - position: relative; - display: inline-block; - width: 50px; - height: 25px; -} - -.switch input { - opacity: 0; - width: 0; - height: 0; -} - -.slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: lightgrey; - -webkit-transition: .4s; - transition: .4s; -} - -.slider:before { - position: absolute; - content: ""; - height: 22px; - width: 22px; - left: 4px; - bottom: 4px; - background-color: white; - -webkit-transition: .4s; - transition: .4s; -} - -input:checked+.slider { - background-color: $medium-blue; -} - -input:focus+.slider { - box-shadow: 0 0 1px $medium-blue; -} - -input:checked+.slider:before { - -webkit-transform: translateX(26px); - -ms-transform: translateX(26px); - transform: translateX(26px); -} - -/* Rounded sliders */ -.slider.round { - border-radius: $standard-border-radius; -} - -.slider.round:before { - border-radius: $standard-border-radius; } \ No newline at end of file diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index f78bb6842..4778c2f10 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -44,7 +44,7 @@ export enum ButtonType { export enum NumButtonType { Slider = "slider", - DropdownOptions = "dropdown", + DropdownOptions = "list", Inline = "inline" } @@ -98,7 +98,7 @@ export class FontIconBox extends DocComponent(Fon * Number button */ @computed get numberButton() { - const numType: string = StrCast(this.rootDoc.numType); + const numBtnType: string = StrCast(this.rootDoc.numBtnType); const setValue = (value: number) => { // Script for running the toggle const script: string = StrCast(this.rootDoc.script) + "(" + value + ")"; @@ -110,14 +110,14 @@ export class FontIconBox extends DocComponent(Fon const checkResult: number = ScriptField.MakeScript(checkScript)?.script.run().result; - if (numType === NumButtonType.Slider) { + if (numBtnType === NumButtonType.Slider) { const dropdown =
{ this._batch = UndoManager.StartBatch("presDuration"); }} onPointerUp={() => { if (this._batch) this._batch.end(); }} onChange={(e: React.ChangeEvent) => { e.stopPropagation(); setValue(Number(e.target.value)); }} @@ -125,21 +125,21 @@ export class FontIconBox extends DocComponent(Fon
; return (
this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)} > {checkResult} - {dropdown} + {this.rootDoc.dropDownOpen ? dropdown : null}
); - } else if (numType === NumButtonType.DropdownOptions) { + } else if (numBtnType === NumButtonType.DropdownOptions) { const items: number[] = []; for (let i = 0; i < 100; i++) { if (i % 2 === 0) { items.push(i); } } - items.map((value) => { + const list = items.map((value) => { return
(Fon }); return (
-
setValue(checkResult - 1))}> - +
setValue(checkResult - 1))}> +
e.stopPropagation()} + className={`button ${'number'}`} + onPointerDown={(e) => { + e.stopPropagation(); + e.preventDefault(); + }} onDoubleClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)} > setValue(Number(e.target.value)))} />
-
setValue(checkResult + 1))}> +
setValue(checkResult + 1))}>
{this.rootDoc.dropDownOpen ?
- {items} + {list}
{ e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} />
: null} @@ -396,17 +400,12 @@ export class FontIconBox extends DocComponent(Fon
{this.label}
; - console.log("switchToggle"); - return ( - !switchToggle ? -
- - {label} -
: -
- ); + ); + } else { + return ( +
+ + {label} +
+ ); + } } @@ -444,11 +454,13 @@ export class FontIconBox extends DocComponent(Fon return true; }; - return
- - ""} SetValue={setValue} contents="..."> - -
; + return ( +
+ + ""} SetValue={setValue} contents="..."> + +
+ ); } @@ -649,4 +661,13 @@ Scripting.addGlobal(function setActiveInkTool(tool: InkTool, checkResult?: boole } else { Doc.UserDoc().activeInkTool = InkTool.None; } +}); + +Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean) { + if (checkResult) { + const width: number = NumCast(Doc.UserDoc().activeInkWidth); + return width; + } + console.log("[width]: " + width); + Doc.UserDoc().activeInkWidth = width; }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 0c5a95f37e91a07041699514155fd214f544a0de Mon Sep 17 00:00:00 2001 From: geireann Date: Tue, 24 Aug 2021 19:06:43 -0400 Subject: updates --- src/client/util/CurrentUserUtils.ts | 14 ++++++----- src/client/views/MainView.tsx | 2 +- src/client/views/nodes/button/FontIconBox.scss | 15 ++++++------ src/client/views/nodes/button/FontIconBox.tsx | 32 ++++++++++++++++++-------- 4 files changed, 40 insertions(+), 23 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 8044f59f0..20274672a 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -967,11 +967,11 @@ export class CurrentUserUtils { static inkTools(doc: Doc) { const tools:Button[] = [ - { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen")'}, - { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")' }, - { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")' }, - { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")' }, - { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "fill-drip", click: 'setActiveInkTool("line")' }, + { title: "Pen", toolTip: "Pen (Ctrl+P)", btnType: ButtonType.ToggleButton, icon: "pen", click: 'setActiveInkTool("pen")', script: 'setActiveInkTool("pen" , true)'}, + { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")', script:'setActiveInkTool("highlighter", true)' }, + { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")', script: 'setActiveInkTool("circle" , true)' }, + { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")', script: 'setActiveInkTool("square" , true)' }, + { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "fill-drip", click: 'setActiveInkTool("line")', script: 'setActiveInkTool("line" , true)' }, { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, ignoreClick: true, script: 'setStrokeWidth'}, ]; return tools; @@ -987,7 +987,7 @@ export class CurrentUserUtils { width: 50, switchToggle: true, icon: "eye", - click: 'toggleSchemaShow()', + ignoreClick: true, script: 'toggleSchemaShow' }, ]; @@ -1071,6 +1071,7 @@ export class CurrentUserUtils { ignoreClick: ignoreClick, _stayInCollection: true, _hideContextMenu: true, + _lockedPosition: true, system: true, dontUndo: true, title, @@ -1097,6 +1098,7 @@ export class CurrentUserUtils { ignoreClick: ignoreClick, _stayInCollection: true, _hideContextMenu: true, + _lockedPosition: true, system: true, dontUndo: true, title, diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 5c1408148..75c57909d 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -331,7 +331,7 @@ export class MainView extends React.Component { PanelHeight={this.getContentsHeight} renderDepth={0} isContentActive={returnTrue} - scriptContext={CollectionDockingView.Instance.props.Document} + scriptContext={CollectionDockingView.Instance?.props.Document} focus={DocUtils.DefaultFocus} whenChildContentsActiveChanged={emptyFunction} bringToFront={emptyFunction} diff --git a/src/client/views/nodes/button/FontIconBox.scss b/src/client/views/nodes/button/FontIconBox.scss index 7a6887d38..dfb549b95 100644 --- a/src/client/views/nodes/button/FontIconBox.scss +++ b/src/client/views/nodes/button/FontIconBox.scss @@ -240,7 +240,9 @@ flex-direction: column; background: transparent; - .numberBtn-slider {} + .menu-slider { + width: 100px; + } .menuButton-dropdownBox { position: absolute; @@ -255,20 +257,20 @@ } .button { - width: 30%; + width: 25%; display: flex; align-items: center; justify-content: center; &.number { - width: 40%; + width: 50%; .button-input { background: none; border: none; text-align: right; width: 100%; - color: black; + color: $white; height: 100%; text-align: center; } @@ -288,9 +290,9 @@ position: absolute; width: fit-content; height: fit-content; - min-width: 50px; + min-width: 50%; max-height: 50vh; - overflow: scroll; + overflow-y: scroll; top: 100%; z-index: 21; background-color: $white; @@ -323,7 +325,6 @@ &:hover { background-color: $close-red; } - } &.drpDownBtn { diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 4778c2f10..744064b9f 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -25,6 +25,7 @@ import { StyleProp } from '../../StyleProvider'; import { FieldView, FieldViewProps } from '.././FieldView'; import { RichTextMenu } from '../formattedText/RichTextMenu'; import './FontIconBox.scss'; +import { SetActiveInkWidth } from '../../InkingStroke'; const FontIconSchema = createSchema({ icon: "string", }); @@ -177,7 +178,7 @@ export class FontIconBox extends DocComponent(Fon {this.rootDoc.dropDownOpen ?
+ style={{ left: "25%" }}> {list}
{ e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} /> @@ -383,12 +384,19 @@ export class FontIconBox extends DocComponent(Fon const switchToggle: boolean = BoolCast(this.rootDoc.switchToggle); // Script for running the toggle - const script: string = StrCast(this.rootDoc.script) + "()"; + const script: string = StrCast(this.rootDoc.script); + // Script for checking the outcome of the toggle - const checkScript: string = StrCast(this.rootDoc.script) + "(true)"; + let checkScript:string; + if (StrCast(this.rootDoc.script).includes("(")){ + checkScript = StrCast(this.rootDoc.script) + + } else { + checkScript = StrCast(this.rootDoc.script) + "(true)"; + } // Function to run the script - const runScript = () => ScriptField.MakeScript(script)?.script.run(); + const runScript = () => ScriptField.MakeScript(script + "()")?.script.run(); const checkResult = ScriptField.MakeScript(checkScript)?.script.run().result; // Colors @@ -456,9 +464,10 @@ export class FontIconBox extends DocComponent(Fon return (
- + HELLO + {/* ""} SetValue={setValue} contents="..."> - + */}
); } @@ -492,6 +501,7 @@ export class FontIconBox extends DocComponent(Fon switch (this.type) { case ButtonType.EditableText: + console.log("Editable text"); button = this.editableText; case ButtonType.NumberButton: button = this.numberButton; @@ -657,7 +667,11 @@ Scripting.addGlobal(function setActiveInkTool(tool: InkTool, checkResult?: boole return Doc.UserDoc().activeInkTool === tool; } if (tool) { - Doc.UserDoc().activeInkTool = tool; + if (Doc.UserDoc().activeInkTool === tool){ + Doc.UserDoc().activeInkTool = InkTool.None; + } else { + Doc.UserDoc().activeInkTool = tool; + } } else { Doc.UserDoc().activeInkTool = InkTool.None; } @@ -668,6 +682,6 @@ Scripting.addGlobal(function setStrokeWidth(width: number, checkResult?: boolean const width: number = NumCast(Doc.UserDoc().activeInkWidth); return width; } - console.log("[width]: " + width); - Doc.UserDoc().activeInkWidth = width; + SetActiveInkWidth(StrCast(width)); + SelectionManager.Views().filter(i => StrCast(i.rootDoc.type) === DocumentType.INK).map(i => i.rootDoc.strokeWidth = Number(width)); }); \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 6376a4b6d6a730c6c170fa25dd5f19dea6b8f82a Mon Sep 17 00:00:00 2001 From: geireann Date: Wed, 25 Aug 2021 05:20:50 -0400 Subject: many updates --- src/client/documents/Documents.ts | 4 +- src/client/util/CurrentUserUtils.ts | 73 ++++--- src/client/util/LinkManager.ts | 7 +- src/client/util/Scripting.ts | 6 - src/client/views/EditableView.tsx | 2 + src/client/views/GestureOverlay.tsx | 5 +- src/client/views/InkingStroke.tsx | 4 +- src/client/views/MainView.tsx | 1 - .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../collectionLinearView/CollectionLinearView.tsx | 3 +- src/client/views/nodes/DocumentView.tsx | 12 +- src/client/views/nodes/button/FontIconBox.scss | 3 +- src/client/views/nodes/button/FontIconBox.tsx | 238 ++++++++++++++------- .../views/nodes/formattedText/RichTextMenu.tsx | 1 + src/fields/InkField.ts | 6 +- 15 files changed, 234 insertions(+), 133 deletions(-) (limited to 'src/client/views/nodes') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 1c4b5218c..f3a472329 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -698,14 +698,14 @@ export namespace Docs { return linkDoc; } - export function InkDocument(color: string, tool: string, strokeWidth: string, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) { + export function InkDocument(color: string, tool: string, strokeWidth: number, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) { const I = new Doc(); I[Initializing] = true; I.type = DocumentType.INK; I.layout = InkingStroke.LayoutString("data"); I.color = color; I.fillColor = fillColor; - I.strokeWidth = Number(strokeWidth); + I.strokeWidth = strokeWidth; I.strokeBezier = strokeBezier; I.strokeStartMarker = arrowStart; I.strokeEndMarker = arrowEnd; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 20274672a..7d1cbebdf 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -946,21 +946,19 @@ export class CurrentUserUtils { title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, list: ["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"], - script: 'changeFont' + script: 'setFont' }, - { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'changeFontSize'}, + { title: "Font size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions, ignoreClick: true, script: 'setFontSize'}, + { title: "Font color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script:'setFontColor' }, { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", click: 'toggleBold()', script: 'toggleBold' }, { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", click: 'toggleItalic()', script: 'toggleItalic' }, { title: "Underline", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", click: 'toggleUnderline()', script: 'toggleUnderline' }, // { title: "Strikethrough", tooltip: "Strikethrough", btnType: ButtonType.ToggleButton, icon: "strikethrough", click: 'toggleStrikethrough()'}, // { title: "Superscript", tooltip: "Superscript", btnType: ButtonType.ToggleButton, icon: "superscript", click: 'toggleSuperscript()'}, // { title: "Subscript", tooltip: "Subscript", btnType: ButtonType.ToggleButton, icon: "subscript", click: 'toggleSubscript()'}, - { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", ignoreClick: true, script:'changeAlignment("left")' }, - { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", ignoreClick: true, script:'changeAlignment("center")' }, - { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", ignoreClick: true, script:'changeAlignment("right")' }, - - { title: "Highlight", toolTip: "Highlight", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, script:'changeFontColor' }, - { title: "Text color", toolTip: "Text color", btnType: ButtonType.ColorButton, icon: "highlighter", ignoreClick: true, script:'changeHighlightColor' }, + { title: "Left align", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", ignoreClick: true, script:'setAlignment("left")' }, + { title: "Center align", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", ignoreClick: true, script:'setAlignment("center")' }, + { title: "Right align", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", ignoreClick: true, script:'setAlignment("right")' }, ]; return tools; } @@ -971,8 +969,10 @@ export class CurrentUserUtils { { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", click: 'setActiveInkTool("highlighter")', script:'setActiveInkTool("highlighter", true)' }, { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", click: 'setActiveInkTool("circle")', script: 'setActiveInkTool("circle" , true)' }, { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveInkTool("square")', script: 'setActiveInkTool("square" , true)' }, - { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "fill-drip", click: 'setActiveInkTool("line")', script: 'setActiveInkTool("line" , true)' }, + { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", click: 'setActiveInkTool("line")', script: 'setActiveInkTool("line" , true)' }, { title: "Stroke width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, numBtnType: NumButtonType.Slider, ignoreClick: true, script: 'setStrokeWidth'}, + { title: "Stroke color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "minus", ignoreClick: true, script:'setStrokeColor' }, + ]; return tools; } @@ -1015,18 +1015,18 @@ export class CurrentUserUtils { CollectionViewType.Multirow, CollectionViewType.Time, CollectionViewType.Carousel, CollectionViewType.Carousel3D, CollectionViewType.Linear, CollectionViewType.Map, CollectionViewType.Grid], - script: 'changeView', - }, + script: 'setView', + }, // Always show { title: "Background", toolTip: "Background", btnType: ButtonType.ColorButton, ignoreClick: true, icon: "fill-drip", - script: "changeBackgroundColor" - }, - { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', script:'toggleOverlay'}, - { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy" }, - { title: "Text", type: "textTools", subMenu: true }, - { title: "Ink & GFX", type: "inkTools", subMenu: true }, - { title: "Web", type: "webTools", subMenu: true }, - { title: "Schema", type: "schemaTools", subMenu: true } + script: "setBackgroundColor", hidden:'selectedDocumentType()' + }, // Only when a document is selected + { title: "Overlay", toolTip: "Overlay", btnType: ButtonType.ToggleButton, icon: "layer-group", click: 'toggleOverlay()', script:'toggleOverlay', hidden:'selectedDocumentType(undefined, "freeform", true)'}, // Only when floating document is selected in freeform + { title: "Alias", btnType: ButtonType.ClickButton, icon: "copy", hidden:'selectedDocumentType()' }, // Only when a document is selected + { title: "Text", type: "textTools", subMenu: true, expanded:'selectedDocumentType("rtf")' }, // Always available + { title: "Ink & GFX", type: "inkTools", subMenu: true, expanded:'selectedDocumentType("ink")' }, // Always available + { title: "Web", type: "webTools", subMenu: true, hidden:'selectedDocumentType("web")' }, // Only when Web is selected + { title: "Schema", type: "schemaTools", subMenu: true, hidden:'selectedDocumentType(undefined, "schema")' } // Only when Schema is selected ]; } @@ -1034,7 +1034,7 @@ export class CurrentUserUtils { static async setupContextMenuButtons(doc: Doc) { const docList: Doc[] = []; - (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu }) => { + (await CurrentUserUtils.contextMenuTools(doc)).map(({ title, width, list, toolTip, ignoreClick, icon, type, btnType, click, script, subMenu, hidden, expanded }) => { const menuDocList: Doc[] = []; if (subMenu) { // default is textTools @@ -1083,7 +1083,17 @@ export class CurrentUserUtils { onClick: click ? ScriptField.MakeScript(click, { doc: Doc.name }) : undefined })); }); - docList.push(CurrentUserUtils.linearButtonList({ linearViewSubMenu: true, flexGap: 5, ignoreClick: true, linearViewExpandable: true, icon:title, _height: 30, backgroundColor: "transparent" }, menuDocList)); + docList.push(CurrentUserUtils.linearButtonList({ + linearViewSubMenu: true, + flexGap: 5, + ignoreClick: true, + linearViewExpandable: true, + icon:title, + _height: 30, + backgroundColor: "transparent", + linearViewIsExpanded: expanded ? ComputedField.MakeFunction(expanded) as any : undefined, + hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined, + }, menuDocList)); } else { docList.push(Docs.Create.FontIconDocument({ _nativeWidth: width ? width : 25, @@ -1105,6 +1115,7 @@ export class CurrentUserUtils { color: Colors.WHITE, backgroundColor: "transparent", _dropAction: "alias", + hidden: hidden ? ComputedField.MakeFunction(hidden) as any : undefined, _removeDropProperties: new List(["dropAction", "_stayInCollection"]), onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined })); @@ -1177,7 +1188,7 @@ export class CurrentUserUtils { } if (doc.myImportPanel === undefined) { const uploads = Cast(doc.myImportDocs, Doc, null); - const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", type: ButtonType.ToolButton, icon: "upload", system: true }); + const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", btnType: ButtonType.ToolButton, icon: "upload", system: true }); doc.myImportPanel = new PrefetchProxy(Docs.Create.StackingDocument([newUpload, uploads], { title: "My ImportPanel", _yMargin: 20, _showTitle: "title", ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true, boxShadow: "0 0" })); } } @@ -1257,8 +1268,8 @@ export class CurrentUserUtils { doc._raiseWhenDragged = true; doc._showLabel = false; doc._showMenuLabel = true; - doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0)"); - doc.activeInkWidth = StrCast(doc.activeInkWidth, "1"); + doc.activeInkColor = StrCast(doc.activeInkColor, "rgb(0, 0, 0, 0)"); + doc.activeInkWidth = NumCast(doc.activeInkWidth, 1); doc.activeInkBezier = StrCast(doc.activeInkBezier, "0"); doc.activeFillColor = StrCast(doc.activeFillColor, ""); doc.activeArrowStart = StrCast(doc.activeArrowStart, ""); @@ -1279,7 +1290,7 @@ export class CurrentUserUtils { doc.filterDocCount = 0; this.setupDefaultIconTemplates(doc); // creates a set of icon templates triggered by the document deoration icon this.setupDocTemplates(doc); // sets up the template menu of templates - this.setupImportSidebar(doc); + this.setupImportSidebar(doc); // sets up the import sidebar this.setupSearchSidebar(doc); // sets up the search sidebar this.setupActiveMobileMenu(doc); // sets up the current mobile menu for Dash Mobile this.setupOverlays(doc); // documents in overlay layer @@ -1567,3 +1578,15 @@ Scripting.addGlobal(function dynamicOffScreenDocs(dashboard: Doc) { } return []; }); +Scripting.addGlobal(function selectedDocumentType(docType?:DocumentType, colType?:CollectionViewType, checkParent?:boolean) { + let selected = SelectionManager.Views().length ? SelectionManager.Views()[0].rootDoc : undefined; + if (selected && checkParent){ + const parentDoc: Doc = Cast(selected.context, Doc, null); + selected = parentDoc; + } + console.log(selected, docType, colType); + if (selected && docType && selected.type === docType) return false; + else if (selected && colType && selected.viewType === colType) return false; + else if (selected && !colType && !docType) return false; + else return true; +}); diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 3579083e4..219c83b12 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -107,7 +107,12 @@ export class LinkManager { public getAllRelatedLinks(anchor: Doc) { return this.relatedLinker(anchor); } // finds all links that contain the given anchor public getAllDirectLinks(anchor: Doc): Doc[] { - return Array.from(Doc.GetProto(anchor)[DirectLinksSym]); + // FIXME:glr Why is Doc undefined? + if (Doc.GetProto(anchor)[DirectLinksSym]){ + return Array.from(Doc.GetProto(anchor)[DirectLinksSym]); + } else { + return []; + } } // finds all links that contain the given anchor relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] { diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index aa7c23527..efee37419 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -171,18 +171,12 @@ function Run(script: string | undefined, customParams: string[], diagnostics: an if (!options.editable) { batch = Doc.MakeReadOnly(); } - const result1 = compiledFunction.apply(thisParam, params); const result = compiledFunction.apply(thisParam, params).apply(thisParam, argsArray); if (batch) { batch.end(); } - if (script.includes('toggleOverlay(true)')){ - console.log("[Scripting.ts] r1: ", result1); - console.log("[Scripting.ts]: ", result); - } - return { success: true, result }; } catch (error) { diff --git a/src/client/views/EditableView.tsx b/src/client/views/EditableView.tsx index d5f8d3fcf..83336c180 100644 --- a/src/client/views/EditableView.tsx +++ b/src/client/views/EditableView.tsx @@ -155,6 +155,8 @@ export class EditableView extends React.Component { return wasFocused !== this._editing; } + + renderEditor() { console.log("render editor", this.props.autosuggestProps); return this.props.autosuggestProps diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index bbf21f22c..24f9501ce 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -14,6 +14,7 @@ import { DocUtils } from "../documents/Documents"; import { CurrentUserUtils } from "../util/CurrentUserUtils"; import { InteractionUtils } from "../util/InteractionUtils"; import { Scripting } from "../util/Scripting"; +import { SelectionManager } from "../util/SelectionManager"; import { Transform } from "../util/Transform"; import { CollectionFreeFormViewChrome } from "./collections/CollectionMenu"; import "./GestureOverlay.scss"; @@ -23,7 +24,6 @@ import { RadialMenu } from "./nodes/RadialMenu"; import HorizontalPalette from "./Palette"; import { Touchable } from "./Touchable"; import TouchScrollableMenu, { TouchScrollableMenuItem } from "./TouchScrollableMenu"; -import { SelectionManager } from "../util/SelectionManager"; @observer export class GestureOverlay extends Touchable { @@ -588,8 +588,9 @@ export class GestureOverlay extends Touchable { this.makePolygon(this.InkShape, false); this.dispatchGesture(GestureUtils.Gestures.Stroke); this._points = []; - if (!CollectionFreeFormViewChrome.Instance._keepPrimitiveMode) { + if (!CollectionFreeFormViewChrome.Instance?._keepPrimitiveMode) { this.InkShape = ""; + Doc.UserDoc().activeInkTool = InkTool.None; } } // if we're not drawing in a toolglass try to recognize as gesture diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 5fc159f14..db09849fd 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -5,7 +5,7 @@ import { Doc } from "../../fields/Doc"; import { documentSchema } from "../../fields/documentSchemas"; import { InkData, InkField, InkTool } from "../../fields/InkField"; import { makeInterface } from "../../fields/Schema"; -import { Cast, StrCast } from "../../fields/Types"; +import { Cast, StrCast, NumCast } from "../../fields/Types"; import { TraceMobx } from "../../fields/util"; import { setupMoveUpEvents, emptyFunction, returnFalse } from "../../Utils"; import { CognitiveServices } from "../cognitive_services/CognitiveServices"; @@ -168,7 +168,7 @@ export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activ export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ""); } export function ActiveArrowEnd(): string { return StrCast(ActiveInkPen()?.activeArrowEnd, ""); } export function ActiveDash(): string { return StrCast(ActiveInkPen()?.activeDash, "0"); } -export function ActiveInkWidth(): string { return StrCast(ActiveInkPen()?.activeInkWidth, "1"); } +export function ActiveInkWidth(): number { return NumCast(ActiveInkPen()?.activeInkWidth); } export function ActiveInkBezierApprox(): string { return StrCast(ActiveInkPen()?.activeInkBezier); } Scripting.addGlobal(function activateBrush(pen: any, width: any, color: any, fill: any, arrowStart: any, arrowEnd: any, dash: any) { CurrentUserUtils.SelectedTool = pen ? InkTool.Highlighter : InkTool.None; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 75c57909d..dcdf6b9c3 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -447,7 +447,6 @@ export class MainView extends React.Component { this._panelContent = "none"; this._sidebarContent.proto = undefined; this._flyoutWidth = 0; - console.log("close flyout"); }); remButtonDoc = (doc: Doc | Doc[]) => (doc instanceof Doc ? [doc] : doc).reduce((flg: boolean, doc) => flg && Doc.RemoveDocFromList(Doc.UserDoc().dockedBtns as Doc, "data", doc), true); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index d09d9b9d7..3c5a63c77 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -506,7 +506,7 @@ export class CollectionFreeFormView extends CollectionSubView { const nested = pair.layout._viewType === CollectionViewType.Linear; const dref = React.createRef(); + const hidden = pair.layout.hidden === true; // const scalable = pair.layout.onClick || pair.layout.onDragStart; - return