diff options
author | bobzel <zzzman@gmail.com> | 2022-07-07 16:20:33 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2022-07-07 16:20:33 -0400 |
commit | b7e66da6b23cdb41c127000dfe13843d35f7d0cc (patch) | |
tree | 97f74e2c5dde93d470cb928a0577435927fc2e80 | |
parent | 897544f4bbe003778bcc48a11fd30b57f9e1ea50 (diff) |
fixed more errors.
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/client/views/InkTranscription.tsx | 354 | ||||
-rw-r--r-- | src/client/views/StyleProvider.tsx | 348 | ||||
-rw-r--r-- | src/client/views/collections/CollectionDockingView.scss | 29 | ||||
-rw-r--r-- | src/client/views/collections/CollectionDockingView.tsx | 337 | ||||
-rw-r--r-- | src/client/views/collections/TabDocView.tsx | 609 | ||||
-rw-r--r-- | src/client/views/collections/collectionSchema/CollectionSchemaView.scss | 27 | ||||
-rw-r--r-- | src/client/views/collections/collectionSchema/CollectionSchemaView.tsx | 610 | ||||
-rw-r--r-- | src/client/views/nodes/button/FontIconBox.tsx | 582 |
9 files changed, 1592 insertions, 1306 deletions
diff --git a/package.json b/package.json index c2b6c24bf..2e3ef0a07 100644 --- a/package.json +++ b/package.json @@ -241,7 +241,7 @@ "path-browserify": "^1.0.1", "pdf-parse": "^1.1.1", "pdfjs": "^2.4.7", - "pdfjs-dist": "^2.13.216", + "pdfjs-dist": "^2.14.305", "probe-image-size": "^4.0.0", "process": "^0.11.10", "prosemirror-commands": "^1.2.1", diff --git a/src/client/views/InkTranscription.tsx b/src/client/views/InkTranscription.tsx index 487bbcd00..5936ea32d 100644 --- a/src/client/views/InkTranscription.tsx +++ b/src/client/views/InkTranscription.tsx @@ -2,16 +2,15 @@ import * as iink from 'iink-js'; import { action, observable } from 'mobx'; import * as React from 'react'; import { Doc, DocListCast, HeightSym, WidthSym } from '../../fields/Doc'; -import { InkData, InkField, InkTool } from "../../fields/InkField"; +import { InkData, InkField, InkTool } from '../../fields/InkField'; import { Cast, DateCast, NumCast } from '../../fields/Types'; import { aggregateBounds } from '../../Utils'; -import { DocumentType } from "../documents/DocumentTypes"; -import { DocumentManager } from "../util/DocumentManager"; +import { DocumentType } from '../documents/DocumentTypes'; +import { DocumentManager } from '../util/DocumentManager'; import { CollectionFreeFormView } from './collections/collectionFreeForm'; import { InkingStroke } from './InkingStroke'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; -import "./InkTranscription.scss"; - +import './InkTranscription.scss'; /** * Class component that handles inking in writing mode @@ -40,124 +39,128 @@ export class InkTranscription extends React.Component { @action setMathRef = (r: any) => { if (!this._mathRegister) { - this._mathRegister = r ? iink.register(r, { - recognitionParams: { - type: 'MATH', - protocol: 'WEBSOCKET', - server: { - host: 'cloud.myscript.com', - applicationKey: process.env.IINKJS_APP, - hmacKey: process.env.IINKJS_HMAC, - websocket: { - pingEnabled: false, - autoReconnect: true - } - }, - iink: { - math: { - mimeTypes: ['application/x-latex', 'application/vnd.myscript.jiix'] - }, - export: { - jiix: { - strokes: true - } - } - } - } - }) : null; + this._mathRegister = r + ? iink.register(r, { + recognitionParams: { + type: 'MATH', + protocol: 'WEBSOCKET', + server: { + host: 'cloud.myscript.com', + applicationKey: process.env.IINKJS_APP, + hmacKey: process.env.IINKJS_HMAC, + websocket: { + pingEnabled: false, + autoReconnect: true, + }, + }, + iink: { + math: { + mimeTypes: ['application/x-latex', 'application/vnd.myscript.jiix'], + }, + export: { + jiix: { + strokes: true, + }, + }, + }, + }, + }) + : null; } r.addEventListener('exported', (e: any) => this.exportInk(e, this._mathRef)); - return this._mathRef = r; - } + return (this._mathRef = r); + }; @action setTextRef = (r: any) => { if (!this._textRegister) { - this._textRegister = r ? iink.register(r, { - recognitionParams: { - type: 'TEXT', - protocol: 'WEBSOCKET', - server: { - host: 'cloud.myscript.com', - applicationKey: '7277ec34-0c2e-4ee1-9757-ccb657e3f89f', - hmacKey: 'f5cb18f2-1f95-4ddb-96ac-3f7c888dffc1', - websocket: { - pingEnabled: false, - autoReconnect: true - } - }, - iink: { - text: { - mimeTypes: ['text/plain'] - }, - export: { - jiix: { - strokes: true - } - } - } - } - }) : null; + this._textRegister = r + ? iink.register(r, { + recognitionParams: { + type: 'TEXT', + protocol: 'WEBSOCKET', + server: { + host: 'cloud.myscript.com', + applicationKey: '7277ec34-0c2e-4ee1-9757-ccb657e3f89f', + hmacKey: 'f5cb18f2-1f95-4ddb-96ac-3f7c888dffc1', + websocket: { + pingEnabled: false, + autoReconnect: true, + }, + }, + iink: { + text: { + mimeTypes: ['text/plain'], + }, + export: { + jiix: { + strokes: true, + }, + }, + }, + }, + }) + : null; } r.addEventListener('exported', (e: any) => this.exportInk(e, this._textRef)); - return this._textRef = r; - } + return (this._textRef = r); + }; /** * Handles processing Dash Doc data for ink transcription. - * - * @param groupDoc the group which contains the ink strokes we want to transcribe - * @param containingLayout the layout which contains the group + * + * @param groupDoc the group which contains the ink strokes we want to transcribe * @param inkDocs the ink docs contained within the selected group * @param math boolean whether to do math transcription or not */ - transcribeInk = (groupDoc: Doc | undefined, containingLayout: Doc, inkDocs: Doc[], math: boolean) => { + transcribeInk = (groupDoc: Doc | undefined, inkDocs: Doc[], math: boolean) => { if (!groupDoc) return; const validInks = inkDocs.filter(s => s.type === DocumentType.INK); const strokes: InkData[] = []; const times: number[] = []; - validInks.filter(i => Cast(i.data, InkField)).forEach(i => { - const d = Cast(i.data, InkField, null); - const inkStroke = DocumentManager.Instance.getDocumentView(i)?.ComponentView as InkingStroke; - strokes.push(d.inkData.map(pd => (inkStroke.ptToScreen({ X: pd.X, Y: pd.Y })))); - times.push(DateCast(i.creationDate).getDate().getTime()); - }); + validInks + .filter(i => Cast(i.data, InkField)) + .forEach(i => { + const d = Cast(i.data, InkField, null); + const inkStroke = DocumentManager.Instance.getDocumentView(i)?.ComponentView as InkingStroke; + strokes.push(d.inkData.map(pd => inkStroke.ptToScreen({ X: pd.X, Y: pd.Y }))); + times.push(DateCast(i.creationDate).getDate().getTime()); + }); this.currGroup = groupDoc; - const pointerData = { "events": strokes.map((stroke, i) => this.inkJSON(stroke, times[i])) }; + const pointerData = { events: strokes.map((stroke, i) => this.inkJSON(stroke, times[i])) }; const processGestures = false; if (math) { this._mathRef.editor.pointerEvents(pointerData, processGestures); - } - else { + } else { this._textRef.editor.pointerEvents(pointerData, processGestures); } - } + }; /** * Converts the Dash Ink Data to JSON. - * + * * @param stroke The dash ink data * @param time the time of the stroke * @returns json object representation of ink data */ inkJSON = (stroke: InkData, time: number) => { return { - "pointerType": "PEN", - "pointerId": 1, - "x": stroke.map(point => point.X), - "y": stroke.map(point => point.Y), - "t": new Array(stroke.length).fill(time), - "p": new Array(stroke.length).fill(1.0) + pointerType: 'PEN', + pointerId: 1, + x: stroke.map(point => point.X), + y: stroke.map(point => point.Y), + t: new Array(stroke.length).fill(time), + p: new Array(stroke.length).fill(1.0), }; - } + }; /** * Creates subgroups for each word for the whole text transcription @@ -181,11 +184,11 @@ export class InkTranscription extends React.Component { if (!marqViewRef) return; this.groupInkDocs(selected, docView, word); }); - } + }; /** * Event listener function for when the 'exported' event is heard. - * + * * @param e the event objects * @param ref the ref to the editor */ @@ -200,24 +203,23 @@ export class InkTranscription extends React.Component { } ref.editor.clear(); - } - else if (exports['text/plain']) { + } else if (exports['text/plain']) { if (exports['application/vnd.myscript.jiix']) { this.lastJiix = JSON.parse(exports['application/vnd.myscript.jiix']); // map timestamp to strokes const timestampWord = new Map<number, string>(); this.lastJiix.words.map((word: any) => { if (word.items) { - word.items.forEach((i: { id: string, timestamp: string, X: Array<number>, Y: Array<number>, F: Array<number> }) => { + word.items.forEach((i: { id: string; timestamp: string; X: Array<number>; Y: Array<number>; F: Array<number> }) => { const ms = Date.parse(i.timestamp); timestampWord.set(ms, word.label); - }) + }); } - }) + }); const wordInkDocMap = new Map<string, Doc[]>(); if (this.currGroup) { - const docList = DocListCast(this.currGroup.data) + const docList = DocListCast(this.currGroup.data); docList.forEach((inkDoc: Doc) => { // just having the times match up and be a unique value (actual timestamp doesn't matter) const ms = DateCast(inkDoc.creationDate).getDate().getTime() + 14400000; @@ -241,108 +243,106 @@ export class InkTranscription extends React.Component { if (this.currGroup) { this.currGroup.transcription = text; - this.currGroup.title = text.split("\n")[0]; + this.currGroup.title = text.split('\n')[0]; } ref.editor.clear(); } } - } - -/** - * Creates the ink grouping once the user leaves the writing mode. - */ -createInkGroup() { - // TODO nda - if document being added to is a inkGrouping then we can just add to that group - if (CurrentUserUtils.ActiveTool === InkTool.Write) { - CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => { - // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those - const selected = ffView.unprocessedDocs; - const newCollection = this.groupInkDocs(selected, ffView); - ffView.unprocessedDocs = []; - - InkTranscription.Instance.transcribeInk(newCollection, ffView.layoutDoc, selected, false); - }); - } - CollectionFreeFormView.collectionsWithUnprocessedInk.clear(); -} + }; -/** - * Creates the groupings for a given list of ink docs on a specific doc view - * @param selected: the list of ink docs to create a grouping of - * @param docView: the view in which we want the grouping to be created - * @param word: optional param if the group we are creating is a word (subgrouping individual words) - * @returns a new collection Doc or undefined if the grouping fails - */ - groupInkDocs(selected: Doc[], docView: CollectionFreeFormView, word?: string): Doc | undefined { - const bounds: { x: number, y: number, width?: number, height?: number }[] = [] - - // calculate the necessary bounds from the selected ink docs - selected.map(action(d => { - const x = NumCast(d.x); - const y = NumCast(d.y); - const width = d[WidthSym](); - const height = d[HeightSym](); - bounds.push({ x, y, width, height }); - })) - - // calculate the aggregated bounds - const aggregBounds = aggregateBounds(bounds, 0, 0); - const marqViewRef = docView._marqueeViewRef.current; - - // set the vals for bounds in marqueeView - if (marqViewRef) { - marqViewRef._downX = aggregBounds.x; - marqViewRef._downY = aggregBounds.y; - marqViewRef._lastX = aggregBounds.r; - marqViewRef._lastY = aggregBounds.b; + /** + * Creates the ink grouping once the user leaves the writing mode. + */ + createInkGroup() { + // TODO nda - if document being added to is a inkGrouping then we can just add to that group + if (CurrentUserUtils.ActiveTool === InkTool.Write) { + CollectionFreeFormView.collectionsWithUnprocessedInk.forEach(ffView => { + // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those + const selected = ffView.unprocessedDocs; + const newCollection = this.groupInkDocs(selected, ffView); + ffView.unprocessedDocs = []; + + InkTranscription.Instance.transcribeInk(newCollection, selected, false); + }); + } + CollectionFreeFormView.collectionsWithUnprocessedInk.clear(); } - // map through all the selected ink strokes and create the groupings - selected.map(action(d => { - const dx = NumCast(d.x); - const dy = NumCast(d.y); - delete d.x; - delete d.y; - delete d.activeFrame; - delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - // calculate pos based on bounds - if (marqViewRef?.Bounds) { - d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2; - d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2; + /** + * Creates the groupings for a given list of ink docs on a specific doc view + * @param selected: the list of ink docs to create a grouping of + * @param docView: the view in which we want the grouping to be created + * @param word: optional param if the group we are creating is a word (subgrouping individual words) + * @returns a new collection Doc or undefined if the grouping fails + */ + groupInkDocs(selected: Doc[], docView: CollectionFreeFormView, word?: string): Doc | undefined { + const bounds: { x: number; y: number; width?: number; height?: number }[] = []; + + // calculate the necessary bounds from the selected ink docs + selected.map( + action(d => { + const x = NumCast(d.x); + const y = NumCast(d.y); + const width = d[WidthSym](); + const height = d[HeightSym](); + bounds.push({ x, y, width, height }); + }) + ); + + // calculate the aggregated bounds + const aggregBounds = aggregateBounds(bounds, 0, 0); + const marqViewRef = docView._marqueeViewRef.current; + + // set the vals for bounds in marqueeView + if (marqViewRef) { + marqViewRef._downX = aggregBounds.x; + marqViewRef._downY = aggregBounds.y; + marqViewRef._lastX = aggregBounds.r; + marqViewRef._lastY = aggregBounds.b; } - return d; - })); - docView.props.removeDocument?.(selected); - // Gets a collection based on the selected nodes using a marquee view ref - const newCollection = marqViewRef?.getCollection(selected, undefined, true); - if (newCollection) { - newCollection.height = newCollection[HeightSym](); - newCollection.width = newCollection[WidthSym](); - // if the grouping we are creating is an individual word - if (word) { - newCollection.title = word; + + // map through all the selected ink strokes and create the groupings + selected.map( + action(d => { + const dx = NumCast(d.x); + const dy = NumCast(d.y); + delete d.x; + delete d.y; + delete d.activeFrame; + delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + // calculate pos based on bounds + if (marqViewRef?.Bounds) { + d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2; + d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2; + } + return d; + }) + ); + docView.props.removeDocument?.(selected); + // Gets a collection based on the selected nodes using a marquee view ref + const newCollection = marqViewRef?.getCollection(selected, undefined, true); + if (newCollection) { + newCollection.height = newCollection[HeightSym](); + newCollection.width = newCollection[WidthSym](); + // if the grouping we are creating is an individual word + if (word) { + newCollection.title = word; + } } - } - // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs - newCollection && docView.props.addDocument?.(newCollection); - return newCollection; -} + // nda - bug: when deleting a stroke before leaving writing mode, delete the stroke from unprocessed ink docs + newCollection && docView.props.addDocument?.(newCollection); + return newCollection; + } render() { return ( <div className="ink-transcription"> - <div className='math-editor' - ref={this.setMathRef} - touch-action="none"> - </div> - <div className='text-editor' - ref={this.setTextRef} - touch-action="none"> - </div> + <div className="math-editor" ref={this.setMathRef} touch-action="none"></div> + <div className="text-editor" ref={this.setTextRef} touch-action="none"></div> </div> - ) + ); } -}
\ No newline at end of file +} diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 35415ae4e..a9770d253 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -1,13 +1,11 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import 'golden-layout/src/css/goldenlayout-base.css'; -import 'golden-layout/src/css/goldenlayout-dark-theme.css'; import { action, runInAction } from 'mobx'; import { extname } from 'path'; -import { Doc, Opt, StrListCast } from "../../fields/Doc"; +import { Doc, Opt, StrListCast } from '../../fields/Doc'; import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; -import { BoolCast, Cast, ImageCast, NumCast, StrCast } from "../../fields/Types"; +import { BoolCast, Cast, ImageCast, NumCast, StrCast } from '../../fields/Types'; import { DashColor, lightOrDark } from '../../Utils'; import { DocumentType } from '../documents/DocumentTypes'; import { CurrentUserUtils } from '../util/CurrentUserUtils'; @@ -16,227 +14,307 @@ import { ColorScheme } from '../util/SettingsManager'; import { SnappingManager } from '../util/SnappingManager'; import { undoBatch, UndoManager } from '../util/UndoManager'; import { CollectionViewType } from './collections/CollectionView'; +import { TreeSort } from './collections/TreeView'; import { Colors } from './global/globalEnums'; +import { InkingStroke } from './InkingStroke'; import { MainView } from './MainView'; -import { DocumentViewProps } from "./nodes/DocumentView"; +import { DocumentViewProps } from './nodes/DocumentView'; import { FieldViewProps } from './nodes/FieldView'; import { SliderBox } from './nodes/SliderBox'; -import "./StyleProvider.scss"; -import React = require("react"); -import { InkingStroke } from './InkingStroke'; -import { TreeSort } from './collections/TreeView'; +import './StyleProvider.scss'; +import React = require('react'); export enum StyleProp { - TreeViewIcon = "treeViewIcon", - TreeViewSortings = "treeViewSortings",// options for how to sort tree view items - DocContents = "docContents", // when specified, the JSX returned will replace the normal rendering of the document view - Opacity = "opacity", // opacity of the document view - Hidden = "hidden", // whether the document view should not be isplayed - BoxShadow = "boxShadow", // box shadow - used for making collections standout and for showing clusters in free form views - BorderRounding = "borderRounding", // border radius of the document view - Color = "color", // foreground color of Document view items - BackgroundColor = "backgroundColor", // background color of a document view - WidgetColor = "widgetColor", // color to display UI widgets on a document view -- used for the sidebar divider dragger on a text note - HideLinkButton = "hideLinkButton", // hides the blue-dot link button. used when a document acts like a button - LinkSource = "linkSource", // source document of a link -- used by LinkAnchorBox - PointerEvents = "pointerEvents", // pointer events for DocumentView -- inherits pointer events if not specified - Decorations = "decorations", // additional decoration to display above a DocumentView -- currently only used to display a Lock for making things background - HeaderMargin = "headerMargin", // margin at top of documentview, typically for displaying a title -- doc contents will start below that - TitleHeight = "titleHeight", // Height of Title area - ShowTitle = "showTitle", // whether to display a title on a Document (optional :hover suffix) - JitterRotation = "jitterRotation", // whether documents should be randomly rotated - BorderPath = "customBorder", // border path for document view - FontSize = "fontSize", // size of text font - FontFamily = "fontFamily", // size of text font + TreeViewIcon = 'treeViewIcon', + TreeViewSortings = 'treeViewSortings', // options for how to sort tree view items + DocContents = 'docContents', // when specified, the JSX returned will replace the normal rendering of the document view + Opacity = 'opacity', // opacity of the document view + Hidden = 'hidden', // whether the document view should not be isplayed + BoxShadow = 'boxShadow', // box shadow - used for making collections standout and for showing clusters in free form views + BorderRounding = 'borderRounding', // border radius of the document view + Color = 'color', // foreground color of Document view items + BackgroundColor = 'backgroundColor', // background color of a document view + WidgetColor = 'widgetColor', // color to display UI widgets on a document view -- used for the sidebar divider dragger on a text note + HideLinkButton = 'hideLinkButton', // hides the blue-dot link button. used when a document acts like a button + LinkSource = 'linkSource', // source document of a link -- used by LinkAnchorBox + PointerEvents = 'pointerEvents', // pointer events for DocumentView -- inherits pointer events if not specified + Decorations = 'decorations', // additional decoration to display above a DocumentView -- currently only used to display a Lock for making things background + HeaderMargin = 'headerMargin', // margin at top of documentview, typically for displaying a title -- doc contents will start below that + TitleHeight = 'titleHeight', // Height of Title area + ShowTitle = 'showTitle', // whether to display a title on a Document (optional :hover suffix) + JitterRotation = 'jitterRotation', // whether documents should be randomly rotated + BorderPath = 'customBorder', // border path for document view + FontSize = 'fontSize', // size of text font + FontFamily = 'fontFamily', // size of text font } -function darkScheme() { return CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark; } +function darkScheme() { + return CurrentUserUtils.ActiveDashboard?.colorScheme === ColorScheme.Dark; +} function toggleLockedPosition(doc: Doc) { - UndoManager.RunInBatch(() => runInAction(() => { - doc._lockedPosition = !doc._lockedPosition; - doc._pointerEvents = doc._lockedPosition ? "none" : undefined; - }), "toggleBackground"); + UndoManager.RunInBatch( + () => + runInAction(() => { + doc._lockedPosition = !doc._lockedPosition; + doc._pointerEvents = doc._lockedPosition ? 'none' : undefined; + }), + 'toggleBackground' + ); } export function testDocProps(toBeDetermined: any): toBeDetermined is DocumentViewProps { - return (toBeDetermined?.isContentActive) ? toBeDetermined : undefined; + return toBeDetermined?.isContentActive ? toBeDetermined : undefined; } export function wavyBorderPath(pw: number, ph: number, inset: number = 0.05) { - return `M ${pw * .5} ${ph * inset} C ${pw * .6} ${ph * inset} ${pw * (1 - 2 * inset)} 0 ${pw * (1 - inset)} ${ph * inset} C ${pw} ${ph * (2 * inset)} ${pw * (1 - inset)} ${ph * .25} ${pw * (1 - inset)} ${ph * .3} C ${pw * (1 - inset)} ${ph * .4} ${pw} ${ph * (1 - 2 * inset)} ${pw * (1 - inset)} ${ph * (1 - inset)} C ${pw * (1 - 2 * inset)} ${ph} ${pw * .6} ${ph * (1 - inset)} ${pw * .5} ${ph * (1 - inset)} C ${pw * .3} ${ph * (1 - inset)} ${pw * (2 * inset)} ${ph} ${pw * inset} ${ph * (1 - inset)} C 0 ${ph * (1 - 2 * inset)} ${pw * inset} ${ph * .8} ${pw * inset} ${ph * .75} C ${pw * inset} ${ph * .7} 0 ${ph * (2 * inset)} ${pw * inset} ${ph * inset} C ${pw * (2 * inset)} 0 ${pw * .25} ${ph * inset} ${pw * .5} ${ph * inset}`; + return `M ${pw * 0.5} ${ph * inset} C ${pw * 0.6} ${ph * inset} ${pw * (1 - 2 * inset)} 0 ${pw * (1 - inset)} ${ph * inset} C ${pw} ${ph * (2 * inset)} ${pw * (1 - inset)} ${ph * 0.25} ${pw * (1 - inset)} ${ph * 0.3} C ${ + pw * (1 - inset) + } ${ph * 0.4} ${pw} ${ph * (1 - 2 * inset)} ${pw * (1 - inset)} ${ph * (1 - inset)} C ${pw * (1 - 2 * inset)} ${ph} ${pw * 0.6} ${ph * (1 - inset)} ${pw * 0.5} ${ph * (1 - inset)} C ${pw * 0.3} ${ph * (1 - inset)} ${pw * (2 * inset)} ${ph} ${ + pw * inset + } ${ph * (1 - inset)} C 0 ${ph * (1 - 2 * inset)} ${pw * inset} ${ph * 0.8} ${pw * inset} ${ph * 0.75} C ${pw * inset} ${ph * 0.7} 0 ${ph * (2 * inset)} ${pw * inset} ${ph * inset} C ${pw * (2 * inset)} 0 ${pw * 0.25} ${ph * inset} ${ + pw * 0.5 + } ${ph * inset}`; } // a preliminary implementation of a dash style sheet for setting rendering properties of documents nested within a Tab -// +// export function DefaultStyleProvider(doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any { - const remoteDocHeader = "author;creationDate;noMargin"; + const remoteDocHeader = 'author;creationDate;noMargin'; const docProps = testDocProps(props) ? props : undefined; - const selected = property.includes(":selected"); - const isCaption = property.includes(":caption"); - const isAnchor = property.includes(":anchor"); - const isAnnotated = property.includes(":annotated"); - const isOpen = property.includes(":open"); - const fieldKey = props?.fieldKey ? props.fieldKey + "-" : isCaption ? "caption-" : ""; - const comicStyle = () => doc && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === "comic"; + const selected = property.includes(':selected'); + const isCaption = property.includes(':caption'); + const isAnchor = property.includes(':anchor'); + const isAnnotated = property.includes(':annotated'); + const isOpen = property.includes(':open'); + const fieldKey = props?.fieldKey ? props.fieldKey + '-' : isCaption ? 'caption-' : ''; + const comicStyle = () => doc && !Doc.IsSystem(doc) && Doc.UserDoc().renderStyle === 'comic'; const isBackground = () => doc && BoolCast(doc._lockedPosition); const backgroundCol = () => props?.styleProvider?.(doc, props, StyleProp.BackgroundColor); const opacity = () => props?.styleProvider?.(doc, props, StyleProp.Opacity); const showTitle = () => props?.styleProvider?.(doc, props, StyleProp.ShowTitle); - const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + ((Math.abs(x * y) * 9301 + 49297) % 233280 / 233280) * (max - min); - switch (property.split(":")[0]) { + const random = (min: number, max: number, x: number, y: number) => /* min should not be equal to max */ min + (((Math.abs(x * y) * 9301 + 49297) % 233280) / 233280) * (max - min); + switch (property.split(':')[0]) { case StyleProp.TreeViewIcon: const img = ImageCast(doc?.icon, ImageCast(doc?.data)); if (img) { const ext = extname(img.url.href); - const url = doc?.icon ? img.url.href : img.url.href.replace(ext, "_s" + ext); - return <img src={url} width={20} height={15} style={{ margin: "auto", display: "block", objectFit: "contain" }} />; + const url = doc?.icon ? img.url.href : img.url.href.replace(ext, '_s' + ext); + return <img src={url} width={20} height={15} style={{ margin: 'auto', display: 'block', objectFit: 'contain' }} />; } return Doc.toIcon(doc, isOpen); case StyleProp.TreeViewSortings: - const allSorts: { [key: string]: { color: string, label: string } | undefined } = {}; - allSorts[TreeSort.Down] = { color: "blue", label: "↓" }; - allSorts[TreeSort.Up] = { color: "crimson", label: "↑" }; - if (doc?._viewType === CollectionViewType.Freeform) allSorts[TreeSort.Zindex] = { color: "green", label: "z" }; - allSorts[TreeSort.None] = { color: "darkgray", label: '\u00A0\u00A0\u00A0' }; + const allSorts: { [key: string]: { color: string; label: string } | undefined } = {}; + allSorts[TreeSort.Down] = { color: 'blue', label: '↓' }; + allSorts[TreeSort.Up] = { color: 'crimson', label: '↑' }; + if (doc?._viewType === CollectionViewType.Freeform) allSorts[TreeSort.Zindex] = { color: 'green', label: 'z' }; + allSorts[TreeSort.None] = { color: 'darkgray', label: '\u00A0\u00A0\u00A0' }; return allSorts; - case StyleProp.DocContents: return undefined; - case StyleProp.WidgetColor: return isAnnotated ? Colors.LIGHT_BLUE : darkScheme() ? "lightgrey" : "dimgrey"; - case StyleProp.Opacity: return Cast(doc?._opacity, "number", Cast(doc?.opacity, "number", null)); - case StyleProp.HideLinkButton: return props?.hideLinkButton || (!selected && (doc?.isLinkButton || doc?.hideLinkButton)); - case StyleProp.FontSize: return StrCast(doc?.[fieldKey + "fontSize"], StrCast(doc?.fontSize, StrCast(Doc.UserDoc().fontSize))); - case StyleProp.FontFamily: return StrCast(doc?.[fieldKey + "fontFamily"], StrCast(doc?.fontFamily, StrCast(Doc.UserDoc().fontFamily))); - case StyleProp.ShowTitle: return (doc && !doc.presentationTargetDoc && - StrCast(doc._showTitle, - props?.showTitle?.() || - (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.LABEL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any) ? - (doc.author === Doc.CurrentUserEmail ? StrCast(Doc.UserDoc().showTitle) : - remoteDocHeader) : "")) || ""); + case StyleProp.DocContents: + return undefined; + case StyleProp.WidgetColor: + return isAnnotated ? Colors.LIGHT_BLUE : darkScheme() ? 'lightgrey' : 'dimgrey'; + case StyleProp.Opacity: + return Cast(doc?._opacity, 'number', Cast(doc?.opacity, 'number', null)); + case StyleProp.HideLinkButton: + return props?.hideLinkButton || (!selected && (doc?.isLinkButton || doc?.hideLinkButton)); + case StyleProp.FontSize: + return StrCast(doc?.[fieldKey + 'fontSize'], StrCast(doc?.fontSize, StrCast(Doc.UserDoc().fontSize))); + case StyleProp.FontFamily: + return StrCast(doc?.[fieldKey + 'fontFamily'], StrCast(doc?.fontFamily, StrCast(Doc.UserDoc().fontFamily))); + case StyleProp.ShowTitle: + return ( + (doc && + !doc.presentationTargetDoc && + StrCast( + doc._showTitle, + props?.showTitle?.() || + (!Doc.IsSystem(doc) && [DocumentType.COL, DocumentType.LABEL, DocumentType.RTF, DocumentType.IMG, DocumentType.VID].includes(doc.type as any) + ? doc.author === Doc.CurrentUserEmail + ? StrCast(Doc.UserDoc().showTitle) + : remoteDocHeader + : '') + )) || + '' + ); case StyleProp.Color: if (MainView.Instance.LastButton === doc) return Colors.DARK_GRAY; - const docColor: Opt<string> = StrCast(doc?.[fieldKey + "color"], StrCast(doc?._color)); + const docColor: Opt<string> = StrCast(doc?.[fieldKey + 'color'], StrCast(doc?._color)); if (docColor) return docColor; const docView = props?.DocumentView?.(); - const backColor = backgroundCol() || docView?.props.styleProvider?.(docView.props.treeViewDoc, docView.props, "backgroundColor"); + const backColor = backgroundCol() || docView?.props.styleProvider?.(docView.props.treeViewDoc, docView.props, 'backgroundColor'); if (!backColor) return undefined; return lightOrDark(backColor); - case StyleProp.Hidden: return BoolCast(doc?.hidden); - case StyleProp.BorderRounding: return StrCast(doc?.[fieldKey + "borderRounding"], StrCast(doc?.borderRounding, doc?._viewType === CollectionViewType.Pile ? "50%" : "")); - case StyleProp.TitleHeight: return 15; - case StyleProp.BorderPath: return comicStyle() && props?.renderDepth && doc?.type !== DocumentType.INK ? { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, .08), width: 3 } : { path: undefined, width: 0 }; - case StyleProp.JitterRotation: return comicStyle() ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0; - case StyleProp.HeaderMargin: return ([CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._viewType as any) || - (doc?.type === DocumentType.RTF && !showTitle()?.includes("noMargin")) || doc?.type === DocumentType.LABEL) && showTitle() && !StrCast(doc?.showTitle).includes(":hover") ? 15 : 0; + case StyleProp.Hidden: + return BoolCast(doc?.hidden); + case StyleProp.BorderRounding: + return StrCast(doc?.[fieldKey + 'borderRounding'], StrCast(doc?.borderRounding, doc?._viewType === CollectionViewType.Pile ? '50%' : '')); + case StyleProp.TitleHeight: + return 15; + case StyleProp.BorderPath: + return comicStyle() && props?.renderDepth && doc?.type !== DocumentType.INK + ? { path: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0), fill: wavyBorderPath(props?.PanelWidth?.() || 0, props?.PanelHeight?.() || 0, 0.08), width: 3 } + : { path: undefined, width: 0 }; + case StyleProp.JitterRotation: + return comicStyle() ? random(-1, 1, NumCast(doc?.x), NumCast(doc?.y)) * ((props?.PanelWidth() || 0) > (props?.PanelHeight() || 0) ? 5 : 10) : 0; + case StyleProp.HeaderMargin: + return ([CollectionViewType.Stacking, CollectionViewType.Masonry, CollectionViewType.Tree].includes(doc?._viewType as any) || (doc?.type === DocumentType.RTF && !showTitle()?.includes('noMargin')) || doc?.type === DocumentType.LABEL) && + showTitle() && + !StrCast(doc?.showTitle).includes(':hover') + ? 15 + : 0; case StyleProp.BackgroundColor: { if (MainView.Instance.LastButton === doc) return Colors.LIGHT_GRAY; - let docColor: Opt<string> = - StrCast(doc?.[fieldKey + "backgroundColor"], - StrCast(doc?._backgroundColor, - StrCast(props?.Document.backgroundColor, isCaption ? "rgba(0,0,0,0.4)" : ""))); + let docColor: Opt<string> = StrCast(doc?.[fieldKey + 'backgroundColor'], StrCast(doc?._backgroundColor, StrCast(props?.Document.backgroundColor, isCaption ? 'rgba(0,0,0,0.4)' : ''))); switch (doc?.type) { - case DocumentType.PRESELEMENT: docColor = docColor || (darkScheme() ? "" : ""); break; - case DocumentType.PRES: docColor = docColor || (darkScheme() ? "transparent" : "transparent"); break; - case DocumentType.FONTICON: docColor = docColor || Colors.DARK_GRAY; break; - case DocumentType.RTF: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break; - case DocumentType.FILTER: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : "rgba(105, 105, 105, 0.432)"); break; - case DocumentType.INK: docColor = doc?.isInkMask ? "rgba(0,0,0,0.7)" : undefined; break; - case DocumentType.SLIDER: break; - case DocumentType.EQUATION: docColor = docColor || "transparent"; break; - case DocumentType.LABEL: docColor = docColor || (doc.annotationOn !== undefined ? "rgba(128, 128, 128, 0.18)" : undefined) || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break; - case DocumentType.BUTTON: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break; - case DocumentType.LINKANCHOR: docColor = isAnchor ? Colors.LIGHT_BLUE : "transparent"; break; - case DocumentType.LINK: docColor = (isAnchor ? docColor : "") || "transparent"; break; + case DocumentType.PRESELEMENT: + docColor = docColor || (darkScheme() ? '' : ''); + break; + case DocumentType.PRES: + docColor = docColor || (darkScheme() ? 'transparent' : 'transparent'); + break; + case DocumentType.FONTICON: + docColor = docColor || Colors.DARK_GRAY; + break; + case DocumentType.RTF: + docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); + break; + case DocumentType.FILTER: + docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : 'rgba(105, 105, 105, 0.432)'); + break; + case DocumentType.INK: + docColor = doc?.isInkMask ? 'rgba(0,0,0,0.7)' : undefined; + break; + case DocumentType.SLIDER: + break; + case DocumentType.EQUATION: + docColor = docColor || 'transparent'; + break; + case DocumentType.LABEL: + docColor = docColor || (doc.annotationOn !== undefined ? 'rgba(128, 128, 128, 0.18)' : undefined) || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); + break; + case DocumentType.BUTTON: + docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); + break; + case DocumentType.LINKANCHOR: + docColor = isAnchor ? Colors.LIGHT_BLUE : 'transparent'; + break; + case DocumentType.LINK: + docColor = (isAnchor ? docColor : '') || 'transparent'; + break; case DocumentType.IMG: case DocumentType.WEB: case DocumentType.PDF: case DocumentType.MAP: case DocumentType.SCREENSHOT: - case DocumentType.VID: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); break; + case DocumentType.VID: + docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY); + break; case DocumentType.COL: if (StrCast(Doc.LayoutField(doc)).includes(SliderBox.name)) break; - docColor = docColor || - (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) ? (darkScheme() ? Colors.DARK_GRAY : Colors.LIGHT_GRAY) : // system docs (seen in treeView) get a grayish background - doc.annotationOn ? "#00000015" : // faint interior for collections on PDFs, images, etc - (doc?._isGroup ? undefined : - Cast((props?.renderDepth || 0) > 0 ? - Doc.UserDoc().activeCollectionNestedBackground : - Doc.UserDoc().activeCollectionBackground, "string") ?? (darkScheme() ? - Colors.BLACK : - "linear-gradient(#065fff, #85c1f9)")) - ); + docColor = + docColor || + (doc?._viewType === CollectionViewType.Pile || Doc.IsSystem(doc) + ? darkScheme() + ? Colors.DARK_GRAY + : Colors.LIGHT_GRAY // system docs (seen in treeView) get a grayish background + : doc.annotationOn + ? '#00000015' // faint interior for collections on PDFs, images, etc + : doc?._isGroup + ? undefined + : Cast((props?.renderDepth || 0) > 0 ? Doc.UserDoc().activeCollectionNestedBackground : Doc.UserDoc().activeCollectionBackground, 'string') ?? (darkScheme() ? Colors.BLACK : 'linear-gradient(#065fff, #85c1f9)')); break; //if (doc._viewType !== CollectionViewType.Freeform && doc._viewType !== CollectionViewType.Time) return "rgb(62,62,62)"; - default: docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); break; + default: + docColor = docColor || (darkScheme() ? Colors.DARK_GRAY : Colors.WHITE); + break; } if (docColor && !doc) docColor = DashColor(docColor).fade(0.5).toString(); return docColor; } case StyleProp.BoxShadow: { - if (!doc || opacity() === 0) return undefined; // if it's not visible, then no shadow) + if (!doc || opacity() === 0) return undefined; // if it's not visible, then no shadow) - if (doc?.isLinkButton && ![DocumentType.LINK, DocumentType.INK].includes(doc.type as any)) return StrCast(doc?._linkButtonShadow, "lightblue 0em 0em 1em"); + if (doc?.isLinkButton && ![DocumentType.LINK, DocumentType.INK].includes(doc.type as any)) return StrCast(doc?._linkButtonShadow, 'lightblue 0em 0em 1em'); switch (doc?.type) { case DocumentType.COL: - return StrCast(doc?.boxShadow, - doc?._viewType === CollectionViewType.Pile ? "4px 4px 10px 2px" : - isBackground() || doc?._isGroup || docProps?.LayoutTemplateString ? undefined : // groups have no drop shadow -- they're supposed to be "invisible". LayoutString's imply collection is being rendered as something else (e.g., title of a Slide) - `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.boxShadow, "0.2vw 0.2vw 0.8vw")}`); + return StrCast( + doc?.boxShadow, + doc?._viewType === CollectionViewType.Pile + ? '4px 4px 10px 2px' + : isBackground() || doc?._isGroup || docProps?.LayoutTemplateString + ? undefined // groups have no drop shadow -- they're supposed to be "invisible". LayoutString's imply collection is being rendered as something else (e.g., title of a Slide) + : `${darkScheme() ? Colors.DARK_GRAY : Colors.MEDIUM_GRAY} ${StrCast(doc.boxShadow, '0.2vw 0.2vw 0.8vw')}` + ); case DocumentType.LABEL: - if (doc?.annotationOn !== undefined) return "black 2px 2px 1px"; + if (doc?.annotationOn !== undefined) return 'black 2px 2px 1px'; default: - return doc.z ? `#9c9396 ${StrCast(doc?.boxShadow, "10px 10px 0.9vw")}` : // if it's a floating doc, give it a big shadow - props?.ContainingCollectionDoc?._useClusters && doc.type !== DocumentType.INK ? (`${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.ContentScaling?.() || 1)}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent - NumCast(doc.group, -1) !== -1 && doc.type !== DocumentType.INK ? (`gray ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.ContentScaling?.() || 1)}px`)}`) : // if it's just in a cluster, make the shadown roughly match the cluster border extent - isBackground() ? undefined : // if it's a background & has a cluster color, make the shadow spread really big - StrCast(doc.boxShadow, ""); + return doc.z + ? `#9c9396 ${StrCast(doc?.boxShadow, '10px 10px 0.9vw')}` // if it's a floating doc, give it a big shadow + : props?.ContainingCollectionDoc?._useClusters && doc.type !== DocumentType.INK + ? `${backgroundCol()} ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.ContentScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent + : NumCast(doc.group, -1) !== -1 && doc.type !== DocumentType.INK + ? `gray ${StrCast(doc.boxShadow, `0vw 0vw ${(isBackground() ? 100 : 50) / (docProps?.ContentScaling?.() || 1)}px`)}` // if it's just in a cluster, make the shadown roughly match the cluster border extent + : isBackground() + ? undefined // if it's a background & has a cluster color, make the shadow spread really big + : StrCast(doc.boxShadow, ''); } } case StyleProp.PointerEvents: if (doc?.pointerEvents) return StrCast(doc.pointerEvents); - if (props?.pointerEvents?.() === "none") return "none"; + if (props?.pointerEvents?.() === 'none') return 'none'; const isInk = doc && StrCast(Doc.Layout(doc).layout).includes(InkingStroke.name); - if (opacity() === 0 || (isInk && !docProps?.treeViewDoc) || doc?.isInkMask) return "none"; - if (!isInk) return "all"; + if (opacity() === 0 || (isInk && !docProps?.treeViewDoc) || doc?.isInkMask) return 'none'; + if (!isInk) return 'all'; return undefined; case StyleProp.Decorations: if (props?.ContainingCollectionDoc?._viewType === CollectionViewType.Freeform || doc?.x !== undefined || doc?.y !== undefined) { - return doc && (isBackground() || selected) && !Doc.IsSystem(doc) && (props?.renderDepth || 0) > 0 && - ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ? + return doc && + (isBackground() || selected) && + !Doc.IsSystem(doc) && + (props?.renderDepth || 0) > 0 && + ((doc.type === DocumentType.COL && doc._viewType !== CollectionViewType.Pile) || [DocumentType.RTF, DocumentType.IMG, DocumentType.INK].includes(doc.type as DocumentType)) ? ( <div className="styleProvider-lock" onClick={() => toggleLockedPosition(doc)}> - <FontAwesomeIcon icon={isBackground() ? "lock" : "unlock"} style={{ color: isBackground() ? "red" : undefined }} size="lg" /> + <FontAwesomeIcon icon={isBackground() ? 'lock' : 'unlock'} style={{ color: isBackground() ? 'red' : undefined }} size="lg" /> </div> - : (null); + ) : null; } } } export function DashboardToggleButton(doc: Doc, field: string, onIcon: IconProp, offIcon: IconProp, clickFunc?: () => void) { - return <div title={field} className={`styleProvider-treeView-icon${doc[field] ? "-active" : ""}`} - onClick={undoBatch(action((e: React.MouseEvent) => { - e.stopPropagation(); - clickFunc ? clickFunc() : (doc[field] = doc[field] ? undefined : true); - }))}> - <FontAwesomeIcon icon={(doc[field] ? onIcon as any : offIcon) as IconProp} size="sm" /> - </div>; + return ( + <div + title={field} + className={`styleProvider-treeView-icon${doc[field] ? '-active' : ''}`} + onClick={undoBatch( + action((e: React.MouseEvent) => { + e.stopPropagation(); + clickFunc ? clickFunc() : (doc[field] = doc[field] ? undefined : true); + }) + )}> + <FontAwesomeIcon icon={(doc[field] ? (onIcon as any) : offIcon) as IconProp} size="sm" /> + </div> + ); } /** * add lock and hide button decorations for the "Dashboards" flyout TreeView */ export function DashboardStyleProvider(doc: Opt<Doc>, props: Opt<FieldViewProps | DocumentViewProps>, property: string) { - - if (doc && property.split(":")[0] === StyleProp.Decorations) { - return doc._viewType === CollectionViewType.Docking ? (null) : + if (doc && property.split(':')[0] === StyleProp.Decorations) { + return doc._viewType === CollectionViewType.Docking ? null : ( <> - {DashboardToggleButton(doc, "hidden", "eye-slash", "eye", () => { + {DashboardToggleButton(doc, 'hidden', 'eye-slash', 'eye', () => { doc.hidden = doc.hidden ? undefined : true; if (!doc.hidden) { DocFocusOrOpen(doc, props?.ContainingCollectionDoc); } })} - </>; + </> + ); } return DefaultStyleProvider(doc, props, property); } diff --git a/src/client/views/collections/CollectionDockingView.scss b/src/client/views/collections/CollectionDockingView.scss index 21045a20e..6d5a39bc2 100644 --- a/src/client/views/collections/CollectionDockingView.scss +++ b/src/client/views/collections/CollectionDockingView.scss @@ -1,5 +1,6 @@ -@import "../global/globalCssVariables.scss"; - +@import '../global/globalCssVariables.scss'; +@import '../../../../node_modules/golden-layout/src/css/goldenlayout-base.css'; +@import '../../../../node_modules/golden-layout/src/css/goldenlayout-dark-theme.css'; .lm_title { -webkit-appearance: none; @@ -153,7 +154,7 @@ background: $white; } - .lm_controls>li { + .lm_controls > li { opacity: 1; transform: scale(1); } @@ -196,7 +197,7 @@ position: absolute; cursor: move; border: 2px solid #cfe8ff; - box-shadow: inset 0 0 60px rgba(0, 0, 0, .2); + box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.2); border-radius: 5px; z-index: 1000; box-sizing: border-box; @@ -205,7 +206,7 @@ .flexlayout__outline_rect_edge { cursor: move; border: 2px solid #b7d1b5; - box-shadow: inset 0 0 60px rgba(0, 0, 0, .2); + box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.2); border-radius: 5px; z-index: 1000; box-sizing: border-box; @@ -214,7 +215,7 @@ .flexlayout__edge_rect { position: absolute; z-index: 1000; - box-shadow: inset 0 0 5px rgba(0, 0, 0, .2); + box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2); background-color: lightgray; } @@ -222,7 +223,7 @@ position: absolute; cursor: move; border: 2px solid #aaaaaa; - box-shadow: inset 0 0 60px rgba(0, 0, 0, .3); + box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.3); border-radius: 5px; z-index: 1000; box-sizing: border-box; @@ -301,7 +302,7 @@ .flexlayout__tab_button:hover .flexlayout__tab_button_trailing, .flexlayout__tab_button--selected .flexlayout__tab_button_trailing { - background: transparent url("../../../../node_modules/flexlayout-react/images/close_white.png") no-repeat center; + background: transparent url('../../../../node_modules/flexlayout-react/images/close_white.png') no-repeat center; } .flexlayout__tab_button_overflow { @@ -314,7 +315,7 @@ font-size: 10px; color: lightgray; font-family: Arial, sans-serif; - background: transparent url("../../../../node_modules/flexlayout-react/images/more.png") no-repeat left; + background: transparent url('../../../../node_modules/flexlayout-react/images/more.png') no-repeat left; } .flexlayout__tabset_header { @@ -369,7 +370,7 @@ height: 20px; border: none; outline-width: 0; - background: transparent url("../../../../node_modules/flexlayout-react/images/maximize.png") no-repeat center; + background: transparent url('../../../../node_modules/flexlayout-react/images/maximize.png') no-repeat center; } .flexlayout__tab_toolbar_button-max { @@ -377,7 +378,7 @@ height: 20px; border: none; outline-width: 0; - background: transparent url("../../../../node_modules/flexlayout-react/images/restore.png") no-repeat center; + background: transparent url('../../../../node_modules/flexlayout-react/images/restore.png') no-repeat center; } .flexlayout__popup_menu_item { @@ -390,7 +391,7 @@ } .flexlayout__popup_menu_container { - box-shadow: inset 0 0 5px rgba(0, 0, 0, .15); + box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.15); border: 1px solid #555; background: #222; border-radius: 3px; @@ -497,7 +498,7 @@ .flexlayout__border_button:hover .flexlayout__border_button_trailing, .flexlayout__border_button--selected .flexlayout__border_button_trailing { - background: transparent url("../../../../node_modules/flexlayout-react/images/close_white.png") no-repeat center; + background: transparent url('../../../../node_modules/flexlayout-react/images/close_white.png') no-repeat center; } .flexlayout__border_toolbar_left { @@ -539,4 +540,4 @@ bottom: 0; right: 0; } -}
\ No newline at end of file +} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 0830b6fdf..27478e59b 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -1,34 +1,32 @@ -import 'golden-layout/src/css/goldenlayout-base.css'; -import 'golden-layout/src/css/goldenlayout-dark-theme.css'; -import { action, IReactionDisposer, observable, reaction, runInAction } from "mobx"; -import { observer } from "mobx-react"; +import { action, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; +import { observer } from 'mobx-react'; import * as ReactDOM from 'react-dom'; -import * as GoldenLayout from "../../../client/goldenLayout"; -import { Doc, DocListCast, Opt } from "../../../fields/Doc"; +import * as GoldenLayout from '../../../client/goldenLayout'; +import { Doc, DocListCast, Opt } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; import { List } from '../../../fields/List'; -import { Cast, NumCast, StrCast } from "../../../fields/Types"; +import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField } from '../../../fields/URLField'; import { inheritParentAcls } from '../../../fields/util'; import { emptyFunction, incrementTitleCopy } from '../../../Utils'; -import { DocServer } from "../../DocServer"; +import { DocServer } from '../../DocServer'; import { Docs } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; -import { DragManager } from "../../util/DragManager"; +import { DragManager } from '../../util/DragManager'; import { InteractionUtils } from '../../util/InteractionUtils'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SelectionManager } from '../../util/SelectionManager'; -import { undoBatch, UndoManager } from "../../util/UndoManager"; +import { undoBatch, UndoManager } from '../../util/UndoManager'; import { LightboxView } from '../LightboxView'; -import "./CollectionDockingView.scss"; +import './CollectionDockingView.scss'; import { CollectionFreeFormView } from './collectionFreeForm'; -import { CollectionSubView, SubCollectionViewProps } from "./CollectionSubView"; +import { CollectionSubView, SubCollectionViewProps } from './CollectionSubView'; import { CollectionViewType } from './CollectionView'; import { TabDocView } from './TabDocView'; -import React = require("react"); -const _global = (window /* browser */ || global /* node */) as any; +import React = require('react'); +const _global = (window /* browser */ || global) /* node */ as any; @observer export class CollectionDockingView extends CollectionSubView() { @@ -41,8 +39,8 @@ export class CollectionDockingView extends CollectionSubView() { width: width, props: { documentId: document[Id], - panelName // name of tab that can be used to close or replace its contents - } + panelName, // name of tab that can be used to close or replace its contents + }, }; } @@ -50,14 +48,16 @@ export class CollectionDockingView extends CollectionSubView() { private _lightboxReactionDisposer?: IReactionDisposer; private _containerRef = React.createRef<HTMLDivElement>(); public _flush: UndoManager.Batch | undefined; - private _ignoreStateChange = ""; + private _ignoreStateChange = ''; public tabMap: Set<any> = new Set(); - public get HasFullScreen() { return this._goldenLayout._maximisedItem !== null; } + public get HasFullScreen() { + return this._goldenLayout._maximisedItem !== null; + } private _goldenLayout: any = null; constructor(props: SubCollectionViewProps) { super(props); - runInAction(() => CollectionDockingView.Instance = this); + runInAction(() => (CollectionDockingView.Instance = this)); //Why is this here? (window as any).React = React; (window as any).ReactDOM = ReactDOM; @@ -66,19 +66,18 @@ export class CollectionDockingView extends CollectionSubView() { /** * Switches from dragging a document around a freeform canvas to dragging it as a tab to be docked. - * + * * @param e fake mouse down event position data containing pageX and pageY coordinates * @param dragDocs the documents to be dragged * @param batch optionally an undo batch that has been started to use instead of starting a new batch - */ - public StartOtherDrag = (e: { pageX: number, pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => { - this._flush = this._flush ?? UndoManager.StartBatch("golden layout drag"); - const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) : - { type: 'row', content: dragDocs.map(doc => CollectionDockingView.makeDocumentConfig(doc)) }; - const dragSource = this._goldenLayout.createDragSource(document.createElement("div"), config); + */ + public StartOtherDrag = (e: { pageX: number; pageY: number }, dragDocs: Doc[], finishDrag?: (aborted: boolean) => void) => { + this._flush = this._flush ?? UndoManager.StartBatch('golden layout drag'); + const config = dragDocs.length === 1 ? CollectionDockingView.makeDocumentConfig(dragDocs[0]) : { type: 'row', content: dragDocs.map(doc => CollectionDockingView.makeDocumentConfig(doc)) }; + const dragSource = this._goldenLayout.createDragSource(document.createElement('div'), config); this.tabDragStart(dragSource, finishDrag); dragSource._dragListener.onMouseDown({ pageX: e.pageX, pageY: e.pageY, preventDefault: emptyFunction, button: 0 }); - } + }; tabItemDropped = () => DragManager.CompleteWindowDrag?.(false); tabDragStart = (proxy: any, finishDrag?: (aborted: boolean) => void) => { @@ -88,7 +87,7 @@ export class CollectionDockingView extends CollectionSubView() { if (aborted) { proxy._dragListener.AbortDrag(); if (this._flush) { - this._flush.cancel(); // cancel the undo change being logged + this._flush.cancel(); // cancel the undo change being logged this._flush = undefined; this.setupGoldenLayout(); // restore golden layout to where it was before the drag (this is a no-op when using StartOtherDrag because the proxy dragged item was never in the golden layout) } @@ -96,16 +95,16 @@ export class CollectionDockingView extends CollectionSubView() { } finishDrag?.(aborted); }; - } + }; @undoBatch public CloseFullScreen = () => { this._goldenLayout._maximisedItem?.toggleMaximise(); this.stateChanged(); - } + }; @undoBatch public static CloseSplit(document: Opt<Doc>, panelName?: string): boolean { - const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document); + const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find(tab => (panelName ? tab.contentItem.config.props.panelName === panelName : tab.DashDoc === document)); if (tab) { const j = tab.header.parent.contentItems.indexOf(tab.contentItem); if (j !== -1) { @@ -121,12 +120,12 @@ export class CollectionDockingView extends CollectionSubView() { public static OpenFullScreen(doc: Doc) { SelectionManager.DeselectAll(); const instance = CollectionDockingView.Instance; - if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === "layout") { + if (doc._viewType === CollectionViewType.Docking && doc.layoutKey === 'layout') { return CurrentUserUtils.openDashboard(doc); } const newItemStackConfig = { type: 'stack', - content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc))] + content: [CollectionDockingView.makeDocumentConfig(Doc.MakeAlias(doc))], }; const docconfig = instance._goldenLayout.root.layoutManager.createContentItem(newItemStackConfig, instance._goldenLayout); instance._goldenLayout.root.contentItems[0].addChild(docconfig); @@ -150,7 +149,7 @@ export class CollectionDockingView extends CollectionSubView() { stack.contentItems[activeContentItemIndex].remove(); return CollectionDockingView.Instance.layoutChanged(); } - const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find((tab) => tab.contentItem.config.props.panelName === panelName); + const tab = Array.from(CollectionDockingView.Instance.tabMap.keys()).find(tab => tab.contentItem.config.props.panelName === panelName); if (tab) { tab.header.parent.addChild(newConfig, undefined); const j = tab.header.parent.contentItems.indexOf(tab.contentItem); @@ -160,11 +159,9 @@ export class CollectionDockingView extends CollectionSubView() { return CollectionDockingView.AddSplit(document, panelName, stack, panelName); } - @undoBatch public static ToggleSplit(doc: Doc, location: string, stack?: any, panelName?: string) { - return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex((tab) => tab.DashDoc === doc) !== -1 ? - CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, location, stack, panelName); + return Array.from(CollectionDockingView.Instance.tabMap.keys()).findIndex(tab => tab.DashDoc === doc) !== -1 ? CollectionDockingView.CloseSplit(doc) : CollectionDockingView.AddSplit(doc, location, stack, panelName); } // @@ -193,35 +190,37 @@ export class CollectionDockingView extends CollectionSubView() { const newItem = glayRoot.layoutManager.createContentItem({ type: 'stack', content: [docContentConfig] }, instance._goldenLayout); newItem.callDownwards('_$init'); return newItem; - } - if (glayRoot.contentItems.length === 0) { // if no rows / columns + }; + if (glayRoot.contentItems.length === 0) { + // if no rows / columns glayRoot.addChild(newContentItem()); } else if (glayRoot.contentItems[0].isStack) { glayRoot.contentItems[0].addChild(docContentConfig); - } else if ( - glayRoot.contentItems.length === 1 && - glayRoot.contentItems[0].contentItems.length === 1 && - glayRoot.contentItems[0].contentItems[0].contentItems.length === 0) { + } else if (glayRoot.contentItems.length === 1 && glayRoot.contentItems[0].contentItems.length === 1 && glayRoot.contentItems[0].contentItems[0].contentItems.length === 0) { glayRoot.contentItems[0].contentItems[0].addChild(docContentConfig); - } - else if (instance._goldenLayout.root.contentItems[0].isRow) { // if row + } else if (instance._goldenLayout.root.contentItems[0].isRow) { + // if row switch (pullSide) { default: - case "right": glayRoot.contentItems[0].addChild(newContentItem()); break; - case "left": glayRoot.contentItems[0].addChild(newContentItem(), 0); break; - case "top": - case "bottom": + case 'right': + glayRoot.contentItems[0].addChild(newContentItem()); + break; + case 'left': + glayRoot.contentItems[0].addChild(newContentItem(), 0); + break; + case 'top': + case 'bottom': // if not going in a row layout, must add already existing content into column const rowlayout = glayRoot.contentItems[0]; - const newColumn = rowlayout.layoutManager.createContentItem({ type: "column" }, instance._goldenLayout); + const newColumn = rowlayout.layoutManager.createContentItem({ type: 'column' }, instance._goldenLayout); const newItem = newContentItem(); instance._goldenLayout.saveScrollTops(rowlayout.element); rowlayout.parent.replaceChild(rowlayout, newColumn); - if (pullSide === "top") { + if (pullSide === 'top') { newColumn.addChild(rowlayout, undefined, true); newColumn.addChild(newItem, 0, true); - } else if (pullSide === "bottom") { + } else if (pullSide === 'bottom') { newColumn.addChild(newItem, undefined, true); newColumn.addChild(rowlayout, 0, true); } @@ -230,21 +229,26 @@ export class CollectionDockingView extends CollectionSubView() { rowlayout.config.height = 50; newItem.config.height = 50; } - } else {// if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column + } else { + // if (instance._goldenLayout.root.contentItems[0].isColumn) { // if column switch (pullSide) { - case "top": glayRoot.contentItems[0].addChild(newContentItem(), 0); break; - case "bottom": glayRoot.contentItems[0].addChild(newContentItem()); break; - case "left": - case "right": + case 'top': + glayRoot.contentItems[0].addChild(newContentItem(), 0); + break; + case 'bottom': + glayRoot.contentItems[0].addChild(newContentItem()); + break; + case 'left': + case 'right': default: // if not going in a row layout, must add already existing content into column const collayout = glayRoot.contentItems[0]; - const newRow = collayout.layoutManager.createContentItem({ type: "row" }, instance._goldenLayout); + const newRow = collayout.layoutManager.createContentItem({ type: 'row' }, instance._goldenLayout); const newItem = newContentItem(); instance._goldenLayout.saveScrollTops(collayout.element); collayout.parent.replaceChild(collayout, newRow); - if (pullSide === "left") { + if (pullSide === 'left') { newRow.addChild(collayout, undefined, true); newRow.addChild(newItem, 0, true); } else { @@ -276,7 +280,7 @@ export class CollectionDockingView extends CollectionSubView() { const config = StrCast(this.props.Document.dockingConfig); if (config) { const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g); - const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) ?? []; + const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) ?? []; await Promise.all(docids.map(id => DocServer.GetRefField(id))); if (this._goldenLayout) { @@ -287,12 +291,12 @@ export class CollectionDockingView extends CollectionSubView() { this._goldenLayout.unbind('tabCreated', this.tabCreated); this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); this._goldenLayout.unbind('stackCreated', this.stackCreated); - } catch (e) { } + } catch (e) {} } this.tabMap.clear(); this._goldenLayout.destroy(); } - const glay = this._goldenLayout = new GoldenLayout(JSON.parse(config)); + const glay = (this._goldenLayout = new GoldenLayout(JSON.parse(config))); glay.on('tabCreated', this.tabCreated); glay.on('tabDestroyed', this.tabDestroyed); glay.on('stackCreated', this.stackCreated); @@ -303,46 +307,52 @@ export class CollectionDockingView extends CollectionSubView() { glay.root.layoutManager.on('dragStart', this.tabDragStart); glay.root.layoutManager.on('activeContentItemChanged', this.stateChanged); } - } + }; componentDidMount: () => void = () => { if (this._containerRef.current) { - this._lightboxReactionDisposer = reaction(() => LightboxView.LightboxDoc, doc => setTimeout(() => !doc && this.onResize(undefined))); + this._lightboxReactionDisposer = reaction( + () => LightboxView.LightboxDoc, + doc => setTimeout(() => !doc && this.onResize(undefined)) + ); new _global.ResizeObserver(this.onResize).observe(this._containerRef.current); - this._reactionDisposer = reaction(() => StrCast(this.props.Document.dockingConfig), + this._reactionDisposer = reaction( + () => StrCast(this.props.Document.dockingConfig), config => { - if (!this._goldenLayout || this._ignoreStateChange !== config) { // bcz: TODO! really need to diff config with ignoreStateChange and modify the current goldenLayout instead of building a new one. + if (!this._goldenLayout || this._ignoreStateChange !== config) { + // bcz: TODO! really need to diff config with ignoreStateChange and modify the current goldenLayout instead of building a new one. this.setupGoldenLayout(); } - this._ignoreStateChange = ""; - }); + this._ignoreStateChange = ''; + } + ); setTimeout(this.setupGoldenLayout); //window.addEventListener('resize', this.onResize); // bcz: would rather add this event to the parent node, but resize events only come from Window } - } + }; componentWillUnmount: () => void = () => { try { this._goldenLayout.unbind('stackCreated', this.stackCreated); this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); - } catch (e) { } + } catch (e) {} this._goldenLayout?.destroy(); window.removeEventListener('resize', this.onResize); this._reactionDisposer?.(); this._lightboxReactionDisposer?.(); - } + }; @action onResize = (event: any) => { const cur = this._containerRef.current; // bcz: since GoldenLayout isn't a React component itself, we need to notify it to resize when its document container's size has changed !LightboxView.LightboxDoc && cur && this._goldenLayout?.updateSize(cur.getBoundingClientRect().width, cur.getBoundingClientRect().height); - } + }; @action onPointerUp = (e: MouseEvent): void => { - window.removeEventListener("pointerup", this.onPointerUp); + window.removeEventListener('pointerup', this.onPointerUp); const flush = this._flush; this._flush = undefined; if (flush) { @@ -350,69 +360,65 @@ export class CollectionDockingView extends CollectionSubView() { if (!this.stateChanged()) flush.cancel(); else flush.end(); } - } + }; @action onPointerDown = (e: React.PointerEvent): void => { let hitFlyout = false; for (let par = e.target as any; !hitFlyout && par; par = par.parentElement) { - hitFlyout = (par.className === "dockingViewButtonSelector"); + hitFlyout = par.className === 'dockingViewButtonSelector'; } if (!hitFlyout) { const htmlTarget = e.target as HTMLElement; - window.addEventListener("mouseup", this.onPointerUp); - if (!htmlTarget.closest("*.lm_content") && (htmlTarget.closest("*.lm_tab") || htmlTarget.closest("*.lm_stack"))) { - const className = typeof htmlTarget.className === "string" ? htmlTarget.className : ""; - if (!className.includes("lm_close") && !className.includes("lm_maximise")) { - this._flush = UndoManager.StartBatch("golden layout edit"); + window.addEventListener('mouseup', this.onPointerUp); + if (!htmlTarget.closest('*.lm_content') && (htmlTarget.closest('*.lm_tab') || htmlTarget.closest('*.lm_stack'))) { + const className = typeof htmlTarget.className === 'string' ? htmlTarget.className : ''; + if (!className.includes('lm_close') && !className.includes('lm_maximise')) { + this._flush = UndoManager.StartBatch('golden layout edit'); } } } - if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && - ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) { + if (!e.nativeEvent.cancelBubble && !InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE) && !InteractionUtils.IsType(e, InteractionUtils.PENTYPE) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(CurrentUserUtils.ActiveTool)) { e.stopPropagation(); } - } + }; public CaptureThumbnail() { const content = this.props.DocumentView?.()?.ContentDiv; if (content) { - const _width = Number(getComputedStyle(content).width.replace("px","")); - const _height = Number(getComputedStyle(content).height.replace("px","")); - return CollectionFreeFormView.UpdateIcon( - this.layoutDoc[Id] + "-icon" + (new Date()).getTime(), - content, - _width, _height, - _width, _height, 0, 1, true, this.layoutDoc[Id] + "-icon", - (iconFile, _nativeWidth, _nativeHeight) => { - const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: this.rootDoc.title+"-icon", _width, _height, _nativeWidth, _nativeHeight}); - const proto = Cast(img.proto, Doc, null)!; - proto["data-nativeWidth"] = _width; - proto["data-nativeHeight"] = _height; - this.dataDoc.thumb = img; - }); + const _width = Number(getComputedStyle(content).width.replace('px', '')); + const _height = Number(getComputedStyle(content).height.replace('px', '')); + return CollectionFreeFormView.UpdateIcon(this.layoutDoc[Id] + '-icon' + new Date().getTime(), content, _width, _height, _width, _height, 0, 1, true, this.layoutDoc[Id] + '-icon', (iconFile, _nativeWidth, _nativeHeight) => { + const img = Docs.Create.ImageDocument(new ImageField(iconFile), { title: this.rootDoc.title + '-icon', _width, _height, _nativeWidth, _nativeHeight }); + const proto = Cast(img.proto, Doc, null)!; + proto['data-nativeWidth'] = _width; + proto['data-nativeHeight'] = _height; + this.dataDoc.thumb = img; + }); } - } - public static async TakeSnapshot(doc: Doc|undefined, clone = false) { + public static async TakeSnapshot(doc: Doc | undefined, clone = false) { if (!doc) return undefined; let json = StrCast(doc.dockingConfig); if (clone) { const cloned = await Doc.MakeClone(doc); - Array.from(cloned.map.entries()).map(entry => json = json.replace(entry[0], entry[1][Id])); + Array.from(cloned.map.entries()).map(entry => (json = json.replace(entry[0], entry[1][Id]))); Doc.GetProto(cloned.clone).dockingConfig = json; return CurrentUserUtils.openDashboard(cloned.clone); } const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); - const origtabids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")) || []; - const origtabs = origtabids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc); + const origtabids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) || []; + const origtabs = origtabids + .map(id => DocServer.GetCachedRefField(id)) + .filter(f => f) + .map(f => f as Doc); const newtabs = origtabs.map(origtab => { const origtabdocs = DocListCast(origtab.data); const newtab = origtabdocs.length ? Doc.MakeCopy(origtab, true, undefined, true) : Doc.MakeAlias(origtab); const newtabdocs = origtabdocs.map(origtabdoc => Doc.MakeAlias(origtabdoc)); if (newtabdocs.length) { Doc.GetProto(newtab).data = new List<Doc>(newtabdocs); - newtabdocs.forEach(ntab => ntab.context = newtab); + newtabdocs.forEach(ntab => (ntab.context = newtab)); } json = json.replace(origtab[Id], newtab[Id]); return newtab; @@ -426,22 +432,27 @@ export class CollectionDockingView extends CollectionSubView() { this._ignoreStateChange = JSON.stringify(this._goldenLayout.toConfig()); const json = JSON.stringify(this._goldenLayout.toConfig()); const matches = json.match(/\"documentId\":\"[a-z0-9-]+\"/g); - const docids = matches?.map(m => m.replace("\"documentId\":\"", "").replace("\"", "")); - const docs = !docids ? [] : docids.map(id => DocServer.GetCachedRefField(id)).filter(f => f).map(f => f as Doc); + const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')); + const docs = !docids + ? [] + : docids + .map(id => DocServer.GetCachedRefField(id)) + .filter(f => f) + .map(f => f as Doc); const changesMade = this.props.Document.dockcingConfig !== json; if (changesMade && !this._flush) { UndoManager.RunInBatch(() => { this.props.Document.dockingConfig = json; this.props.Document.data = new List<Doc>(docs); - }, "state changed"); + }, 'state changed'); } return changesMade; - } + }; tabDestroyed = (tab: any) => { - if(tab.DashDoc?.type !== DocumentType.KVP) { - Doc.AddDocToList(CurrentUserUtils.MyHeaderBar, "data", tab.DashDoc); - Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, "data", tab.DashDoc, undefined, true, true); + if (tab.DashDoc?.type !== DocumentType.KVP) { + Doc.AddDocToList(CurrentUserUtils.MyHeaderBar, 'data', tab.DashDoc); + Doc.AddDocToList(CurrentUserUtils.MyRecentlyClosed, 'data', tab.DashDoc, undefined, true, true); } const dview = CollectionDockingView.Instance.props.Document; const fieldKey = CollectionDockingView.Instance.props.fieldKey; @@ -450,63 +461,95 @@ export class CollectionDockingView extends CollectionSubView() { tab._disposers && Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); tab.reactComponents?.forEach((ele: any) => ReactDOM.unmountComponentAtNode(ele)); this.stateChanged(); - } + }; tabCreated = (tab: any) => { this.tabMap.add(tab); - tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content) - } + tab.contentItem.element[0]?.firstChild?.firstChild?.InitTab?.(tab); // have to explicitly initialize tabs that reuse contents from previous tabs (ie, when dragging a tab around a new tab is created for the old content) + }; stackCreated = (stack: any) => { stack.header?.element.on('mousedown', (e: any) => { const dashboard = CurrentUserUtils.ActiveDashboard; if (dashboard && e.target === stack.header?.element[0] && e.button === 2) { - dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1; + dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1; const docToAdd = Docs.Create.FreeformDocument([], { - _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _backgroundGridShow: true, _fitWidth: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}`, + _width: this.props.PanelWidth(), + _height: this.props.PanelHeight(), + _backgroundGridShow: true, + _fitWidth: true, + title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`, }); this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd); - CollectionDockingView.AddSplit(docToAdd, "", stack); + CollectionDockingView.AddSplit(docToAdd, '', stack); } }); - stack.header?.controlsContainer.find('.lm_close') //get the close icon + stack.header?.controlsContainer + .find('.lm_close') //get the close icon .off('click') //unbind the current click handler - .click(action(() => { - //if (confirm('really close this?')) { - if (!stack.parent.parent.isRoot || stack.parent.contentItems.length > 1) { - stack.remove(); - } else { - alert('cant delete the last stack'); - } - })); + .click( + action(() => { + //if (confirm('really close this?')) { + if (!stack.parent.parent.isRoot || stack.parent.contentItems.length > 1) { + stack.remove(); + } else { + alert('cant delete the last stack'); + } + }) + ); - stack.header?.controlsContainer.find('.lm_maximise') //get the close icon + stack.header?.controlsContainer + .find('.lm_maximise') //get the close icon .click(() => setTimeout(this.stateChanged)); - stack.header?.controlsContainer.find('.lm_popout') //get the popout icon + stack.header?.controlsContainer + .find('.lm_popout') //get the popout icon .off('click') //unbind the current click handler - .click(action(() => { - // stack.config.fixed = !stack.config.fixed; // force the stack to have a fixed size - const dashboard = CurrentUserUtils.ActiveDashboard; - if (dashboard) { - dashboard["pane-count"] = NumCast(dashboard["pane-count"]) + 1; - const docToAdd = Docs.Create.FreeformDocument([], { - _width: this.props.PanelWidth(), _height: this.props.PanelHeight(), _fitWidth: true, _backgroundGridShow: true, title: `Untitled Tab ${NumCast(dashboard["pane-count"])}` - }); - this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd); - CollectionDockingView.AddSplit(docToAdd, "", stack); - } - })); - } + .click( + action(() => { + // stack.config.fixed = !stack.config.fixed; // force the stack to have a fixed size + const dashboard = CurrentUserUtils.ActiveDashboard; + if (dashboard) { + dashboard['pane-count'] = NumCast(dashboard['pane-count']) + 1; + const docToAdd = Docs.Create.FreeformDocument([], { + _width: this.props.PanelWidth(), + _height: this.props.PanelHeight(), + _fitWidth: true, + _backgroundGridShow: true, + title: `Untitled Tab ${NumCast(dashboard['pane-count'])}`, + }); + this.props.Document.isShared && inheritParentAcls(this.props.Document, docToAdd); + CollectionDockingView.AddSplit(docToAdd, '', stack); + } + }) + ); + }; render() { return <div className="collectiondockingview-container" onPointerDown={this.onPointerDown} ref={this._containerRef} />; } } -ScriptingGlobals.add(function openInLightbox(doc: any) { LightboxView.AddDocTab(doc, "lightbox"); }, - "opens up document in a lightbox", "(doc: any)"); -ScriptingGlobals.add(function openOnRight(doc: any) { return CollectionDockingView.AddSplit(doc, "right"); }, - "opens up document in tab on right side of the screen", "(doc: any)"); -ScriptingGlobals.add(function openInOverlay(doc: any) { return Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc); }, - "opens up document in screen overlay layer", "(doc: any)"); -ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.ReplaceTab(doc, "right", undefined, shiftKey); });
\ No newline at end of file +ScriptingGlobals.add( + function openInLightbox(doc: any) { + LightboxView.AddDocTab(doc, 'lightbox'); + }, + 'opens up document in a lightbox', + '(doc: any)' +); +ScriptingGlobals.add( + function openOnRight(doc: any) { + return CollectionDockingView.AddSplit(doc, 'right'); + }, + 'opens up document in tab on right side of the screen', + '(doc: any)' +); +ScriptingGlobals.add( + function openInOverlay(doc: any) { + return Doc.AddDocToList(CurrentUserUtils.MyOverlayDocs, undefined, doc); + }, + 'opens up document in screen overlay layer', + '(doc: any)' +); +ScriptingGlobals.add(function useRightSplit(doc: any, shiftKey?: boolean) { + CollectionDockingView.ReplaceTab(doc, 'right', undefined, shiftKey); +}); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 62d07b0e4..c0a61c90f 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -1,43 +1,41 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; -import 'golden-layout/src/css/goldenlayout-base.css'; -import 'golden-layout/src/css/goldenlayout-dark-theme.css'; import { clamp } from 'lodash'; -import { action, computed, IReactionDisposer, observable, reaction } from "mobx"; -import { observer } from "mobx-react"; +import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; +import { observer } from 'mobx-react'; import * as ReactDOM from 'react-dom'; -import { DataSym, Doc, HeightSym, Opt, WidthSym } from "../../../fields/Doc"; +import { DataSym, Doc, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; -import { FieldId } from "../../../fields/RefField"; -import { BoolCast, Cast, NumCast, StrCast } from "../../../fields/Types"; -import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from "../../../Utils"; -import { DocServer } from "../../DocServer"; +import { FieldId } from '../../../fields/RefField'; +import { BoolCast, Cast, NumCast, StrCast } from '../../../fields/Types'; +import { emptyFunction, lightOrDark, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents, simulateMouseClick, Utils } from '../../../Utils'; +import { DocServer } from '../../DocServer'; import { DocUtils } from '../../documents/Documents'; import { DocumentType } from '../../documents/DocumentTypes'; import { CurrentUserUtils } from '../../util/CurrentUserUtils'; import { DocumentManager } from '../../util/DocumentManager'; -import { DragManager, dropActionType } from "../../util/DragManager"; +import { DragManager, dropActionType } from '../../util/DragManager'; import { SelectionManager } from '../../util/SelectionManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; -import { undoBatch, UndoManager } from "../../util/UndoManager"; +import { undoBatch, UndoManager } from '../../util/UndoManager'; import { Colors, Shadows } from '../global/globalEnums'; import { LightboxView } from '../LightboxView'; import { MainView } from '../MainView'; -import { DocFocusOptions, DocumentView, DocumentViewProps } from "../nodes/DocumentView"; +import { DocFocusOptions, DocumentView, DocumentViewProps } from '../nodes/DocumentView'; import { DashFieldView } from '../nodes/formattedText/DashFieldView'; import { PinProps, PresBox, PresMovement } from '../nodes/trails'; import { DefaultStyleProvider, StyleProp } from '../StyleProvider'; import { CollectionDockingView } from './CollectionDockingView'; import { CollectionFreeFormView } from './collectionFreeForm/CollectionFreeFormView'; import { CollectionView, CollectionViewType } from './CollectionView'; -import "./TabDocView.scss"; -import React = require("react"); +import './TabDocView.scss'; +import React = require('react'); import { listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; -const _global = (window /* browser */ || global /* node */) as any; +const _global = (window /* browser */ || global) /* node */ as any; interface TabDocViewProps { documentId: FieldId; @@ -54,9 +52,15 @@ export class TabDocView extends React.Component<TabDocViewProps> { @observable _document: Doc | undefined; @observable _view: DocumentView | undefined; - @computed get layoutDoc() { return this._document && Doc.Layout(this._document); } - @computed get tabColor() { return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor))); } - @computed get tabTextColor() { return this._document?.type === DocumentType.PRES ? "black" : StrCast(this._document?._color, StrCast(this._document?.color, DefaultStyleProvider(this._document, undefined, StyleProp.Color))); } + @computed get layoutDoc() { + return this._document && Doc.Layout(this._document); + } + @computed get tabColor() { + return StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor))); + } + @computed get tabTextColor() { + return this._document?.type === DocumentType.PRES ? 'black' : StrCast(this._document?._color, StrCast(this._document?.color, DefaultStyleProvider(this._document, undefined, StyleProp.Color))); + } // @computed get renderBounds() { // const bounds = this._document ? Cast(this._document._renderContentBounds, listSpec("number"), [0, 0, this.returnMiniSize(), this.returnMiniSize()]) : [0, 0, 0, 0]; // const xbounds = bounds[2] - bounds[0]; @@ -65,74 +69,91 @@ export class TabDocView extends React.Component<TabDocViewProps> { // return { l: bounds[0] + xbounds / 2 - dim / 2, t: bounds[1] + ybounds / 2 - dim / 2, cx: bounds[0] + xbounds / 2, cy: bounds[1] + ybounds / 2, dim }; // } - get stack() { return (this.props as any).glContainer.parent.parent; } - get tab() { return (this.props as any).glContainer.tab; } - get view() { return this._view; } + get stack() { + return (this.props as any).glContainer.parent.parent; + } + get tab() { + return (this.props as any).glContainer.tab; + } + get view() { + return this._view; + } _lastTab: any; _lastView: DocumentView | undefined; @action init = (tab: any, doc: Opt<Doc>) => { if (tab.contentItem === tab.header.parent.getActiveContentItem()) this._activated = true; - if (tab.DashDoc !== doc && doc && tab.hasOwnProperty("contentItem") && tab.contentItem.config.type !== "stack") { + if (tab.DashDoc !== doc && doc && tab.hasOwnProperty('contentItem') && tab.contentItem.config.type !== 'stack') { tab._disposers = {} as { [name: string]: IReactionDisposer }; tab.contentItem.config.fixed && (tab.contentItem.parent.config.fixed = true); tab.DashDoc = doc; const iconType: IconProp = Doc.toIcon(doc); // setup the title element and set its size according to the # of chars in the title. Show the full title when clicked. const titleEle = tab.titleElement[0]; - const iconWrap = document.createElement("div"); - const closeWrap = document.createElement("div"); + const iconWrap = document.createElement('div'); + const closeWrap = document.createElement('div'); titleEle.size = StrCast(doc.title).length + 3; titleEle.value = doc.title; titleEle.onkeydown = (e: KeyboardEvent) => { e.stopPropagation(); }; - titleEle.onchange = undoBatch(action((e: any) => { - titleEle.size = e.currentTarget.value.length + 3; - Doc.GetProto(doc).title = e.currentTarget.value; - })); + titleEle.onchange = undoBatch( + action((e: any) => { + titleEle.size = e.currentTarget.value.length + 3; + Doc.GetProto(doc).title = e.currentTarget.value; + }) + ); if (tab.element[0].children[1].children.length === 1) { - iconWrap.className = "lm_iconWrap lm_moreInfo"; + iconWrap.className = 'lm_iconWrap lm_moreInfo'; const dragBtnDown = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), returnFalse, action(e => { - if (this.view) { - SelectionManager.SelectView(this.view, false); - let child = this.view.ContentDiv!.children[0]; - while (child.children.length) { - const next = Array.from(child.children).find(c => c.className?.toString().includes("SVGAnimatedString") || typeof (c.className) === "string"); - if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break; - if (next?.className?.toString().includes(DashFieldView.name)) break; - if (next) child = next; - else break; + setupMoveUpEvents( + this, + e, + e => !e.defaultPrevented && DragManager.StartDocumentDrag([iconWrap], new DragManager.DocumentDragData([doc], doc.dropAction as dropActionType), e.clientX, e.clientY), + returnFalse, + action(e => { + if (this.view) { + SelectionManager.SelectView(this.view, false); + let child = this.view.ContentDiv!.children[0]; + while (child.children.length) { + const next = Array.from(child.children).find(c => c.className?.toString().includes('SVGAnimatedString') || typeof c.className === 'string'); + if (next?.className?.toString().includes(DocumentView.ROOT_DIV)) break; + if (next?.className?.toString().includes(DashFieldView.name)) break; + if (next) child = next; + else break; + } + simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); + } else { + this._activated = true; + setTimeout(() => this.view && SelectionManager.SelectView(this.view, false)); } - simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); - } - else { this._activated = true; - setTimeout(() =>this.view && SelectionManager.SelectView(this.view, false)); - } - })); + }) + ); }; - + const docIcon = <FontAwesomeIcon onPointerDown={dragBtnDown} icon={iconType} />; - const closeIcon = <FontAwesomeIcon icon={"eye"} />; + const closeIcon = <FontAwesomeIcon icon={'eye'} />; ReactDOM.render(docIcon, iconWrap); ReactDOM.render(closeIcon, closeWrap); tab.reactComponents = [iconWrap, closeWrap]; tab.element[0].prepend(iconWrap); - tab._disposers.layerDisposer = reaction(() => ({ layer: tab.DashDoc.activeLayer, color: this.tabColor }), + 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"; + titleEle.style.backgroundColor = 'transparent'; iconWrap.style.color = textColor; closeWrap.style.color = textColor; - tab.element[0].style.background = !layer ? color : "dimgrey"; - }, { fireImmediately: true }); + tab.element[0].style.background = !layer ? color : 'dimgrey'; + }, + { fireImmediately: true } + ); } // shifts the focus to this tab when another tab is dragged over it tab.element[0].onmouseenter = (e: MouseEvent) => { @@ -142,157 +163,167 @@ export class TabDocView extends React.Component<TabDocViewProps> { } }; - // select the tab document when the tab is directly clicked and activate the tab whenver the tab document is selected titleEle.onpointerdown = action((e: any) => { - if (e.target.className !== "lm_iconWrap") { + if (e.target.className !== 'lm_iconWrap') { if (this.view) SelectionManager.SelectView(this.view, false); else this._activated = true; if (Date.now() - titleEle.lastClick < 1000) titleEle.select(); titleEle.lastClick = Date.now(); - (document.activeElement !== titleEle) && titleEle.focus(); + document.activeElement !== titleEle && titleEle.focus(); } }); - tab._disposers.selectionDisposer = reaction(() => SelectionManager.Views().some(v => v.topMost && v.props.Document === doc), - action((selected) => { + tab._disposers.selectionDisposer = reaction( + () => SelectionManager.Views().some(v => v.topMost && v.props.Document === doc), + action(selected => { if (selected) this._activated = true; const toggle = tab.element[0].children[1].children[0] as HTMLInputElement; - selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && - UndoManager.RunInBatch(() => tab.header.parent.setActiveContentItem(tab.contentItem), "tab switch"); + selected && tab.contentItem !== tab.header.parent.getActiveContentItem() && UndoManager.RunInBatch(() => tab.header.parent.setActiveContentItem(tab.contentItem), 'tab switch'); // toggle.style.fontWeight = selected ? "bold" : ""; // toggle.style.textTransform = selected ? "uppercase" : ""; - })); - + }) + ); // highlight the tab when the tab document is brushed in any part of the UI - tab._disposers.reactionDisposer = reaction(() => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), ({ title, degree }) => { - //titleEle.value = title; - // titleEle.style.padding = degree ? 0 : 2; - // titleEle.style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`; - }, { fireImmediately: true }); + tab._disposers.reactionDisposer = reaction( + () => ({ title: doc.title, degree: Doc.IsBrushedDegree(doc) }), + ({ title, degree }) => { + //titleEle.value = title; + // titleEle.style.padding = degree ? 0 : 2; + // titleEle.style.border = `${["gray", "gray", "gray"][degree]} ${["none", "dashed", "solid"][degree]} 2px`; + }, + { fireImmediately: true } + ); // clean up the tab when it is closed - tab.closeElement.off('click') //unbind the current click handler + tab.closeElement + .off('click') //unbind the current click handler .click(function () { Object.values(tab._disposers).forEach((disposer: any) => disposer?.()); SelectionManager.DeselectAll(); - UndoManager.RunInBatch(() => tab.contentItem.remove(), "delete tab"); + UndoManager.RunInBatch(() => tab.contentItem.remove(), 'delete tab'); }); } - } + }; /** * Adds a document to the presentation view **/ @action - public static PinDoc(docs: Doc|Doc[], pinProps?: PinProps) { - const docList = ((docs instanceof Doc) ? [docs]: docs); - const batch = UndoManager.StartBatch("pinning doc"); + public static PinDoc(docs: Doc | Doc[], pinProps?: PinProps) { + const docList = docs instanceof Doc ? [docs] : docs; + const batch = UndoManager.StartBatch('pinning doc'); // all docs will be added to the ActivePresentation as stored on CurrentUserUtils const curPres = CurrentUserUtils.ActivePresentation; - curPres && docList.forEach(doc => { - // Edge Case 1: Cannot pin document to itself - if (doc === curPres) { alert("Cannot pin presentation document to itself"); return; } - const pinDoc = Doc.MakeAlias(doc); - pinDoc.presentationTargetDoc = doc; - pinDoc.title = doc.title + " - Slide"; - pinDoc.data = new List<Doc>(); // the children of the alias' layout are the presentation slide children. the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data - pinDoc.presMovement = PresMovement.Zoom; - pinDoc.groupWithUp = false; - pinDoc.context = curPres; - // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time - pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area - pinDoc.treeViewHeaderWidth = "100%"; // forces the header to grow to be the same size as its largest sibling. - pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc. - pinDoc.treeViewFieldKey = "data"; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field - pinDoc.treeViewExpandedView = "data";// in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view - pinDoc.treeViewGrowsHorizontally = true;// the document expands horizontally when displayed as a tree view header - pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header - const presArray: Doc[] = PresBox.Instance?.sortArray(); - const size: number = PresBox.Instance?._selectedArray.size; - const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined; - const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null); - // If pinWithView option set then update scale and x / y props of slide - if (pinProps?.pinWithView) { - const viewProps = pinProps.pinWithView; - pinDoc.presPinView = true; - pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2; - pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2; - pinDoc.presPinViewScale = viewProps.scale; - pinDoc.contentBounds = new List<number>([viewProps.bounds.left, viewProps.bounds.top, viewProps.bounds.left+viewProps.bounds.width, viewProps.bounds.top+viewProps.bounds.height]); - } - if (pinProps?.pinDocView) { - const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(pinDoc.type as any) || pinDoc._viewType === CollectionViewType.Stacking; - const pannable: boolean = ((pinDoc.type === DocumentType.COL && doc._viewType === CollectionViewType.Freeform) || doc.type === DocumentType.IMG); - if (scrollable) { - const scroll = doc._scrollTop; - pinDoc.presPinView = true; - pinDoc.presPinViewScroll = scroll; - } else if ([DocumentType.AUDIO, DocumentType.VID].includes(doc.type as any)) { - pinDoc.presPinView = true; - pinDoc.presStartTime = doc._currentTimecode; - pinDoc.presEndTime = NumCast(doc._currentTimecode) + 0.1; - } else if (pannable) { + curPres && + docList.forEach(doc => { + // Edge Case 1: Cannot pin document to itself + if (doc === curPres) { + alert('Cannot pin presentation document to itself'); + return; + } + const pinDoc = Doc.MakeAlias(doc); + pinDoc.presentationTargetDoc = doc; + pinDoc.title = doc.title + ' - Slide'; + pinDoc.data = new List<Doc>(); // the children of the alias' layout are the presentation slide children. the alias' data field might be children of a collection, PDF data, etc -- in any case we don't want the tree view to "see" this data + pinDoc.presMovement = PresMovement.Zoom; + pinDoc.groupWithUp = false; + pinDoc.context = curPres; + // these should potentially all be props passed down by the CollectionTreeView to the TreeView elements. That way the PresBox could configure all of its children at render time + pinDoc.treeViewRenderAsBulletHeader = true; // forces a tree view to render the document next to the bullet in the header area + pinDoc.treeViewHeaderWidth = '100%'; // forces the header to grow to be the same size as its largest sibling. + pinDoc.treeViewChildrenOnRoot = true; // tree view will look for hierarchical children on the root doc, not the data doc. + pinDoc.treeViewFieldKey = 'data'; // tree view will treat the 'data' field as the field where the hierarchical children are located instead of using the document's layout string field + pinDoc.treeViewExpandedView = 'data'; // in case the data doc has an expandedView set, this will mask that field and use the 'data' field when expanding the tree view + pinDoc.treeViewGrowsHorizontally = true; // the document expands horizontally when displayed as a tree view header + pinDoc.treeViewHideHeaderIfTemplate = true; // this will force the document to render itself as the tree view header + const presArray: Doc[] = PresBox.Instance?.sortArray(); + const size: number = PresBox.Instance?._selectedArray.size; + const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined; + const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null); + // If pinWithView option set then update scale and x / y props of slide + if (pinProps?.pinWithView) { + const viewProps = pinProps.pinWithView; pinDoc.presPinView = true; - pinDoc.presPinViewX = pinDoc._panX; - pinDoc.presPinViewY = pinDoc._panY; - pinDoc.presPinViewScale = pinDoc._viewScale; - const pw = NumCast(pinProps.panelWidth); - const ph = NumCast(pinProps.panelHeight); - const ps = NumCast(pinDoc._viewScale); - if (pw && ph && ps) { - pinDoc.contentBounds = new List<number>([ - NumCast(pinDoc.panX)-pw/2/ps, - NumCast(pinDoc.panY)-ph/2/ps, - NumCast(pinDoc.panX)+pw/2/ps, - NumCast(pinDoc.panY)+ph/2/ps]); + pinDoc.presPinViewX = viewProps.bounds.left + viewProps.bounds.width / 2; + pinDoc.presPinViewY = viewProps.bounds.top + viewProps.bounds.height / 2; + pinDoc.presPinViewScale = viewProps.scale; + pinDoc.contentBounds = new List<number>([viewProps.bounds.left, viewProps.bounds.top, viewProps.bounds.left + viewProps.bounds.width, viewProps.bounds.top + viewProps.bounds.height]); + } + if (pinProps?.pinDocView) { + const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(pinDoc.type as any) || pinDoc._viewType === CollectionViewType.Stacking; + const pannable: boolean = (pinDoc.type === DocumentType.COL && doc._viewType === CollectionViewType.Freeform) || doc.type === DocumentType.IMG; + if (scrollable) { + const scroll = doc._scrollTop; + pinDoc.presPinView = true; + pinDoc.presPinViewScroll = scroll; + } else if ([DocumentType.AUDIO, DocumentType.VID].includes(doc.type as any)) { + pinDoc.presPinView = true; + pinDoc.presStartTime = doc._currentTimecode; + pinDoc.presEndTime = NumCast(doc._currentTimecode) + 0.1; + } else if (pannable) { + pinDoc.presPinView = true; + pinDoc.presPinViewX = pinDoc._panX; + pinDoc.presPinViewY = pinDoc._panY; + pinDoc.presPinViewScale = pinDoc._viewScale; + const pw = NumCast(pinProps.panelWidth); + const ph = NumCast(pinProps.panelHeight); + const ps = NumCast(pinDoc._viewScale); + if (pw && ph && ps) { + pinDoc.contentBounds = new List<number>([NumCast(pinDoc.panX) - pw / 2 / ps, NumCast(pinDoc.panY) - ph / 2 / ps, NumCast(pinDoc.panX) + pw / 2 / ps, NumCast(pinDoc.panY) + ph / 2 / ps]); + } + } else if (doc.type === DocumentType.COMPARISON) { + const width = doc._clipWidth; + pinDoc.presPinClipWidth = width; + pinDoc.presPinView = true; } - } else if (doc.type === DocumentType.COMPARISON) { - const width = doc._clipWidth; - pinDoc.presPinClipWidth = width; - pinDoc.presPinView = true; } - } - pinDoc.onClick = ScriptField.MakeFunction("navigateToDoc(self.presentationTargetDoc, self)"); - Doc.AddDocToList(curPres, "data", pinDoc, presSelected); - if (!pinProps?.audioRange && duration !== undefined) { - pinDoc.mediaStart = "manual"; - pinDoc.mediaStop = "manual"; - pinDoc.presStartTime = NumCast(doc.clipStart); - pinDoc.presEndTime = NumCast(doc.clipEnd, duration); - } - //save position - if (pinProps?.setPosition || pinDoc.isInkMask) { - pinDoc.setPosition = true; - pinDoc.y = doc.y; - pinDoc.x = doc.x; - pinDoc.presHideAfter = true; - pinDoc.presHideBefore = true; - pinDoc.title = doc.title + " (move)"; - pinDoc.presMovement = PresMovement.None; - } - if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; - PresBox.Instance?._selectedArray.clear(); - pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array - }); - if (!Array.from(CollectionDockingView.Instance.tabMap).map(d => d.DashDoc).includes(curPres)) { + pinDoc.onClick = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)'); + Doc.AddDocToList(curPres, 'data', pinDoc, presSelected); + if (!pinProps?.audioRange && duration !== undefined) { + pinDoc.mediaStart = 'manual'; + pinDoc.mediaStop = 'manual'; + pinDoc.presStartTime = NumCast(doc.clipStart); + pinDoc.presEndTime = NumCast(doc.clipEnd, duration); + } + //save position + if (pinProps?.setPosition || pinDoc.isInkMask) { + pinDoc.setPosition = true; + pinDoc.y = doc.y; + pinDoc.x = doc.x; + pinDoc.presHideAfter = true; + pinDoc.presHideBefore = true; + pinDoc.title = doc.title + ' (move)'; + pinDoc.presMovement = PresMovement.None; + } + if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; + PresBox.Instance?._selectedArray.clear(); + pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array + }); + if ( + !Array.from(CollectionDockingView.Instance.tabMap) + .map(d => d.DashDoc) + .includes(curPres) + ) { const docs = Cast(CurrentUserUtils.MyOverlayDocs.data, listSpec(Doc), []); if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1); - CollectionDockingView.AddSplit(curPres, "right"); + CollectionDockingView.AddSplit(curPres, 'right'); setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things } setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs } componentDidMount() { - new _global.ResizeObserver(action((entries: any) => { - for (const entry of entries) { - this._panelWidth = entry.contentRect.width; - this._panelHeight = entry.contentRect.height; - } - })).observe(this.props.glContainer._element[0]); - this.props.glContainer.layoutManager.on("activeContentItemChanged", this.onActiveContentItemChanged); + new _global.ResizeObserver( + action((entries: any) => { + for (const entry of entries) { + this._panelWidth = entry.contentRect.width; + this._panelHeight = entry.contentRect.height; + } + }) + ).observe(this.props.glContainer._element[0]); + this.props.glContainer.layoutManager.on('activeContentItemChanged', this.onActiveContentItemChanged); this.props.glContainer.tab?.isActive && this.onActiveContentItemChanged(undefined); // this._tabReaction = reaction(() => ({ selected: this.active(), title: this.tab?.titleElement[0] }), // ({ selected, title }) => title && (title.style.backgroundColor = selected ? "white" : ""), @@ -306,7 +337,7 @@ export class TabDocView extends React.Component<TabDocViewProps> { this._tabReaction?.(); this._view && DocumentManager.Instance.RemoveView(this._view); - this.props.glContainer.layoutManager.off("activeContentItemChanged", this.onActiveContentItemChanged); + this.props.glContainer.layoutManager.off('activeContentItemChanged', this.onActiveContentItemChanged); } @action.bound @@ -326,25 +357,31 @@ export class TabDocView extends React.Component<TabDocViewProps> { // inPlace - will add the document to any collection along the path from the document to the docking view that has a field isInPlaceContainer. if none is found, inPlace adds a tab to current stack addDocTab = (doc: Doc, location: string) => { SelectionManager.DeselectAll(); - const locationFields = doc._viewType === CollectionViewType.Docking ? ["dashboard"] : location.split(":"); - const locationParams = locationFields.length > 1 ? locationFields[1] : ""; + const locationFields = doc._viewType === CollectionViewType.Docking ? ['dashboard'] : location.split(':'); + const locationParams = locationFields.length > 1 ? locationFields[1] : ''; switch (locationFields[0]) { - case "dashboard": return CurrentUserUtils.openDashboard(doc); - case "close": return CollectionDockingView.CloseSplit(doc, locationParams); - case "fullScreen": return CollectionDockingView.OpenFullScreen(doc); - case "replace": return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack); + case 'dashboard': + return CurrentUserUtils.openDashboard(doc); + case 'close': + return CollectionDockingView.CloseSplit(doc, locationParams); + case 'fullScreen': + return CollectionDockingView.OpenFullScreen(doc); + case 'replace': + return CollectionDockingView.ReplaceTab(doc, locationParams, this.stack); // case "lightbox": { // // TabDocView.PinDoc(doc, { hidePresBox: true }); // return LightboxView.AddDocTab(doc, location, undefined, this.addDocTab); // } - case "lightbox": return LightboxView.AddDocTab(doc, location, undefined, this.addDocTab); - case "toggle": return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack); - case "inPlace": - case "add": + case 'lightbox': + return LightboxView.AddDocTab(doc, location, undefined, this.addDocTab); + case 'toggle': + return CollectionDockingView.ToggleSplit(doc, locationParams, this.stack); + case 'inPlace': + case 'add': default: return CollectionDockingView.AddSplit(doc, locationParams, this.stack); } - } + }; remDocTab = (doc: Doc | Doc[]) => { if (doc === this._document) { SelectionManager.DeselectAll(); @@ -352,11 +389,11 @@ export class TabDocView extends React.Component<TabDocViewProps> { return true; } return false; - } + }; getCurrentFrame = () => { return NumCast(Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null)._currentFrame); - } + }; @action focusFunc = (doc: Doc, options?: DocFocusOptions) => { const shrinkwrap = options?.originalTarget === this._document && this.view?.ComponentView?.shrinkWrap; @@ -364,104 +401,113 @@ export class TabDocView extends React.Component<TabDocViewProps> { const focusSpeed = 1000; shrinkwrap(); this._document._viewTransition = `transform ${focusSpeed}ms`; - setTimeout(action(() => { - this._document!._viewTransition = undefined; - options?.afterFocus?.(false); - }), focusSpeed); + setTimeout( + action(() => { + this._document!._viewTransition = undefined; + options?.afterFocus?.(false); + }), + focusSpeed + ); } else { options?.afterFocus?.(false); } if (!this.tab.header.parent._activeContentItem || this.tab.header.parent._activeContentItem !== this.tab.contentItem) { this.tab.header.parent.setActiveContentItem(this.tab.contentItem); // glr: Panning does not work when this is set - (this line is for trying to make a tab that is not topmost become topmost) } - } + }; active = () => this._isActive; @observable _forceInvalidateScreenToLocal = 0; ScreenToLocalTransform = () => { this._forceInvalidateScreenToLocal; const { translateX, translateY } = Utils.GetScreenTransform(this._mainCont?.children?.[0] as HTMLElement); return CollectionDockingView.Instance?.props.ScreenToLocalTransform().translate(-translateX, -translateY); - } + }; PanelWidth = () => this._panelWidth; PanelHeight = () => this._panelHeight; miniMapColor = () => this.tabColor; tabView = () => this._view; - disableMinimap = () => !this._document || (this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform); + disableMinimap = () => !this._document || this._document.layout !== CollectionView.LayoutString(Doc.LayoutFieldKey(this._document)) || this._document?._viewType !== CollectionViewType.Freeform; hideMinimap = () => this.disableMinimap() || BoolCast(this._document?.hideMinimap); @computed get docView() { - return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? (null) : - <><DocumentView key={this._document[Id]} ref={action((r: DocumentView) => { - this._lastView && DocumentManager.Instance.RemoveView(this._lastView); - this._view = r; - this._lastView = this._view; - })} - renderDepth={0} - Document={this._document} - DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - onBrowseClick={MainView.Instance.exploreMode} - isContentActive={returnTrue} - PanelWidth={this.PanelWidth} - PanelHeight={this.PanelHeight} - styleProvider={DefaultStyleProvider} - docFilters={CollectionDockingView.Instance.childDocFilters} - docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters} - searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} - addDocument={undefined} - removeDocument={this.remDocTab} - addDocTab={this.addDocTab} - ScreenToLocalTransform={this.ScreenToLocalTransform} - dontCenter={"y"} - rootSelected={returnTrue} - whenChildContentsActiveChanged={emptyFunction} - focus={this.focusFunc} - docViewPath={returnEmptyDoclist} - bringToFront={emptyFunction} - pinToPres={TabDocView.PinDoc} /> - <TabMinimapView key="minimap" - hideMinimap={this.hideMinimap} - addDocTab={this.addDocTab} - PanelHeight={this.PanelHeight} + return !this._activated || !this._document || this._document._viewType === CollectionViewType.Docking ? null : ( + <> + <DocumentView + key={this._document[Id]} + ref={action((r: DocumentView) => { + this._lastView && DocumentManager.Instance.RemoveView(this._lastView); + this._view = r; + this._lastView = this._view; + })} + renderDepth={0} + Document={this._document} + DataDoc={!Doc.AreProtosEqual(this._document[DataSym], this._document) ? this._document[DataSym] : undefined} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + onBrowseClick={MainView.Instance.exploreMode} + isContentActive={returnTrue} PanelWidth={this.PanelWidth} - background={this.miniMapColor} - document={this._document} - tabView={this.tabView} /> - <Tooltip key="ttip" title={<div className="dash-tooltip">{this._document.hideMinimap ? "Open minimap" : "Close minimap"}</div>}> - <div className="miniMap-hidden" + PanelHeight={this.PanelHeight} + styleProvider={DefaultStyleProvider} + docFilters={CollectionDockingView.Instance.childDocFilters} + docRangeFilters={CollectionDockingView.Instance.childDocRangeFilters} + searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} + addDocument={undefined} + removeDocument={this.remDocTab} + addDocTab={this.addDocTab} + ScreenToLocalTransform={this.ScreenToLocalTransform} + dontCenter={'y'} + rootSelected={returnTrue} + whenChildContentsActiveChanged={emptyFunction} + focus={this.focusFunc} + docViewPath={returnEmptyDoclist} + bringToFront={emptyFunction} + pinToPres={TabDocView.PinDoc} + /> + <TabMinimapView key="minimap" hideMinimap={this.hideMinimap} addDocTab={this.addDocTab} PanelHeight={this.PanelHeight} PanelWidth={this.PanelWidth} background={this.miniMapColor} document={this._document} tabView={this.tabView} /> + <Tooltip key="ttip" title={<div className="dash-tooltip">{this._document.hideMinimap ? 'Open minimap' : 'Close minimap'}</div>}> + <div + className="miniMap-hidden" style={{ - display: this.disableMinimap() || this._document._viewType !== "freeform" ? "none" : undefined, + display: this.disableMinimap() || this._document._viewType !== 'freeform' ? 'none' : undefined, color: this._document.hideMinimap ? Colors.BLACK : Colors.WHITE, backgroundColor: this._document.hideMinimap ? Colors.LIGHT_GRAY : Colors.MEDIUM_BLUE, - boxShadow: this._document.hideMinimap ? Shadows.STANDARD_SHADOW : undefined + boxShadow: this._document.hideMinimap ? Shadows.STANDARD_SHADOW : undefined, }} onPointerDown={e => e.stopPropagation()} - onClick={action(e => { e.stopPropagation(); this._document!.hideMinimap = !this._document!.hideMinimap; })} > - <FontAwesomeIcon icon={"globe-asia"} size="lg" /> + onClick={action(e => { + e.stopPropagation(); + this._document!.hideMinimap = !this._document!.hideMinimap; + })}> + <FontAwesomeIcon icon={'globe-asia'} size="lg" /> </div> </Tooltip> - </>; + </> + ); } render() { return ( - <div className="collectionDockingView-content" style={{ - fontFamily: Doc.UserDoc().renderStyle === "comic" ? "Comic Sans MS" : undefined, - height: "100%", width: "100%" - }} ref={ref => { - if (this._mainCont = ref) { - if (this._lastTab) { - this._view && DocumentManager.Instance.RemoveView(this._view); + <div + className="collectionDockingView-content" + style={{ + fontFamily: Doc.UserDoc().renderStyle === 'comic' ? 'Comic Sans MS' : undefined, + height: '100%', + width: '100%', + }} + ref={ref => { + if ((this._mainCont = ref)) { + if (this._lastTab) { + this._view && DocumentManager.Instance.RemoveView(this._view); + } + this._lastTab = this.tab; + (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document); + DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document))); + new _global.ResizeObserver(action((entries: any) => this._forceInvalidateScreenToLocal++)).observe(ref); } - this._lastTab = this.tab; - (this._mainCont as any).InitTab = (tab: any) => this.init(tab, this._document); - DocServer.GetRefField(this.props.documentId).then(action(doc => doc instanceof Doc && (this._document = doc) && this.tab && this.init(this.tab, this._document))); - new _global.ResizeObserver(action((entries: any) => this._forceInvalidateScreenToLocal++)).observe(ref); - } - }} > + }}> {this.docView} - </div > + </div> ); } } @@ -479,29 +525,38 @@ interface TabMinimapViewProps { export class TabMinimapView extends React.Component<TabMinimapViewProps> { static miniStyleProvider = (doc: Opt<Doc>, props: Opt<DocumentViewProps>, property: string): any => { if (doc) { - switch (property.split(":")[0]) { - default: return DefaultStyleProvider(doc, props, property); - case StyleProp.PointerEvents: return "none"; + switch (property.split(':')[0]) { + default: + return DefaultStyleProvider(doc, props, property); + case StyleProp.PointerEvents: + return 'none'; case StyleProp.DocContents: const background = ((type: DocumentType) => { switch (type) { - case DocumentType.PDF: return "pink"; - case DocumentType.AUDIO: return "lightgreen"; - case DocumentType.WEB: return "brown"; - case DocumentType.IMG: return "blue"; - case DocumentType.MAP: return "orange"; - case DocumentType.VID: return "purple"; - case DocumentType.RTF: return "yellow"; - case DocumentType.COL: return undefined; - default: return "gray"; + case DocumentType.PDF: + return 'pink'; + case DocumentType.AUDIO: + return 'lightgreen'; + case DocumentType.WEB: + return 'brown'; + case DocumentType.IMG: + return 'blue'; + case DocumentType.MAP: + return 'orange'; + case DocumentType.VID: + return 'purple'; + case DocumentType.RTF: + return 'yellow'; + case DocumentType.COL: + return undefined; + default: + return 'gray'; } })(doc.type as DocumentType); - return !background ? - undefined : - <div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: "absolute", display: "block", background }} />; + return !background ? undefined : <div style={{ width: doc[WidthSym](), height: doc[HeightSym](), position: 'absolute', display: 'block', background }} />; } } - } + }; @computed get renderBounds() { const compView = this.props.tabView()?.ComponentView as CollectionFreeFormView; const bounds = compView?.freeformData?.(true)?.bounds; @@ -517,20 +572,27 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> { const doc = this.props.document; const renderBounds = this.renderBounds ?? { l: 0, r: 0, t: 0, b: 0, dim: 1 }; const miniSize = this.returnMiniSize(); - doc && setupMoveUpEvents(this, e, action((e: PointerEvent, down: number[], delta: number[]) => { - doc._panX = clamp(NumCast(doc._panX) + delta[0] / miniSize * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim); - doc._panY = clamp(NumCast(doc._panY) + delta[1] / miniSize * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim); - return false; - }), emptyFunction, emptyFunction); - } + doc && + setupMoveUpEvents( + this, + e, + action((e: PointerEvent, down: number[], delta: number[]) => { + doc._panX = clamp(NumCast(doc._panX) + (delta[0] / miniSize) * renderBounds.dim, renderBounds.l, renderBounds.l + renderBounds.dim); + doc._panY = clamp(NumCast(doc._panY) + (delta[1] / miniSize) * renderBounds.dim, renderBounds.t, renderBounds.t + renderBounds.dim); + return false; + }), + emptyFunction, + emptyFunction + ); + }; render() { - if (!this.renderBounds) return (null); - const miniWidth = this.props.PanelWidth() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim * 100; - const miniHeight = this.props.PanelHeight() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim * 100; - const miniLeft = 50 + (NumCast(this.props.document._panX) - this.renderBounds.cx) / this.renderBounds.dim * 100 - miniWidth / 2; - const miniTop = 50 + (NumCast(this.props.document._panY) - this.renderBounds.cy) / this.renderBounds.dim * 100 - miniHeight / 2; + if (!this.renderBounds) return null; + const miniWidth = (this.props.PanelWidth() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim) * 100; + const miniHeight = (this.props.PanelHeight() / NumCast(this.props.document._viewScale, 1) / this.renderBounds.dim) * 100; + const miniLeft = 50 + ((NumCast(this.props.document._panX) - this.renderBounds.cx) / this.renderBounds.dim) * 100 - miniWidth / 2; + const miniTop = 50 + ((NumCast(this.props.document._panY) - this.renderBounds.cy) / this.renderBounds.dim) * 100 - miniHeight / 2; const miniSize = this.returnMiniSize(); - return this.props.hideMinimap() ? (null) : + return this.props.hideMinimap() ? null : ( <div className="miniMap" style={{ width: miniSize, height: miniSize, background: this.props.background() }}> <CollectionFreeFormView Document={this.props.document} @@ -567,9 +629,10 @@ export class TabMinimapView extends React.Component<TabMinimapViewProps> { searchFilterDocs={CollectionDockingView.Instance.searchFilterDocs} fitContentsToBox={returnTrue} /> - <div className="miniOverlay" onPointerDown={this.miniDown} > - <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% `, }} /> + <div className="miniOverlay" onPointerDown={this.miniDown}> + <div className="miniThumb" style={{ width: `${miniWidth}% `, height: `${miniHeight}% `, left: `${miniLeft}% `, top: `${miniTop}% ` }} /> </div> - </div>; + </div> + ); } -}
\ No newline at end of file +} diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss index b64e9dac1..19401c7f0 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.scss +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.scss @@ -1,4 +1,5 @@ -@import "../../global/globalCssVariables.scss"; +@import '../../global/globalCssVariables.scss'; +@import '../../../../../node_modules/react-table/react-table.css'; .collectionSchemaView-container { border-width: $COLLECTION_BORDER_WIDTH; border-color: $medium-gray; @@ -218,8 +219,6 @@ } } - - .collectionSchemaView-header { height: 100%; color: gray; @@ -289,8 +288,6 @@ button.add-column { } } - - .keys-dropdown { position: relative; //width: 100%; @@ -300,13 +297,12 @@ button.add-column { padding: 3px; height: 28px; font-weight: bold; - letter-spacing: "2px"; - text-transform: "uppercase"; + letter-spacing: '2px'; + text-transform: 'uppercase'; &:focus { font-weight: normal; } } - } .columnMenu-colors { display: flex; @@ -338,7 +334,6 @@ button.add-column { margin-right: 5px; font-size: 10px; border-radius: 3px; - } .keys-options-wrapper { @@ -348,7 +343,7 @@ button.add-column { top: 100%; z-index: 21; background-color: #ffffff; - box-shadow: 0px 3px 4px rgba(0,0,0,30%); + box-shadow: 0px 3px 4px rgba(0, 0, 0, 30%); padding: 1px; .key-option { cursor: pointer; @@ -473,8 +468,8 @@ button.add-column { } .collectionSchemaView-cellContents-docButton { float: right; - width: "15px"; - height: "15px"; + width: '15px'; + height: '15px'; } .collectionSchemaView-dropdownWrapper { border: grey; @@ -490,10 +485,10 @@ button.add-column { display: inline-block; //float: right; height: 100%; - display: "flex"; + display: 'flex'; font-size: 13; - justify-content: "center"; - align-items: "center"; + justify-content: 'center'; + align-items: 'center'; } } .collectionSchemaView-dropdownContainer { @@ -601,4 +596,4 @@ button.add-column { font-size: 10.5px; margin-left: 50px; margin-top: 10px; -}
\ No newline at end of file +} diff --git a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx index 9eba788a9..c4ee1805f 100644 --- a/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx +++ b/src/client/views/collections/collectionSchema/CollectionSchemaView.tsx @@ -1,30 +1,29 @@ -import React = require("react"); +import React = require('react'); import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, observable, untracked } from "mobx"; -import { observer } from "mobx-react"; -import Measure from "react-measure"; -import { Resize } from "react-table"; -import "react-table/react-table.css"; -import { Doc, Opt } from "../../../../fields/Doc"; -import { List } from "../../../../fields/List"; -import { listSpec } from "../../../../fields/Schema"; -import { PastelSchemaPalette, SchemaHeaderField } from "../../../../fields/SchemaHeaderField"; -import { Cast, NumCast } from "../../../../fields/Types"; -import { TraceMobx } from "../../../../fields/util"; -import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from "../../../../Utils"; -import { DocUtils } from "../../../documents/Documents"; -import { SelectionManager } from "../../../util/SelectionManager"; -import { SnappingManager } from "../../../util/SnappingManager"; -import { Transform } from "../../../util/Transform"; -import { undoBatch } from "../../../util/UndoManager"; -import { ContextMenu } from "../../ContextMenu"; -import { ContextMenuProps } from "../../ContextMenuItem"; +import { action, computed, observable, untracked } from 'mobx'; +import { observer } from 'mobx-react'; +import Measure from 'react-measure'; +import { Resize } from 'react-table'; +import { Doc, Opt } from '../../../../fields/Doc'; +import { List } from '../../../../fields/List'; +import { listSpec } from '../../../../fields/Schema'; +import { PastelSchemaPalette, SchemaHeaderField } from '../../../../fields/SchemaHeaderField'; +import { Cast, NumCast } from '../../../../fields/Types'; +import { TraceMobx } from '../../../../fields/util'; +import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils'; +import { DocUtils } from '../../../documents/Documents'; +import { SelectionManager } from '../../../util/SelectionManager'; +import { SnappingManager } from '../../../util/SnappingManager'; +import { Transform } from '../../../util/Transform'; +import { undoBatch } from '../../../util/UndoManager'; +import { ContextMenu } from '../../ContextMenu'; +import { ContextMenuProps } from '../../ContextMenuItem'; import { COLLECTION_BORDER_WIDTH, SCHEMA_DIVIDER_WIDTH } from '../../global/globalCssVariables.scss'; -import { DocumentView } from "../../nodes/DocumentView"; -import { DefaultStyleProvider } from "../../StyleProvider"; -import { CollectionSubView } from "../CollectionSubView"; -import "./CollectionSchemaView.scss"; -import { SchemaTable } from "./SchemaTable"; +import { DocumentView } from '../../nodes/DocumentView'; +import { DefaultStyleProvider } from '../../StyleProvider'; +import { CollectionSubView } from '../CollectionSubView'; +import './CollectionSchemaView.scss'; +import { SchemaTable } from './SchemaTable'; // bcz: need to add drag and drop of rows and columns. This seems like it might work for rows: https://codesandbox.io/s/l94mn1q657 export enum ColumnType { @@ -35,14 +34,21 @@ export enum ColumnType { Doc, Image, List, - Date + Date, } // this map should be used for keys that should have a const type of value const columnTypes: Map<string, ColumnType> = new Map([ - ["title", ColumnType.String], - ["x", ColumnType.Number], ["y", ColumnType.Number], ["_width", ColumnType.Number], ["_height", ColumnType.Number], - ["_nativeWidth", ColumnType.Number], ["_nativeHeight", ColumnType.Number], ["isPrototype", ColumnType.Boolean], - ["_curPage", ColumnType.Number], ["_currentTimecode", ColumnType.Number], ["zIndex", ColumnType.Number] + ['title', ColumnType.String], + ['x', ColumnType.Number], + ['y', ColumnType.Number], + ['_width', ColumnType.Number], + ['_height', ColumnType.Number], + ['_nativeWidth', ColumnType.Number], + ['_nativeHeight', ColumnType.Number], + ['isPrototype', ColumnType.Boolean], + ['_curPage', ColumnType.Number], + ['_currentTimecode', ColumnType.Number], + ['zIndex', ColumnType.Number], ]); @observer @@ -51,7 +57,7 @@ export class CollectionSchemaView extends CollectionSubView() { @observable _previewDoc: Doc | undefined = undefined; @observable _focusedTable: Doc = this.props.Document; - @observable _col: any = ""; + @observable _col: any = ''; @observable _menuWidth = 0; @observable _headerOpen = false; @observable _headerIsEditing = false; @@ -60,19 +66,33 @@ export class CollectionSchemaView extends CollectionSubView() { @observable _pointerY = 0; @observable _openTypes: boolean = false; - @computed get previewWidth() { return () => NumCast(this.props.Document.schemaPreviewWidth); } - @computed get previewHeight() { return () => this.props.PanelHeight() - 2 * this.borderWidth; } - @computed get tableWidth() { return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth(); } - @computed get borderWidth() { return Number(COLLECTION_BORDER_WIDTH); } - @computed get scale() { return this.props.ScreenToLocalTransform().Scale; } - @computed get columns() { return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []); } - set columns(columns: SchemaHeaderField[]) { this.props.Document._schemaHeaders = new List<SchemaHeaderField>(columns); } + @computed get previewWidth() { + return () => NumCast(this.props.Document.schemaPreviewWidth); + } + @computed get previewHeight() { + return () => this.props.PanelHeight() - 2 * this.borderWidth; + } + @computed get tableWidth() { + return this.props.PanelWidth() - 2 * this.borderWidth - Number(SCHEMA_DIVIDER_WIDTH) - this.previewWidth(); + } + @computed get borderWidth() { + return Number(COLLECTION_BORDER_WIDTH); + } + @computed get scale() { + return this.props.ScreenToLocalTransform().Scale; + } + @computed get columns() { + return Cast(this.props.Document._schemaHeaders, listSpec(SchemaHeaderField), []); + } + set columns(columns: SchemaHeaderField[]) { + this.props.Document._schemaHeaders = new List<SchemaHeaderField>(columns); + } @computed get menuCoordinates() { let searchx = 0; let searchy = 0; if (this.props.Document._searchDoc) { - const el = document.getElementsByClassName("collectionSchemaView-searchContainer")[0]; + const el = document.getElementsByClassName('collectionSchemaView-searchContainer')[0]; if (el !== undefined) { const rect = el.getBoundingClientRect(); searchx = rect.x; @@ -93,13 +113,13 @@ export class CollectionSchemaView extends CollectionSubView() { // 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 - untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => keys[key] = false)))); + untracked(() => docs.map(doc => Doc.GetAllPrototypes(doc).map(proto => Object.keys(proto).forEach(key => (keys[key] = false))))); - this.columns.forEach(key => keys[key.heading] = true); + this.columns.forEach(key => (keys[key.heading] = true)); return Array.from(Object.keys(keys)); } - @action setHeaderIsEditing = (isEditing: boolean) => this._headerIsEditing = isEditing; + @action setHeaderIsEditing = (isEditing: boolean) => (this._headerIsEditing = isEditing); @undoBatch setColumnType = action((columnField: SchemaHeaderField, type: ColumnType): void => { @@ -124,7 +144,7 @@ export class CollectionSchemaView extends CollectionSubView() { columns[index] = columnField; this.columns = columns; // need to set the columns to trigger rerender } - } + }; @undoBatch @action @@ -137,80 +157,109 @@ export class CollectionSchemaView extends CollectionSubView() { column.setDesc(descending); columns[index] = column; this.columns = columns; - } + }; renderTypes = (col: any) => { - if (columnTypes.get(col.heading)) return (null); + if (columnTypes.get(col.heading)) return null; const type = col.type; - const anyType = <div className={"columnMenu-option" + (type === ColumnType.Any ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Any)}> - <FontAwesomeIcon icon={"align-justify"} size="sm" /> - Any - </div>; - - const numType = <div className={"columnMenu-option" + (type === ColumnType.Number ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Number)}> - <FontAwesomeIcon icon={"hashtag"} size="sm" /> - Number - </div>; - - const textType = <div className={"columnMenu-option" + (type === ColumnType.String ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.String)}> - <FontAwesomeIcon icon={"font"} size="sm" /> - Text - </div>; - - const boolType = <div className={"columnMenu-option" + (type === ColumnType.Boolean ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Boolean)}> - <FontAwesomeIcon icon={"check-square"} size="sm" /> - Checkbox - </div>; - - const listType = <div className={"columnMenu-option" + (type === ColumnType.List ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.List)}> - <FontAwesomeIcon icon={"list-ul"} size="sm" /> - List - </div>; - - const docType = <div className={"columnMenu-option" + (type === ColumnType.Doc ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Doc)}> - <FontAwesomeIcon icon={"file"} size="sm" /> - Document - </div>; - - const imageType = <div className={"columnMenu-option" + (type === ColumnType.Image ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Image)}> - <FontAwesomeIcon icon={"image"} size="sm" /> - Image - </div>; - - const dateType = <div className={"columnMenu-option" + (type === ColumnType.Date ? " active" : "")} onClick={() => this.setColumnType(col, ColumnType.Date)}> - <FontAwesomeIcon icon={"calendar"} size="sm" /> - Date - </div>; - - - const allColumnTypes = <div className="columnMenu-types"> - {anyType} - {numType} - {textType} - {boolType} - {listType} - {docType} - {imageType} - {dateType} - </div>; - - const justColType = type === ColumnType.Any ? anyType : type === ColumnType.Number ? numType : - type === ColumnType.String ? textType : type === ColumnType.Boolean ? boolType : - type === ColumnType.List ? listType : type === ColumnType.Doc ? docType : - type === ColumnType.Date ? dateType : imageType; + const anyType = ( + <div className={'columnMenu-option' + (type === ColumnType.Any ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Any)}> + <FontAwesomeIcon icon={'align-justify'} size="sm" /> + Any + </div> + ); + + const numType = ( + <div className={'columnMenu-option' + (type === ColumnType.Number ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Number)}> + <FontAwesomeIcon icon={'hashtag'} size="sm" /> + Number + </div> + ); + + const textType = ( + <div className={'columnMenu-option' + (type === ColumnType.String ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.String)}> + <FontAwesomeIcon icon={'font'} size="sm" /> + Text + </div> + ); + + const boolType = ( + <div className={'columnMenu-option' + (type === ColumnType.Boolean ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Boolean)}> + <FontAwesomeIcon icon={'check-square'} size="sm" /> + Checkbox + </div> + ); + + const listType = ( + <div className={'columnMenu-option' + (type === ColumnType.List ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.List)}> + <FontAwesomeIcon icon={'list-ul'} size="sm" /> + List + </div> + ); + + const docType = ( + <div className={'columnMenu-option' + (type === ColumnType.Doc ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Doc)}> + <FontAwesomeIcon icon={'file'} size="sm" /> + Document + </div> + ); + + const imageType = ( + <div className={'columnMenu-option' + (type === ColumnType.Image ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Image)}> + <FontAwesomeIcon icon={'image'} size="sm" /> + Image + </div> + ); + + const dateType = ( + <div className={'columnMenu-option' + (type === ColumnType.Date ? ' active' : '')} onClick={() => this.setColumnType(col, ColumnType.Date)}> + <FontAwesomeIcon icon={'calendar'} size="sm" /> + Date + </div> + ); + + const allColumnTypes = ( + <div className="columnMenu-types"> + {anyType} + {numType} + {textType} + {boolType} + {listType} + {docType} + {imageType} + {dateType} + </div> + ); + + const justColType = + type === ColumnType.Any + ? anyType + : type === ColumnType.Number + ? numType + : type === ColumnType.String + ? textType + : type === ColumnType.Boolean + ? boolType + : type === ColumnType.List + ? listType + : type === ColumnType.Doc + ? docType + : type === ColumnType.Date + ? dateType + : imageType; return ( - <div className="collectionSchema-headerMenu-group" onClick={action(() => this._openTypes = !this._openTypes)}> + <div className="collectionSchema-headerMenu-group" onClick={action(() => (this._openTypes = !this._openTypes))}> <div> - <label style={{ cursor: "pointer" }}>Column type:</label> - <FontAwesomeIcon icon={"caret-down"} size="lg" style={{ float: "right", transform: `rotate(${this._openTypes ? "180deg" : 0})`, transition: "0.2s all ease" }} /> + <label style={{ cursor: 'pointer' }}>Column type:</label> + <FontAwesomeIcon icon={'caret-down'} size="lg" style={{ float: 'right', transform: `rotate(${this._openTypes ? '180deg' : 0})`, transition: '0.2s all ease' }} /> </div> {this._openTypes ? allColumnTypes : justColType} - </div > + </div> ); - } + }; renderSorting = (col: any) => { const sort = col.desc; @@ -218,11 +267,11 @@ export class CollectionSchemaView extends CollectionSubView() { <div className="collectionSchema-headerMenu-group"> <label>Sort by:</label> <div className="columnMenu-sort"> - <div className={"columnMenu-option" + (sort === true ? " active" : "")} onClick={() => this.setColumnSort(col, true)}> + <div className={'columnMenu-option' + (sort === true ? ' active' : '')} onClick={() => this.setColumnSort(col, true)}> <FontAwesomeIcon icon="sort-amount-down" size="sm" /> Sort descending </div> - <div className={"columnMenu-option" + (sort === false ? " active" : "")} onClick={() => this.setColumnSort(col, false)}> + <div className={'columnMenu-option' + (sort === false ? ' active' : '')} onClick={() => this.setColumnSort(col, false)}> <FontAwesomeIcon icon="sort-amount-up" size="sm" /> Sort ascending </div> @@ -233,42 +282,42 @@ export class CollectionSchemaView extends CollectionSubView() { </div> </div> ); - } + }; renderColors = (col: any) => { const selected = col.color; - const pink = PastelSchemaPalette.get("pink2"); - const purple = PastelSchemaPalette.get("purple2"); - const blue = PastelSchemaPalette.get("bluegreen1"); - const yellow = PastelSchemaPalette.get("yellow4"); - const red = PastelSchemaPalette.get("red2"); - const gray = "#f1efeb"; + const pink = PastelSchemaPalette.get('pink2'); + const purple = PastelSchemaPalette.get('purple2'); + const blue = PastelSchemaPalette.get('bluegreen1'); + const yellow = PastelSchemaPalette.get('yellow4'); + const red = PastelSchemaPalette.get('red2'); + const gray = '#f1efeb'; return ( <div className="collectionSchema-headerMenu-group"> <label>Color:</label> <div className="columnMenu-colors"> - <div className={"columnMenu-colorPicker" + (selected === pink ? " active" : "")} style={{ backgroundColor: pink }} onClick={() => this.setColumnColor(col, pink!)}></div> - <div className={"columnMenu-colorPicker" + (selected === purple ? " active" : "")} style={{ backgroundColor: purple }} onClick={() => this.setColumnColor(col, purple!)}></div> - <div className={"columnMenu-colorPicker" + (selected === blue ? " active" : "")} style={{ backgroundColor: blue }} onClick={() => this.setColumnColor(col, blue!)}></div> - <div className={"columnMenu-colorPicker" + (selected === yellow ? " active" : "")} style={{ backgroundColor: yellow }} onClick={() => this.setColumnColor(col, yellow!)}></div> - <div className={"columnMenu-colorPicker" + (selected === red ? " active" : "")} style={{ backgroundColor: red }} onClick={() => this.setColumnColor(col, red!)}></div> - <div className={"columnMenu-colorPicker" + (selected === gray ? " active" : "")} style={{ backgroundColor: gray }} onClick={() => this.setColumnColor(col, gray)}></div> + <div className={'columnMenu-colorPicker' + (selected === pink ? ' active' : '')} style={{ backgroundColor: pink }} onClick={() => this.setColumnColor(col, pink!)}></div> + <div className={'columnMenu-colorPicker' + (selected === purple ? ' active' : '')} style={{ backgroundColor: purple }} onClick={() => this.setColumnColor(col, purple!)}></div> + <div className={'columnMenu-colorPicker' + (selected === blue ? ' active' : '')} style={{ backgroundColor: blue }} onClick={() => this.setColumnColor(col, blue!)}></div> + <div className={'columnMenu-colorPicker' + (selected === yellow ? ' active' : '')} style={{ backgroundColor: yellow }} onClick={() => this.setColumnColor(col, yellow!)}></div> + <div className={'columnMenu-colorPicker' + (selected === red ? ' active' : '')} style={{ backgroundColor: red }} onClick={() => this.setColumnColor(col, red!)}></div> + <div className={'columnMenu-colorPicker' + (selected === gray ? ' active' : '')} style={{ backgroundColor: gray }} onClick={() => this.setColumnColor(col, gray)}></div> </div> </div> ); - } + }; @undoBatch @action changeColumns = (oldKey: string, newKey: string, addNew: boolean, filter?: string) => { const columns = this.columns; if (columns === undefined) { - this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, "f1efeb")]); + this.columns = new List<SchemaHeaderField>([new SchemaHeaderField(newKey, 'f1efeb')]); } else { if (addNew) { - columns.push(new SchemaHeaderField(newKey, "f1efeb")); + columns.push(new SchemaHeaderField(newKey, 'f1efeb')); this.columns = columns; } else { const index = columns.map(c => c.heading).indexOf(oldKey); @@ -278,15 +327,14 @@ export class CollectionSchemaView extends CollectionSubView() { columns[index] = column; this.columns = columns; if (filter) { - Doc.setDocFilter(this.props.Document, newKey, filter, "match"); - } - else { + Doc.setDocFilter(this.props.Document, newKey, filter, 'match'); + } else { this.props.Document._docFilters = undefined; } } } } - } + }; @action openHeader = (col: any, screenx: number, screeny: number) => { @@ -294,10 +342,12 @@ export class CollectionSchemaView extends CollectionSubView() { this._headerOpen = true; this._pointerX = screenx; this._pointerY = screeny; - } + }; @action - closeHeader = () => { this._headerOpen = false; } + closeHeader = () => { + this._headerOpen = false; + }; @undoBatch @action @@ -313,16 +363,16 @@ export class CollectionSchemaView extends CollectionSubView() { } } this.closeHeader(); - } + }; getPreviewTransform = (): Transform => { - return this.props.ScreenToLocalTransform().translate(- this.borderWidth - NumCast(COLLECTION_BORDER_WIDTH) - this.tableWidth, - this.borderWidth); - } + return this.props.ScreenToLocalTransform().translate(-this.borderWidth - NumCast(COLLECTION_BORDER_WIDTH) - this.tableWidth, -this.borderWidth); + }; @action onHeaderClick = (e: React.PointerEvent) => { e.stopPropagation(); - } + }; @action onWheel(e: React.WheelEvent) { @@ -332,40 +382,45 @@ export class CollectionSchemaView extends CollectionSubView() { @computed get renderMenuContent() { TraceMobx(); - return <div className="collectionSchema-header-menuOptions"> - {this.renderTypes(this._col)} - {this.renderColors(this._col)} - <div className="collectionSchema-headerMenu-group"> - <button onClick={() => { this.deleteColumn(this._col.heading); }}> - Hide Column - </button> + return ( + <div className="collectionSchema-header-menuOptions"> + {this.renderTypes(this._col)} + {this.renderColors(this._col)} + <div className="collectionSchema-headerMenu-group"> + <button + onClick={() => { + this.deleteColumn(this._col.heading); + }}> + Hide Column + </button> + </div> </div> - </div>; + ); } private createTarget = (ele: HTMLDivElement) => { this._previewCont = ele; super.CreateDropTarget(ele); - } + }; isFocused = (doc: Doc, outsideReaction: boolean): boolean => this.props.isSelected(outsideReaction) && doc === this._focusedTable; - @action setFocused = (doc: Doc) => this._focusedTable = doc; + @action setFocused = (doc: Doc) => (this._focusedTable = doc); @action setPreviewDoc = (doc: Opt<Doc>) => { SelectionManager.SelectSchemaViewDoc(doc); this._previewDoc = doc; - } + }; //toggles preview side-panel of schema @action toggleExpander = () => { this.props.Document.schemaPreviewWidth = this.previewWidth() === 0 ? Math.min(this.tableWidth / 3, 200) : 0; - } + }; onDividerDown = (e: React.PointerEvent) => { setupMoveUpEvents(this, e, this.onDividerMove, emptyFunction, this.toggleExpander); - } + }; @action onDividerMove = (e: PointerEvent, down: number[], delta: number[]) => { const nativeWidth = this._previewCont!.getBoundingClientRect(); @@ -375,136 +430,146 @@ export class CollectionSchemaView extends CollectionSubView() { const width = movedWidth < minWidth ? minWidth : movedWidth > maxWidth ? maxWidth : movedWidth; this.props.Document.schemaPreviewWidth = width; return false; - } + }; onPointerDown = (e: React.PointerEvent): void => { if (e.button === 0 && !e.altKey && !e.ctrlKey && !e.metaKey) { if (this.props.isSelected(true)) e.stopPropagation(); else this.props.select(false); } - } + }; @computed - get previewDocument(): Doc | undefined { return this._previewDoc; } + get previewDocument(): Doc | undefined { + return this._previewDoc; + } @computed get dividerDragger() { - return this.previewWidth() === 0 ? (null) : - <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown} > + return this.previewWidth() === 0 ? null : ( + <div className="collectionSchemaView-dividerDragger" onPointerDown={this.onDividerDown}> <div className="collectionSchemaView-dividerDragger" /> - </div>; + </div> + ); } @computed get previewPanel() { - return <div ref={this.createTarget} style={{ width: `${this.previewWidth()}px` }}> - {!this.previewDocument ? (null) : - <DocumentView - Document={this.previewDocument} - DataDoc={undefined} - fitContentsToBox={returnTrue} - dontCenter={"y"} - focus={DocUtils.DefaultFocus} - renderDepth={this.props.renderDepth} - rootSelected={this.rootSelected} - PanelWidth={this.previewWidth} - PanelHeight={this.previewHeight} - isContentActive={returnTrue} - isDocumentActive={returnFalse} - ScreenToLocalTransform={this.getPreviewTransform} - docFilters={this.childDocFilters} - docRangeFilters={this.childDocRangeFilters} - searchFilterDocs={this.searchFilterDocs} - styleProvider={DefaultStyleProvider} - docViewPath={returnEmptyDoclist} - ContainingCollectionDoc={this.props.CollectionView?.props.Document} - ContainingCollectionView={this.props.CollectionView} - moveDocument={this.props.moveDocument} - addDocument={this.props.addDocument} - removeDocument={this.props.removeDocument} - whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} - addDocTab={this.props.addDocTab} - pinToPres={this.props.pinToPres} - bringToFront={returnFalse} - />} - </div>; + return ( + <div ref={this.createTarget} style={{ width: `${this.previewWidth()}px` }}> + {!this.previewDocument ? null : ( + <DocumentView + Document={this.previewDocument} + DataDoc={undefined} + fitContentsToBox={returnTrue} + dontCenter={'y'} + focus={DocUtils.DefaultFocus} + renderDepth={this.props.renderDepth} + rootSelected={this.rootSelected} + PanelWidth={this.previewWidth} + PanelHeight={this.previewHeight} + isContentActive={returnTrue} + isDocumentActive={returnFalse} + ScreenToLocalTransform={this.getPreviewTransform} + docFilters={this.childDocFilters} + docRangeFilters={this.childDocRangeFilters} + searchFilterDocs={this.searchFilterDocs} + styleProvider={DefaultStyleProvider} + docViewPath={returnEmptyDoclist} + ContainingCollectionDoc={this.props.CollectionView?.props.Document} + ContainingCollectionView={this.props.CollectionView} + moveDocument={this.props.moveDocument} + addDocument={this.props.addDocument} + removeDocument={this.props.removeDocument} + whenChildContentsActiveChanged={this.props.whenChildContentsActiveChanged} + addDocTab={this.props.addDocTab} + pinToPres={this.props.pinToPres} + bringToFront={returnFalse} + /> + )} + </div> + ); } @computed get schemaTable() { - return <SchemaTable - Document={this.props.Document} - PanelHeight={this.props.PanelHeight} - PanelWidth={this.props.PanelWidth} - childDocs={this.childDocs} - CollectionView={this.props.CollectionView} - ContainingCollectionView={this.props.ContainingCollectionView} - ContainingCollectionDoc={this.props.ContainingCollectionDoc} - fieldKey={this.props.fieldKey} - renderDepth={this.props.renderDepth} - moveDocument={this.props.moveDocument} - ScreenToLocalTransform={this.props.ScreenToLocalTransform} - active={this.props.isContentActive} - onDrop={this.onExternalDrop} - addDocTab={this.props.addDocTab} - pinToPres={this.props.pinToPres} - isSelected={this.props.isSelected} - isFocused={this.isFocused} - setFocused={this.setFocused} - setPreviewDoc={this.setPreviewDoc} - deleteDocument={this.props.removeDocument} - addDocument={this.props.addDocument} - dataDoc={this.props.DataDoc} - columns={this.columns} - documentKeys={this.documentKeys} - headerIsEditing={this._headerIsEditing} - openHeader={this.openHeader} - onClick={this.onTableClick} - onPointerDown={emptyFunction} - onResizedChange={this.onResizedChange} - setColumns={this.setColumns} - reorderColumns={this.reorderColumns} - changeColumns={this.changeColumns} - setHeaderIsEditing={this.setHeaderIsEditing} - changeColumnSort={this.setColumnSort} - />; + return ( + <SchemaTable + Document={this.props.Document} + PanelHeight={this.props.PanelHeight} + PanelWidth={this.props.PanelWidth} + childDocs={this.childDocs} + CollectionView={this.props.CollectionView} + ContainingCollectionView={this.props.ContainingCollectionView} + ContainingCollectionDoc={this.props.ContainingCollectionDoc} + fieldKey={this.props.fieldKey} + renderDepth={this.props.renderDepth} + moveDocument={this.props.moveDocument} + ScreenToLocalTransform={this.props.ScreenToLocalTransform} + active={this.props.isContentActive} + onDrop={this.onExternalDrop} + addDocTab={this.props.addDocTab} + pinToPres={this.props.pinToPres} + isSelected={this.props.isSelected} + isFocused={this.isFocused} + setFocused={this.setFocused} + setPreviewDoc={this.setPreviewDoc} + deleteDocument={this.props.removeDocument} + addDocument={this.props.addDocument} + dataDoc={this.props.DataDoc} + columns={this.columns} + documentKeys={this.documentKeys} + headerIsEditing={this._headerIsEditing} + openHeader={this.openHeader} + onClick={this.onTableClick} + onPointerDown={emptyFunction} + onResizedChange={this.onResizedChange} + setColumns={this.setColumns} + reorderColumns={this.reorderColumns} + changeColumns={this.changeColumns} + setHeaderIsEditing={this.setHeaderIsEditing} + changeColumnSort={this.setColumnSort} + /> + ); } @computed public get schemaToolbar() { - return <div className="collectionSchemaView-toolbar"> - <div className="collectionSchemaView-toolbar-item"> - <div id="preview-schema-checkbox-div"> - <input type="checkbox" key={"Show Preview"} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} /> - Show Preview + return ( + <div className="collectionSchemaView-toolbar"> + <div className="collectionSchemaView-toolbar-item"> + <div id="preview-schema-checkbox-div"> + <input type="checkbox" key={'Show Preview'} checked={this.previewWidth() !== 0} onChange={this.toggleExpander} /> + Show Preview + </div> </div> </div> - </div>; + ); } onSpecificMenu = (e: React.MouseEvent) => { - if ((e.target as any)?.className?.includes?.("collectionSchemaView-cell") || (e.target instanceof HTMLSpanElement)) { + if ((e.target as any)?.className?.includes?.('collectionSchemaView-cell') || e.target instanceof HTMLSpanElement) { const cm = ContextMenu.Instance; - const options = cm.findByDescription("Options..."); - const optionItems: ContextMenuProps[] = options && "subitems" in options ? options.subitems : []; - optionItems.push({ description: "remove", event: () => this._previewDoc && this.props.removeDocument?.(this._previewDoc), icon: "trash" }); - !options && cm.addItem({ description: "Options...", subitems: optionItems, icon: "compass" }); + const options = cm.findByDescription('Options...'); + const optionItems: ContextMenuProps[] = options && 'subitems' in options ? options.subitems : []; + optionItems.push({ description: 'remove', event: () => this._previewDoc && this.props.removeDocument?.(this._previewDoc), icon: 'trash' }); + !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'compass' }); cm.displayMenu(e.clientX, e.clientY); (e.nativeEvent as any).SchemaHandled = true; // not sure why this is needed, but if you right-click quickly on a cell, the Document/Collection contextMenu handlers still fire without this. e.stopPropagation(); } - } + }; @action onTableClick = (e: React.MouseEvent): void => { - if (!(e.target as any)?.className?.includes?.("collectionSchemaView-cell") && !(e.target instanceof HTMLSpanElement)) { + if (!(e.target as any)?.className?.includes?.('collectionSchemaView-cell') && !(e.target instanceof HTMLSpanElement)) { this.setPreviewDoc(undefined); } else { e.stopPropagation(); } this.setFocused(this.props.Document); this.closeHeader(); - } + }; onResizedChange = (newResized: Resize[], event: any) => { const columns = this.columns; @@ -515,23 +580,23 @@ export class CollectionSchemaView extends CollectionSubView() { columns[index] = column; }); this.columns = columns; - } + }; @action - setColumns = (columns: SchemaHeaderField[]) => this.columns = columns + setColumns = (columns: SchemaHeaderField[]) => (this.columns = columns); @undoBatch reorderColumns = (toMove: SchemaHeaderField, relativeTo: SchemaHeaderField, before: boolean, columnsValues: SchemaHeaderField[]) => { const columns = [...columnsValues]; const oldIndex = columns.indexOf(toMove); const relIndex = columns.indexOf(relativeTo); - const newIndex = (oldIndex > relIndex && !before) ? relIndex + 1 : (oldIndex < relIndex && before) ? relIndex - 1 : relIndex; + const newIndex = oldIndex > relIndex && !before ? relIndex + 1 : oldIndex < relIndex && before ? relIndex - 1 : relIndex; if (oldIndex === newIndex) return; columns.splice(newIndex, 0, columns.splice(oldIndex, 1)[0]); this.columns = columns; - } + }; onZoomMenu = (e: React.WheelEvent) => this.props.isContentActive(true) && e.stopPropagation(); @@ -539,35 +604,44 @@ export class CollectionSchemaView extends CollectionSubView() { TraceMobx(); if (!this.props.isContentActive()) setTimeout(() => this.closeHeader(), 0); const menuContent = this.renderMenuContent; - const menu = <div className="collectionSchema-header-menu" - onWheel={e => this.onZoomMenu(e)} - onPointerDown={e => this.onHeaderClick(e)} - style={{ transform: `translate(${(this.menuCoordinates[0])}px, ${(this.menuCoordinates[1])}px)` }}> - <Measure offset onResize={action((r: any) => { - const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height); - this._menuWidth = dim[0]; this._menuHeight = dim[1]; - })}> - {({ measureRef }) => <div ref={measureRef}> {menuContent} </div>} - </Measure> - </div>; - return <div className={"collectionSchemaView" + (this.props.Document._searchDoc ? "-searchContainer" : "-container")} - style={{ - overflow: this.props.scrollOverflow === true ? "scroll" : undefined, backgroundColor: "white", - pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? "none" : undefined, - width: this.props.PanelWidth() || "100%", height: this.props.PanelHeight() || "100%", position: "relative", - }} > - <div className="collectionSchemaView-tableContainer" - style={{ width: `calc(100% - ${this.previewWidth()}px)` }} - onContextMenu={this.onSpecificMenu} - onPointerDown={this.onPointerDown} - onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} - onDrop={e => this.onExternalDrop(e, {})} - ref={this.createTarget}> - {this.schemaTable} + const menu = ( + <div className="collectionSchema-header-menu" onWheel={e => this.onZoomMenu(e)} onPointerDown={e => this.onHeaderClick(e)} style={{ transform: `translate(${this.menuCoordinates[0]}px, ${this.menuCoordinates[1]}px)` }}> + <Measure + offset + onResize={action((r: any) => { + const dim = this.props.ScreenToLocalTransform().inverse().transformDirection(r.offset.width, r.offset.height); + this._menuWidth = dim[0]; + this._menuHeight = dim[1]; + })}> + {({ measureRef }) => <div ref={measureRef}> {menuContent} </div>} + </Measure> </div> - {this.dividerDragger} - {!this.previewWidth() ? (null) : this.previewPanel} - {this._headerOpen && this.props.isContentActive() ? menu : null} - </div>; + ); + return ( + <div + className={'collectionSchemaView' + (this.props.Document._searchDoc ? '-searchContainer' : '-container')} + style={{ + overflow: this.props.scrollOverflow === true ? 'scroll' : undefined, + backgroundColor: 'white', + pointerEvents: this.props.Document._searchDoc !== undefined && !this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined, + width: this.props.PanelWidth() || '100%', + height: this.props.PanelHeight() || '100%', + position: 'relative', + }}> + <div + className="collectionSchemaView-tableContainer" + style={{ width: `calc(100% - ${this.previewWidth()}px)` }} + onContextMenu={this.onSpecificMenu} + onPointerDown={this.onPointerDown} + onWheel={e => this.props.isContentActive(true) && e.stopPropagation()} + onDrop={e => this.onExternalDrop(e, {})} + ref={this.createTarget}> + {this.schemaTable} + </div> + {this.dividerDragger} + {!this.previewWidth() ? null : this.previewPanel} + {this._headerOpen && this.props.isContentActive() ? menu : null} + </div> + ); } -}
\ 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 28874220a..539fb5c99 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -14,7 +14,7 @@ import { WebField } from '../../../../fields/URLField'; import { aggregateBounds, Utils } from '../../../../Utils'; import { DocumentType } from '../../../documents/DocumentTypes'; import { CurrentUserUtils } from '../../../util/CurrentUserUtils'; -import { ScriptingGlobals } from "../../../util/ScriptingGlobals"; +import { ScriptingGlobals } from '../../../util/ScriptingGlobals'; import { SelectionManager } from '../../../util/SelectionManager'; import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm'; @@ -34,23 +34,23 @@ import { FontIconBadge } from './FontIconBadge'; import './FontIconBox.scss'; export enum ButtonType { - TextButton = "textBtn", - MenuButton = "menuBtn", - DropdownList = "drpdownList", - DropdownButton = "drpdownBtn", - ClickButton = "clickBtn", - DoubleButton = "dblBtn", - ToggleButton = "tglBtn", - ColorButton = "colorBtn", - ToolButton = "toolBtn", - NumberButton = "numBtn", - EditableText = "editableText" + TextButton = 'textBtn', + MenuButton = 'menuBtn', + DropdownList = 'drpdownList', + DropdownButton = 'drpdownBtn', + ClickButton = 'clickBtn', + DoubleButton = 'dblBtn', + ToggleButton = 'tglBtn', + ColorButton = 'colorBtn', + ToolButton = 'toolBtn', + NumberButton = 'numBtn', + EditableText = 'editableText', } export enum NumButtonType { - Slider = "slider", - DropdownOptions = "list", - Inline = "inline" + Slider = 'slider', + DropdownOptions = 'list', + Inline = 'inline', } export interface ButtonProps extends FieldViewProps { @@ -58,29 +58,39 @@ export interface ButtonProps extends FieldViewProps { } @observer export class FontIconBox extends DocComponent<ButtonProps>() { - public static LayoutString(fieldKey: string) { return FieldView.LayoutString(FontIconBox, fieldKey); } + 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)'); }; + 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.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" }); + 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' }); } - } + }; - static GetShowLabels() { return BoolCast(Doc.UserDoc()._showLabel); } - static SetShowLabels(show:boolean) { Doc.UserDoc()._showLabel = show; } + static GetShowLabels() { + return BoolCast(Doc.UserDoc()._showLabel); + } + static SetShowLabels(show: boolean) { + Doc.UserDoc()._showLabel = show; + } // 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 icon = StrCast(this.dataDoc.icon, 'user') as any; @observable private dropdown: boolean = BoolCast(this.rootDoc.dropDownOpen); @observable private buttonList: string[] = StrListCast(this.rootDoc.btnList); @observable private type = StrCast(this.rootDoc.btnType); @@ -92,11 +102,11 @@ export class FontIconBox extends DocComponent<ButtonProps>() { * - Expandable button (CollectionLinearView) * - Button inside of CollectionLinearView vs. outside of CollectionLinearView * - Action button - * - Dropdown button + * - Dropdown button * - Color button * - Dropdown list * - Number button - **/ + **/ _batch: UndoManager.Batch | undefined = undefined; /** @@ -110,25 +120,30 @@ export class FontIconBox extends DocComponent<ButtonProps>() { // Script for checking the outcome of the toggle const checkResult: number = numScript?.script.run({ value: 0, _readOnly_: true }).result || 0; - const label = !FontIconBox.GetShowLabels() ? (null) : - <div className="fontIconBox-label"> - {this.label} - </div>; + const label = !FontIconBox.GetShowLabels() ? null : <div className="fontIconBox-label">{this.label}</div>; if (numBtnType === NumButtonType.Slider) { - const dropdown = <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()} > - <input type="range" step="1" min={NumCast(this.rootDoc.numBtnMin, 0)} max={NumCast(this.rootDoc.numBtnMax, 100)} value={checkResult} - className={"menu-slider"} id="slider" - onPointerDown={() => this._batch = UndoManager.StartBatch("presDuration")} - onPointerUp={() => this._batch?.end()} - onChange={e => { e.stopPropagation(); setValue(Number(e.target.value)); }} - /> - </div>; + const dropdown = ( + <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()}> + <input + type="range" + step="1" + min={NumCast(this.rootDoc.numBtnMin, 0)} + max={NumCast(this.rootDoc.numBtnMax, 100)} + value={checkResult} + className={'menu-slider'} + id="slider" + onPointerDown={() => (this._batch = UndoManager.StartBatch('presDuration'))} + onPointerUp={() => this._batch?.end()} + onChange={e => { + e.stopPropagation(); + setValue(Number(e.target.value)); + }} + /> + </div> + ); return ( - <div - className={`menuButton ${this.type} ${numBtnType}`} - onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)} - > + <div className={`menuButton ${this.type} ${numBtnType}`} onClick={action(() => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}> {checkResult} {label} {this.rootDoc.dropDownOpen ? dropdown : null} @@ -141,62 +156,56 @@ export class FontIconBox extends DocComponent<ButtonProps>() { items.push(i); } } - const list = items.map((value) => { - return <div className="list-item" key={`${value}`} - style={{ - backgroundColor: value === checkResult ? Colors.LIGHT_BLUE : undefined - }} - onClick={() => setValue(value)}> - {value} - </div>; + const list = items.map(value => { + return ( + <div + className="list-item" + key={`${value}`} + style={{ + backgroundColor: value === checkResult ? Colors.LIGHT_BLUE : undefined, + }} + onClick={() => setValue(value)}> + {value} + </div> + ); }); return ( - <div - className={`menuButton ${this.type} ${numBtnType}`} - > - <div className={`button`} onClick={action((e) => setValue(Number(checkResult) - 1))}> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"minus"} /> + <div className={`menuButton ${this.type} ${numBtnType}`}> + <div className={`button`} onClick={action(e => setValue(Number(checkResult) - 1))}> + <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'minus'} /> </div> <div className={`button ${'number'}`} - onPointerDown={(e) => { + onPointerDown={e => { e.stopPropagation(); e.preventDefault(); }} - onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)} - > - <input - style={{ width: 30 }} - className="button-input" - type="number" - value={checkResult} - onChange={action((e) => setValue(Number(e.target.value)))} - /> + onClick={action(() => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}> + <input style={{ width: 30 }} className="button-input" type="number" value={checkResult} onChange={action(e => setValue(Number(e.target.value)))} /> </div> - <div className={`button`} onClick={action((e) => setValue(Number(checkResult) + 1))}> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"plus"} /> + <div className={`button`} onClick={action(e => setValue(Number(checkResult) + 1))}> + <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'plus'} /> </div> - {this.rootDoc.dropDownOpen ? + {this.rootDoc.dropDownOpen ? ( <div> - <div className="menuButton-dropdownList" - style={{ left: "25%" }}> + <div className="menuButton-dropdownList" style={{ left: '25%' }}> {list} </div> - <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} /> - </div> : null} - + <div + className="dropbox-background" + onClick={e => { + e.stopPropagation(); + this.rootDoc.dropDownOpen = false; + }} + /> + </div> + ) : null} </div> ); } else { - return ( - <div> - - </div> - ); + return <div></div>; } - - } /** @@ -207,20 +216,21 @@ export class FontIconBox extends DocComponent<ButtonProps>() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); return ( - <div className={`menuButton ${this.type} ${active}`} + <div + className={`menuButton ${this.type} ${active}`} style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} - onClick={action(() => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen)}> + onClick={action(() => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}> <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> - {!this.label || !FontIconBox.GetShowLabels() ? (null) : <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>} - <div - className="menuButton-dropdown" - style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}> + {!this.label || !FontIconBox.GetShowLabels() ? null : ( + <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> + {' '} + {this.label}{' '} + </div> + )} + <div className="menuButton-dropdown" style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}> <FontAwesomeIcon icon={'caret-down'} color={color} size="sm" /> </div> - {this.rootDoc.dropDownOpen ? - <div className="menuButton-dropdownBox"> - {/* DROPDOWN BOX CONTENTS */} - </div> : null} + {this.rootDoc.dropDownOpen ? <div className="menuButton-dropdownBox">{/* DROPDOWN BOX CONTENTS */}</div> : null} </div> ); } @@ -234,12 +244,14 @@ export class FontIconBox extends DocComponent<ButtonProps>() { const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); const script = ScriptCast(this.rootDoc.script); - if (!script) { return null; } + if (!script) { + return null; + } let noviceList: string[] = []; let text: string | undefined; let dropdown = true; - let icon: IconProp = "caret-down"; + let icon: IconProp = 'caret-down'; try { if (script.script.originalScript.startsWith('setView')) { const selected = SelectionManager.Docs().lastElement(); @@ -248,100 +260,110 @@ export class FontIconBox extends DocComponent<ButtonProps>() { text = StrCast(selected._viewType); } else { dropdown = false; - text = selected.type === DocumentType.RTF ? "Text" : StrCast(selected.type); + text = selected.type === DocumentType.RTF ? 'Text' : StrCast(selected.type); icon = Doc.toIcon(selected); } } else { dropdown = false; - icon = "globe-asia"; - text = "User Default"; + icon = 'globe-asia'; + text = 'User Default'; } noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking]; } else if (script.script.originalScript.startsWith('setFont')) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); - noviceList = ["Roboto", "Times New Roman", "Arial", "Georgia", - "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]; + noviceList = ['Roboto', 'Times New Roman', 'Arial', 'Georgia', 'Comic Sans MS', 'Tahoma', 'Impact', 'Crimson Text']; } } catch (e) { console.log(e); } // Get items to place into the list - const list = this.buttonList.map((value) => { + const list = this.buttonList.map(value => { if (Doc.noviceMode && !noviceList.includes(value)) { return; } - return <div className="list-item" key={`${value}`} - style={{ - fontFamily: script.script.originalScript.startsWith('setFont') ? value : undefined, - backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined - }} - onClick={() => script.script.run({ value }).result}> - {value[0].toUpperCase() + value.slice(1)} - </div>; + return ( + <div + className="list-item" + key={`${value}`} + style={{ + fontFamily: script.script.originalScript.startsWith('setFont') ? value : undefined, + backgroundColor: value === text ? Colors.LIGHT_BLUE : undefined, + }} + onClick={() => script.script.run({ value }).result}> + {value[0].toUpperCase() + value.slice(1)} + </div> + ); }); - const label = !this.label || !FontIconBox.GetShowLabels() ? (null) : - <div className="fontIconBox-label" style={{ bottom: 0, position: "absolute", color: color, backgroundColor: backgroundColor }}> - {this.label} - </div>; + const label = + !this.label || !FontIconBox.GetShowLabels() ? null : ( + <div className="fontIconBox-label" style={{ bottom: 0, position: 'absolute', color: color, backgroundColor: backgroundColor }}> + {this.label} + </div> + ); return ( - <div className={`menuButton ${this.type} ${active}`} - style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : "flex" }} - onClick={dropdown ? () => this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen : undefined}> - {dropdown ? (null) : <FontAwesomeIcon style={{ marginLeft: 5 }} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />} - <div className="menuButton-dropdown-header"> - {text && text[0].toUpperCase() + text.slice(1)} - </div> + <div + className={`menuButton ${this.type} ${active}`} + style={{ backgroundColor: this.rootDoc.dropDownOpen ? Colors.MEDIUM_BLUE : backgroundColor, color: color, display: dropdown ? undefined : 'flex' }} + onClick={dropdown ? () => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen) : undefined}> + {dropdown ? null : <FontAwesomeIcon style={{ marginLeft: 5 }} className={`fontIconBox-icon-${this.type}`} icon={icon} color={color} />} + <div className="menuButton-dropdown-header">{text && text[0].toUpperCase() + text.slice(1)}</div> {label} - {!dropdown ? (null) : <div className="menuButton-dropDown"> - <FontAwesomeIcon icon={icon} color={color} size="sm" /> - </div>} - {this.rootDoc.dropDownOpen ? + {!dropdown ? null : ( + <div className="menuButton-dropDown"> + <FontAwesomeIcon icon={icon} color={color} size="sm" /> + </div> + )} + {this.rootDoc.dropDownOpen ? ( <div> - <div className="menuButton-dropdownList" - style={{ left: 0 }}> + <div className="menuButton-dropdownList" style={{ left: 0 }}> {list} </div> - <div className="dropbox-background" onClick={(e) => { e.stopPropagation(); this.rootDoc.dropDownOpen = false; }} /> + <div + className="dropbox-background" + onClick={e => { + e.stopPropagation(); + this.rootDoc.dropDownOpen = false; + }} + /> </div> - : null} + ) : null} </div> ); } @observable colorPickerClosed: boolean = true; - @computed get colorScript() { return ScriptCast(this.rootDoc.script); } + @computed get colorScript() { + return ScriptCast(this.rootDoc.script); + } colorPicker = (curColor: string) => { const change = (value: ColorState) => { const s = this.colorScript; s && undoBatch(() => s.script.run({ value: Utils.colorString(value), _readOnly_: false }).result)(); }; - const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', - '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', - '#FFFFFF', '#f1efeb', "transparent"]; - return <SketchPicker - onChange={change} - color={curColor} - presetColors={presets} />; - } + const presets = ['#D0021B', '#F5A623', '#F8E71C', '#8B572A', '#7ED321', '#417505', '#9013FE', '#4A90E2', '#50E3C2', '#B8E986', '#000000', '#4A4A4A', '#9B9B9B', '#FFFFFF', '#f1efeb', 'transparent']; + return <SketchPicker onChange={change} color={curColor} presetColors={presets} />; + }; /** * Color button */ @computed get colorButton() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? "transparent"; - - const label = !this.label || !FontIconBox.GetShowLabels() ? (null) : - <div className="fontIconBox-label" style={{ color, backgroundColor }}> - {this.label} - </div>; - - // dropdown caret seems superfluous since clicking the color button does the same thing + const curColor = this.colorScript?.script.run({ value: undefined, _readOnly_: true }).result ?? 'transparent'; + + const label = + !this.label || !FontIconBox.GetShowLabels() ? null : ( + <div className="fontIconBox-label" style={{ color, backgroundColor }}> + {this.label} + </div> + ); + + // dropdown caret seems superfluous since clicking the color button does the same thing // const dropdownCaret = <div // className="menuButton-dropDown" // style={{ borderBottomRightRadius: this.dropdown ? 0 : undefined }}> @@ -349,26 +371,30 @@ export class FontIconBox extends DocComponent<ButtonProps>() { // </div>; setTimeout(() => this.colorPicker(curColor)); // cause an update to the color picker rendered in MainView return ( - <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")} ${this.colorPickerClosed}`} + <div + className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')} ${this.colorPickerClosed}`} style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} - onClick={action(() => this.colorPickerClosed = !this.colorPickerClosed)} + onClick={action(() => (this.colorPickerClosed = !this.colorPickerClosed))} onPointerDown={e => e.stopPropagation()}> <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> <div className="colorButton-color" style={{ backgroundColor: curColor }} /> {label} {/* {dropdownCaret} */} - {this.colorPickerClosed ? (null) : + {this.colorPickerClosed ? null : ( <div> - <div className="menuButton-dropdownBox" - onPointerDown={e => e.stopPropagation()} - onClick={e => e.stopPropagation()}> + <div className="menuButton-dropdownBox" onPointerDown={e => e.stopPropagation()} onClick={e => e.stopPropagation()}> {this.colorPicker(curColor)} </div> - <div className="dropbox-background" onPointerDown={action((e) => { - e.preventDefault(); - e.stopPropagation(); this.colorPickerClosed = true; - })} /> - </div>} + <div + className="dropbox-background" + onPointerDown={action(e => { + e.preventDefault(); + e.stopPropagation(); + this.colorPickerClosed = true; + })} + /> + </div> + )} </div> ); } @@ -382,27 +408,26 @@ export class FontIconBox extends DocComponent<ButtonProps>() { const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); // Button label - const label = !this.label || !FontIconBox.GetShowLabels() ? (null) : - <div className="fontIconBox-label" style={{ color, backgroundColor }}> - {this.label} - </div>; + const label = + !this.label || !FontIconBox.GetShowLabels() ? null : ( + <div className="fontIconBox-label" style={{ color, backgroundColor }}> + {this.label} + </div> + ); if (switchToggle) { return ( <div className={`menuButton ${this.type} ${'switch'}`}> {buttonText ? buttonText : null} <label className="switch"> - <input type="checkbox" - checked={backgroundColor === Colors.MEDIUM_BLUE} - /> + <input type="checkbox" checked={backgroundColor === Colors.MEDIUM_BLUE} /> <span className="slider round" /> </label> </div> ); } else { return ( - <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")}`} - style={{ opacity: 1, backgroundColor, color }}> + <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ opacity: 1, backgroundColor, color }}> <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> {label} </div> @@ -410,8 +435,6 @@ export class FontIconBox extends DocComponent<ButtonProps>() { } } - - /** * Default */ @@ -420,12 +443,15 @@ export class FontIconBox extends DocComponent<ButtonProps>() { const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); const active: string = StrCast(this.rootDoc.dropDownOpen); return ( - <div className={`menuButton ${this.type}`} onContextMenu={this.specificContextMenu} - style={{ backgroundColor: "transparent", borderBottomLeftRadius: this.dropdown ? 0 : undefined }}> + <div className={`menuButton ${this.type}`} onContextMenu={this.specificContextMenu} style={{ backgroundColor: 'transparent', borderBottomLeftRadius: this.dropdown ? 0 : undefined }}> <div className="menuButton-wrap"> - <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={"black"} size={"sm"} /> - {!this.label || !FontIconBox.GetShowLabels() ? (null) : - <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> {this.label} </div>} + <FontAwesomeIcon className={`menuButton-icon-${this.type}`} icon={this.icon} color={'black'} size={'sm'} /> + {!this.label || !FontIconBox.GetShowLabels() ? null : ( + <div className="fontIconBox-label" style={{ color: color, backgroundColor: backgroundColor }}> + {' '} + {this.label}{' '} + </div> + )} </div> </div> ); @@ -435,14 +461,14 @@ export class FontIconBox extends DocComponent<ButtonProps>() { // Script for running the toggle const script = ScriptCast(this.rootDoc.script); // Function to run the script - const checkResult = script?.script.run({ value: "", _readOnly_: true }).result; + const checkResult = script?.script.run({ value: '', _readOnly_: true }).result; const setValue = (value: string, shiftDown?: boolean): boolean => script?.script.run({ value, _readOnly_: false }).result; return ( <div className="menuButton editableText"> - <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={"lock"} /> - <div style={{ width: "calc(100% - .875em)", paddingLeft: "4px" }}> - <EditableView GetValue={() => script?.script.run({ value: "", _readOnly_: true }).result} SetValue={setValue} contents={checkResult} /> + <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={'lock'} /> + <div style={{ width: 'calc(100% - .875em)', paddingLeft: '4px' }}> + <EditableView GetValue={() => script?.script.run({ value: '', _readOnly_: true }).result} SetValue={setValue} contents={checkResult} /> </div> </div> ); @@ -451,15 +477,19 @@ export class FontIconBox extends DocComponent<ButtonProps>() { render() { const color = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.Color); const backgroundColor = this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.BackgroundColor); - const label = !this.label || !FontIconBox.GetShowLabels() ? (null) : - <div className="fontIconBox-label" style={{ color, backgroundColor }}> - {this.label} - </div>; + const label = + !this.label || !FontIconBox.GetShowLabels() ? null : ( + <div className="fontIconBox-label" style={{ color, backgroundColor }}> + {this.label} + </div> + ); - const menuLabel = !this.label || !FontIconBox.GetShowLabels() ? (null) : - <div className="fontIconBox-label" style={{ color, backgroundColor: "transparent" }}> - {this.label} - </div>; + const menuLabel = + !this.label || !FontIconBox.GetShowLabels() ? null : ( + <div className="fontIconBox-label" style={{ color, backgroundColor: 'transparent' }}> + {this.label} + </div> + ); const buttonText = StrCast(this.rootDoc.buttonText); @@ -471,11 +501,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { button = ( <div className={`menuButton ${this.type}`} style={{ color, backgroundColor, opacity: 1, gridAutoColumns: `${NumCast(this.rootDoc._height)} auto` }}> <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> - {buttonText ? - <div className="button-text"> - {buttonText} - </div> - : null} + {buttonText ? <div className="button-text">{buttonText}</div> : null} {label} </div> ); @@ -498,7 +524,7 @@ export class FontIconBox extends DocComponent<ButtonProps>() { break; case ButtonType.ToolButton: button = ( - <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")}`} style={{ opacity: 1, backgroundColor, color }}> + <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ opacity: 1, backgroundColor, color }}> <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> {label} </div> @@ -510,48 +536,45 @@ export class FontIconBox extends DocComponent<ButtonProps>() { break; case ButtonType.ClickButton: button = ( - <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? "Label" : "")}`} style={{ color, backgroundColor, opacity: 1 }}> + <div className={`menuButton ${this.type + (FontIconBox.GetShowLabels() ? 'Label' : '')}`} style={{ color, backgroundColor, opacity: 1 }}> <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} /> {label} </div> ); break; case ButtonType.MenuButton: - const trailsIcon = <img src={`/assets/${"presTrails.png"}`} - style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? "0%" : "100%"})` }} />; + const trailsIcon = <img src={`/assets/${'presTrails.png'}`} style={{ width: 30, height: 30, filter: `invert(${color === Colors.DARK_GRAY ? '0%' : '100%'})` }} />; button = ( <div className={`menuButton ${this.type}`} style={{ color, backgroundColor }}> - {this.icon === "pres-trail" ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />} + {this.icon === 'pres-trail' ? trailsIcon : <FontAwesomeIcon className={`fontIconBox-icon-${this.type}`} icon={this.icon} color={color} />} {menuLabel} - <FontIconBadge value={Cast(this.Document.badgeValue,"string",null)} /> - </div > + <FontIconBadge value={Cast(this.Document.badgeValue, 'string', null)} /> + </div> ); break; default: break; } - return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? button : - button !== null ? - <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}> - {button} - </Tooltip > : null + return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? ( + button + ) : button !== null ? ( + <Tooltip title={<div className="dash-tooltip">{StrCast(this.layoutDoc.toolTip)}</div>}>{button}</Tooltip> + ) : null; } } - // toggle: Set overlay status of selected document ScriptingGlobals.add(function setView(view: string) { const selected = SelectionManager.Docs().lastElement(); - selected ? selected._viewType = view : console.log("[FontIconBox.tsx] changeView failed"); + selected ? (selected._viewType = view) : console.log('[FontIconBox.tsx] changeView failed'); }); - // toggle: Set overlay status of selected document ScriptingGlobals.add(function setBackgroundColor(color?: string, checkResult?: boolean) { const selected = SelectionManager.Docs().lastElement(); if (checkResult) { - return selected?._backgroundColor ?? "transparent"; + return selected?._backgroundColor ?? 'transparent'; } if (selected) selected._backgroundColor = color; }); @@ -563,7 +586,7 @@ ScriptingGlobals.add(function setHeaderColor(color?: string, checkResult?: boole } Doc.SharingDoc().userColor = undefined; Doc.GetProto(Doc.SharingDoc()).userColor = color; - Doc.UserDoc().showTitle = color === "transparent" ? undefined : StrCast(Doc.UserDoc().showTitle, "creationDate"); + Doc.UserDoc().showTitle = color === 'transparent' ? undefined : StrCast(Doc.UserDoc().showTitle, 'creationDate'); }); // toggle: Set overlay status of selected document @@ -571,9 +594,9 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { const selected = SelectionManager.Views().length ? SelectionManager.Views()[0] : undefined; if (checkResult) { if (NumCast(selected?.Document.z) >= 1) return Colors.MEDIUM_BLUE; - return "transparent"; + return 'transparent'; } - selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log("[FontIconBox.tsx] toggleOverlay failed"); + selected ? selected.props.CollectionFreeFormDocumentView?.().float() : console.log('[FontIconBox.tsx] toggleOverlay failed'); }); /** TEXT @@ -590,7 +613,7 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { // toggle: Set overlay status of selected document ScriptingGlobals.add(function setFont(font: string, checkResult?: boolean) { - SelectionManager.Docs().map(doc => doc._fontFamily = font); + SelectionManager.Docs().map(doc => (doc._fontFamily = font)); const editorView = RichTextMenu.Instance.TextView?.EditorView; if (checkResult) { return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); @@ -599,32 +622,36 @@ ScriptingGlobals.add(function setFont(font: string, checkResult?: boolean) { else Doc.UserDoc().fontFamily = font; }); -ScriptingGlobals.add(function getActiveTextInfo(info: "family" | "size" | "color" | "highlight") { +ScriptingGlobals.add(function getActiveTextInfo(info: 'family' | 'size' | 'color' | 'highlight') { const editorView = RichTextMenu.Instance.TextView?.EditorView; const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection(); switch (info) { - case "family": return style?.activeFamilies[0]; - case "size": return style?.activeSizes[0]; - case "color": return style?.activeColors[0]; - case "highlight": return style?.activeHighlights[0]; + case 'family': + return style?.activeFamilies[0]; + case 'size': + return style?.activeSizes[0]; + case 'color': + return style?.activeColors[0]; + case 'highlight': + return style?.activeHighlights[0]; } }); -ScriptingGlobals.add(function setAlignment(align: "left" | "right" | "center", checkResult?: boolean) { +ScriptingGlobals.add(function setAlignment(align: 'left' | 'right' | 'center', checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { - return (editorView ? RichTextMenu.Instance.textAlign : Doc.UserDoc().textAlign) === align ? Colors.MEDIUM_BLUE : "transparent"; + return (editorView ? RichTextMenu.Instance.textAlign : Doc.UserDoc().textAlign) === align ? Colors.MEDIUM_BLUE : 'transparent'; } if (editorView?.state) RichTextMenu.Instance.align(editorView, editorView.dispatch, align); else Doc.UserDoc().textAlign = align; }); -ScriptingGlobals.add(function setBulletList(mapStyle: "bullet" | "decimal", checkResult?: boolean) { +ScriptingGlobals.add(function setBulletList(mapStyle: 'bullet' | 'decimal', checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { const active = editorView?.state && RichTextMenu.Instance.getActiveListStyle(); if (active === mapStyle) return Colors.MEDIUM_BLUE; - return "transparent"; + return 'transparent'; } editorView?.state && RichTextMenu.Instance.changeListType(mapStyle); }); @@ -662,17 +689,17 @@ ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boo ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { - return RichTextMenu.Instance.fontSize.replace("px", ""); + return RichTextMenu.Instance.fontSize.replace('px', ''); } - if (typeof size === "number") size = size.toString(); - if (size && Number(size).toString() === size) size += "px"; + if (typeof size === 'number') size = size.toString(); + if (size && Number(size).toString() === size) size += 'px'; if (editorView) RichTextMenu.Instance.setFontSize(size); else Doc.UserDoc()._fontSize = size; }); ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { - return (editorView ? RichTextMenu.Instance.noAutoLink : false) ? Colors.MEDIUM_BLUE : "transparent"; + return (editorView ? RichTextMenu.Instance.noAutoLink : false) ? Colors.MEDIUM_BLUE : 'transparent'; } if (editorView) RichTextMenu.Instance?.toggleNoAutoLinkAnchor(); }); @@ -680,31 +707,30 @@ ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) { ScriptingGlobals.add(function toggleBold(checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { - return (editorView ? RichTextMenu.Instance.bold : Doc.UserDoc().fontWeight === "bold") ? Colors.MEDIUM_BLUE : "transparent"; + return (editorView ? RichTextMenu.Instance.bold : Doc.UserDoc().fontWeight === 'bold') ? Colors.MEDIUM_BLUE : 'transparent'; } if (editorView) RichTextMenu.Instance?.toggleBold(); - else Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === "bold" ? undefined : "bold"; + else Doc.UserDoc().fontWeight = Doc.UserDoc().fontWeight === 'bold' ? undefined : 'bold'; }); ScriptingGlobals.add(function toggleUnderline(checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { - return (editorView ? RichTextMenu.Instance.underline : Doc.UserDoc().textDecoration === "underline") ? Colors.MEDIUM_BLUE : "transparent"; + return (editorView ? RichTextMenu.Instance.underline : Doc.UserDoc().textDecoration === 'underline') ? Colors.MEDIUM_BLUE : 'transparent'; } if (editorView) RichTextMenu.Instance?.toggleUnderline(); - else Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === "underline" ? undefined : "underline"; + else Doc.UserDoc().textDecoration = Doc.UserDoc().textDecoration === 'underline' ? undefined : 'underline'; }); ScriptingGlobals.add(function toggleItalic(checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { - return (editorView ? RichTextMenu.Instance.italics : Doc.UserDoc().fontStyle === "italics") ? Colors.MEDIUM_BLUE : "transparent"; + return (editorView ? RichTextMenu.Instance.italics : Doc.UserDoc().fontStyle === 'italics') ? Colors.MEDIUM_BLUE : 'transparent'; } if (editorView) RichTextMenu.Instance?.toggleItalics(); - else Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === "italics" ? undefined : "italics"; + else Doc.UserDoc().fontStyle = Doc.UserDoc().fontStyle === 'italics' ? undefined : 'italics'; }); - export function checkInksToGroup() { // console.log("getting here to inks group"); if (CurrentUserUtils.ActiveTool === InkTool.Write) { @@ -725,15 +751,17 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) { // TODO: nda - will probably want to go through ffView unprocessed docs and then see if any of the inksToGroup docs are in it and only use those const selected = ffView.unprocessedDocs; // loop through selected an get the bound - const bounds: { x: number, y: number, width?: number, height?: number }[] = [] - - selected.map(action(d => { - const x = NumCast(d.x); - const y = NumCast(d.y); - const width = d[WidthSym](); - const height = d[HeightSym](); - bounds.push({ x, y, width, height }); - })) + const bounds: { x: number; y: number; width?: number; height?: number }[] = []; + + selected.map( + action(d => { + const x = NumCast(d.x); + const y = NumCast(d.y); + const width = d[WidthSym](); + const height = d[HeightSym](); + bounds.push({ x, y, width, height }); + }) + ); const aggregBounds = aggregateBounds(bounds, 0, 0); const marqViewRef = ffView._marqueeViewRef.current; @@ -746,21 +774,23 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) { marqViewRef._lastY = aggregBounds.b; } - selected.map(action(d => { - const dx = NumCast(d.x); - const dy = NumCast(d.y); - delete d.x; - delete d.y; - delete d.activeFrame; - delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection - // calculate pos based on bounds - if (marqViewRef?.Bounds) { - d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2; - d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2; - } - return d; - })); + selected.map( + action(d => { + const dx = NumCast(d.x); + const dy = NumCast(d.y); + delete d.x; + delete d.y; + delete d.activeFrame; + delete d._timecodeToShow; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + delete d._timecodeToHide; // bcz: this should be automatic somehow.. along with any other properties that were logically associated with the original collection + // calculate pos based on bounds + if (marqViewRef?.Bounds) { + d.x = dx - marqViewRef.Bounds.left - marqViewRef.Bounds.width / 2; + d.y = dy - marqViewRef.Bounds.top - marqViewRef.Bounds.height / 2; + } + return d; + }) + ); ffView.props.removeDocument?.(selected); // TODO: nda - this is the code to actually get a new grouped collection const newCollection = marqViewRef?.getCollection(selected, undefined, true); @@ -774,13 +804,12 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) { // TODO: nda - will probably need to go through and only remove the unprocessed selected docs ffView.unprocessedDocs = []; - InkTranscription.Instance.transcribeInk(newCollection, selected, false, ffView); + InkTranscription.Instance.transcribeInk(newCollection, selected, false); }); } CollectionFreeFormView.collectionsWithUnprocessedInk.clear(); } - /** INK * setActiveTool * setStrokeWidth @@ -790,10 +819,9 @@ export function createInkGroup(inksToGroup?: Doc[], isSubGroup?: boolean) { ScriptingGlobals.add(function setActiveTool(tool: string, checkResult?: boolean) { InkTranscription.Instance?.createInkGroup(); if (checkResult) { - return ((CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool) ? - Colors.MEDIUM_BLUE : "transparent"; + return (CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance?.InkShape) || GestureOverlay.Instance?.InkShape === tool ? Colors.MEDIUM_BLUE : 'transparent'; } - if (["circle", "square", "line"].includes(tool)) { + if (['circle', 'square', 'line'].includes(tool)) { if (GestureOverlay.Instance.InkShape === tool) { CurrentUserUtils.ActiveTool = InkTool.None; GestureOverlay.Instance.InkShape = InkTool.None; @@ -801,16 +829,17 @@ ScriptingGlobals.add(function setActiveTool(tool: string, checkResult?: boolean) CurrentUserUtils.ActiveTool = InkTool.Pen; GestureOverlay.Instance.InkShape = tool; } - } else if (tool) { // pen or eraser + } else if (tool) { + // pen or eraser if (CurrentUserUtils.ActiveTool === tool && !GestureOverlay.Instance.InkShape) { CurrentUserUtils.ActiveTool = InkTool.None; } else if (tool == InkTool.Write) { // console.log("write mode selected - create groupDoc here!", tool) CurrentUserUtils.ActiveTool = tool; - GestureOverlay.Instance.InkShape = ""; + GestureOverlay.Instance.InkShape = ''; } else { CurrentUserUtils.ActiveTool = tool as any; - GestureOverlay.Instance.InkShape = ""; + GestureOverlay.Instance.InkShape = ''; } } else { CurrentUserUtils.ActiveTool = InkTool.None; @@ -827,7 +856,9 @@ ScriptingGlobals.add(function setFillColor(color?: string, checkResult?: boolean return ActiveFillColor(); } SetActiveFillColor(StrCast(color)); - SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.fillColor = color); + SelectionManager.Docs() + .filter(doc => doc.type === DocumentType.INK) + .map(doc => (doc.fillColor = color)); }); ScriptingGlobals.add(function setStrokeWidth(width: number, checkResult?: boolean) { @@ -839,7 +870,9 @@ ScriptingGlobals.add(function setStrokeWidth(width: number, checkResult?: boolea return ActiveInkWidth(); } SetActiveInkWidth(width.toString()); - SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.strokeWidth = Number(width)); + SelectionManager.Docs() + .filter(doc => doc.type === DocumentType.INK) + .map(doc => (doc.strokeWidth = Number(width))); }); // toggle: Set overlay status of selected document @@ -852,10 +885,11 @@ ScriptingGlobals.add(function setStrokeColor(color?: string, checkResult?: boole return ActiveInkColor(); } SetActiveInkColor(StrCast(color)); - SelectionManager.Docs().filter(doc => doc.type === DocumentType.INK).map(doc => doc.color = String(color)); + SelectionManager.Docs() + .filter(doc => doc.type === DocumentType.INK) + .map(doc => (doc.color = String(color))); }); - /** WEB * webSetURL **/ @@ -870,21 +904,20 @@ ScriptingGlobals.add(function webSetURL(url: string, checkResult?: boolean) { } }); ScriptingGlobals.add(function webForward(checkResult?: boolean) { - const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox); + const selected = SelectionManager.Views().lastElement()?.ComponentView as WebBox; if (checkResult) { - return selected?.forward(checkResult) ? undefined : "lightGray"; + return selected?.forward(checkResult) ? undefined : 'lightGray'; } selected?.forward(); }); ScriptingGlobals.add(function webBack(checkResult?: boolean) { - const selected = (SelectionManager.Views().lastElement()?.ComponentView as WebBox); + const selected = SelectionManager.Views().lastElement()?.ComponentView as WebBox; if (checkResult) { - return selected?.back(checkResult) ? undefined : "lightGray"; + return selected?.back(checkResult) ? undefined : 'lightGray'; } selected?.back(); }); - /** Schema * toggleSchemaPreview **/ @@ -893,9 +926,8 @@ ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) { if (checkResult && selected) { const result: boolean = NumCast(selected.schemaPreviewWidth) > 0; if (result) return Colors.MEDIUM_BLUE; - else return "transparent"; - } - else if (selected) { + else return 'transparent'; + } else if (selected) { if (NumCast(selected.schemaPreviewWidth) > 0) { selected.schemaPreviewWidth = 0; } else { @@ -908,11 +940,11 @@ ScriptingGlobals.add(function toggleSchemaPreview(checkResult?: boolean) { * groupBy */ ScriptingGlobals.add(function setGroupBy(key: string, checkResult?: boolean) { - SelectionManager.Docs().map(doc => doc._fontFamily = key); + SelectionManager.Docs().map(doc => (doc._fontFamily = key)); const editorView = RichTextMenu.Instance.TextView?.EditorView; if (checkResult) { return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); } if (editorView) RichTextMenu.Instance.setFontFamily(key); else Doc.UserDoc().fontFamily = key; -});
\ No newline at end of file +}); |