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 --- .../collections/CollectionStackedTimeline.scss | 140 ++- .../collections/CollectionStackedTimeline.tsx | 1147 +++++++++++++------- 2 files changed, 856 insertions(+), 431 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss index e456c0664..92476e298 100644 --- a/src/client/views/collections/CollectionStackedTimeline.scss +++ b/src/client/views/collections/CollectionStackedTimeline.scss @@ -1,70 +1,92 @@ .collectionStackedTimeline { + position: absolute; + width: 100%; + height: 100%; + border: gray solid 1px; + border-radius: 3px; + z-index: 1000; + overflow: hidden; + top: 0px; + + .collectionStackedTimeline-trim-shade { position: absolute; - width: 100%; height: 100%; - border: gray solid 1px; - border-radius: 3px; - z-index: 1000; - overflow: hidden; - top: 0px; + background-color: black; + opacity: 0.3; + } - .collectionStackedTimeline-selector { - position: absolute; - width: 10px; - top: 2.5%; - height: 95%; - background: lightblue; - border-radius: 5px; - opacity: 0.3; - z-index: 500; - border-style: solid; - border-color: darkblue; - border-width: 1px; - } + .collectionStackedTimeline-trim-controls { + height: 100%; + position: absolute; + border: 2px solid yellow; + display: flex; + justify-content: space-between; - .collectionStackedTimeline-current { - width: 1px; - height: 100%; - background-color: red; - position: absolute; - top: 0px; - pointer-events: none; + .collectionStackedTimeline-trim-handle { + background-color: yellow; + height: 100%; + width: 5px; + cursor: ew-resize; } + } + + .collectionStackedTimeline-selector { + position: absolute; + width: 10px; + top: 2.5%; + height: 95%; + background: lightblue; + border-radius: 5px; + opacity: 0.3; + z-index: 500; + border-style: solid; + border-color: darkblue; + border-width: 1px; + } - .collectionStackedTimeline-marker-timeline { - position: absolute; - top: 2.5%; - height: 95%; - border-radius: 4px; - &:hover { - opacity: 1; - } + .collectionStackedTimeline-current { + width: 1px; + height: 100%; + background-color: red; + position: absolute; + top: 0px; + pointer-events: none; + } - .collectionStackedTimeline-left-resizer, - .collectionStackedTimeline-resizer { - background: dimgrey; - position: absolute; - top: 0; - height: 100%; - width: 10px; - pointer-events: all; - cursor: ew-resize; - z-index: 100; - } - .collectionStackedTimeline-resizer { - right: 0; - } - .collectionStackedTimeline-left-resizer { - left: 0; - } + .collectionStackedTimeline-marker-timeline { + position: absolute; + top: 2.5%; + height: 95%; + border-radius: 4px; + &:hover { + opacity: 1; } - .collectionStackedTimeline-waveform { - position: absolute; - width: 100%; - height: 100%; - top: 0; - left: 0; - pointer-events: none; + .collectionStackedTimeline-left-resizer, + .collectionStackedTimeline-resizer { + background: dimgrey; + position: absolute; + top: 0; + height: 100%; + width: 10px; + pointer-events: all; + cursor: ew-resize; + z-index: 100; + } + .collectionStackedTimeline-resizer { + right: 0; + } + .collectionStackedTimeline-left-resizer { + left: 0; } -} \ No newline at end of file + } + + .collectionStackedTimeline-waveform { + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + pointer-events: none; + } +} diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index a2c95df6e..230238ba8 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -1,5 +1,12 @@ import React = require("react"); -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from "mobx"; +import { + action, + computed, + IReactionDisposer, + observable, + reaction, + runInAction, +} from "mobx"; import { observer } from "mobx-react"; import { computedFn } from "mobx-utils"; import { Doc, DocListCast } from "../../../fields/Doc"; @@ -8,7 +15,16 @@ import { List } from "../../../fields/List"; import { listSpec, makeInterface } from "../../../fields/Schema"; import { ComputedField, ScriptField } from "../../../fields/ScriptField"; import { Cast, NumCast } from "../../../fields/Types"; -import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, StopEvent, returnTrue } from "../../../Utils"; +import { + emptyFunction, + formatTime, + OmitKeys, + returnFalse, + returnOne, + setupMoveUpEvents, + StopEvent, + returnTrue, +} from "../../../Utils"; import { Docs } from "../../documents/Documents"; import { LinkManager } from "../../util/LinkManager"; import { Scripting } from "../../util/Scripting"; @@ -18,412 +34,799 @@ import { undoBatch } from "../../util/UndoManager"; import { AudioWaveform } from "../AudioWaveform"; import { CollectionSubView } from "../collections/CollectionSubView"; import { LightboxView } from "../LightboxView"; -import { DocAfterFocusFunc, DocFocusFunc, DocumentView, DocumentViewProps } from "../nodes/DocumentView"; +import { + DocAfterFocusFunc, + DocFocusFunc, + DocumentView, + DocumentViewProps, +} from "../nodes/DocumentView"; import { LabelBox } from "../nodes/LabelBox"; import "./CollectionStackedTimeline.scss"; type PanZoomDocument = makeInterface<[]>; const PanZoomDocument = makeInterface(); export type CollectionStackedTimelineProps = { - duration: number; - Play: () => void; - Pause: () => void; - playLink: (linkDoc: Doc) => void; - playFrom: (seekTimeInSeconds: number, endTime?: number) => void; - playing: () => boolean; - setTime: (time: number) => void; - startTag: string; - endTag: string; - mediaPath: string; - dictationKey: string; + duration: number; + Play: () => void; + Pause: () => void; + playLink: (linkDoc: Doc) => void; + playFrom: (seekTimeInSeconds: number, endTime?: number) => void; + playing: () => boolean; + setTime: (time: number) => void; + startTag: string; + endTag: string; + mediaPath: string; + dictationKey: string; + trimming: boolean; + trimBounds: { start: number; end: number }; }; @observer -export class CollectionStackedTimeline extends CollectionSubView(PanZoomDocument) { - @observable static SelectingRegion: CollectionStackedTimeline | undefined = undefined; - static RangeScript: ScriptField; - static LabelScript: ScriptField; - static RangePlayScript: ScriptField; - static LabelPlayScript: ScriptField; - - private _timeline: HTMLDivElement | null = null; - private _markerStart: number = 0; - @observable _markerEnd: number = 0; - - get duration() { return this.props.duration; } - @computed get currentTime() { return NumCast(this.layoutDoc._currentTimecode); } - @computed get selectionContainer() { - return CollectionStackedTimeline.SelectingRegion !== this ? (null) :
; +export class CollectionStackedTimeline extends CollectionSubView< + PanZoomDocument, + CollectionStackedTimelineProps +>(PanZoomDocument) { + @observable static SelectingRegion: + | CollectionStackedTimeline + | undefined = undefined; + static RangeScript: ScriptField; + static LabelScript: ScriptField; + static RangePlayScript: ScriptField; + static LabelPlayScript: ScriptField; + + private _timeline: HTMLDivElement | null = null; + private _markerStart: number = 0; + @observable _markerEnd: number = 0; + + get minLength() { + const rect = this._timeline?.getBoundingClientRect(); + if (rect) { + return 0.05 * this.duration; } + return 0; + } + + get trimStart() { + return this.props.trimBounds.start; + } + + get trimEnd() { + return this.props.trimBounds.end; + } + + set trimStart(start: number) { + this.props.trimBounds.start = start; + } + + set trimEnd(end: number) { + this.props.trimBounds.end = end; + } + + get duration() { + return this.props.duration; + } + @computed get currentTime() { + return NumCast(this.layoutDoc._currentTimecode); + } + @computed get selectionContainer() { + return CollectionStackedTimeline.SelectingRegion !== this ? null : ( +
+ ); + } + + constructor(props: any) { + super(props); + // onClick play scripts + CollectionStackedTimeline.RangeScript = + CollectionStackedTimeline.RangeScript || + ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, { + self: Doc.name, + scriptContext: "any", + clientX: "number", + })!; + CollectionStackedTimeline.RangePlayScript = + CollectionStackedTimeline.RangePlayScript || + ScriptField.MakeFunction(`scriptContext.playOnClick(this, clientX)`, { + self: Doc.name, + scriptContext: "any", + clientX: "number", + })!; + } + + componentDidMount() { + document.addEventListener("keydown", this.keyEvents, true); + } + componentWillUnmount() { + document.removeEventListener("keydown", this.keyEvents, true); + if (CollectionStackedTimeline.SelectingRegion === this) + runInAction( + () => (CollectionStackedTimeline.SelectingRegion = undefined) + ); + } + + anchorStart = (anchor: Doc) => + NumCast(anchor._timecodeToShow, NumCast(anchor[this.props.startTag])); + anchorEnd = (anchor: Doc, val: any = null) => { + const endVal = NumCast(anchor[this.props.endTag], val); + return NumCast( + anchor._timecodeToHide, + endVal === undefined ? null : endVal + ); + }; + toTimeline = (screen_delta: number, width: number) => + Math.max( + 0, + Math.min(this.duration, (screen_delta / width) * this.duration) + ); + rangeClickScript = () => CollectionStackedTimeline.RangeScript; + rangePlayScript = () => CollectionStackedTimeline.RangePlayScript; - constructor(props: any) { - super(props); - // onClick play scripts - CollectionStackedTimeline.RangeScript = CollectionStackedTimeline.RangeScript || ScriptField.MakeFunction(`scriptContext.clickAnchor(this, clientX)`, { self: Doc.name, scriptContext: "any", clientX: "number" })!; - CollectionStackedTimeline.RangePlayScript = CollectionStackedTimeline.RangePlayScript || ScriptField.MakeFunction(`scriptContext.playOnClick(this, clientX)`, { self: Doc.name, scriptContext: "any", clientX: "number" })!; + // for creating key anchors with key events + @action + keyEvents = (e: KeyboardEvent) => { + if ( + !(e.target instanceof HTMLInputElement) && + this.props.isSelected(true) + ) { + switch (e.key) { + case " ": + if (!CollectionStackedTimeline.SelectingRegion) { + this._markerStart = this._markerEnd = this.currentTime; + CollectionStackedTimeline.SelectingRegion = this; + } else { + CollectionStackedTimeline.createAnchor( + this.rootDoc, + this.dataDoc, + this.props.fieldKey, + this.props.startTag, + this.props.endTag, + this.currentTime + ); + CollectionStackedTimeline.SelectingRegion = undefined; + } + } } + }; - componentDidMount() { document.addEventListener("keydown", this.keyEvents, true); } - componentWillUnmount() { - document.removeEventListener("keydown", this.keyEvents, true); - if (CollectionStackedTimeline.SelectingRegion === this) runInAction(() => CollectionStackedTimeline.SelectingRegion = undefined); + getLinkData(l: Doc) { + let la1 = l.anchor1 as Doc; + let la2 = l.anchor2 as Doc; + const linkTime = NumCast( + la2[this.props.startTag], + NumCast(la1[this.props.startTag]) + ); + if (Doc.AreProtosEqual(la1, this.dataDoc)) { + la1 = l.anchor2 as Doc; + la2 = l.anchor1 as Doc; } + return { la1, la2, linkTime }; + } - anchorStart = (anchor: Doc) => NumCast(anchor._timecodeToShow, NumCast(anchor[this.props.startTag])); - anchorEnd = (anchor: Doc, val: any = null) => { - const endVal = NumCast(anchor[this.props.endTag], val); - return NumCast(anchor._timecodeToHide, endVal === undefined ? null : endVal); + // starting the drag event for anchor resizing + @action + onPointerDownTimeline = (e: React.PointerEvent): void => { + const rect = this._timeline?.getBoundingClientRect(); + const clientX = e.clientX; + if (rect && this.props.isContentActive()) { + const wasPlaying = this.props.playing(); + if (wasPlaying) this.props.Pause(); + const wasSelecting = CollectionStackedTimeline.SelectingRegion === this; + setupMoveUpEvents( + this, + e, + action((e) => { + if ( + !wasSelecting && + CollectionStackedTimeline.SelectingRegion !== this + ) { + this._markerStart = this._markerEnd = this.toTimeline( + clientX - rect.x, + rect.width + ); + CollectionStackedTimeline.SelectingRegion = this; + } + this._markerEnd = this.toTimeline(e.clientX - rect.x, rect.width); + return false; + }), + action((e, movement, isClick) => { + this._markerEnd = this.toTimeline(e.clientX - rect.x, rect.width); + if (this._markerEnd < this._markerStart) { + const tmp = this._markerStart; + this._markerStart = this._markerEnd; + this._markerEnd = tmp; + } + if ( + !isClick && + CollectionStackedTimeline.SelectingRegion === this && + Math.abs(movement[0]) > 15 && + !this.props.trimming + ) { + CollectionStackedTimeline.createAnchor( + this.rootDoc, + this.dataDoc, + this.props.fieldKey, + this.props.startTag, + this.props.endTag, + this._markerStart, + this._markerEnd + ); + } + (!isClick || !wasSelecting) && + (CollectionStackedTimeline.SelectingRegion = undefined); + }), + (e, doubleTap) => { + this.props.select(false); + e.shiftKey && + CollectionStackedTimeline.createAnchor( + this.rootDoc, + this.dataDoc, + this.props.fieldKey, + this.props.startTag, + this.props.endTag, + this.currentTime + ); + !wasPlaying && doubleTap && this.props.Play(); + }, + this.props.isSelected(true) || this.props.isContentActive(), + undefined, + () => + !wasPlaying && + this.props.setTime(((clientX - rect.x) / rect.width) * this.duration) + ); } - toTimeline = (screen_delta: number, width: number) => Math.max(0, Math.min(this.duration, screen_delta / width * this.duration)); - rangeClickScript = () => CollectionStackedTimeline.RangeScript; - rangePlayScript = () => CollectionStackedTimeline.RangePlayScript; - - // for creating key anchors with key events - @action - keyEvents = (e: KeyboardEvent) => { - if (!(e.target instanceof HTMLInputElement) && this.props.isSelected(true)) { - switch (e.key) { - case " ": - if (!CollectionStackedTimeline.SelectingRegion) { - this._markerStart = this._markerEnd = this.currentTime; - CollectionStackedTimeline.SelectingRegion = this; - } else { - CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime); - CollectionStackedTimeline.SelectingRegion = undefined; - } - } - } + }; + + @action + startTrimLeft = (e: React.PointerEvent): void => { + document.addEventListener("pointermove", this.dragTrimLeft); + document.addEventListener("pointerup", this.endTrimLeft); + }; + + @action + startTrimRight = (e: React.PointerEvent): void => { + document.addEventListener("pointermove", this.dragTrimRight); + document.addEventListener("pointerup", this.endTrimRight); + }; + + @action + dragTrimLeft = (e: MouseEvent) => { + const rect = this._timeline?.getBoundingClientRect(); + if (rect && this.props.isContentActive()) { + this.trimStart = Math.min( + Math.max( + this.trimStart + (e.movementX / rect.width) * this.duration, + 0 + ), + this.trimEnd - this.minLength + ); } + }; - getLinkData(l: Doc) { - let la1 = l.anchor1 as Doc; - let la2 = l.anchor2 as Doc; - const linkTime = NumCast(la2[this.props.startTag], NumCast(la1[this.props.startTag])); - if (Doc.AreProtosEqual(la1, this.dataDoc)) { - la1 = l.anchor2 as Doc; - la2 = l.anchor1 as Doc; - } - return { la1, la2, linkTime }; + @action + dragTrimRight = (e: MouseEvent) => { + const rect = this._timeline?.getBoundingClientRect(); + if (rect && this.props.isContentActive()) { + this.trimEnd = Math.max( + Math.min( + this.trimEnd + (e.movementX / rect.width) * this.duration, + this.duration + ), + this.trimStart + this.minLength + ); } + }; - // starting the drag event for anchor resizing - @action - onPointerDownTimeline = (e: React.PointerEvent): void => { - const rect = this._timeline?.getBoundingClientRect(); - const clientX = e.clientX; - if (rect && this.props.isContentActive()) { - const wasPlaying = this.props.playing(); - if (wasPlaying) this.props.Pause(); - const wasSelecting = CollectionStackedTimeline.SelectingRegion === this; - setupMoveUpEvents(this, e, - action(e => { - if (!wasSelecting && CollectionStackedTimeline.SelectingRegion !== this) { - this._markerStart = this._markerEnd = this.toTimeline(clientX - rect.x, rect.width); - CollectionStackedTimeline.SelectingRegion = this; - } - this._markerEnd = this.toTimeline(e.clientX - rect.x, rect.width); - return false; - }), - action((e, movement, isClick) => { - this._markerEnd = this.toTimeline(e.clientX - rect.x, rect.width); - if (this._markerEnd < this._markerStart) { - const tmp = this._markerStart; - this._markerStart = this._markerEnd; - this._markerEnd = tmp; - } - if (!isClick && CollectionStackedTimeline.SelectingRegion === this && (Math.abs(movement[0]) > 15)) { - CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, - this._markerStart, this._markerEnd); - } - (!isClick || !wasSelecting) && (CollectionStackedTimeline.SelectingRegion = undefined); - }), - (e, doubleTap) => { - this.props.select(false); - e.shiftKey && CollectionStackedTimeline.createAnchor(this.rootDoc, this.dataDoc, this.props.fieldKey, this.props.startTag, this.props.endTag, this.currentTime); - !wasPlaying && doubleTap && this.props.Play(); - }, - this.props.isSelected(true) || this.props.isContentActive(), undefined, - () => !wasPlaying && this.props.setTime((clientX - rect.x) / rect.width * this.duration)); - } + endTrimLeft = () => { + document.removeEventListener("pointermove", this.dragTrimLeft); + document.removeEventListener("pointerup", this.endTrimLeft); + }; + + endTrimRight = () => { + document.removeEventListener("pointermove", this.dragTrimRight); + document.removeEventListener("pointerup", this.endTrimRight); + }; + + @undoBatch + @action + static createAnchor( + rootDoc: Doc, + dataDoc: Doc, + fieldKey: string, + startTag: string, + endTag: string, + anchorStartTime?: number, + anchorEndTime?: number + ) { + if (anchorStartTime === undefined) return rootDoc; + const anchor = Docs.Create.LabelDocument({ + title: ComputedField.MakeFunction( + `"#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"])` + ) as any, + useLinkSmallAnchor: true, + hideLinkButton: true, + annotationOn: rootDoc, + _timelineLabel: true, + }); + Doc.GetProto(anchor)[startTag] = anchorStartTime; + Doc.GetProto(anchor)[endTag] = anchorEndTime; + if (Cast(dataDoc[fieldKey], listSpec(Doc), null) !== undefined) { + Cast(dataDoc[fieldKey], listSpec(Doc), []).push(anchor); + } else { + dataDoc[fieldKey] = new List([anchor]); } + return anchor; + } - @undoBatch - @action - static createAnchor(rootDoc: Doc, dataDoc: Doc, fieldKey: string, startTag: string, endTag: string, anchorStartTime?: number, anchorEndTime?: number) { - if (anchorStartTime === undefined) return rootDoc; - const anchor = Docs.Create.LabelDocument({ - title: ComputedField.MakeFunction(`"#" + formatToTime(self["${startTag}"]) + "-" + formatToTime(self["${endTag}"])`) as any, - useLinkSmallAnchor: true, - hideLinkButton: true, - annotationOn: rootDoc, - _timelineLabel: true - }); - Doc.GetProto(anchor)[startTag] = anchorStartTime; - Doc.GetProto(anchor)[endTag] = anchorEndTime; - if (Cast(dataDoc[fieldKey], listSpec(Doc), null) !== undefined) { - Cast(dataDoc[fieldKey], listSpec(Doc), []).push(anchor); + @action + playOnClick = (anchorDoc: Doc, clientX: number) => { + const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25; + const endTime = this.anchorEnd(anchorDoc); + if (this.layoutDoc.autoPlayAnchors) { + if (this.props.playing()) this.props.Pause(); + else this.props.playFrom(seekTimeInSeconds, endTime); + } else { + if ( + seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) && + endTime > NumCast(this.layoutDoc._currentTimecode) + ) { + if (!this.layoutDoc.autoPlayAnchors && this.props.playing()) { + this.props.Pause(); } else { - dataDoc[fieldKey] = new List([anchor]); + this.props.Play(); } - return anchor; + } else { + this.props.playFrom(seekTimeInSeconds, endTime); + } } + return { select: true }; + }; - @action - playOnClick = (anchorDoc: Doc, clientX: number) => { - const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25; - const endTime = this.anchorEnd(anchorDoc); - if (this.layoutDoc.autoPlayAnchors) { - if (this.props.playing()) this.props.Pause(); - else this.props.playFrom(seekTimeInSeconds, endTime); - } else { - if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) && endTime > NumCast(this.layoutDoc._currentTimecode)) { - if (!this.layoutDoc.autoPlayAnchors && this.props.playing()) { - this.props.Pause(); - } else { - this.props.Play(); - } - } else { - this.props.playFrom(seekTimeInSeconds, endTime); - } - } - return { select: true }; + @action + clickAnchor = (anchorDoc: Doc, clientX: number) => { + if (anchorDoc.isLinkButton) + LinkManager.FollowLink(undefined, anchorDoc, this.props, false); + const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25; + const endTime = this.anchorEnd(anchorDoc); + if ( + seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 && + endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4 + ) { + if (this.props.playing()) this.props.Pause(); + else if (this.layoutDoc.autoPlayAnchors) this.props.Play(); + else if (!this.layoutDoc.autoPlayAnchors) { + const rect = this._timeline?.getBoundingClientRect(); + rect && + this.props.setTime(this.toTimeline(clientX - rect.x, rect.width)); + } + } else { + if (this.layoutDoc.autoPlayAnchors) + this.props.playFrom(seekTimeInSeconds, endTime); + else this.props.setTime(seekTimeInSeconds); } + return { select: true }; + }; - @action - clickAnchor = (anchorDoc: Doc, clientX: number) => { - if (anchorDoc.isLinkButton) LinkManager.FollowLink(undefined, anchorDoc, this.props, false); - const seekTimeInSeconds = this.anchorStart(anchorDoc) - 0.25; - const endTime = this.anchorEnd(anchorDoc); - if (seekTimeInSeconds < NumCast(this.layoutDoc._currentTimecode) + 1e-4 && endTime > NumCast(this.layoutDoc._currentTimecode) - 1e-4) { - if (this.props.playing()) this.props.Pause(); - else if (this.layoutDoc.autoPlayAnchors) this.props.Play(); - else if (!this.layoutDoc.autoPlayAnchors) { - const rect = this._timeline?.getBoundingClientRect(); - rect && this.props.setTime(this.toTimeline(clientX - rect.x, rect.width)); - } - } else { - if (this.layoutDoc.autoPlayAnchors) this.props.playFrom(seekTimeInSeconds, endTime); - else this.props.setTime(seekTimeInSeconds); + // makes sure no anchors overlaps each other by setting the correct position and width + getLevel = ( + m: Doc, + placed: { anchorStartTime: number; anchorEndTime: number; level: number }[] + ) => { + const timelineContentWidth = this.props.PanelWidth(); + const x1 = this.anchorStart(m); + const x2 = this.anchorEnd( + m, + x1 + (10 / timelineContentWidth) * this.duration + ); + let max = 0; + const overlappedLevels = new Set( + placed.map((p) => { + const y1 = p.anchorStartTime; + const y2 = p.anchorEndTime; + if ( + (x1 >= y1 && x1 <= y2) || + (x2 >= y1 && x2 <= y2) || + (y1 >= x1 && y1 <= x2) || + (y2 >= x1 && y2 <= x2) + ) { + max = Math.max(max, p.level); + return p.level; } - return { select: true }; - } + }) + ); + let level = max + 1; + for (let j = max; j >= 0; j--) !overlappedLevels.has(j) && (level = j); + placed.push({ anchorStartTime: x1, anchorEndTime: x2, level }); + return level; + }; - // makes sure no anchors overlaps each other by setting the correct position and width - getLevel = (m: Doc, placed: { anchorStartTime: number, anchorEndTime: number, level: number }[]) => { - const timelineContentWidth = this.props.PanelWidth(); - const x1 = this.anchorStart(m); - const x2 = this.anchorEnd(m, x1 + 10 / timelineContentWidth * this.duration); - let max = 0; - const overlappedLevels = new Set(placed.map(p => { - const y1 = p.anchorStartTime; - const y2 = p.anchorEndTime; - if ((x1 >= y1 && x1 <= y2) || (x2 >= y1 && x2 <= y2) || - (y1 >= x1 && y1 <= x2) || (y2 >= x1 && y2 <= x2)) { - max = Math.max(max, p.level); - return p.level; - } - })); - let level = max + 1; - for (let j = max; j >= 0; j--) !overlappedLevels.has(j) && (level = j); - - placed.push({ anchorStartTime: x1, anchorEndTime: x2, level }); - return level; - } + dictationHeightPercent = 50; + dictationHeight = () => + (this.props.PanelHeight() * (100 - this.dictationHeightPercent)) / 100; + timelineContentHeight = () => + (this.props.PanelHeight() * this.dictationHeightPercent) / 100; + dictationScreenToLocalTransform = () => + this.props + .ScreenToLocalTransform() + .translate(0, -this.timelineContentHeight()); + @computed get renderDictation() { + const dictation = Cast(this.dataDoc[this.props.dictationKey], Doc, null); + return !dictation ? null : ( +
+ +
+ ); + } + @computed get renderAudioWaveform() { + return !this.props.mediaPath ? null : ( +
+ +
+ ); + } - dictationHeightPercent = 50; - dictationHeight = () => this.props.PanelHeight() * (100 - this.dictationHeightPercent) / 100; - timelineContentHeight = () => this.props.PanelHeight() * this.dictationHeightPercent / 100; - dictationScreenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.timelineContentHeight()); - @computed get renderDictation() { - const dictation = Cast(this.dataDoc[this.props.dictationKey], Doc, null); - return !dictation ? (null) :
- - -
; - } - @computed get renderAudioWaveform() { - return !this.props.mediaPath ? (null) : -
- -
; - } - currentTimecode = () => this.currentTime; - render() { - const timelineContentWidth = this.props.PanelWidth(); - const overlaps: { anchorStartTime: number, anchorEndTime: number, level: number }[] = []; - const drawAnchors = this.childDocs.map(anchor => ({ level: this.getLevel(anchor, overlaps), anchor })); - const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2; - const isActive = this.props.isContentActive() || this.props.isSelected(false); - return
this._timeline = timeline} - onClick={e => isActive && StopEvent(e)} onPointerDown={e => isActive && this.onPointerDownTimeline(e)}> - {drawAnchors.map(d => { - const start = this.anchorStart(d.anchor); - const end = this.anchorEnd(d.anchor, start + 10 / timelineContentWidth * this.duration); - const left = start / this.duration * timelineContentWidth; - const top = d.level / maxLevel * this.timelineContentHeight(); - const timespan = end - start; - return this.props.Document.hideAnchors ? (null) : -
{ this.props.playFrom(start, this.anchorEnd(d.anchor)); e.stopPropagation(); }} > - -
; - })} - {this.selectionContainer} - {this.renderAudioWaveform} - {this.renderDictation} - -
-
; - } + currentTimecode = () => this.currentTime; + render() { + const timelineContentWidth = this.props.PanelWidth(); + const overlaps: { + anchorStartTime: number; + anchorEndTime: number; + level: number; + }[] = []; + const drawAnchors = this.childDocs.map((anchor) => ({ + level: this.getLevel(anchor, overlaps), + anchor, + })); + const maxLevel = overlaps.reduce((m, o) => Math.max(m, o.level), 0) + 2; + const isActive = + this.props.isContentActive() || this.props.isSelected(false); + return ( +
(this._timeline = timeline)} + onClick={(e) => isActive && StopEvent(e)} + onPointerDown={(e) => isActive && this.onPointerDownTimeline(e)} + > + {drawAnchors.map((d) => { + const start = this.anchorStart(d.anchor); + const end = this.anchorEnd( + d.anchor, + start + (10 / timelineContentWidth) * this.duration + ); + const left = (start / this.duration) * timelineContentWidth; + const top = (d.level / maxLevel) * this.timelineContentHeight(); + const timespan = end - start; + return this.props.Document.hideAnchors ? null : ( +
{ + this.props.playFrom(start, this.anchorEnd(d.anchor)); + e.stopPropagation(); + }} + > + +
+ ); + })} + {!this.props.trimming && this.selectionContainer} + {this.renderAudioWaveform} + {this.renderDictation} + +
+ + {this.props.trimming && ( +
+
+ +
+
+
+
+ +
+
+ )} +
+ ); + } } interface StackedTimelineAnchorProps { - mark: Doc; - rangeClickScript: () => ScriptField; - rangePlayScript: () => ScriptField; - left: number; - top: number; - width: number; - height: number; - toTimeline: (screen_delta: number, width: number) => number; - playLink: (linkDoc: Doc) => void; - setTime: (time: number) => void; - startTag: string; - endTag: string; - renderDepth: number; - layoutDoc: Doc; - ScreenToLocalTransform: () => Transform; - _timeline: HTMLDivElement | null; - focus: DocFocusFunc; - currentTimecode: () => number; - isSelected: (outsideReaction?: boolean) => boolean; - stackedTimeline: CollectionStackedTimeline; + mark: Doc; + rangeClickScript: () => ScriptField; + rangePlayScript: () => ScriptField; + left: number; + top: number; + width: number; + height: number; + toTimeline: (screen_delta: number, width: number) => number; + playLink: (linkDoc: Doc) => void; + setTime: (time: number) => void; + startTag: string; + endTag: string; + renderDepth: number; + layoutDoc: Doc; + ScreenToLocalTransform: () => Transform; + _timeline: HTMLDivElement | null; + focus: DocFocusFunc; + currentTimecode: () => number; + isSelected: (outsideReaction?: boolean) => boolean; + stackedTimeline: CollectionStackedTimeline; } @observer class StackedTimelineAnchor extends React.Component { - _lastTimecode: number; - _disposer: IReactionDisposer | undefined; - constructor(props: any) { - super(props); - this._lastTimecode = this.props.currentTimecode(); - } - componentDidMount() { - this._disposer = reaction(() => this.props.currentTimecode(), - (time) => { - const dictationDoc = Cast(this.props.layoutDoc["data-dictation"], Doc, null); - const isDictation = dictationDoc && DocListCast(this.props.mark.links).some(link => Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc); - if (!LightboxView.LightboxDoc - // bcz: when should links be followed? we don't want to move away from the video to follow a link but we can open it in a sidebar/etc. But we don't know that upfront. - // for now, we won't follow any links when the lightbox is oepn to avoid "losing" the video. - /*(isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this.props.layoutDoc))*/ - && DocListCast(this.props.mark.links).length && - time > NumCast(this.props.mark[this.props.startTag]) && - time < NumCast(this.props.mark[this.props.endTag]) && - this._lastTimecode < NumCast(this.props.mark[this.props.startTag])) { - LinkManager.FollowLink(undefined, this.props.mark, this.props as any as DocumentViewProps, false, true); - } - this._lastTimecode = time; - }); - } - componentWillUnmount() { - this._disposer?.(); - } - // starting the drag event for anchor resizing - onAnchorDown = (e: React.PointerEvent, anchor: Doc, left: boolean): void => { - this.props._timeline?.setPointerCapture(e.pointerId); - const newTime = (e: PointerEvent) => { - const rect = (e.target as any).getBoundingClientRect(); - return this.props.toTimeline(e.clientX - rect.x, rect.width); - }; - const changeAnchor = (anchor: Doc, left: boolean, time: number) => { - const timelineOnly = Cast(anchor[this.props.startTag], "number", null) !== undefined; - if (timelineOnly) Doc.SetInPlace(anchor, left ? this.props.startTag : this.props.endTag, time, true); - else left ? anchor._timecodeToShow = time : anchor._timecodeToHide = time; - return false; - }; - setupMoveUpEvents(this, e, - (e) => changeAnchor(anchor, left, newTime(e)), - (e) => { - this.props.setTime(newTime(e)); - this.props._timeline?.releasePointerCapture(e.pointerId); - }, - emptyFunction); - } - renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), x: number, y: number, width: number, height: number) { - const anchor = observable({ view: undefined as any }); - const focusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, docTransform?: Transform) => { - this.props.playLink(mark); - this.props.focus(doc, { willZoom, scale, afterFocus, docTransform }); - }; - return { - anchor, view: anchor.view = r)} - Document={mark} - DataDoc={undefined} - renderDepth={this.props.renderDepth + 1} - LayoutTemplate={undefined} - LayoutTemplateString={LabelBox.LayoutString("data")} - isDocumentActive={returnFalse} - PanelWidth={() => width} - PanelHeight={() => height} - ScreenToLocalTransform={() => this.props.ScreenToLocalTransform().translate(-x, -y)} - focus={focusFunc} - rootSelected={returnFalse} - onClick={script} - onDoubleClick={this.props.layoutDoc.autoPlayAnchors ? undefined : doublescript} - ignoreAutoHeight={false} - hideResizeHandles={true} - bringToFront={emptyFunction} - scriptContext={this.props.stackedTimeline} /> - }; - }); - render() { - const inner = this.renderInner(this.props.mark, this.props.rangeClickScript, this.props.rangePlayScript, this.props.left, this.props.top, this.props.width, this.props.height); - return <> - {inner.view} - {!inner.anchor.view || !SelectionManager.IsSelected(inner.anchor.view) ? (null) : - <> -
this.onAnchorDown(e, this.props.mark, true)} /> -
this.onAnchorDown(e, this.props.mark, false)} /> - } - ; - } + _lastTimecode: number; + _disposer: IReactionDisposer | undefined; + constructor(props: any) { + super(props); + this._lastTimecode = this.props.currentTimecode(); + } + componentDidMount() { + this._disposer = reaction( + () => this.props.currentTimecode(), + (time) => { + const dictationDoc = Cast( + this.props.layoutDoc["data-dictation"], + Doc, + null + ); + const isDictation = + dictationDoc && + DocListCast(this.props.mark.links).some( + (link) => + Cast(link.anchor1, Doc, null)?.annotationOn === dictationDoc + ); + if ( + !LightboxView.LightboxDoc && + // bcz: when should links be followed? we don't want to move away from the video to follow a link but we can open it in a sidebar/etc. But we don't know that upfront. + // for now, we won't follow any links when the lightbox is oepn to avoid "losing" the video. + /*(isDictation || !Doc.AreProtosEqual(LightboxView.LightboxDoc, this.props.layoutDoc))*/ + DocListCast(this.props.mark.links).length && + time > NumCast(this.props.mark[this.props.startTag]) && + time < NumCast(this.props.mark[this.props.endTag]) && + this._lastTimecode < NumCast(this.props.mark[this.props.startTag]) + ) { + LinkManager.FollowLink( + undefined, + this.props.mark, + (this.props as any) as DocumentViewProps, + false, + true + ); + } + this._lastTimecode = time; + } + ); + } + componentWillUnmount() { + this._disposer?.(); + } + // starting the drag event for anchor resizing + onAnchorDown = (e: React.PointerEvent, anchor: Doc, left: boolean): void => { + this.props._timeline?.setPointerCapture(e.pointerId); + const newTime = (e: PointerEvent) => { + const rect = (e.target as any).getBoundingClientRect(); + return this.props.toTimeline(e.clientX - rect.x, rect.width); + }; + const changeAnchor = (anchor: Doc, left: boolean, time: number) => { + const timelineOnly = + Cast(anchor[this.props.startTag], "number", null) !== undefined; + if (timelineOnly) + Doc.SetInPlace( + anchor, + left ? this.props.startTag : this.props.endTag, + time, + true + ); + else + left + ? (anchor._timecodeToShow = time) + : (anchor._timecodeToHide = time); + return false; + }; + setupMoveUpEvents( + this, + e, + (e) => changeAnchor(anchor, left, newTime(e)), + (e) => { + this.props.setTime(newTime(e)); + this.props._timeline?.releasePointerCapture(e.pointerId); + }, + emptyFunction + ); + }; + renderInner = computedFn(function ( + this: StackedTimelineAnchor, + mark: Doc, + script: undefined | (() => ScriptField), + doublescript: undefined | (() => ScriptField), + x: number, + y: number, + width: number, + height: number + ) { + const anchor = observable({ view: undefined as any }); + const focusFunc = ( + doc: Doc, + willZoom?: boolean, + scale?: number, + afterFocus?: DocAfterFocusFunc, + docTransform?: Transform + ) => { + this.props.playLink(mark); + this.props.focus(doc, { willZoom, scale, afterFocus, docTransform }); + }; + return { + anchor, + view: ( + (anchor.view = r))} + Document={mark} + DataDoc={undefined} + renderDepth={this.props.renderDepth + 1} + LayoutTemplate={undefined} + LayoutTemplateString={LabelBox.LayoutString("data")} + isDocumentActive={returnFalse} + PanelWidth={() => width} + PanelHeight={() => height} + ScreenToLocalTransform={() => + this.props.ScreenToLocalTransform().translate(-x, -y) + } + focus={focusFunc} + rootSelected={returnFalse} + onClick={script} + onDoubleClick={ + this.props.layoutDoc.autoPlayAnchors ? undefined : doublescript + } + ignoreAutoHeight={false} + hideResizeHandles={true} + bringToFront={emptyFunction} + scriptContext={this.props.stackedTimeline} + /> + ), + }; + }); + render() { + const inner = this.renderInner( + this.props.mark, + this.props.rangeClickScript, + this.props.rangePlayScript, + this.props.left, + this.props.top, + this.props.width, + this.props.height + ); + return ( + <> + {inner.view} + {!inner.anchor.view || + !SelectionManager.IsSelected(inner.anchor.view) ? null : ( + <> +
this.onAnchorDown(e, this.props.mark, true)} + /> +
+ this.onAnchorDown(e, this.props.mark, false) + } + /> + + )} + + ); + } } -Scripting.addGlobal(function formatToTime(time: number): any { return formatTime(time); }); \ No newline at end of file +Scripting.addGlobal(function formatToTime(time: number): any { + return formatTime(time); +}); -- cgit v1.2.3-70-g09d2 From 48dbde117728e4b36bae11f0f16a796059ecf7ee Mon Sep 17 00:00:00 2001 From: dg314 Date: Sat, 17 Jul 2021 15:13:39 -0400 Subject: initial search panel --- src/client/util/CurrentUserUtils.ts | 27 ++++- src/client/views/MainView.tsx | 11 +- src/client/views/StyleProvider.tsx | 2 +- .../views/collections/CollectionSchemaView.tsx | 2 +- src/client/views/collections/SchemaTable.tsx | 3 +- src/client/views/search/SearchBox.tsx | 117 ++------------------- 6 files changed, 41 insertions(+), 121 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 12733e815..596ccd9a6 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -35,6 +35,8 @@ import { UndoManager } from "./UndoManager"; import { SnappingManager } from "./SnappingManager"; import { InkTool } from "../../fields/InkField"; import { computedFn } from "mobx-utils"; +import { CollectionSchemaBooleanCell } from "../views/collections/CollectionSchemaCells"; +import { Console } from "console"; export let resolvedPorts: { server: number, socket: number }; @@ -53,6 +55,7 @@ export class CurrentUserUtils { @observable public static GuestDashboard: Doc | undefined; @observable public static GuestMobile: Doc | undefined; @observable public static propertiesWidth: number = 0; + @observable public static searchPanelWidth: number = 0; // sets up the default User Templates - slideView, headerView static setupUserTemplateButtons(doc: Doc) { @@ -516,6 +519,7 @@ export class CurrentUserUtils { static async menuBtnDescriptions(doc: Doc) { return [ { title: "Dashboards", target: Cast(doc.myDashboards, Doc, null), icon: "desktop", click: 'selectMainMenu(self)' }, + { title: "Search", target: Cast(doc.mySearchPanel, Doc, null), icon: "search", 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)' }, @@ -529,14 +533,15 @@ export class CurrentUserUtils { ]; } - static setupSearchPanel(doc: Doc) { + /*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 @@ -807,6 +812,7 @@ export class CurrentUserUtils { (doc.myRecentlyClosedDocs as any as Doc).contextMenuLabels = new List(["Clear All"]); } } + static setupFilterDocs(doc: Doc) { // setup Filter item if (doc.currentFilter === undefined) { @@ -939,6 +945,21 @@ export class CurrentUserUtils { } } + // Search sidebar is where searches within the document are performed + static setupSearchSidebar(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; + } + if (doc.mySearchPanel === undefined) { + const searchPanelDoc = Cast(doc.mySearchPanelDoc, Doc, null); + //const newUpload = CurrentUserUtils.ficon({ onClick: ScriptField.MakeScript("importDocument()"), toolTip: "Import External document", _stayInCollection: true, _hideContextMenu: true, title: "Import", icon: "upload", system: true }); + doc.mySearchPanel = new PrefetchProxy(Docs.Create.StackingDocument([searchPanelDoc], { title: "Search", _yMargin: 20, ignoreClick: true, _chromeHidden: true, _stayInCollection: true, _hideContextMenu: true, _lockedPosition: true, system: true })); + } + } + 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 @@ -1023,8 +1044,8 @@ export class CurrentUserUtils { 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.setupSearchSidebar(doc); // sets up the search sidebar 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 await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 4eeb1fc95..bc9c6f4d2 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -180,8 +180,8 @@ export class MainView extends React.Component { const targClass = targets[0].className.toString(); if (SearchBox.Instance._searchbarOpen || SearchBox.Instance.open) { const check = targets.some((thing) => - (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" || - thing.className === "collectionSchema-header-menuOptions")); + (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" || + thing.className === "collectionSchema-header-menuOptions")); !check && SearchBox.Instance.resetSearch(true); } !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu(); @@ -388,7 +388,6 @@ export class MainView extends React.Component { SettingsManager.Instance.open(); break; case "Catalog": - SearchBox.Instance._searchFullDB = "My Stuff"; SearchBox.Instance.enter(undefined); break; case "Help": @@ -523,7 +522,7 @@ export class MainView extends React.Component { ; } - @computed get search() { + /*@computed get search() { TraceMobx(); return
; - } + }*/ @computed get invisibleWebBox() { // see note under the makeLink method in HypothesisUtils.ts return !DocumentLinksButton.invisibleWebDoc ? null : @@ -604,7 +603,7 @@ export class MainView extends React.Component { - {this.search} + {/*this.search*/} {LinkDescriptionPopup.descriptionPopup ? : null} {DocumentLinksButton.LinkEditorDocView ? : (null)} diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 9e61351c4..e670420d1 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -100,7 +100,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt 400 || col.alpha() < 0.25) return "black"; diff --git a/src/client/views/collections/CollectionSchemaView.tsx b/src/client/views/collections/CollectionSchemaView.tsx index b33c437a9..971dd5cbf 100644 --- a/src/client/views/collections/CollectionSchemaView.tsx +++ b/src/client/views/collections/CollectionSchemaView.tsx @@ -556,7 +556,7 @@ export class CollectionSchemaView extends CollectionSubView(doc => doc) { style={{ overflow: this.props.scrollOverflow === true ? "scroll" : undefined, backgroundColor: "white", pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined, - width: name === "collectionSchemaView-searchContainer" ? "auto" : this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative", + width: this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative", }} >
{
this.changeSorting(col)} style={{ width: 21, padding: 1, display: "inline", zIndex: 1, background: "inherit", cursor: "hand" }}>
+ {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined :
+ new
}
; return { @@ -559,7 +560,7 @@ export class SchemaTable extends React.Component { onPointerDown={this.props.onPointerDown} onClick={this.props.onClick} onWheel={e => this.props.active(true) && e.stopPropagation()} onDrop={e => this.props.onDrop(e, {})} onContextMenu={this.onContextMenu} > {this.reactTable} - {this.props.Document._chromeHidden ? undefined :
+ new
} + {this.props.Document._chromeHidden || this.props.addDocument == returnFalse ? undefined :
+ new
} {!this._showDoc ? (null) :
(); - @observable _undoBackground: string | undefined = ""; @observable _icons: string[] = this._allIcons; @@ -116,62 +114,6 @@ export class SearchBox extends ViewBoxBaseComponent i === 0 ? newWords.push(key + mod + word) : newWords.push("AND " + key + mod + word)); - query = `(${query}) AND (${newWords.join(" ")})`; - } - else { - for (let i = 0; i < values.length; i++) { - const mod = "_t:"; - const newWords: string[] = []; - const oldWords = values[i].split(" "); - oldWords.forEach((word, i) => i === 0 ? newWords.push(key + mod + word) : newWords.push("AND " + key + mod + word)); - const v = "(" + newWords.join(" ") + ")"; - if (i === 0) { - query = `(${query}) AND (${v}` + (values.length === 1 ? ")" : ""); - } - else query = query + " OR " + v + (i === values.length - 1 ? ")" : ""); - } - } - } - - return query.replace(/-\s+/g, ''); - } - @action filterDocsByType(docs: Doc[]) { const finalDocs: Doc[] = []; @@ -340,7 +282,6 @@ export class SearchBox extends ViewBoxBaseComponent { - const res = await this.getAllResults(this.getFinalQuery(StrCast(this.layoutDoc._searchString))); - const filtered = this.filterDocsByType(res.docs); - const docs = filtered.map(doc => Doc.GetT(doc, "isPrototype", "boolean", true) ? Doc.MakeDelegate(doc) : Doc.MakeAlias(doc)); - let x = 0; - let y = 0; - for (const doc of docs.map(d => Doc.Layout(d))) { - doc.x = x; - doc.y = y; - const size = 200; - const aspect = Doc.NativeHeight(doc) / (Doc.NativeWidth(doc) || 1); - if (aspect > 1) { - doc._height = size; - doc._width = size / aspect; - } else if (aspect > 0) { - doc._width = size; - doc._height = size * aspect; - } else { - doc._width = size; - doc._height = size; - } - x += 250; - if (x > 1000) { - x = 0; - y += 300; - } - } - const headers = Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []).map(h => { const v = h[Copy](); v.color = "#f1efeb"; return v; }); - return Docs.Create.SchemaDocument(headers, DocListCast(this.dataDoc[this.fieldKey]), { _autoHeight: true, _viewType: CollectionViewType.Schema, title: StrCast(this.layoutDoc._searchString) }); - } - @action.bound openSearch(e: React.SyntheticEvent) { e.stopPropagation(); @@ -479,6 +389,7 @@ export class SearchBox extends ViewBoxBaseComponent
@@ -487,7 +398,7 @@ export class SearchBox extends ViewBoxBaseComponent window.location.assign(Utils.prepend("/logout"))}> Logoff -
+
DocServer.UPDATE_SERVER_CACHE()}> {`UI project`} @@ -499,10 +410,10 @@ export class SearchBox extends ViewBoxBaseComponent
CurrentUserUtils.createNewDashboard(Doc.UserDoc()))}> New -
+
CurrentUserUtils.snapshotDashboard(Doc.UserDoc()))}> Snapshot -
+
@@ -511,24 +422,12 @@ export class SearchBox extends ViewBoxBaseComponent
- drag search results as collection
}> -
StrCast(this.layoutDoc._searchString) ? this.startDragCollection() : undefined)} icon={"search"} size="lg" - style={{ cursor: "hand", color: "black", padding: 1, position: "relative" }} />
- +
{`${this._results.length}` + " of " + `${this.realTotalResults}`}
- {Doc.UserDoc().noviceMode ? (null) :
- only display documents matching search
} > -
- { e.stopPropagation(); SetupDrag(this.collectionRef, () => this.layoutDoc._searchString ? this.startDragCollection() : undefined); }} - onClick={action(() => this.setSearchFilter(this.currentSelectedCollection, this.filter ? undefined : this.docsforfilter))} /> -
- -
}
-- cgit v1.2.3-70-g09d2 From d511c116330c3a125e6557aa5be70392aca4914b Mon Sep 17 00:00:00 2001 From: dg314 Date: Thu, 5 Aug 2021 16:38:00 -0400 Subject: comments --- src/client/views/MainView.tsx | 6 - .../views/collections/CollectionSchemaHeaders.tsx | 2 +- src/client/views/search/SearchBox.scss | 22 +- src/client/views/search/SearchBox.tsx | 382 +++++++++------------ 4 files changed, 177 insertions(+), 235 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 35222e91f..97c88930e 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -178,12 +178,6 @@ export class MainView extends React.Component { const targets = document.elementsFromPoint(e.x, e.y); if (targets.length) { const targClass = targets[0].className.toString(); - /*if (SearchBox.Instance._resultsOpen) { - const check = targets.some((thing) => - (thing.className === "collectionSchemaView-searchContainer" || (thing as any)?.dataset.icon === "filter" || - thing.className === "collectionSchema-header-menuOptions")); - !check && SearchBox.Instance.resetSearch(true); - }*/ !targClass.includes("contextMenu") && ContextMenu.Instance.closeMenu(); !["timeline-menu-desc", "timeline-menu-item", "timeline-menu-input"].includes(targClass) && TimelineMenu.Instance.closeMenu(); } diff --git a/src/client/views/collections/CollectionSchemaHeaders.tsx b/src/client/views/collections/CollectionSchemaHeaders.tsx index 3b52e6408..c98dcd1d0 100644 --- a/src/client/views/collections/CollectionSchemaHeaders.tsx +++ b/src/client/views/collections/CollectionSchemaHeaders.tsx @@ -424,7 +424,7 @@ export class KeysDropdown extends React.Component { e.target.checked === true ? Doc.setDocFilter(this.props.Document, this._key, key, "check") : Doc.setDocFilter(this.props.Document, this._key, key, "remove"); e.target.checked === true ? this.closeResultsVisibility = "contents" : console.log(""); e.target.checked === true ? this.props.col.setColor("green") : this.updateFilter(); - e.target.checked === true && SearchBox.Instance.filter === true ? Doc.setDocFilter(docs[0], this._key, key, "check") : Doc.setDocFilter(docs[0], this._key, key, "remove"); + e.target.checked === true ? Doc.setDocFilter(docs[0], this._key, key, "check") : Doc.setDocFilter(docs[0], this._key, key, "remove"); }} checked={bool} /> diff --git a/src/client/views/search/SearchBox.scss b/src/client/views/search/SearchBox.scss index e4d1ac6a3..e7526dac8 100644 --- a/src/client/views/search/SearchBox.scss +++ b/src/client/views/search/SearchBox.scss @@ -66,13 +66,13 @@ display: inline-block; vertical-align: middle; width: 100%; - height: 40px; + height: 50px; cursor: pointer; font-size: 15px; - padding: 10px; + padding: 11px; &.searchBox-results-scroll-view-result-selected { - background: gray; + background: #999; } .searchBox-result-title { @@ -84,11 +84,25 @@ .searchBox-result-type { font-size: 12px; + margin-top: 6px; display: relative; float: right; width: 60px; text-align: right; - color: #333; + color: #222; + } + + .searchBox-result-keys { + font-size: 10px; + margin-top: 1px; + display: relative; + float: left; + width: 100%; + text-align: left; + color: #555; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } } } diff --git a/src/client/views/search/SearchBox.tsx b/src/client/views/search/SearchBox.tsx index 3cbb4b3b1..8ef09aa56 100644 --- a/src/client/views/search/SearchBox.tsx +++ b/src/client/views/search/SearchBox.tsx @@ -27,66 +27,105 @@ import { undoBatch } from "../../util/UndoManager"; import { DocServer } from "../../DocServer"; import { MainView } from "../MainView"; import { DocumentManager } from "../../util/DocumentManager"; -import { CollectionSchemaBooleanCell } from "../collections/CollectionSchemaCells"; -import { transpileModule } from "typescript"; export const searchSchema = createSchema({ Document: Doc }); type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>; const SearchBoxDocument = makeInterface(documentSchema, searchSchema); -const selectValues = ["all", "rtf", "image", "pdf", "web", "video", "audio", "collection"] - +/** + * This is the SearchBox component. It represents the search box input and results in + * the search panel on the left side of the screen. + */ @observer export class SearchBox extends ViewBoxBaseComponent(SearchBoxDocument) { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); } public static Instance: SearchBox; - private _resultsSet = new Map(); private _inputRef = React.createRef(); + private _selectedCollection = CollectionDockingView.Instance; @observable _searchString = ""; @observable _docTypeString = "all"; - @observable _results: [Doc, string[], string[]][] = []; + @observable _results: [Doc, string[]][] = []; @observable _selectedResult: Doc | undefined = undefined; @observable _deletedDocsStatus: boolean = false; @observable _onlyAliases: boolean = true; + /** + * This is the constructor for the SearchBox class. + */ constructor(props: any) { super(props); SearchBox.Instance = this; - this._searchString = "reset_search"; - this.submitSearch(); - this._searchString = ""; - this.submitSearch(); } + /** + * This method is called when the SearchBox component is first mounted. When the user opens + * the search panel, the search input box is automatically selected. This allows the user to + * type in the search input box immediately, without needing clicking on it first. + */ componentDidMount = action(() => { if (this._inputRef.current) { this._inputRef.current.focus(); } }); + /** + * This method is called when the SearchBox component is about to be unmounted. When the user + * closes the search panel, the search and its results are reset. + */ componentWillUnmount() { - this.resetSearch; + this.resetSearch(); } + /** + * This method is called when the text in the search input box is modified by the user. The + * _searchString is updated to the new value of the text in the input box and submitSearch + * is called to update the search results accordingly. + * + * (Note: There is no longer a need to press enter to submit a search. Any update to the input + * causes a search to be submitted automatically.) + */ onInputChange = action((e: React.ChangeEvent) => { this._searchString = e.target.value; this.submitSearch(); }); + /** + * This method is called when the option in the select drop-down menu is changed. The + * _docTypeString is updated to the new value of the option in the drop-down menu. This + * is used to filter the results of the search to documents of a specific type. + * + * (Note: This doesn't affect the results array, so there is no need to submit a new + * search here. The results of the search on the _searchString query are simply filtered + * by type directly before rendering them.) + */ onSelectChange = action((e: React.ChangeEvent) => { this._docTypeString = e.target.value; }); + /** + * @param {Doc} doc - doc of the search result that has been clicked on + * + * This method is called when the user clicks on a search result. The _selectedResult is + * updated accordingly and the doc is highlighted with the selectElement method. + */ onResultClick = action((doc: Doc) => { this.selectElement(doc); this._selectedResult = doc; }); - static foreachRecursiveDoc(docs: Doc[], func: (doc: Doc) => void) { + /** + * @param {Doc[]} docs - docs to be searched through recursively + * @param {number, Doc => void} func - function to be called on each doc + * + * This method iterates through an array of docs and all docs within those docs, calling + * the function func on each doc. + */ + static foreachRecursiveDoc(docs: Doc[], func: (depth: number, doc: Doc) => void) { let newarray: Doc[] = []; + var depth = 0; while (docs.length > 0) { newarray = []; docs.filter(d => d).forEach(d => { @@ -94,12 +133,45 @@ export class SearchBox extends ViewBoxBaseComponent void} func - function to be called on each doc + * + * This method iterates asynchronously through an array of docs and all docs within those + * docs, calling the function func on each doc. + */ + static async foreachRecursiveDocAsync(docs: Doc[], func: (depth: number, doc: Doc) => void) { + let newarray: Doc[] = []; + var depth = 0; + while (docs.length > 0) { + newarray = []; + await Promise.all(docs.filter(d => d).map(async d => { + const fieldKey = Doc.LayoutFieldKey(d); + const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView"); + const data = d[annos ? fieldKey + "-annotations" : fieldKey]; + const docs = await DocListCastAsync(data); + docs && newarray.push(...docs); + func(depth, d); + })); + docs = newarray; + depth++; + } + } + + /** + * @param {String} type - string representing the type of a doc + * + * This method converts a doc type string of any length to a 3-letter doc type string in + * which the first letter is capitalized. This is used when displaying the type on the + * right side of each search result. + */ static formatType(type: String): String { if (type == "pdf") { return "PDF"; @@ -111,57 +183,75 @@ export class SearchBox extends ViewBoxBaseComponent { + if (this._selectedCollection !== undefined) { + const docs = DocListCast(this._selectedCollection.dataDoc[Doc.LayoutFieldKey(this._selectedCollection.dataDoc)]); + const docIDs: String[] = [] + SearchBox.foreachRecursiveDoc(docs, (depth: number, doc: Doc) => { const dtype = StrCast(doc.type, "string") as DocumentType; - if (dtype && !blockedTypes.includes(dtype)) { + if (dtype && !blockedTypes.includes(dtype) && !docIDs.includes(doc[Id]) && depth > 0) { const hlights = new Set(); SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key)); - Array.from(hlights.keys()).length > 0 && this._results.push([doc, Array.from(hlights.keys()), []]); + blockedKeys.forEach(key => { + hlights.delete(key); + }) + Array.from(hlights.keys()).length > 0 && this._results.push([doc, Array.from(hlights.keys())]); } + docIDs.push(doc[Id]) }); } - - this._results = Array.from(new Set(this._results)) - this._selectedResult = undefined } + /** + * @param {Doc} doc - doc for which keys are returned + * + * This method returns a list of a document doc's keys. + */ static documentKeys(doc: Doc) { const keys: { [key: string]: boolean } = {}; - // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. - // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be - // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked. - // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu - // is displayed (unlikely) it won't show up until something else changes. - //TODO Types Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false)); return Array.from(Object.keys(keys)); } + /** + * This method submits a search with the _searchString as its query and updates + * the results array accordingly. + */ @action submitSearch = async () => { this.resetSearch(); - //this.dataDoc[this.fieldKey] = new List([]); let query = StrCast(this._searchString); Doc.SetSearchQuery(query); this._results = []; - this._resultsSet.clear(); if (query) { this.searchCollection(query); } } + /** + * This method resets the search by iterating through each result and removing all + * brushes and highlights. All search matches are cleared as well. + */ resetSearch = action(() => { this._results.forEach(result => { Doc.UnBrushDoc(result[0]); @@ -170,10 +260,32 @@ export class SearchBox extends ViewBoxBaseComponent { - await DocumentManager.Instance.jumpToDocument(doc, true); // documents open in new tab instead of on right + await DocumentManager.Instance.jumpToDocument(doc, true); + } + + /** + * This method returns a JSX list of the options in the select drop-down menu, which + * is used to filter the types of documents that appear in the search results. + */ + @computed + public get selectOptions() { + const selectValues = ["all", "rtf", "image", "pdf", "web", "video", "audio", "collection"] + + return selectValues.map(value => { + return + }) } + /** + * This method renders the search input box, select drop-down menu, and search results. + */ render() { var validResults = 0; @@ -186,7 +298,19 @@ export class SearchBox extends ViewBoxBaseComponent this.onResultClick(result[0])} className={className}>
{result[0].title}
{SearchBox.formatType(StrCast(result[0].type))}
) + return ( +
this.onResultClick(result[0])} className={className}> +
+ {result[0].title} +
+
+ {SearchBox.formatType(StrCast(result[0].type))} +
+
+ {result[1].join(", ")} +
+
+ ) } return null; @@ -194,15 +318,11 @@ export class SearchBox extends ViewBoxBaseComponent result); - const selectOptions = selectValues.map(value => { - return - }) - return (
@@ -217,190 +337,4 @@ export class SearchBox extends ViewBoxBaseComponent ); } -} - - - -/* -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { Tooltip } from '@material-ui/core'; -import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { Doc, DocListCast, Field, Opt, DocListCastAsync, DataSym, HeightSym, FieldsSym } from '../../../fields/Doc'; -import { documentSchema } from "../../../fields/documentSchemas"; -import { Copy, Id, ToString } from '../../../fields/FieldSymbols'; -import { List } from '../../../fields/List'; -import { createSchema, listSpec, makeInterface } from '../../../fields/Schema'; -import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; -import { Cast, NumCast, StrCast } from '../../../fields/Types'; -import { emptyFunction, returnFalse, returnZero, setupMoveUpEvents, Utils } from '../../../Utils'; -import { Docs } from '../../documents/Documents'; -import { DocumentType } from "../../documents/DocumentTypes"; -import { CurrentUserUtils } from "../../util/CurrentUserUtils"; -import { SetupDrag } from '../../util/DragManager'; -import { SearchUtil } from '../../util/SearchUtil'; -import { Transform } from '../../util/Transform'; -import { CollectionDockingView } from "../collections/CollectionDockingView"; -import { CollectionSchemaView, ColumnType } from "../collections/CollectionSchemaView"; -import { CollectionViewType } from '../collections/CollectionView'; -import { ViewBoxBaseComponent } from "../DocComponent"; -import { FieldView, FieldViewProps } from '../nodes/FieldView'; -import "./SearchBox.scss"; -import { undoBatch } from "../../util/UndoManager"; -import { DocServer } from "../../DocServer"; -import { MainView } from "../MainView"; -import { SelectionManager } from "../../util/SelectionManager"; -import { CollectionSchemaBooleanCell } from "../collections/CollectionSchemaCells"; -import { transpileModule } from "typescript"; -import { DocumentManager } from "../../util/DocumentManager"; - -export const searchSchema = createSchema({ Document: Doc }); - -type SearchBoxDocument = makeInterface<[typeof documentSchema, typeof searchSchema]>; -const SearchBoxDocument = makeInterface(documentSchema, searchSchema); - -@observer -export class SearchBox extends ViewBoxBaseComponent(SearchBoxDocument) { - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(SearchBox, fieldKey); } - public static Instance: SearchBox; - - @observable _searchString = ""; - @observable _docTypeString = "all"; - @observable _results: [Doc, string[], string[]][] = []; - @observable _selectedResult: Doc | undefined = undefined; - @observable _deletedDocsStatus: boolean = false; - @observable _onlyAliases: boolean = true; - - constructor(props: any) { - super(props); - SearchBox.Instance = this; - } - - onInputChange = action((e: React.ChangeEvent) => { - this._searchString = e.target.value; - this.submitSearch(); - }); - - onSelectChange = action((e: React.ChangeEvent) => { - this._docTypeString = e.target.value; - this.submitSearch(); - }); - - onResultClick = action((doc: Doc) => { - this.selectElement(doc); - this._selectedResult = doc; - }); - - static foreachRecursiveDoc(docs: Doc[], func: (doc: Doc) => void) { - const blockedTypes = [DocumentType.PRESELEMENT, DocumentType.KVP, DocumentType.FILTER, DocumentType.SEARCH, DocumentType.SEARCHITEM, DocumentType.FONTICON, DocumentType.BUTTON, DocumentType.SCRIPTING]; - let newarray: Doc[] = []; - while (docs.length > 0) { - newarray = []; - docs.filter(d => d).forEach(d => { - const dtype = StrCast(d.type, "string") as DocumentType; - if (dtype && !blockedTypes.includes(dtype)) { - const fieldKey = Doc.LayoutFieldKey(d); - const annos = !Field.toString(Doc.LayoutField(d) as Field).includes("CollectionView"); - const data = d[annos ? fieldKey + "-annotations" : fieldKey]; - data && newarray.push(...DocListCast(data)); - func(d); - } - }); - docs = newarray; - } - } - - @action - searchCollection(query: string) { - const selectedCollection = CollectionDockingView.Instance; - query = query.toLowerCase(); - - if (selectedCollection !== undefined) { - console.log("hello111") - // this._currentSelectedCollection = selectedCollection; - const docs = DocListCast(selectedCollection.dataDoc[Doc.LayoutFieldKey(selectedCollection.dataDoc)]); - const found: [Doc, string[], string[]][] = []; - SearchBox.foreachRecursiveDoc(docs, (doc: Doc) => { - console.log("HELLO") - if (this._docTypeString == "all" || this._docTypeString == doc.type) { - const hlights = new Set(); - SearchBox.documentKeys(doc).forEach(key => Field.toString(doc[key] as Field).toLowerCase().includes(query) && hlights.add(key)); - Array.from(hlights.keys()).length > 0 && found.push([doc, Array.from(hlights.keys()), []]); - } - }); - - this._results = found; - //this.setSearchFilter(selectedCollection, this.filter && found.length ? this._docsforfilter : undefined); - } - } - - static documentKeys(doc: Doc) { - const keys: { [key: string]: boolean } = {}; - // bcz: ugh. this is untracked since otherwise a large collection of documents will blast the server for all their fields. - // then as each document's fields come back, we update the documents _proxies. Each time we do this, the whole schema will be - // invalidated and re-rendered. This workaround will inquire all of the document fields before the options button is clicked. - // then by the time the options button is clicked, all of the fields should be in place. If a new field is added while this menu - // is displayed (unlikely) it won't show up until something else changes. - //TODO Types - Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false)); - return Array.from(Object.keys(keys)); - } - - @action - submitSearch = async () => { - Doc.ClearSearchMatches(); - this._results = []; - - this.dataDoc[this.fieldKey] = new List([]); - let query = StrCast(this._searchString); - Doc.SetSearchQuery(query); - this._results = []; - - if (query) { - this.searchCollection(query); - } - } - - selectElement = async (doc: Doc) => { - await DocumentManager.Instance.jumpToDocument(doc, true); // documents open in new tab instead of on right - } - - render() { - const results = this._results.map(result => { - var className = "searchBox-results-scroll-view-result"; - - if (this._selectedResult == result[0]) { - className += " searchBox-results-scroll-view-result-selected" - } - - return (
this.onResultClick(result[0])} className={className}>
{result[0].title}
) - }) - - return ( -
-
- - -
-
-
- {`${this._results.length}` + " result" + (this._results.length == 1 ? "" : "s")} -
-
- {results} -
-
-
- ); - } -}*/ \ No newline at end of file +} \ No newline at end of file -- 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/collections') 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 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/collections') 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 917ebb4a7b6840f57abbf8dbcc197919f3ee3aac Mon Sep 17 00:00:00 2001 From: geireann Date: Wed, 18 Aug 2021 17:27:07 -0400 Subject: updates towards a menu --- src/client/documents/Documents.ts | 1 + src/client/util/CurrentUserUtils.ts | 6 +- src/client/views/MainView.scss | 5 - src/client/views/MainView.tsx | 2 + src/client/views/StyleProvider.tsx | 3 +- src/client/views/collections/CollectionMenu.scss | 1255 ++++++++++---------- src/client/views/collections/CollectionMenu.tsx | 101 +- .../collectionLinearView/CollectionLinearView.scss | 33 +- .../collectionLinearView/CollectionLinearView.tsx | 43 +- src/client/views/nodes/DocumentView.tsx | 29 +- src/client/views/nodes/button/FontIconBadge.tsx | 13 +- src/client/views/nodes/button/FontIconBox.scss | 262 ++-- src/client/views/nodes/button/FontIconBox.tsx | 81 +- 13 files changed, 1022 insertions(+), 812 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 36cbd2059..817fbb9d6 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -227,6 +227,7 @@ export class DocumentOptions { //LINEAR VIEW linearViewIsExpanded?: boolean; // is linear view expanded linearViewExpandable?: boolean; // can linear view be expanded + linearViewToggleButton?: string; // button to open close linear view group 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 620602070..cd4c217b5 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -547,7 +547,7 @@ export class CurrentUserUtils { { 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: "Pres. Trails", target: Cast(doc.myPresentations, Doc, null), icon: "pres-trail", 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)' }, @@ -594,6 +594,7 @@ 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, @@ -998,7 +999,7 @@ export class CurrentUserUtils { onClick: click ? ScriptField.MakeScript(click, { scriptContext: "any" }) : undefined })); }); - docList.push(CurrentUserUtils.blist({ ignoreClick: true, linearViewExpandable: true, _height: 30, backgroundColor: "transparent" }, textDocList)); + docList.push(CurrentUserUtils.blist({ ignoreClick: true, linearViewExpandable: true, icon:"Text", _height: 30, backgroundColor: "transparent" }, textDocList)); } else { docList.push(Docs.Create.FontIconDocument({ _nativeWidth: width ? width : 30, @@ -1197,6 +1198,7 @@ export class CurrentUserUtils { 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 + this.setupContextMenuButtons(doc); // set up context menu buttons this.setupDockedButtons(doc); // the bottom bar of font icons await this.setupSidebarButtons(doc); // the pop-out left sidebar of tools/panels await this.setupMenuPanel(doc, sharingDocumentId, linkDatabaseId); diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index d913f2069..817e45699 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -265,11 +265,6 @@ height: 35px; padding: 5px; } - - svg { - width: 95% !important; - height: 95%; - } } .mainView-searchPanel { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 6a388c5b4..5c1408148 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -439,6 +439,7 @@ export class MainView extends React.Component { this._flyoutWidth = (this._flyoutWidth || 250); this._sidebarContent.proto = button.target as any; this.LastButton = button; + console.log(button.title); }); closeFlyout = action(() => { @@ -446,6 +447,7 @@ 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/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index c9e532745..85520f6b3 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -96,6 +96,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt = StrCast(doc?.[fieldKey + "color"], StrCast(doc?._color)); if (docColor) return docColor; const backColor = backgroundCol(); @@ -114,8 +115,8 @@ export function DefaultStyleProvider(doc: Opt, props: Opt = StrCast(doc?.[fieldKey + "backgroundColor"], StrCast(doc?._backgroundColor, isCaption ? "rgba(0,0,0,0.4)" : "")); - if (MainView.Instance.LastButton === doc) return darkScheme() ? Colors.MEDIUM_GRAY : Colors.LIGHT_GRAY; switch (doc?.type) { case DocumentType.PRESELEMENT: docColor = docColor || (darkScheme() ? "" : ""); break; case DocumentType.PRES: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break; diff --git a/src/client/views/collections/CollectionMenu.scss b/src/client/views/collections/CollectionMenu.scss index f04b19ef7..163566d22 100644 --- a/src/client/views/collections/CollectionMenu.scss +++ b/src/client/views/collections/CollectionMenu.scss @@ -1,628 +1,659 @@ @import "../global/globalCssVariables"; -.collectionMenu-cont { - position: relative; - display: inline-flex; - width: 100%; - opacity: 0.9; - z-index: 901; - transition: top .5s; - background: $dark-gray; - color: $white; - transform-origin: top left; - top: 0; - width: 100%; - - .recordButtonOutline { - border-radius: 100%; - width: 18px; - height: 18px; - border: solid 1px $white; - display: flex; - align-items: center; - justify-content: center; - } - - .recordButtonInner { - border-radius: 100%; - width: 70%; - height: 70%; - background: $white; - } - - .collectionMenu { - display: flex; - height: 100%; - overflow: visible; - z-index: 901; - border: unset; - - .collectionMenu-divider { - height: 100%; - margin-left: 3px; - margin-right: 3px; - width: 2px; - background-color: $medium-gray; - } - - .collectionViewBaseChrome { - display: flex; - align-items: center; - - .collectionViewBaseChrome-viewPicker { - font-size: $small-text; - outline-color: $black; - color: $white; - border: none; - background: $dark-gray; - } - - .collectionViewBaseChrome-viewPicker:focus { - outline: none; - border: none; - } - - .collectionViewBaseChrome-viewPicker:active { - outline-color: $black; - } - - .collectionViewBaseChrome-button { - font-size: $small-text; - text-transform: uppercase; - letter-spacing: 2px; - background: $white; - color: $pink; - outline-color: $black; - border: none; - padding: 12px 10px 11px 10px; - margin-left: 10px; - } - - .collectionViewBaseChrome-cmdPicker { - margin-left: 3px; - margin-right: 0px; - font-size: $small-text; - text-transform: capitalize; - color: $white; - border: none; - background: $dark-gray; - } - - .collectionViewBaseChrome-cmdPicker:focus { - border: none; - outline: none; - } - - .commandEntry-outerDiv { - pointer-events: all; - background-color: transparent; - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - height: 100%; - overflow: hidden; - - .commandEntry-drop { - color: $white; - width: 30px; - margin-top: auto; - margin-bottom: auto; - } - } - - .commandEntry-outerDiv:hover{ - background-color: $drop-shadow; - - .collectionViewBaseChrome-viewPicker, - .collectionViewBaseChrome-cmdPicker{ - background: $dark-gray; - } - } - - .collectionViewBaseChrome-collapse { - transition: all .5s, opacity 0.3s; - position: absolute; - width: 30px; - transform-origin: top left; - pointer-events: all; - // margin-top: 10px; - } - - @media only screen and (max-device-width: 480px) { - .collectionViewBaseChrome-collapse { - display: none; - } - } - - .collectionViewBaseChrome-template, - .collectionViewBaseChrome-viewModes { - align-items: center; - height: 100%; - display: flex; - background: transparent; - color: $medium-gray; - justify-content: center; - } - - .collectionViewBaseChrome-viewSpecs { - margin-left: 5px; - display: grid; - border: none; - border-right: solid $medium-gray 1px; - - .collectionViewBaseChrome-filterIcon { - position: relative; - display: flex; - margin: auto; - background: $dark-gray; - color: $white; - width: 30px; - height: 30px; - align-items: center; - justify-content: center; - border: none; - border-right: solid $medium-gray 1px; - } - - .collectionViewBaseChrome-viewSpecsInput { - padding: 12px 10px 11px 10px; - border: 0px; - color: $medium-gray; - text-align: center; - letter-spacing: 2px; - outline-color: $black; - font-size: $small-text; - background: $white; - height: 100%; - width: 75px; - } - - .collectionViewBaseChrome-viewSpecsMenu { - overflow: hidden; - transition: height .5s, display .5s; - position: absolute; - top: 60px; - z-index: 100; - display: flex; - flex-direction: column; - background: $white; - box-shadow: $medium-gray 2px 2px 4px; - - .qs-datepicker { - left: unset; - right: 0; - } - - .collectionViewBaseChrome-viewSpecsMenu-row { - display: grid; - grid-template-columns: 150px 200px 150px; - margin-top: 10px; - margin-right: 10px; - - .collectionViewBaseChrome-viewSpecsMenu-rowLeft, - .collectionViewBaseChrome-viewSpecsMenu-rowMiddle, - .collectionViewBaseChrome-viewSpecsMenu-rowRight { - font-size: $small-text; - letter-spacing: 2px; - color: $medium-gray; - margin-left: 10px; - padding: 5px; - border: none; - outline-color: $black; - } - } - - .collectionViewBaseChrome-viewSpecsMenu-lastRow { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - grid-gap: 10px; - margin: 10px; - } - } - } - } - - .collectionStackingViewChrome-cont, - .collectionTreeViewChrome-cont, - .collection3DCarouselViewChrome-cont { - display: flex; - justify-content: space-between; - } - - .collectionGridViewChrome-cont { - display: flex; - margin-left: 10; - - .collectionGridViewChrome-viewPicker { - font-size: $small-text; - //text-transform: uppercase; - //letter-spacing: 2px; - background: $dark-gray; - color: $white; - outline-color: $black; - color: $white; - border: none; - border-right: solid $medium-gray 1px; - } - - .collectionGridViewChrome-viewPicker:active { - outline-color: $black; - } - - .grid-control { - align-self: center; - display: flex; - flex-direction: row; - margin-right: 5px; - - .grid-icon { - margin-right: 5px; - align-self: center; - } - - .flexLabel { - margin-bottom: 0; - } - - .collectionGridViewChrome-entryBox { - width: 50%; - color: $black; - } - - .collectionGridViewChrome-columnButton { - color: $black; - } - } - } - - .collectionStackingViewChrome-sort, - .collectionTreeViewChrome-sort { - display: flex; - align-items: center; - justify-content: space-between; - - .collectionStackingViewChrome-sortIcon, - .collectionTreeViewChrome-sortIcon { - transition: transform .5s; - margin-left: 10px; - } - } - - button:hover { - transform: scale(1); - } - - - .collectionStackingViewChrome-pivotField-cont, - .collectionTreeViewChrome-pivotField-cont, - .collection3DCarouselViewChrome-scrollSpeed-cont { - justify-self: right; - align-items: center; - display: flex; - grid-auto-columns: auto; - font-size: $small-text; - letter-spacing: 2px; - - .collectionStackingViewChrome-pivotField-label, - .collectionTreeViewChrome-pivotField-label, - .collection3DCarouselViewChrome-scrollSpeed-label { - grid-column: 1; - margin-right: 7px; - user-select: none; - font-family: $sans-serif; - letter-spacing: normal; - } - - .collectionStackingViewChrome-sortIcon { - transition: transform .5s; - grid-column: 3; - text-align: center; - display: flex; - justify-content: center; - align-items: center; - cursor: pointer; - width: 25px; - height: 25px; - border-radius: 100%; - } - - .collectionStackingViewChrome-sortIcon:hover { - background-color: $drop-shadow; - } - - .collectionStackingViewChrome-pivotField, - .collectionTreeViewChrome-pivotField, - .collection3DCarouselViewChrome-scrollSpeed { - color: $white; - grid-column: 2; - grid-row: 1; - width: 90%; - min-width: 100px; - display: flex; - height: 80%; - border-radius: 7px; - align-items: center; - background: $white; - - .editable-view-input, - input, - .editableView-container-editing-oneLine, - .editableView-container-editing { - margin: auto; - border: 0px; - color: $light-gray !important; - text-align: center; - letter-spacing: 2px; - outline-color: $black; - height: 100%; - } - - .react-autosuggest__container { - margin: 0; - color: $medium-gray; - padding: 0px; - } - } - } - - .collectionStackingViewChrome-pivotField:hover, - .collectionTreeViewChrome-pivotField:hover, - .collection3DCarouselViewChrome-scrollSpeed:hover { - cursor: text; - } - - } -} - -.collectionMenu-webUrlButtons { - margin-left: 44; - background: lightGray; +.collectionMenu-container { display: flex; -} - -.webBox-urlEditor { - position: relative; - opacity: 0.9; - z-index: 901; - transition: top .5s; - - .urlEditor { - display: grid; - grid-template-columns: 1fr auto; - padding-bottom: 10px; - overflow: hidden; - margin-top: 5px; - height: 35px; - - .editorBase { - display: flex; - - .editor-collapse { - transition: all .5s, opacity 0.3s; - position: absolute; - width: 40px; - transform-origin: top left; - } - - .switchToText { - color: $medium-gray; - } - - .switchToText:hover { - color: $dark-gray; - } - } - - button:hover { - transform: scale(1); - } - } -} - -.collectionMenu-urlInput { - padding: 12px 10px 11px 10px; - border: 0px; - color: $black; - font-size: $small-text; - letter-spacing: 2px; - outline-color: $black; - background: $white; - width: 100%; - min-width: 350px; - margin-right: 10px; - height: 100%; -} - -.collectionFreeFormMenu-cont { - display: inline-flex; position: relative; + align-content: center; + justify-content: space-between; + background-color: $dark-gray; + height: 35px; + border-bottom: $standard-border; + padding-right: 5px; align-items: center; - height: 100%; - - .color-previewI { - width: 60%; - top: 80%; - position: absolute; - height: 4px; - } - - .color-previewII { - width: 80%; - height: 80%; - margin-left: 10%; - position: absolute; - bottom: 5; - } - - .btn-group { - display: grid; - grid-template-columns: auto auto auto auto; - margin: auto; - /* Make the buttons appear below each other */ - } - .btn-draw { - display: inline-flex; - margin: auto; - /* Make the buttons appear below each other */ - } - - .fwdKeyframe, - .numKeyframe, - .backKeyframe { + .collectionMenu-hardCodedButton { cursor: pointer; - position: relative; - width: 20; - height: 30; - bottom: 0; - background: $dark-gray; - display: inline-flex; - align-items: center; color: $white; - } - - .backKeyframe { - svg { - display: block; - margin: auto; - } - } - - - .numKeyframe { - flex-direction: column; - padding-top: 5px; - } - - .fwdKeyframe { - svg { - display: block; - margin: auto; - } - - border-right: solid $medium-gray 1px; - } -} - -.collectionSchemaViewChrome-cont { - display: flex; - font-size: $small-text; - - .collectionSchemaViewChrome-toggle { - display: flex; - margin-left: 10px; - } - - .collectionSchemaViewChrome-label { - text-transform: uppercase; - letter-spacing: 2px; - margin-right: 5px; + width: 31.5px; + height: 90%; + padding: 5; + text-align: center; display: flex; - flex-direction: column; justify-content: center; - } - - .collectionSchemaViewChrome-toggler { - width: 100px; - height: 35px; - background-color: $black; + align-items: center; position: relative; - } - - .collectionSchemaViewChrome-togglerButton { - width: 47px; - height: 30px; - background-color: $light-gray; - // position: absolute; - transition: all 0.5s ease; - // top: 3px; - margin-top: 3px; - color: $medium-gray; - letter-spacing: 2px; - text-transform: uppercase; - display: flex; - flex-direction: column; - justify-content: center; - text-align: center; + transition: 0.2s; - &.on { - margin-left: 3px; - } - - &.off { - margin-left: 50px; + &:hover { + border-radius:5px; + background-color: rgba(0,0,0,0.2); } } } - -.commandEntry-outerDiv { - display: flex; - flex-direction: column; - height: 40px; -} - -.commandEntry-inputArea { - display: flex; - flex-direction: row; - width: 150px; - margin: auto auto auto auto; -} - -.react-autosuggest__container { - position: relative; - width: 100%; - margin-left: 5px; - margin-right: 5px; -} - -.react-autosuggest__input { - border: 1px solid $light-gray; - border-radius: 4px; - width: 100%; -} - -.react-autosuggest__input--focused { - outline: none; -} - -.react-autosuggest__input--open { - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; -} - -.react-autosuggest__suggestions-container { - display: none; -} - -.react-autosuggest__suggestions-container--open { - display: block; - position: fixed; - overflow-y: auto; - max-height: 400px; - width: 180px; - border: 1px solid $light-gray; - background-color: $white; - font-family: $sans-serif; - font-weight: 300; - font-size: $large-header; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - z-index: 2; -} - -.react-autosuggest__suggestions-list { - margin: 0; - padding: 0; - list-style-type: none; -} - -.react-autosuggest__suggestion { - cursor: pointer; - padding: 10px 20px; -} - -.react-autosuggest__suggestion--highlighted { - background-color: $light-gray; -} \ No newline at end of file +// .collectionMenu-cont { +// position: relative; +// display: inline-flex; +// width: 100%; +// opacity: 0.9; +// z-index: 901; +// transition: top .5s; +// background: $dark-gray; +// color: $white; +// transform-origin: top left; +// top: 0; +// width: 100%; + +// .recordButtonOutline { +// border-radius: 100%; +// width: 18px; +// height: 18px; +// border: solid 1px $white; +// display: flex; +// align-items: center; +// justify-content: center; +// } + +// .recordButtonInner { +// border-radius: 100%; +// width: 70%; +// height: 70%; +// background: $white; +// } + +// .collectionMenu { +// display: flex; +// height: 100%; +// overflow: visible; +// z-index: 901; +// border: unset; + +// .collectionMenu-divider { +// height: 100%; +// margin-left: 3px; +// margin-right: 3px; +// width: 2px; +// background-color: $medium-gray; +// } + +// .collectionViewBaseChrome { +// display: flex; +// align-items: center; + +// .collectionViewBaseChrome-viewPicker { +// font-size: $small-text; +// outline-color: $black; +// color: $white; +// border: none; +// background: $dark-gray; +// } + +// .collectionViewBaseChrome-viewPicker:focus { +// outline: none; +// border: none; +// } + +// .collectionViewBaseChrome-viewPicker:active { +// outline-color: $black; +// } + +// .collectionViewBaseChrome-button { +// font-size: $small-text; +// text-transform: uppercase; +// letter-spacing: 2px; +// background: $white; +// color: $pink; +// outline-color: $black; +// border: none; +// padding: 12px 10px 11px 10px; +// margin-left: 10px; +// } + +// .collectionViewBaseChrome-cmdPicker { +// margin-left: 3px; +// margin-right: 0px; +// font-size: $small-text; +// text-transform: capitalize; +// color: $white; +// border: none; +// background: $dark-gray; +// } + +// .collectionViewBaseChrome-cmdPicker:focus { +// border: none; +// outline: none; +// } + +// .commandEntry-outerDiv { +// pointer-events: all; +// background-color: transparent; +// display: flex; +// flex-direction: row; +// align-items: center; +// justify-content: center; +// height: 100%; +// overflow: hidden; + +// .commandEntry-drop { +// color: $white; +// width: 30px; +// margin-top: auto; +// margin-bottom: auto; +// } +// } + +// .commandEntry-outerDiv:hover{ +// background-color: $drop-shadow; + +// .collectionViewBaseChrome-viewPicker, +// .collectionViewBaseChrome-cmdPicker{ +// background: $dark-gray; +// } +// } + +// .collectionViewBaseChrome-collapse { +// transition: all .5s, opacity 0.3s; +// position: absolute; +// width: 30px; +// transform-origin: top left; +// pointer-events: all; +// // margin-top: 10px; +// } + +// @media only screen and (max-device-width: 480px) { +// .collectionViewBaseChrome-collapse { +// display: none; +// } +// } + +// .collectionViewBaseChrome-template, +// .collectionViewBaseChrome-viewModes { +// align-items: center; +// height: 100%; +// display: flex; +// background: transparent; +// color: $medium-gray; +// justify-content: center; +// } + +// .collectionViewBaseChrome-viewSpecs { +// margin-left: 5px; +// display: grid; +// border: none; +// border-right: solid $medium-gray 1px; + +// .collectionViewBaseChrome-filterIcon { +// position: relative; +// display: flex; +// margin: auto; +// background: $dark-gray; +// color: $white; +// width: 30px; +// height: 30px; +// align-items: center; +// justify-content: center; +// border: none; +// border-right: solid $medium-gray 1px; +// } + +// .collectionViewBaseChrome-viewSpecsInput { +// padding: 12px 10px 11px 10px; +// border: 0px; +// color: $medium-gray; +// text-align: center; +// letter-spacing: 2px; +// outline-color: $black; +// font-size: $small-text; +// background: $white; +// height: 100%; +// width: 75px; +// } + +// .collectionViewBaseChrome-viewSpecsMenu { +// overflow: hidden; +// transition: height .5s, display .5s; +// position: absolute; +// top: 60px; +// z-index: 100; +// display: flex; +// flex-direction: column; +// background: $white; +// box-shadow: $medium-gray 2px 2px 4px; + +// .qs-datepicker { +// left: unset; +// right: 0; +// } + +// .collectionViewBaseChrome-viewSpecsMenu-row { +// display: grid; +// grid-template-columns: 150px 200px 150px; +// margin-top: 10px; +// margin-right: 10px; + +// .collectionViewBaseChrome-viewSpecsMenu-rowLeft, +// .collectionViewBaseChrome-viewSpecsMenu-rowMiddle, +// .collectionViewBaseChrome-viewSpecsMenu-rowRight { +// font-size: $small-text; +// letter-spacing: 2px; +// color: $medium-gray; +// margin-left: 10px; +// padding: 5px; +// border: none; +// outline-color: $black; +// } +// } + +// .collectionViewBaseChrome-viewSpecsMenu-lastRow { +// display: grid; +// grid-template-columns: 1fr 1fr 1fr; +// grid-gap: 10px; +// margin: 10px; +// } +// } +// } +// } + +// .collectionStackingViewChrome-cont, +// .collectionTreeViewChrome-cont, +// .collection3DCarouselViewChrome-cont { +// display: flex; +// justify-content: space-between; +// } + +// .collectionGridViewChrome-cont { +// display: flex; +// margin-left: 10; + +// .collectionGridViewChrome-viewPicker { +// font-size: $small-text; +// //text-transform: uppercase; +// //letter-spacing: 2px; +// background: $dark-gray; +// color: $white; +// outline-color: $black; +// color: $white; +// border: none; +// border-right: solid $medium-gray 1px; +// } + +// .collectionGridViewChrome-viewPicker:active { +// outline-color: $black; +// } + +// .grid-control { +// align-self: center; +// display: flex; +// flex-direction: row; +// margin-right: 5px; + +// .grid-icon { +// margin-right: 5px; +// align-self: center; +// } + +// .flexLabel { +// margin-bottom: 0; +// } + +// .collectionGridViewChrome-entryBox { +// width: 50%; +// color: $black; +// } + +// .collectionGridViewChrome-columnButton { +// color: $black; +// } +// } +// } + +// .collectionStackingViewChrome-sort, +// .collectionTreeViewChrome-sort { +// display: flex; +// align-items: center; +// justify-content: space-between; + +// .collectionStackingViewChrome-sortIcon, +// .collectionTreeViewChrome-sortIcon { +// transition: transform .5s; +// margin-left: 10px; +// } +// } + +// button:hover { +// transform: scale(1); +// } + + +// .collectionStackingViewChrome-pivotField-cont, +// .collectionTreeViewChrome-pivotField-cont, +// .collection3DCarouselViewChrome-scrollSpeed-cont { +// justify-self: right; +// align-items: center; +// display: flex; +// grid-auto-columns: auto; +// font-size: $small-text; +// letter-spacing: 2px; + +// .collectionStackingViewChrome-pivotField-label, +// .collectionTreeViewChrome-pivotField-label, +// .collection3DCarouselViewChrome-scrollSpeed-label { +// grid-column: 1; +// margin-right: 7px; +// user-select: none; +// font-family: $sans-serif; +// letter-spacing: normal; +// } + +// .collectionStackingViewChrome-sortIcon { +// transition: transform .5s; +// grid-column: 3; +// text-align: center; +// display: flex; +// justify-content: center; +// align-items: center; +// cursor: pointer; +// width: 25px; +// height: 25px; +// border-radius: 100%; +// } + +// .collectionStackingViewChrome-sortIcon:hover { +// background-color: $drop-shadow; +// } + +// .collectionStackingViewChrome-pivotField, +// .collectionTreeViewChrome-pivotField, +// .collection3DCarouselViewChrome-scrollSpeed { +// color: $white; +// grid-column: 2; +// grid-row: 1; +// width: 90%; +// min-width: 100px; +// display: flex; +// height: 80%; +// border-radius: 7px; +// align-items: center; +// background: $white; + +// .editable-view-input, +// input, +// .editableView-container-editing-oneLine, +// .editableView-container-editing { +// margin: auto; +// border: 0px; +// color: $light-gray !important; +// text-align: center; +// letter-spacing: 2px; +// outline-color: $black; +// height: 100%; +// } + +// .react-autosuggest__container { +// margin: 0; +// color: $medium-gray; +// padding: 0px; +// } +// } +// } + +// .collectionStackingViewChrome-pivotField:hover, +// .collectionTreeViewChrome-pivotField:hover, +// .collection3DCarouselViewChrome-scrollSpeed:hover { +// cursor: text; +// } + +// } +// } + +// .collectionMenu-webUrlButtons { +// margin-left: 44; +// background: lightGray; +// display: flex; +// } + +// .webBox-urlEditor { +// position: relative; +// opacity: 0.9; +// z-index: 901; +// transition: top .5s; + +// .urlEditor { +// display: grid; +// grid-template-columns: 1fr auto; +// padding-bottom: 10px; +// overflow: hidden; +// margin-top: 5px; +// height: 35px; + +// .editorBase { +// display: flex; + +// .editor-collapse { +// transition: all .5s, opacity 0.3s; +// position: absolute; +// width: 40px; +// transform-origin: top left; +// } + +// .switchToText { +// color: $medium-gray; +// } + +// .switchToText:hover { +// color: $dark-gray; +// } +// } + +// button:hover { +// transform: scale(1); +// } +// } +// } + +// .collectionMenu-urlInput { +// padding: 12px 10px 11px 10px; +// border: 0px; +// color: $black; +// font-size: $small-text; +// letter-spacing: 2px; +// outline-color: $black; +// background: $white; +// width: 100%; +// min-width: 350px; +// margin-right: 10px; +// height: 100%; +// } + +// .collectionFreeFormMenu-cont { +// display: inline-flex; +// position: relative; +// align-items: center; +// height: 100%; + +// .color-previewI { +// width: 60%; +// top: 80%; +// position: absolute; +// height: 4px; +// } + +// .color-previewII { +// width: 80%; +// height: 80%; +// margin-left: 10%; +// position: absolute; +// bottom: 5; +// } + +// .btn-group { +// display: grid; +// grid-template-columns: auto auto auto auto; +// margin: auto; +// /* Make the buttons appear below each other */ +// } + +// .btn-draw { +// display: inline-flex; +// margin: auto; +// /* Make the buttons appear below each other */ +// } + +// .fwdKeyframe, +// .numKeyframe, +// .backKeyframe { +// cursor: pointer; +// position: relative; +// width: 20; +// height: 30; +// bottom: 0; +// background: $dark-gray; +// display: inline-flex; +// align-items: center; +// color: $white; +// } + +// .backKeyframe { +// svg { +// display: block; +// margin: auto; +// } +// } + + +// .numKeyframe { +// flex-direction: column; +// padding-top: 5px; +// } + +// .fwdKeyframe { +// svg { +// display: block; +// margin: auto; +// } + +// border-right: solid $medium-gray 1px; +// } +// } + +// .collectionSchemaViewChrome-cont { +// display: flex; +// font-size: $small-text; + +// .collectionSchemaViewChrome-toggle { +// display: flex; +// margin-left: 10px; +// } + +// .collectionSchemaViewChrome-label { +// text-transform: uppercase; +// letter-spacing: 2px; +// margin-right: 5px; +// display: flex; +// flex-direction: column; +// justify-content: center; +// } + +// .collectionSchemaViewChrome-toggler { +// width: 100px; +// height: 35px; +// background-color: $black; +// position: relative; +// } + +// .collectionSchemaViewChrome-togglerButton { +// width: 47px; +// height: 30px; +// background-color: $light-gray; +// // position: absolute; +// transition: all 0.5s ease; +// // top: 3px; +// margin-top: 3px; +// color: $medium-gray; +// letter-spacing: 2px; +// text-transform: uppercase; +// display: flex; +// flex-direction: column; +// justify-content: center; +// text-align: center; + +// &.on { +// margin-left: 3px; +// } + +// &.off { +// margin-left: 50px; +// } +// } +// } + + +// .commandEntry-outerDiv { +// display: flex; +// flex-direction: column; +// height: 40px; +// } + +// .commandEntry-inputArea { +// display: flex; +// flex-direction: row; +// width: 150px; +// margin: auto auto auto auto; +// } + +// .react-autosuggest__container { +// position: relative; +// width: 100%; +// margin-left: 5px; +// margin-right: 5px; +// } + +// .react-autosuggest__input { +// border: 1px solid $light-gray; +// border-radius: 4px; +// width: 100%; +// } + +// .react-autosuggest__input--focused { +// outline: none; +// } + +// .react-autosuggest__input--open { +// border-bottom-left-radius: 0; +// border-bottom-right-radius: 0; +// } + +// .react-autosuggest__suggestions-container { +// display: none; +// } + +// .react-autosuggest__suggestions-container--open { +// display: block; +// position: fixed; +// overflow-y: auto; +// max-height: 400px; +// width: 180px; +// border: 1px solid $light-gray; +// background-color: $white; +// font-family: $sans-serif; +// font-weight: 300; +// font-size: $large-header; +// border-bottom-left-radius: 4px; +// border-bottom-right-radius: 4px; +// z-index: 2; +// } + +// .react-autosuggest__suggestions-list { +// margin: 0; +// padding: 0; +// list-style-type: none; +// } + +// .react-autosuggest__suggestion { +// cursor: pointer; +// padding: 10px 20px; +// } + +// .react-autosuggest__suggestion--highlighted { +// background-color: $light-gray; +// } \ No newline at end of file diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 8f4df4a92..55af4650f 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -15,29 +15,31 @@ import { RichTextField } from "../../../fields/RichTextField"; import { listSpec } from "../../../fields/Schema"; import { ScriptField } from "../../../fields/ScriptField"; import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types"; -import { emptyFunction, setupMoveUpEvents, Utils } from "../../../Utils"; +import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents, Utils } from "../../../Utils"; +import { Docs } from "../../documents/Documents"; import { DocumentType } from "../../documents/DocumentTypes"; import { CurrentUserUtils } from "../../util/CurrentUserUtils"; import { DragManager } from "../../util/DragManager"; import { Scripting } from "../../util/Scripting"; import { SelectionManager } from "../../util/SelectionManager"; +import { Transform } from "../../util/Transform"; import { undoBatch } from "../../util/UndoManager"; import { AntimodeMenu, AntimodeMenuProps } from "../AntimodeMenu"; import { EditableView } from "../EditableView"; import { GestureOverlay } from "../GestureOverlay"; -import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, ActiveArrowStart, ActiveArrowEnd } from "../InkingStroke"; +import { ActiveFillColor, ActiveInkColor, SetActiveArrowEnd, SetActiveArrowStart, SetActiveBezierApprox, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from "../InkingStroke"; +import { LightboxView } from "../LightboxView"; import { CollectionFreeFormDocumentView } from "../nodes/CollectionFreeFormDocumentView"; import { DocumentView } from "../nodes/DocumentView"; +import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox"; import { RichTextMenu } from "../nodes/formattedText/RichTextMenu"; import { PresBox } from "../nodes/trails/PresBox"; +import { DefaultStyleProvider } from "../StyleProvider"; +import { CollectionDockingView } from "./CollectionDockingView"; +import { CollectionLinearView } from "./CollectionLinearView"; import "./CollectionMenu.scss"; import { CollectionViewType, COLLECTION_BORDER_WIDTH } from "./CollectionView"; import { TabDocView } from "./TabDocView"; -import { LightboxView } from "../LightboxView"; -import { Docs } from "../../documents/Documents"; -import { DocumentManager } from "../../util/DocumentManager"; -import { CollectionDockingView } from "./CollectionDockingView"; -import { FormattedTextBox } from "../nodes/formattedText/FormattedTextBox"; @observer export class CollectionMenu extends AntimodeMenu { @@ -46,6 +48,8 @@ export class CollectionMenu extends AntimodeMenu { @observable SelectedCollection: DocumentView | undefined; @observable FieldKey: string; + private _docBtnRef = React.createRef(); + constructor(props: any) { super(props); this.FieldKey = ""; @@ -82,30 +86,85 @@ export class CollectionMenu extends AntimodeMenu { } } + buttonBarXf = () => { + if (!this._docBtnRef.current) return Transform.Identity(); + const { scale, translateX, translateY } = Utils.GetScreenTransform(this._docBtnRef.current); + return new Transform(-translateX, -translateY, 1 / scale); + } + + @computed get contMenuButtons() { + const selDoc = Doc.UserDoc().contextMenuBtns; + return !(selDoc instanceof Doc) ? (null) :
    + 100} + PanelHeight={() => 35} + renderDepth={0} + focus={() => undefined} + whenChildContentsActiveChanged={emptyFunction} + docFilters={returnEmptyFilter} + docRangeFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} /> +
    ; + } + render() { - const button = Pin Menu} 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 9f24c29df130ce4e206bc655ed7025e58c1517ce Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 19 Aug 2021 02:22:38 -0400 Subject: changed how treeview cycles between options for expanded views to show 'data' and some more adjustments. --- src/client/views/collections/CollectionTreeView.tsx | 2 ++ src/client/views/collections/TreeView.tsx | 9 +++++---- .../collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/CollectionTreeView.tsx b/src/client/views/collections/CollectionTreeView.tsx index c0553ca60..a78034dca 100644 --- a/src/client/views/collections/CollectionTreeView.tsx +++ b/src/client/views/collections/CollectionTreeView.tsx @@ -27,6 +27,7 @@ import { TreeView } from "./TreeView"; import React = require("react"); import { InkTool } from '../../../fields/InkField'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; +import { CollectionView, CollectionViewType } from './CollectionView'; const _global = (window /* browser */ || global /* node */) as any; export type collectionTreeViewProps = { @@ -52,6 +53,7 @@ export class CollectionTreeView extends CollectionSubView { const next = (modes: any[]) => modes[(modes.indexOf(StrCast(this.doc.treeViewExpandedView)) + 1) % modes.length]; const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : ""; const links = () => DocListCast(this.doc.links).length ? "links" : ""; - const children = () => this.childDocs ? this.fieldKey : ""; - this.doc.treeViewExpandedView = next(this.props.treeView.fileSysMode ? - (Doc.UserDoc().noviceMode ? ["layout", "aliases"] : ["layout", "aliases", "fields"]) : - (Doc.UserDoc().noviceMode ? [children(), "layout"] : [children(), "fields", "layout", links(), annos()]).filter(mode => mode)); + const data = () => this.childDocs && !this.props.treeView.dashboardMode ? this.fieldKey : ""; + const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases"; + const fields = () => Doc.UserDoc().noviceMode ? "" : "fields"; + const modes = [data(), "layout", ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()]; + this.doc.treeViewExpandedView = next(modes.filter(mode => mode)); } this.treeViewOpen = true; } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index fa7e75202..d09d9b9d7 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1210,7 +1210,7 @@ export class CollectionFreeFormView extends CollectionSubView([]); -- cgit v1.2.3-70-g09d2 From d35d6cb036a4bd78f3df967b3564a0517d1b9843 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 19 Aug 2021 03:04:36 -0400 Subject: fixed links from sometimes disappearing on refresh. cleaned up cycling through tree view expansion modes --- src/client/util/LinkManager.ts | 13 ++++++++----- src/client/views/collections/TreeView.tsx | 26 +++++++++++++++----------- 2 files changed, 23 insertions(+), 16 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 933b98a8c..c33dff8d1 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -36,9 +36,11 @@ export class LinkManager { setTimeout(() => { LinkManager.userLinkDBs = []; const addLinkToDoc = (link: Doc) => { - const a1 = link?.anchor1; - const a2 = link?.anchor2; - Promise.all([a1, a2]).then(action(() => { + const a1Prom = link?.anchor1; + const a2Prom = link?.anchor2; + Promise.all([a1Prom, a2Prom]).then(action((all) => { + const a1 = all[0]; + const a2 = all[1]; if (a1 instanceof Doc && a2 instanceof Doc && ((a1.author !== undefined && a2.author !== undefined) || link.author === Doc.CurrentUserEmail)) { Doc.GetProto(a1)[DirectLinksSym].add(link); Doc.GetProto(a2)[DirectLinksSym].add(link); @@ -110,9 +112,10 @@ export class LinkManager { relatedLinker = computedFn(function relatedLinker(this: any, anchor: Doc): Doc[] { const lfield = Doc.LayoutFieldKey(anchor); - return DocListCast(anchor[lfield + "-annotations"]).concat(DocListCast(anchor[lfield + "-annotations-timeline"])).reduce((list, anno) => + const related = DocListCast(anchor[lfield + "-annotations"]).concat(DocListCast(anchor[lfield + "-annotations-timeline"])).reduce((list, anno) => [...list, ...LinkManager.Instance.relatedLinker(anno)], - Array.from(Doc.GetProto(anchor)[DirectLinksSym]).slice());// LinkManager.Instance.directLinker(anchor).slice()); + Array.from(Doc.GetProto(anchor)[DirectLinksSym]).slice()); + return related; }, true); // returns map of group type to anchor's links in that group type diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 84b2fcef6..3ee9dbf59 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -100,13 +100,14 @@ export class TreeView extends React.Component { @observable _dref: DocumentView | undefined | null; get displayName() { return "TreeView(" + this.props.document.title + ")"; } // this makes mobx trace() statements more descriptive get defaultExpandedView() { - return this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "aliases") : - this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields"); + return this.doc.viewType === CollectionViewType.Docking ? this.fieldKey : + this.props.treeView.fileSysMode ? (this.doc.isFolder ? this.fieldKey : "layout") : + this.props.treeView.outlineMode || this.childDocs ? this.fieldKey : Doc.UserDoc().noviceMode ? "layout" : StrCast(this.props.treeView.doc.treeViewExpandedView, "fields"); } @computed get doc() { return this.props.document; } @computed get treeViewOpen() { return (!this.treeViewOpenIsTransient && Doc.GetT(this.doc, "treeViewOpen", "boolean", true)) || this._transientOpenState; } - @computed get treeViewExpandedView() { return StrCast(this.doc.treeViewExpandedView, this.defaultExpandedView); } + @computed get treeViewExpandedView() { return this.validExpandViewTypes.includes(StrCast(this.doc.treeViewExpandedView)) ? StrCast(this.doc.treeViewExpandedView) : this.defaultExpandedView; } @computed get MAX_EMBED_HEIGHT() { return NumCast(this.props.containerCollection.maxEmbedHeight, 200); } @computed get dataDoc() { return this.doc[DataSym]; } @computed get layoutDoc() { return Doc.Layout(this.doc); } @@ -476,17 +477,20 @@ export class TreeView extends React.Component {
    ; } + @computed get validExpandViewTypes() { + if (this.doc.viewType === CollectionViewType.Docking) return [this.fieldKey]; + const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : ""; + const links = () => DocListCast(this.doc.links).length ? "links" : ""; + const data = () => this.childDocs && !this.props.treeView.dashboardMode ? this.fieldKey : ""; + const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases"; + const fields = () => Doc.UserDoc().noviceMode ? "" : "fields"; + return [data(), "layout", ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()].filter(m => m); + } @action expandNextviewType = () => { if (this.treeViewOpen && !this.doc.isFolder && !this.props.treeView.outlineMode && !this.doc.treeViewExpandedViewLock) { - const next = (modes: any[]) => modes[(modes.indexOf(StrCast(this.doc.treeViewExpandedView)) + 1) % modes.length]; - const annos = () => DocListCast(this.doc[this.fieldKey + "-annotations"]).length ? "annotations" : ""; - const links = () => DocListCast(this.doc.links).length ? "links" : ""; - const data = () => this.childDocs && !this.props.treeView.dashboardMode ? this.fieldKey : ""; - const aliases = () => this.props.treeView.dashboardMode ? "" : "aliases"; - const fields = () => Doc.UserDoc().noviceMode ? "" : "fields"; - const modes = [data(), "layout", ...(this.props.treeView.fileSysMode ? [aliases(), links(), annos()] : []), fields()]; - this.doc.treeViewExpandedView = next(modes.filter(mode => mode)); + const next = (modes: any[]) => modes[(modes.indexOf(StrCast(this.treeViewExpandedView)) + 1) % modes.length]; + this.doc.treeViewExpandedView = next(this.validExpandViewTypes); } this.treeViewOpen = true; } -- 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/collections') 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/collections') 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 a4340dc70a52f45af18435e28d1a3f2a163d3379 Mon Sep 17 00:00:00 2001 From: dinhanhtruong <70963346+dinhanhtruong@users.noreply.github.com> Date: Thu, 19 Aug 2021 21:35:26 -0400 Subject: added randomly colored link relationships default color is black until link relationship is edited manually --- src/client/util/LinkManager.ts | 4 ++-- .../collectionFreeForm/CollectionFreeFormLinkView.tsx | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'src/client/views/collections') diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index c15e91df6..175de0fa5 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -34,7 +34,7 @@ export class LinkManager { static links: Doc[] = []; constructor() { LinkManager._instance = this; - this.createLinkrelationshipList(); + this.createLinkrelationshipLists(); setTimeout(() => { LinkManager.userLinkDBs = []; const addLinkToDoc = (link: Doc) => { @@ -98,7 +98,7 @@ export class LinkManager { } - public createLinkrelationshipList = () => { + public createLinkrelationshipLists = () => { const linkRelationshipList = new List(); const linkColorList = new List(); Doc.UserDoc().linkRelationshipList = linkRelationshipList; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index a8f5e6dd2..ba67bbcef 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -9,6 +9,9 @@ import { SnappingManager } from "../../../util/SnappingManager"; import { DocumentView } from "../../nodes/DocumentView"; import "./CollectionFreeFormLinkView.scss"; import React = require("react"); +import { LinkManager } from "../../../util/LinkManager"; +import { List } from "../../../fields/List"; + export interface CollectionFreeFormLinkViewProps { A: DocumentView; @@ -173,8 +176,14 @@ 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 + console.log(strokeColor); return !a.width || !b.width || ((!this.props.LinkDocs[0].linkDisplay) && !aActive && !bActive) ? (null) : (<> - {textX === undefined ? (null) : {StrCast(this.props.LinkDocs[0].description)} -- 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/collections') 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 b147d26c510d98357bd4ddf8525750f72bc9069a Mon Sep 17 00:00:00 2001 From: geireann Date: Thu, 19 Aug 2021 21:59:58 -0400 Subject: mini update --- .../views/collections/collectionLinearView/CollectionLinearView.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views/collections') diff --git a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss index b37233892..2b3f8f2c9 100644 --- a/src/client/views/collections/collectionLinearView/CollectionLinearView.scss +++ b/src/client/views/collections/collectionLinearView/CollectionLinearView.scss @@ -10,7 +10,7 @@ padding-left: 5px; padding-right: 5px; border-left: $standard-border; - background-color: #4476f780; + background-color: #4476f73d; } >input:not(:checked)~&.true { -- 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/collections') 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"}
    }> + )}
    ; } @@ -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 =