From a5bf418707321711559782f0348063681c553cbb Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Wed, 10 Aug 2022 16:11:23 -0400 Subject: added placeholder text box for files being uploaded --- src/client/views/collections/CollectionSubView.tsx | 43 +++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 5479929bd..35a14704d 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -448,6 +448,46 @@ export function CollectionSubView(moreProps?: X) { } this.slowLoadDocuments(files, options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end); } + + /** + * Creates a placeholder doc view for files being uploaded and removes placeholder docs once files are uplodaded. + * + * @param files the files to upload that we want to create placeholders for + * @param options the document options (primarily the x and y coordinates to put doc) + * @param text in the case of youtube the text is the url to the video + * @returns a disposer action that removes the placeholders created after files get uploaded + */ + placeHolderDisposer = (files: File[] | string, options: DocumentOptions, text: string) => { + // TODO: nda - create a specialized view for placeholder upload with a spinner and ability to retry upload + let placeholders: Doc[] = []; + // handle yt case + if (typeof files === 'string') { + placeholders.push(Docs.Create.TextDocument('Loading: ' + text, { ...options, title: text, _width: 500, _height: 200 })); + } else { + // every other doc type is an array of File + + // Get the file names as a text + let textStr = ''; + files.forEach(file => { + textStr += file.name + '\n'; + }); + placeholders.push(Docs.Create.TextDocument('Loading: \n' + textStr, { ...options, title: files.length + ' files', _width: 500, _height: files.length * 40 })); + } + // disposer action to remove placeholders once files are uploaded + const remove = action(() => { + if (!this.props.DataDoc) { + return; + } + for (let i = 0; i < placeholders.length; i++) { + Doc.RemoveDocFromList(this.props.DataDoc, 'data', placeholders[i]); + } + }); + placeholders.forEach(pl => { + this.addDocument(pl); + }); + return remove; + }; + slowLoadDocuments = async ( files: File[] | string, options: DocumentOptions, @@ -458,7 +498,8 @@ export function CollectionSubView(moreProps?: X) { clientY: number, addDocument: (doc: Doc | Doc[]) => boolean ) => { - const disposer = OverlayView.Instance.addElement(, { x: clientX - 125, y: clientY - 125 }); + const disposer = this.placeHolderDisposer(files, options, text); + // const disposer = OverlayView.Instance.addElement(, { x: clientX - 125, y: clientY - 125 }); if (typeof files === 'string') { generatedDocuments.push(...(await DocUtils.uploadYoutubeVideo(files, options))); } else { -- cgit v1.2.3-70-g09d2 From 6c349731fb84f1c44993d2bc55410da19db29a07 Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Wed, 10 Aug 2022 16:16:00 -0400 Subject: added placeholder text box for files being uploaded --- src/client/views/collections/CollectionSubView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 35a14704d..2f3f57bac 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -462,7 +462,7 @@ export function CollectionSubView(moreProps?: X) { let placeholders: Doc[] = []; // handle yt case if (typeof files === 'string') { - placeholders.push(Docs.Create.TextDocument('Loading: ' + text, { ...options, title: text, _width: 500, _height: 200 })); + placeholders.push(Docs.Create.TextDocument('Loading: ' + text, { ...options, title: text, _width: 400, _height: 30 })); } else { // every other doc type is an array of File -- cgit v1.2.3-70-g09d2 From 820d40eea4eeb5977889e0ef6c35f9092df44b4b Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Wed, 10 Aug 2022 17:08:47 -0400 Subject: created placeholder loading box but have to get location of final doc to update with loading box --- src/client/documents/DocumentTypes.ts | 3 ++- src/client/documents/Documents.ts | 18 +++++++++++++ src/client/views/collections/CollectionSubView.tsx | 8 ++++-- src/client/views/nodes/DocumentContentsView.tsx | 2 ++ src/client/views/nodes/LoadingBox.scss | 7 +++++ src/client/views/nodes/LoadingBox.tsx | 31 ++++++++++++++++++++++ 6 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 src/client/views/nodes/LoadingBox.scss create mode 100644 src/client/views/nodes/LoadingBox.tsx (limited to 'src/client/views') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 9dfadf778..d99cd2dac 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -26,6 +26,7 @@ export enum DocumentType { FUNCPLOT = 'funcplot', // function plotter MAP = 'map', DATAVIZ = 'dataviz', + LOADING = 'loading', // special purpose wrappers that either take no data or are compositions of lower level types LINK = 'link', @@ -63,5 +64,5 @@ export enum CollectionViewType { Grid = 'grid', Pile = 'pileup', StackedTimeline = 'stacked timeline', - NoteTaking = "notetaking" + NoteTaking = 'notetaking', } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index c7ea04839..6ccb4358a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -48,6 +48,7 @@ import { KeyValueBox } from '../views/nodes/KeyValueBox'; import { LabelBox } from '../views/nodes/LabelBox'; import { LinkBox } from '../views/nodes/LinkBox'; import { LinkDescriptionPopup } from '../views/nodes/LinkDescriptionPopup'; +import { LoadingBox } from '../views/nodes/LoadingBox'; import { MapBox } from '../views/nodes/MapBox/MapBox'; import { PDFBox } from '../views/nodes/PDFBox'; import { RecordingBox } from '../views/nodes/RecordingBox/RecordingBox'; @@ -648,6 +649,13 @@ export namespace Docs { options: { _fitWidth: true, nativeDimModifiable: true, links: '@links(self)' }, }, ], + [ + DocumentType.LOADING, + { + layout: { view: LoadingBox, dataField: defaultDataKey }, + options: { _fitWidth: true, _fitHeight: true, nativeDimModifiable: true, links: '@links(self)' }, + }, + ], ]); const suffix = 'Proto'; @@ -875,6 +883,16 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COLOR), '', options); } + export function LoadingDocument(title: string, text: string, width?: number, height?: number, options: DocumentOptions = {}) { + let myWidth = 300; + let myHeight = 300; + if (height && width) { + myWidth = width; + myHeight = height; + } + return InstanceFromProto(Prototypes.get(DocumentType.LOADING), '', { ...options, title, text, _width: myWidth, _height: myHeight }); + } + export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') { return InstanceFromProto(Prototypes.get(DocumentType.RTF), field, options, undefined, fieldKey); } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 2f3f57bac..79f629072 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -462,7 +462,8 @@ export function CollectionSubView(moreProps?: X) { let placeholders: Doc[] = []; // handle yt case if (typeof files === 'string') { - placeholders.push(Docs.Create.TextDocument('Loading: ' + text, { ...options, title: text, _width: 400, _height: 30 })); + placeholders.push(Docs.Create.LoadingDocument('Loading...', text, 500, 500, { ...options })); + // placeholders.push(Docs.Create.TextDocument('Loading: ' + text, { ...options, title: text, _width: 400, _height: 30 })); } else { // every other doc type is an array of File @@ -471,7 +472,9 @@ export function CollectionSubView(moreProps?: X) { files.forEach(file => { textStr += file.name + '\n'; }); - placeholders.push(Docs.Create.TextDocument('Loading: \n' + textStr, { ...options, title: files.length + ' files', _width: 500, _height: files.length * 40 })); + placeholders.push(Docs.Create.LoadingDocument('Loading...', textStr, 500, 500, { ...options })); + + // placeholders.push(Docs.Create.TextDocument('Loading: \n' + textStr, { ...options, title: files.length + ' files', _width: 500, _height: files.length * 40 })); } // disposer action to remove placeholders once files are uploaded const remove = action(() => { @@ -498,6 +501,7 @@ export function CollectionSubView(moreProps?: X) { clientY: number, addDocument: (doc: Doc | Doc[]) => boolean ) => { + // TODO: once loading thing is moved it should update the x and y of the file it is placeholder for const disposer = this.placeHolderDisposer(files, options, text); // const disposer = OverlayView.Instance.addElement(, { x: clientX - 125, y: clientY - 125 }); if (typeof files === 'string') { diff --git a/src/client/views/nodes/DocumentContentsView.tsx b/src/client/views/nodes/DocumentContentsView.tsx index 381436a56..4d4985f2a 100644 --- a/src/client/views/nodes/DocumentContentsView.tsx +++ b/src/client/views/nodes/DocumentContentsView.tsx @@ -43,6 +43,7 @@ import { VideoBox } from './VideoBox'; import { WebBox } from './WebBox'; import React = require('react'); import XRegExp = require('xregexp'); +import { LoadingBox } from './LoadingBox'; const JsxParser = require('react-jsx-parser').default; //TODO Why does this need to be imported like this? @@ -267,6 +268,7 @@ export class DocumentContentsView extends React.Component< DataVizBox, HTMLtag, ComparisonBox, + LoadingBox, }} bindings={bindings} jsx={layoutFrame} diff --git a/src/client/views/nodes/LoadingBox.scss b/src/client/views/nodes/LoadingBox.scss new file mode 100644 index 000000000..239faa78e --- /dev/null +++ b/src/client/views/nodes/LoadingBox.scss @@ -0,0 +1,7 @@ +.loadingBoxContainer { + display: flex; + flex-direction: column; + align-content: center; + justify-content: center; + background-color: #fdfdfd; +} diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx new file mode 100644 index 000000000..0e0619241 --- /dev/null +++ b/src/client/views/nodes/LoadingBox.tsx @@ -0,0 +1,31 @@ +import { observer } from 'mobx-react'; +import { ViewBoxAnnotatableComponent } from '../DocComponent'; +import { FieldView, FieldViewProps } from './FieldView'; +import * as React from 'react'; +import './LoadingBox.scss'; +import ReactLoading from 'react-loading'; + +export interface LoadingBoxProps { + title: string; + text: string; +} + +@observer +export class LoadingBox extends ViewBoxAnnotatableComponent() { + public static LayoutString(fieldKey: string) { + return FieldView.LayoutString(LoadingBox, fieldKey); + } + + constructor(props: any) { + super(props); + } + + render() { + return ( +
+ Loading: {this.dataDoc.text} + +
+ ); + } +} -- cgit v1.2.3-70-g09d2 From bb2b38b0e47eaf8e64554d101b605bf35a178239 Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Tue, 16 Aug 2022 16:43:01 -0400 Subject: updated placeholder --- src/.DS_Store | Bin 10244 -> 10244 bytes src/client/documents/Documents.ts | 63 +++++++++++++------ src/client/util/CurrentUserUtils.ts | 1 + src/client/views/collections/CollectionSubView.tsx | 70 +++++++-------------- src/client/views/nodes/LoadingBox.tsx | 25 ++++++++ 5 files changed, 94 insertions(+), 65 deletions(-) (limited to 'src/client/views') diff --git a/src/.DS_Store b/src/.DS_Store index 4751acf44..4ed785983 100644 Binary files a/src/.DS_Store and b/src/.DS_Store differ diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 6ccb4358a..d8497e3af 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,4 +1,5 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; +import { files } from 'jszip'; import { action, runInAction } from 'mobx'; import { basename } from 'path'; import { DateField } from '../../fields/DateField'; @@ -794,7 +795,7 @@ export namespace Docs { * only when creating a DockDocument from the current user's already existing * main document. */ - function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string) { + function InstanceFromProto(proto: Doc, data: Field | undefined, options: DocumentOptions, delegId?: string, fieldKey: string = 'data', protoId?: string, placeholderDoc?: Doc) { const viewKeys = ['x', 'y', 'system']; // keys that should be addded to the view document even though they don't begin with an "_" const { omit: dataProps, extract: viewProps } = OmitKeys(options, viewKeys, '^_'); @@ -813,13 +814,22 @@ export namespace Docs { // without this, if a doc has no annotations but the user has AddOnly privileges, they won't be able to add an annotation because they would have needed to create the field's list which they don't have permissions to do. dataProps[fieldKey + '-annotations'] = new List(); dataProps[fieldKey + '-sidebar'] = new List(); - const dataDoc = Doc.assign(Doc.MakeDelegate(proto, protoId), dataProps, undefined, true); + + const dataDoc = Doc.assign(placeholderDoc ? Doc.GetProto(placeholderDoc) : Doc.MakeDelegate(proto, protoId), dataProps, undefined, true); + + if (placeholderDoc) { + dataDoc.proto = proto; + } const viewFirstProps: { [id: string]: any } = {}; viewFirstProps['acl-Public'] = options['_acl-Public'] ? options['_acl-Public'] : Doc.defaultAclPrivate ? SharingPermissions.None : SharingPermissions.Augment; viewFirstProps['acl-Override'] = 'None'; viewFirstProps.author = Doc.CurrentUserEmail; - const viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true); + let viewDoc: Doc; + if (placeholderDoc) { + viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true); + } + viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true); Doc.assign(viewDoc, viewProps, true, true); ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc); @@ -883,14 +893,12 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COLOR), '', options); } - export function LoadingDocument(title: string, text: string, width?: number, height?: number, options: DocumentOptions = {}) { - let myWidth = 300; - let myHeight = 300; - if (height && width) { - myWidth = width; - myHeight = height; - } - return InstanceFromProto(Prototypes.get(DocumentType.LOADING), '', { ...options, title, text, _width: myWidth, _height: myHeight }); + export const filesToDocs = new Map(); + + export function LoadingDocument(file: File, options: DocumentOptions, ytString?: string) { + const loading = InstanceFromProto(Prototypes.get(DocumentType.LOADING), file.name, { _height: 300, _width: 300, ...options }); + // filesToDocs.set(loading, file); + return loading; } export function RTFDocument(field: RichTextField, options: DocumentOptions = {}, fieldKey: string = 'text') { @@ -969,13 +977,13 @@ export namespace Docs { return I; } - export function PdfDocument(url: string, options: DocumentOptions = {}) { + export function PdfDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) { const width = options._width || undefined; const height = options._height || undefined; const nwid = options._nativeWidth || undefined; const nhght = options._nativeHeight || undefined; if (!nhght && width && height && nwid) options._nativeHeight = (Number(nwid) * Number(height)) / Number(width); - return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(url), options); + return InstanceFromProto(Prototypes.get(DocumentType.PDF), new PdfField(url), options, undefined, undefined, undefined, overwriteDoc); } export function WebDocument(url: string, options: DocumentOptions = {}) { @@ -1462,8 +1470,8 @@ export namespace DocUtils { return created; } - export async function DocumentFromType(type: string, path: string, options: DocumentOptions): Promise> { - let ctor: ((path: string, options: DocumentOptions) => Doc | Promise) | undefined = undefined; + export async function DocumentFromType(type: string, path: string, options: DocumentOptions, rootDoc?: Doc): Promise> { + let ctor: ((path: string, options: DocumentOptions, overwriteDoc?: Doc) => Doc | Promise) | undefined = undefined; if (type.indexOf('image') !== -1) { ctor = Docs.Create.ImageDocument; if (!options._width) options._width = 300; @@ -1512,7 +1520,7 @@ export namespace DocUtils { options = { ...options, _width: 400, _height: 512, title: path }; } - return ctor ? ctor(path, options) : undefined; + return ctor ? ctor(path, options, rootDoc) : undefined; } export function addDocumentCreatorMenuItems(docTextAdder: (d: Doc) => void, docAdder: (d: Doc) => void, x: number, y: number, simpleMenu: boolean = false, pivotField?: string, pivotValue?: string): void { @@ -1728,14 +1736,14 @@ export namespace DocUtils { return dd; } - async function processFileupload(generatedDocuments: Doc[], name: string, type: string, result: Error | Upload.FileInformation, options: DocumentOptions) { + async function processFileupload(generatedDocuments: Doc[], name: string, type: string, result: Error | Upload.FileInformation, options: DocumentOptions, rootDoc?: Doc) { if (result instanceof Error) { alert(`Upload failed: ${result.message}`); return; } const full = { ...options, _width: 400, title: name }; const pathname = Utils.prepend(result.accessPaths.agnostic.client); - const doc = await DocUtils.DocumentFromType(type, pathname, full); + const doc = await DocUtils.DocumentFromType(type, pathname, full, rootDoc); if (doc) { const proto = Doc.GetProto(doc); proto.text = result.rawText; @@ -1813,6 +1821,25 @@ export namespace DocUtils { return generatedDocuments; } + export function uploadFileToDoc(file: File, options: DocumentOptions, overwriteDoc: Doc) { + const generatedDocuments: Doc[] = []; + Networking.UploadFilesToServer([file]).then(upfiles => { + const { + source: { name, type }, + result, + } = upfiles.lastElement(); + console.log(name, type); + name && type && processFileupload(generatedDocuments, name, type, result, options, overwriteDoc); + }); + } + + export function generatePlaceHolder(file: File, options: DocumentOptions) { + return Docs.Create.LoadingDocument(file, options); + // placeholder.file = file + // TODO: nda - modify loading doc so it only takes in options + // Docs.Create.LoadingDocument(options, ) + } + // copies the specified drag factory document export function copyDragFactory(dragFactory: Doc) { if (!dragFactory) return undefined; diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index d19874720..492513a61 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -196,6 +196,7 @@ export class CurrentUserUtils { makeIconTemplate(DocumentType.RTF, "text", { iconTemplate:DocumentType.LABEL, _showTitle: "creationDate"}), makeIconTemplate(DocumentType.IMG, "data", { iconTemplate:DocumentType.IMG, _height: undefined}), makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}), + makeIconTemplate(DocumentType.COL, "icon", { iconTemplate:DocumentType.IMG}), makeIconTemplate(DocumentType.VID, "icon", { iconTemplate:DocumentType.IMG}), makeIconTemplate(DocumentType.BUTTON,"data", { iconTemplate:DocumentType.FONTICON}), //nasty hack .. templates are looked up exclusively by type -- but we want a template for a document with a certain field (transcription) .. so this hack and the companion hack in createCustomView does this for now diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 79f629072..1ff4f5ab8 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -449,48 +449,6 @@ export function CollectionSubView(moreProps?: X) { this.slowLoadDocuments(files, options, generatedDocuments, text, completed, e.clientX, e.clientY, addDocument).then(batch.end); } - /** - * Creates a placeholder doc view for files being uploaded and removes placeholder docs once files are uplodaded. - * - * @param files the files to upload that we want to create placeholders for - * @param options the document options (primarily the x and y coordinates to put doc) - * @param text in the case of youtube the text is the url to the video - * @returns a disposer action that removes the placeholders created after files get uploaded - */ - placeHolderDisposer = (files: File[] | string, options: DocumentOptions, text: string) => { - // TODO: nda - create a specialized view for placeholder upload with a spinner and ability to retry upload - let placeholders: Doc[] = []; - // handle yt case - if (typeof files === 'string') { - placeholders.push(Docs.Create.LoadingDocument('Loading...', text, 500, 500, { ...options })); - // placeholders.push(Docs.Create.TextDocument('Loading: ' + text, { ...options, title: text, _width: 400, _height: 30 })); - } else { - // every other doc type is an array of File - - // Get the file names as a text - let textStr = ''; - files.forEach(file => { - textStr += file.name + '\n'; - }); - placeholders.push(Docs.Create.LoadingDocument('Loading...', textStr, 500, 500, { ...options })); - - // placeholders.push(Docs.Create.TextDocument('Loading: \n' + textStr, { ...options, title: files.length + ' files', _width: 500, _height: files.length * 40 })); - } - // disposer action to remove placeholders once files are uploaded - const remove = action(() => { - if (!this.props.DataDoc) { - return; - } - for (let i = 0; i < placeholders.length; i++) { - Doc.RemoveDocFromList(this.props.DataDoc, 'data', placeholders[i]); - } - }); - placeholders.forEach(pl => { - this.addDocument(pl); - }); - return remove; - }; - slowLoadDocuments = async ( files: File[] | string, options: DocumentOptions, @@ -501,13 +459,31 @@ export function CollectionSubView(moreProps?: X) { clientY: number, addDocument: (doc: Doc | Doc[]) => boolean ) => { + // create placeholder docs + // inside placeholder docs have some func that + // TODO: once loading thing is moved it should update the x and y of the file it is placeholder for - const disposer = this.placeHolderDisposer(files, options, text); + let pileUpDoc = undefined; // const disposer = OverlayView.Instance.addElement(, { x: clientX - 125, y: clientY - 125 }); if (typeof files === 'string') { - generatedDocuments.push(...(await DocUtils.uploadYoutubeVideo(files, options))); + // uploadYoutubeVideo and similar should return a placeholder, one for each placeholder + // generatedDocuments.push(Docs.Create.LoadingDocument(files, options)); } else { - generatedDocuments.push(...(await DocUtils.uploadFilesToDocs(files, options))); + // uploadFilesToDocs and similar should return a placeholder, one for each placeholder + generatedDocuments.push( + ...files.map(file => { + const loading = Docs.Create.LoadingDocument(file, options); + // now that there is a doc do whatever slowload was going to do with that file + if (typeof file === 'string') { + // uploadYoutubeVideo and similar should return a placeholder, one for each placeholder + // (await DocUtils.uploadYoutubeVideo(files, options))); + } else { + // uploadFilesToDocs and similar should return a placeholder, one for each placeholder + DocUtils.uploadFileToDoc(file, {}, loading); + } + return loading; + }) + ); } if (generatedDocuments.length) { // Creating a dash document @@ -523,7 +499,8 @@ export function CollectionSubView(moreProps?: X) { if (completed) completed(set); else { if (isFreeformView && generatedDocuments.length > 1) { - addDocument(DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!); + pileUpDoc = DocUtils.pileup(generatedDocuments, options.x as number, options.y as number)!; + addDocument(pileUpDoc); } else { generatedDocuments.forEach(addDocument); } @@ -535,7 +512,6 @@ export function CollectionSubView(moreProps?: X) { alert('Document upload failed - possibly an unsupported file type.'); } } - disposer(); }; } diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index 0e0619241..9d4668dde 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -4,6 +4,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import * as React from 'react'; import './LoadingBox.scss'; import ReactLoading from 'react-loading'; +import { Docs, DocUtils } from '../../documents/Documents'; export interface LoadingBoxProps { title: string; @@ -16,6 +17,30 @@ export class LoadingBox extends ViewBoxAnnotatableComponent() { return FieldView.LayoutString(LoadingBox, fieldKey); } + componentDidMount() { + // const file = Docs.Create.filesToDocs.get(this.rootDoc); + // if (file) { + // console.log('Got to file'); + // Docs.Create.filesToDocs.delete(this.rootDoc); + // // now that there is a doc do whatever slowload was going to do with that file + // if (typeof file === 'string') { + // // uploadYoutubeVideo and similar should return a placeholder, one for each placeholder + // // (await DocUtils.uploadYoutubeVideo(files, options))); + // } else { + // // uploadFilesToDocs and similar should return a placeholder, one for each placeholder + // DocUtils.uploadFileToDoc(file, {}, this.rootDoc); + // } + // } else { + // // check if file now exists on server or not + // // if it does we need to retreieve it and create the appropriate doc (rest of what uploadFileToDoc was doing minus uploading) + // // if it doesn't display an error message "upload failed" + // } + // query endpoints to: + // check if file now exists on server or not + // if it does we need to retreieve it and create the appropriate doc (rest of what uploadFileToDoc was doing minus uploading) + // if it doesn't display an error message "upload failed" + } + constructor(props: any) { super(props); } -- cgit v1.2.3-70-g09d2 From 27945b9a931fa9504404174dd08964556dc3aea2 Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Tue, 16 Aug 2022 21:16:03 -0400 Subject: added loading for diff doc types --- src/client/documents/Documents.ts | 47 ++++++++++++++++------ src/client/views/collections/CollectionSubView.tsx | 11 ++--- src/client/views/nodes/LoadingBox.scss | 5 +++ src/client/views/nodes/LoadingBox.tsx | 12 +++--- 4 files changed, 48 insertions(+), 27 deletions(-) (limited to 'src/client/views') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index d8497e3af..e54fe16de 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -844,9 +844,9 @@ export namespace Docs { return viewDoc; } - export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}) { + export function ImageDocument(url: string | ImageField, options: DocumentOptions = {}, overwriteDoc?: Doc) { const imgField = url instanceof ImageField ? url : new ImageField(url); - return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options }); + return InstanceFromProto(Prototypes.get(DocumentType.IMG), imgField, { title: basename(imgField.url.href), ...options }, undefined, undefined, undefined, overwriteDoc); } export function PresDocument(options: DocumentOptions = {}) { @@ -857,12 +857,12 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.SCRIPTING), script ? script : undefined, { ...options, layout: fieldKey ? ScriptingBox.LayoutString(fieldKey) : undefined }); } - export function VideoDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(url), options); + export function VideoDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) { + return InstanceFromProto(Prototypes.get(DocumentType.VID), new VideoField(url), options, undefined, undefined, undefined, overwriteDoc); } - export function YoutubeDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(url), options); + export function YoutubeDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) { + return InstanceFromProto(Prototypes.get(DocumentType.YOUTUBE), new YoutubeField(url), options, undefined, undefined, undefined, overwriteDoc); } export function WebCamDocument(url: string, options: DocumentOptions = {}) { @@ -877,8 +877,16 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COMPARISON), '', options); } - export function AudioDocument(url: string, options: DocumentOptions = {}) { - return InstanceFromProto(Prototypes.get(DocumentType.AUDIO), new AudioField(url), { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any }); + export function AudioDocument(url: string, options: DocumentOptions = {}, overwriteDoc?: Doc) { + return InstanceFromProto( + Prototypes.get(DocumentType.AUDIO), + new AudioField(url), + { ...options, backgroundColor: ComputedField.MakeFunction("this._mediaState === 'playing' ? 'green':'gray'") as any }, + undefined, + undefined, + undefined, + overwriteDoc + ); } export function RecordingDocument(url: string, options: DocumentOptions = {}) { @@ -895,8 +903,10 @@ export namespace Docs { export const filesToDocs = new Map(); - export function LoadingDocument(file: File, options: DocumentOptions, ytString?: string) { - const loading = InstanceFromProto(Prototypes.get(DocumentType.LOADING), file.name, { _height: 300, _width: 300, ...options }); + export function LoadingDocument(file: File | string, options: DocumentOptions, ytString?: string) { + const fileName = typeof file == 'string' ? file : file.name; + options.title = fileName; + const loading = InstanceFromProto(Prototypes.get(DocumentType.LOADING), fileName, { _height: 150, _width: 150, ...options }); // filesToDocs.set(loading, file); return loading; } @@ -1114,8 +1124,8 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.PRESELEMENT), undefined, { ...(options || {}) }); } - export function DataVizDocument(url: string, options?: DocumentOptions) { - return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', ...options }); + export function DataVizDocument(url: string, options?: DocumentOptions, overwriteDoc?: Doc) { + return InstanceFromProto(Prototypes.get(DocumentType.DATAVIZ), new CsvField(url), { title: 'Data Viz', ...options }, undefined, undefined, undefined, overwriteDoc); } export function DockDocument(documents: Array, config: string, options: DocumentOptions, id?: string) { @@ -1809,6 +1819,18 @@ export namespace DocUtils { } return generatedDocuments; } + + export function uploadYoutubeVideoLoading(videoId: string, options: DocumentOptions, overwriteDoc?: Doc) { + const generatedDocuments: Doc[] = []; + Networking.UploadYoutubeToServer(videoId).then(upfiles => { + const { + source: { name, type }, + result, + } = upfiles.lastElement(); + name && type && processFileupload(generatedDocuments, name, type, result, options, overwriteDoc); + }); + } + export async function uploadFilesToDocs(files: File[], options: DocumentOptions) { const generatedDocuments: Doc[] = []; const upfiles = await Networking.UploadFilesToServer(files); @@ -1828,7 +1850,6 @@ export namespace DocUtils { source: { name, type }, result, } = upfiles.lastElement(); - console.log(name, type); name && type && processFileupload(generatedDocuments, name, type, result, options, overwriteDoc); }); } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 1ff4f5ab8..30467efa0 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -468,19 +468,16 @@ export function CollectionSubView(moreProps?: X) { if (typeof files === 'string') { // uploadYoutubeVideo and similar should return a placeholder, one for each placeholder // generatedDocuments.push(Docs.Create.LoadingDocument(files, options)); + const loading = Docs.Create.LoadingDocument(files, options); + generatedDocuments.push(loading); + DocUtils.uploadYoutubeVideoLoading(files, {}, loading); } else { // uploadFilesToDocs and similar should return a placeholder, one for each placeholder generatedDocuments.push( ...files.map(file => { const loading = Docs.Create.LoadingDocument(file, options); // now that there is a doc do whatever slowload was going to do with that file - if (typeof file === 'string') { - // uploadYoutubeVideo and similar should return a placeholder, one for each placeholder - // (await DocUtils.uploadYoutubeVideo(files, options))); - } else { - // uploadFilesToDocs and similar should return a placeholder, one for each placeholder - DocUtils.uploadFileToDoc(file, {}, loading); - } + DocUtils.uploadFileToDoc(file, {}, loading); return loading; }) ); diff --git a/src/client/views/nodes/LoadingBox.scss b/src/client/views/nodes/LoadingBox.scss index 239faa78e..e8890cd82 100644 --- a/src/client/views/nodes/LoadingBox.scss +++ b/src/client/views/nodes/LoadingBox.scss @@ -5,3 +5,8 @@ justify-content: center; background-color: #fdfdfd; } + +.text { + text-overflow: ellipsis; + text-align: center; +} diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index 9d4668dde..96620aff9 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -4,12 +4,7 @@ import { FieldView, FieldViewProps } from './FieldView'; import * as React from 'react'; import './LoadingBox.scss'; import ReactLoading from 'react-loading'; -import { Docs, DocUtils } from '../../documents/Documents'; - -export interface LoadingBoxProps { - title: string; - text: string; -} +import { StrCast } from '../../../fields/Types'; @observer export class LoadingBox extends ViewBoxAnnotatableComponent() { @@ -18,6 +13,7 @@ export class LoadingBox extends ViewBoxAnnotatableComponent() { } componentDidMount() { + console.log(this.rootDoc); // const file = Docs.Create.filesToDocs.get(this.rootDoc); // if (file) { // console.log('Got to file'); @@ -48,7 +44,9 @@ export class LoadingBox extends ViewBoxAnnotatableComponent() { render() { return (
- Loading: {this.dataDoc.text} +

Loading:

+

+

{StrCast(this.rootDoc.title)}

); -- cgit v1.2.3-70-g09d2 From 94c38310c6b54d93e907007f20ba032d12697ca0 Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Thu, 18 Aug 2022 12:33:34 -0400 Subject: fixed sizing bug --- src/client/documents/Documents.ts | 14 +++++-------- src/client/views/collections/CollectionSubView.tsx | 2 ++ src/client/views/nodes/LoadingBox.tsx | 24 ++++++++++++++++++---- 3 files changed, 27 insertions(+), 13 deletions(-) (limited to 'src/client/views') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index e54fe16de..f3ab7c788 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -827,9 +827,12 @@ export namespace Docs { viewFirstProps.author = Doc.CurrentUserEmail; let viewDoc: Doc; if (placeholderDoc) { + placeholderDoc._height = Number(options._height); + placeholderDoc._width = Number(options._width); viewDoc = Doc.assign(placeholderDoc, viewFirstProps, true, true); + } else { + viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true); } - viewDoc = Doc.assign(Doc.MakeDelegate(dataDoc, delegId), viewFirstProps, true, true); Doc.assign(viewDoc, viewProps, true, true); ![DocumentType.LINK, DocumentType.MARKER, DocumentType.LABEL].includes(viewDoc.type as any) && DocUtils.MakeLinkToActiveAudio(() => viewDoc); @@ -901,7 +904,7 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COLOR), '', options); } - export const filesToDocs = new Map(); + export const filesToDocs = new Map(); export function LoadingDocument(file: File | string, options: DocumentOptions, ytString?: string) { const fileName = typeof file == 'string' ? file : file.name; @@ -1854,13 +1857,6 @@ export namespace DocUtils { }); } - export function generatePlaceHolder(file: File, options: DocumentOptions) { - return Docs.Create.LoadingDocument(file, options); - // placeholder.file = file - // TODO: nda - modify loading doc so it only takes in options - // Docs.Create.LoadingDocument(options, ) - } - // copies the specified drag factory document export function copyDragFactory(dragFactory: Doc) { if (!dragFactory) return undefined; diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 30467efa0..fd2c722d2 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -470,6 +470,7 @@ export function CollectionSubView(moreProps?: X) { // generatedDocuments.push(Docs.Create.LoadingDocument(files, options)); const loading = Docs.Create.LoadingDocument(files, options); generatedDocuments.push(loading); + Docs.Create.filesToDocs.set(loading, files); DocUtils.uploadYoutubeVideoLoading(files, {}, loading); } else { // uploadFilesToDocs and similar should return a placeholder, one for each placeholder @@ -477,6 +478,7 @@ export function CollectionSubView(moreProps?: X) { ...files.map(file => { const loading = Docs.Create.LoadingDocument(file, options); // now that there is a doc do whatever slowload was going to do with that file + Docs.Create.filesToDocs.set(loading, file); DocUtils.uploadFileToDoc(file, {}, loading); return loading; }) diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index 96620aff9..249461b67 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -5,6 +5,8 @@ import * as React from 'react'; import './LoadingBox.scss'; import ReactLoading from 'react-loading'; import { StrCast } from '../../../fields/Types'; +import { computed, observable } from 'mobx'; +import { Docs } from '../../documents/Documents'; @observer export class LoadingBox extends ViewBoxAnnotatableComponent() { @@ -12,6 +14,12 @@ export class LoadingBox extends ViewBoxAnnotatableComponent() { return FieldView.LayoutString(LoadingBox, fieldKey); } + @computed + private get isLoading() { + const file = Docs.Create.filesToDocs.get(this.rootDoc); + return file ? true : false; + } + componentDidMount() { console.log(this.rootDoc); // const file = Docs.Create.filesToDocs.get(this.rootDoc); @@ -44,10 +52,18 @@ export class LoadingBox extends ViewBoxAnnotatableComponent() { render() { return (
-

Loading:

-

-

{StrCast(this.rootDoc.title)}

- + {this.isLoading ? ( +
+

Loading:

+

+ {StrCast(this.rootDoc.title)} + +
+ ) : ( +
+ Error Loading File: {StrCast(this.rootDoc.title)} +
+ )}
); } -- cgit v1.2.3-70-g09d2 From b7c4d65a3bf04ff7d2c10d93be282a1b0a4650b3 Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Fri, 19 Aug 2022 12:32:23 -0400 Subject: added comments, cleaned up code, could not get spinner to center --- src/client/documents/Documents.ts | 8 ++- src/client/views/collections/CollectionSubView.tsx | 4 +- src/client/views/nodes/LoadingBox.scss | 21 +++++++ src/client/views/nodes/LoadingBox.tsx | 64 ++++++++++------------ 4 files changed, 58 insertions(+), 39 deletions(-) (limited to 'src/client/views') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f3ab7c788..9e140ccd1 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -815,6 +815,7 @@ export namespace Docs { dataProps[fieldKey + '-annotations'] = new List(); dataProps[fieldKey + '-sidebar'] = new List(); + // users placeholderDoc as proto if it exists const dataDoc = Doc.assign(placeholderDoc ? Doc.GetProto(placeholderDoc) : Doc.MakeDelegate(proto, protoId), dataProps, undefined, true); if (placeholderDoc) { @@ -826,6 +827,7 @@ export namespace Docs { viewFirstProps['acl-Override'] = 'None'; viewFirstProps.author = Doc.CurrentUserEmail; let viewDoc: Doc; + // determines whether viewDoc should be created using placeholder Doc or deafult if (placeholderDoc) { placeholderDoc._height = Number(options._height); placeholderDoc._width = Number(options._width); @@ -904,13 +906,13 @@ export namespace Docs { return InstanceFromProto(Prototypes.get(DocumentType.COLOR), '', options); } - export const filesToDocs = new Map(); + // Mapping of all loading docs to files, i.e. keeps track of files being uploaded in current session + export const docsToFiles = new Map(); export function LoadingDocument(file: File | string, options: DocumentOptions, ytString?: string) { const fileName = typeof file == 'string' ? file : file.name; options.title = fileName; - const loading = InstanceFromProto(Prototypes.get(DocumentType.LOADING), fileName, { _height: 150, _width: 150, ...options }); - // filesToDocs.set(loading, file); + const loading = InstanceFromProto(Prototypes.get(DocumentType.LOADING), fileName, { _height: 150, _width: 200, ...options }); return loading; } diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index fd2c722d2..eef485b95 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -470,7 +470,7 @@ export function CollectionSubView(moreProps?: X) { // generatedDocuments.push(Docs.Create.LoadingDocument(files, options)); const loading = Docs.Create.LoadingDocument(files, options); generatedDocuments.push(loading); - Docs.Create.filesToDocs.set(loading, files); + Docs.Create.docsToFiles.set(loading, files); DocUtils.uploadYoutubeVideoLoading(files, {}, loading); } else { // uploadFilesToDocs and similar should return a placeholder, one for each placeholder @@ -478,7 +478,7 @@ export function CollectionSubView(moreProps?: X) { ...files.map(file => { const loading = Docs.Create.LoadingDocument(file, options); // now that there is a doc do whatever slowload was going to do with that file - Docs.Create.filesToDocs.set(loading, file); + Docs.Create.docsToFiles.set(loading, file); DocUtils.uploadFileToDoc(file, {}, loading); return loading; }) diff --git a/src/client/views/nodes/LoadingBox.scss b/src/client/views/nodes/LoadingBox.scss index e8890cd82..f6912f547 100644 --- a/src/client/views/nodes/LoadingBox.scss +++ b/src/client/views/nodes/LoadingBox.scss @@ -6,7 +6,28 @@ background-color: #fdfdfd; } +.textContainer { + margin: 5px; +} + +.textContainer { + justify-content: center; + align-content: center; +} + +.textContainer, .text { + overflow: hidden; text-overflow: ellipsis; + max-width: 80%; + text-align: center; +} + +.headerText { + text-align: center; + font-weight: bold; +} + +.spinner { text-align: center; } diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index 249461b67..ab8878fc1 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -5,46 +5,42 @@ import * as React from 'react'; import './LoadingBox.scss'; import ReactLoading from 'react-loading'; import { StrCast } from '../../../fields/Types'; -import { computed, observable } from 'mobx'; +import { computed } from 'mobx'; import { Docs } from '../../documents/Documents'; +/** + * LoadingBox Class represents a placeholder doc for documents that are currently + * being uploaded to the server and being fetched by the client. The LoadingBox doc is then used to + * generate the actual type of doc that is required once the document has been successfully uploaded. + * + * Design considerations: + * We are using the docToFiles map in Documents to keep track of all files being uploaded in one session of the client. + * If the file is not found we assume an error has occurred with the file upload, e.g. it has been interrupted by a client refresh + * or network issues. The docToFiles essentially gets reset everytime the page is refreshed. + * + * TODOs: + * 1) ability to query server to retrieve files that already exist if users upload duplicate files. + * 2) ability to restart upload if there is an error + * 3) detect network error and notify the user + * 4 )if file upload gets interrupted, it still gets uploaded to the server if there are no network interruptions which leads to unused space. this could be + * handled with (1) + * + * @author naafiyan + */ @observer export class LoadingBox extends ViewBoxAnnotatableComponent() { public static LayoutString(fieldKey: string) { return FieldView.LayoutString(LoadingBox, fieldKey); } - @computed - private get isLoading() { - const file = Docs.Create.filesToDocs.get(this.rootDoc); + /** + * Returns true if file is uploading, false otherwise + */ + @computed private get isLoading(): boolean { + const file = Docs.Create.docsToFiles.get(this.rootDoc); return file ? true : false; } - componentDidMount() { - console.log(this.rootDoc); - // const file = Docs.Create.filesToDocs.get(this.rootDoc); - // if (file) { - // console.log('Got to file'); - // Docs.Create.filesToDocs.delete(this.rootDoc); - // // now that there is a doc do whatever slowload was going to do with that file - // if (typeof file === 'string') { - // // uploadYoutubeVideo and similar should return a placeholder, one for each placeholder - // // (await DocUtils.uploadYoutubeVideo(files, options))); - // } else { - // // uploadFilesToDocs and similar should return a placeholder, one for each placeholder - // DocUtils.uploadFileToDoc(file, {}, this.rootDoc); - // } - // } else { - // // check if file now exists on server or not - // // if it does we need to retreieve it and create the appropriate doc (rest of what uploadFileToDoc was doing minus uploading) - // // if it doesn't display an error message "upload failed" - // } - // query endpoints to: - // check if file now exists on server or not - // if it does we need to retreieve it and create the appropriate doc (rest of what uploadFileToDoc was doing minus uploading) - // if it doesn't display an error message "upload failed" - } - constructor(props: any) { super(props); } @@ -53,15 +49,15 @@ export class LoadingBox extends ViewBoxAnnotatableComponent() { return (
{this.isLoading ? ( -
-

Loading:

-

+
+

Loading:

{StrCast(this.rootDoc.title)}
) : ( -
- Error Loading File: {StrCast(this.rootDoc.title)} +
+

Error Loading File:

+ {StrCast(this.rootDoc.title)}
)}
-- cgit v1.2.3-70-g09d2 From 5b20224b59f4a6ebc02fbd0a68441bb241c68d63 Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Fri, 19 Aug 2022 14:37:32 -0400 Subject: cleaned up comments --- src/client/views/collections/CollectionSubView.tsx | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index d7d988c19..e70cfb13c 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -460,18 +460,13 @@ export function CollectionSubView(moreProps?: X) { // create placeholder docs // inside placeholder docs have some func that - // TODO: once loading thing is moved it should update the x and y of the file it is placeholder for let pileUpDoc = undefined; - // const disposer = OverlayView.Instance.addElement(, { x: clientX - 125, y: clientY - 125 }); if (typeof files === 'string') { - // uploadYoutubeVideo and similar should return a placeholder, one for each placeholder - // generatedDocuments.push(Docs.Create.LoadingDocument(files, options)); const loading = Docs.Create.LoadingDocument(files, options); generatedDocuments.push(loading); Docs.Create.docsToFiles.set(loading, files); DocUtils.uploadYoutubeVideoLoading(files, {}, loading); } else { - // uploadFilesToDocs and similar should return a placeholder, one for each placeholder generatedDocuments.push( ...files.map(file => { const loading = Docs.Create.LoadingDocument(file, options); -- cgit v1.2.3-70-g09d2 From 85626694045f3863f6df9351156808017753de9e Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Fri, 19 Aug 2022 14:37:57 -0400 Subject: cleaned up comments --- src/client/views/nodes/LoadingBox.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/client/views') diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index ab8878fc1..90bf90095 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -24,6 +24,8 @@ import { Docs } from '../../documents/Documents'; * 3) detect network error and notify the user * 4 )if file upload gets interrupted, it still gets uploaded to the server if there are no network interruptions which leads to unused space. this could be * handled with (1) + * 5) Fixing the stacking view bug + * 6) Fixing the CSS * * @author naafiyan */ -- cgit v1.2.3-70-g09d2 From 0ed7131587c6739483da64a93d9f2ab6fdfbc15a Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 25 Aug 2022 11:06:38 -0400 Subject: fixed crashes in notetaking view and cleaned up code a bit. fixed undo of column deletion. --- src/client/util/CurrentUserUtils.ts | 11 ++- .../views/collections/CollectionNoteTakingView.tsx | 103 ++++++++------------- .../collections/CollectionNoteTakingViewColumn.tsx | 12 ++- .../collectionFreeForm/CollectionFreeFormView.tsx | 18 ++-- .../views/nodes/CollectionFreeFormDocumentView.tsx | 10 +- src/client/views/nodes/DocumentView.scss | 3 + src/fields/SchemaHeaderField.ts | 2 +- src/fields/ScriptField.ts | 10 +- 8 files changed, 79 insertions(+), 90 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 99a8c895f..2321d18ee 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -597,14 +597,17 @@ export class CurrentUserUtils { /// initializes the required buttons in the expanding button menu at the bottom of the Dash window static setupDockedButtons(doc: Doc, field="myDockedBtns") { const dockedBtns = DocCast(doc[field]); - const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string}) => + const dockBtn = (opts: DocumentOptions, scripts: {[key:string]:string}, funcs?: {[key:string]:string}) => DocUtils.AssignScripts(DocUtils.AssignOpts(DocListCast(dockedBtns?.data)?.find(doc => doc.title === opts.title), opts) ?? - CurrentUserUtils.createToolButton(opts), scripts); + CurrentUserUtils.createToolButton(opts), scripts, funcs); const btnDescs = [// setup reactions to change the highlights on the undo/redo buttons -- would be better to encode this in the undo/redo buttons, but the undo/redo stacks are not wired up that way yet { scripts: { onClick: "undo()"}, opts: { title: "undo", icon: "undo-alt", toolTip: "Click to undo" }}, - { scripts: { onClick: "redo()"}, opts: { title: "redo", icon: "redo-alt", toolTip: "Click to redo" }} - ]; + { scripts: { onClick: "redo()"}, opts: { title: "redo", icon: "redo-alt", toolTip: "Click to redo" }}, + // { scripts: { onClick: 'prevKeyFrame(_readOnly_)'}, opts: { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, width: 20}}, + // { scripts: { onClick:""}, opts: { title: "Num", icon: "", toolTip: "Frame Number", btnType: ButtonType.TextButton, width: 20}, funcs: { buttonText: 'selectedDocs()?.lastElement()?.currentFrame.toString()'}}, + // { scripts: { onClick: 'nextKeyFrame(_readOnly_)'}, opts:{title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, width: 20,} }, + ]; const btns = btnDescs.map(desc => dockBtn({_width: 30, _height: 30, dontUndo: true, _stayInCollection: true, ...desc.opts}, desc.scripts)); const dockBtnsReqdOpts = { title: "docked buttons", _height: 40, flexGap: 0, linearViewFloating: true, diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index b359ef420..5a6d899ef 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -11,7 +11,7 @@ import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Ty import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, returnZero, smoothScroll, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; -import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; +import { DocumentType } from '../../documents/DocumentTypes'; import { DragManager, dropActionType } from '../../util/DragManager'; import { SnappingManager } from '../../util/SnappingManager'; import { Transform } from '../../util/Transform'; @@ -19,7 +19,6 @@ import { undoBatch } from '../../util/UndoManager'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { LightboxView } from '../LightboxView'; -import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocumentView'; import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment } from '../nodes/DocumentView'; import { FieldViewProps } from '../nodes/FieldView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; @@ -30,13 +29,6 @@ import { CollectionNoteTakingViewDivider } from './CollectionNoteTakingViewDivid import { CollectionSubView } from './CollectionSubView'; const _global = (window /* browser */ || global) /* node */ as any; -export type collectionNoteTakingViewProps = { - chromeHidden?: boolean; - viewType?: CollectionViewType; - NativeWidth?: () => number; - NativeHeight?: () => number; -}; - /** * CollectionNoteTakingView is a column-based view for displaying documents. In this view, the user can (1) * add and remove columns (2) change column sizes and (3) move documents within and between columns. This @@ -45,28 +37,32 @@ export type collectionNoteTakingViewProps = { * the rest of Dash, so it may be worthwhile to transition the headers to simple documents. */ @observer -export class CollectionNoteTakingView extends CollectionSubView>() { +export class CollectionNoteTakingView extends CollectionSubView() { _disposers: { [key: string]: IReactionDisposer } = {}; _masonryGridRef: HTMLDivElement | null = null; _draggerRef = React.createRef(); + notetakingCategoryField = 'NotetakingCategory'; + dividerWidth = 16; @observable docsDraggedRowCol: number[] = []; @observable _cursor: CursorProperty = 'grab'; @observable _scroll = 0; @computed get chromeHidden() { - return this.props.chromeHidden || BoolCast(this.layoutDoc.chromeHidden); + return BoolCast(this.layoutDoc.chromeHidden); } // columnHeaders returns the list of SchemaHeaderFields currently being used by the layout doc to render the columns @computed get columnHeaders() { const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null); - const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders.find(sh => sh.heading === 'unset')); + const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset')); if (needsUnsetCategory) { - setTimeout(() => columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1))); + setTimeout(() => { + const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset')); + if (needsUnsetCategory) { + if (columnHeaders) columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1)); + else this.dataDoc.columnHeaders = new List(); + } + }); } - return columnHeaders; - } - // notetakingCategoryField returns the key to accessing a document's column value - @computed get notetakingCategoryField() { - return 'NotetakingCategory'; + return columnHeaders ?? ([] as SchemaHeaderField[]); } @computed get headerMargin() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin); @@ -74,10 +70,6 @@ export class CollectionNoteTakingView extends CollectionSubView([new SchemaHeaderField('New Column', undefined, undefined, 1)]); - } - } - // children is passed as a prop to the NoteTakingField, which uses this function // to render the docs you see within an individual column. children = (docs: Doc[]) => { @@ -149,7 +132,7 @@ export class CollectionNoteTakingView extends CollectionSubView rowCol[1]) { const offset = 0; sections.get(columnHeaders[rowCol[1]])?.splice(rowCol[0] - offset, 0, ...DragManager.docsBeingDragged); } @@ -236,9 +219,6 @@ export class CollectionNoteTakingView extends CollectionSubView (d[this.notetakingCategoryField] = colHeader)); // used to notify sections to re-render this.docsDraggedRowCol.length = 0; - this.docsDraggedRowCol.push(dropInd, this.getColumnFromXCoord(xCoord)); + const columnFromCoord = this.getColumnFromXCoord(xCoord); + columnFromCoord !== undefined && this.docsDraggedRowCol.push(dropInd, columnFromCoord); } }; // getColumnFromXCoord returns the column index for a given x-coordinate (currently always the client's mouse coordinate). // This function is used to know which document a column SHOULD be in while it is being dragged. - getColumnFromXCoord = (xCoord: number): number => { + getColumnFromXCoord = (xCoord: number): number | undefined => { + let colIndex: number | undefined = undefined; const numColumns = this.columnHeaders.length; const coords = []; let colStartXCoord = 0; @@ -411,7 +393,6 @@ export class CollectionNoteTakingView extends CollectionSubView coords[i] && xCoord < coords[i + 1]) { colIndex = i; @@ -423,20 +404,16 @@ export class CollectionNoteTakingView extends CollectionSubView { - const colIndex = this.getColumnFromXCoord(xCoord); - const colHeader = StrCast(this.columnHeaders[colIndex].heading); - // const docs = this.childDocList - const docs = this.childDocs; const docsMatchingHeader: Doc[] = []; - if (docs) { - docs.map(d => { - if (d instanceof Promise) return; - const sectionValue = (d[this.notetakingCategoryField] as object) ?? 'unset'; - if (sectionValue.toString() == colHeader) { - docsMatchingHeader.push(d); - } - }); - } + const colIndex = this.getColumnFromXCoord(xCoord); + const colHeader = colIndex === undefined ? 'unset' : StrCast(this.columnHeaders[colIndex].heading); + this.childDocs?.map(d => { + if (d instanceof Promise) return; + const sectionValue = (d[this.notetakingCategoryField] as object) ?? 'unset'; + if (sectionValue.toString() == colHeader) { + docsMatchingHeader.push(d); + } + }); return docsMatchingHeader; }; @@ -511,7 +488,7 @@ export class CollectionNoteTakingView extends CollectionSubView this.addDocument(doc)); const newDoc = this.childDocs.lastElement(); - const colHeader = StrCast(this.columnHeaders[colInd].heading); + const colHeader = colInd === undefined ? 'unset' : StrCast(this.columnHeaders[colInd].heading); newDoc[this.notetakingCategoryField] = colHeader; const docs = this.childDocList; if (docs && targInd !== -1) { @@ -570,9 +547,9 @@ export class CollectionNoteTakingView extends CollectionSubView { - for (const header of this.columnHeaders) { - if (header.heading == value) { - alert('You cannot use an existing column name. Please try a new column name'); - return value; + if (this.columnHeaders) { + for (const header of this.columnHeaders) { + if (header.heading == value) { + alert('You cannot use an existing column name. Please try a new column name'); + return value; + } } } const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null); @@ -678,10 +657,10 @@ export class CollectionNoteTakingView extends CollectionSubView { - const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null); - if (columnHeaders && this.props.headingObject) { - const index = columnHeaders.indexOf(this.props.headingObject); + const acolumnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null); + if (acolumnHeaders && this.props.headingObject) { + const index = acolumnHeaders.indexOf(this.props.headingObject); + const columnHeaders = new List(acolumnHeaders.map(header => header[Copy]())); // needed for undo to work properly. otherwise we end up changing field values in the undo stack since they are shared by reference const newColIndex = index > 0 ? index - 1 : 1; const newColHeader = this.props.columnHeaders ? this.props.columnHeaders[newColIndex] : undefined; const newHeading = newColHeader ? newColHeader.heading : 'unset'; this.props.docList.forEach(d => (d[this.props.pivotField] = newHeading)); const colWidth = this.props.columnHeaders ? this.props.columnHeaders[index].width : 0; columnHeaders.splice(index, 1); + Doc.GetProto(this.props.Document).columnHeaders = columnHeaders; this.props.resizeColumns(false, colWidth, index); } }; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 052cbd3bb..c44b33ed0 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -44,7 +44,6 @@ import { PresBox } from '../../nodes/trails/PresBox'; import { VideoBox } from '../../nodes/VideoBox'; import { CreateImage } from '../../nodes/WebBoxRenderer'; import { StyleProp } from '../../StyleProvider'; -import { CollectionDockingView } from '../CollectionDockingView'; import { CollectionSubView } from '../CollectionSubView'; import { TreeViewType } from '../CollectionTreeView'; import { TabDocView } from '../TabDocView'; @@ -233,11 +232,11 @@ export class CollectionFreeFormView extends CollectionSubView newBox[field]); - CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[`${field}-indexed`]); - CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[field]); + const vals = CollectionFreeFormDocumentView.animFields.map(field => newBox[field.key]); + CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[`${field.key}-indexed`]); + CollectionFreeFormDocumentView.animFields.forEach(field => delete newBox[field.key]); delete newBox.activeFrame; - CollectionFreeFormDocumentView.animFields.forEach((field, i) => field !== 'opacity' && (newBox[field] = vals[i])); + CollectionFreeFormDocumentView.animFields.forEach((field, i) => field.key !== 'opacity' && (newBox[field.key] = vals[i])); } } if (this.Document._currentFrame !== undefined && !this.props.isAnnotationOverlay) { @@ -275,10 +274,11 @@ export class CollectionFreeFormView extends CollectionSubView() { - public static animFields = ['_height', '_width', 'x', 'y', '_scrollTop', 'opacity']; // fields that are configured to be animatable using animation frames + public static animFields: { key: string; val?: number }[] = [{ key: '_height' }, { key: '_width' }, { key: 'x' }, { key: 'y' }, { key: '_scrollTop' }, { key: 'opacity', val: 1 }, { key: 'viewScale', val: 1 }, { key: 'panX' }, { key: 'panY' }]; // fields that are configured to be animatable using animation frames public static animStringFields = ['backgroundColor', 'color']; // fields that are configured to be animatable using animation frames public static animDataFields = ['data', 'text']; // fields that are configured to be animatable using animation frames @observable _animPos: number[] | undefined = undefined; @@ -88,9 +88,9 @@ export class CollectionFreeFormDocumentView extends DocComponent { - p[val] = Cast(`${val}-indexed`, listSpec('number'), [NumCast(doc[val])]).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as number); + p[val.key] = Cast(`${val}-indexed`, listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : []).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as number); return p; }, {} as { [val: string]: Opt }); } @@ -117,7 +117,7 @@ export class CollectionFreeFormDocumentView extends DocComponent { doc._viewTransition = doc.dataTransition = 'all 1s'; CollectionFreeFormDocumentView.animFields.forEach(val => { - const findexed = Cast(doc[`${val}-indexed`], listSpec('number'), null); + const findexed = Cast(doc[`${val.key}-indexed`], listSpec('number'), null); findexed?.length <= timecode + 1 && findexed.push(undefined as any as number); }); CollectionFreeFormDocumentView.animStringFields.forEach(val => { @@ -174,7 +174,7 @@ export class CollectionFreeFormDocumentView extends DocComponent(numberRange(currTimecode + 1).map(t => (!doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1))); } - CollectionFreeFormDocumentView.animFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedNumber(val, 'activeFrame', doc, currTimecode))); + CollectionFreeFormDocumentView.animFields.forEach(val => (doc[val.key] = ComputedField.MakeInterpolatedNumber(val.key, 'activeFrame', doc, currTimecode, val.val))); CollectionFreeFormDocumentView.animStringFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode))); CollectionFreeFormDocumentView.animDataFields.forEach(val => (Doc.GetProto(doc)[val] = ComputedField.MakeInterpolatedDataField(val, 'activeFrame', Doc.GetProto(doc), currTimecode))); const targetDoc = doc.type === DocumentType.RTF ? Doc.GetProto(doc) : doc; // data fields, like rtf 'text' exist on the data doc, so diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 9aaaf1e68..ab7116150 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -2,6 +2,7 @@ .documentView-effectsWrapper { border-radius: inherit; + transition: inherit; } // documentViews have a docView-hack tag which is replaced by this tag when capturing bitmaps (when the dom is converted to an html string) @@ -212,10 +213,12 @@ display: flex; width: 100%; height: 100%; + transition: inherit; .contentFittingDocumentView-previewDoc { position: relative; display: inline; + transition: inherit; } .contentFittingDocumentView-input { diff --git a/src/fields/SchemaHeaderField.ts b/src/fields/SchemaHeaderField.ts index 1321bc327..0b51db70b 100644 --- a/src/fields/SchemaHeaderField.ts +++ b/src/fields/SchemaHeaderField.ts @@ -115,7 +115,7 @@ export class SchemaHeaderField extends ObjectField { } [ToScriptString]() { - return `header(${this.heading},${this.type}})`; + return `header(${this.heading},${this.type},${this.width}})`; } [ToString]() { return `SchemaHeaderField`; diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 48d5c5563..d38a019b3 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -189,13 +189,13 @@ export class ComputedField extends ScriptField { const compiled = ScriptField.CompileScript(script, params, true, capturedVariables); return compiled.compiled ? new ComputedField(compiled) : undefined; } - public static MakeInterpolatedNumber(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) { + public static MakeInterpolatedNumber(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number, defaultVal: Opt) { if (!doc[`${fieldKey}-indexed`]) { const flist = new List(numberRange(curTimecode + 1).map(i => undefined) as any as number[]); - flist[curTimecode] = NumCast(doc[fieldKey]); + flist[curTimecode] = Cast(doc[fieldKey], 'number', null); doc[`${fieldKey}-indexed`] = flist; } - const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey})`, {}, true, {}); + const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, ${defaultVal})`, {}, true, {}); const setField = ScriptField.CompileScript(`setIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey}, value)`, { value: 'any' }, true, {}); return getField.compiled ? new ComputedField(getField, setField?.compiled ? setField : undefined) : undefined; } @@ -260,8 +260,8 @@ ScriptingGlobals.add( ); ScriptingGlobals.add( - function getIndexVal(list: any[], index: number) { - return list?.reduce((p, x, i) => ((i <= index && x !== undefined) || p === undefined ? x : p), undefined as any); + function getIndexVal(list: any[], index: number, defaultVal: Opt = undefined) { + return list?.reduce((p, x, i) => ((i <= index && x !== undefined) || p === undefined ? x : p), defaultVal); }, 'returns the value at a given index of a list', '(list: any[], index: number)' -- cgit v1.2.3-70-g09d2 From ef2cd862c062556008a1897b408399dedaee8210 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 25 Aug 2022 17:04:53 -0400 Subject: fixed issues with reloading notetaking view and having columns be exactly the same. fixed adding columns so that changes are actually propagated to the DB. SchemaHeaderFields are bad news... --- .../collections/CollectionNoteTakingView.scss | 32 +++++--- .../views/collections/CollectionNoteTakingView.tsx | 42 +++++----- .../collections/CollectionNoteTakingViewColumn.tsx | 89 +++++++++------------- .../CollectionNoteTakingViewDivider.tsx | 1 - 4 files changed, 80 insertions(+), 84 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/CollectionNoteTakingView.scss b/src/client/views/collections/CollectionNoteTakingView.scss index fe98f307e..5582fd391 100644 --- a/src/client/views/collections/CollectionNoteTakingView.scss +++ b/src/client/views/collections/CollectionNoteTakingView.scss @@ -1,7 +1,7 @@ @import '../global/globalCssVariables'; .collectionNoteTakingView-DocumentButtons { - display: flex; + display: none; justify-content: space-between; margin: auto; } @@ -51,6 +51,17 @@ display: flex; } +.collectionNoteTakingViewFieldColumn { + height: 100%; + display: flex; + flex-direction: colum; +} +.collectionNoteTakingViewFieldColumn:hover { + .collectionNoteTakingView-DocumentButtons { + display: flex; + } +} + // TODO:glr Turn this into a seperate class .documentButtonMenu { position: relative; @@ -82,7 +93,6 @@ top: 0; overflow-y: auto; overflow-x: hidden; - flex-wrap: wrap; transition: top 0.5s; > div { @@ -133,6 +143,16 @@ margin-left: -5; } + .collectionNoteTakingView-sectionDelete { + display: none; + position: absolute; + right: 0; + width: max-content; + height: max-content; + top: 10; + padding: 2; + } + // Documents in NoteTaking view .collectionNoteTakingView-columnDoc { display: flex; @@ -347,14 +367,6 @@ } } } - - .collectionNoteTakingView-sectionDelete { - position: absolute; - right: 25px; - top: 0; - height: 100%; - display: none; - } } .collectionNoteTakingView-sectionHeader:hover { diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 5a6d899ef..615141485 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -3,7 +3,7 @@ import { CursorProperty } from 'csstype'; import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { DataSym, Doc, Field, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; -import { Id } from '../../../fields/FieldSymbols'; +import { Copy, Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; import { SchemaHeaderField } from '../../../fields/SchemaHeaderField'; @@ -42,7 +42,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { _masonryGridRef: HTMLDivElement | null = null; _draggerRef = React.createRef(); notetakingCategoryField = 'NotetakingCategory'; - dividerWidth = 16; + public DividerWidth = 16; @observable docsDraggedRowCol: number[] = []; @observable _cursor: CursorProperty = 'grab'; @observable _scroll = 0; @@ -53,10 +53,11 @@ export class CollectionNoteTakingView extends CollectionSubView() { @computed get columnHeaders() { const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null); const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset')); - if (needsUnsetCategory) { + if (needsUnsetCategory || columnHeaders === undefined || columnHeaders.length === 0) { setTimeout(() => { + const columnHeaders = Cast(this.dataDoc.columnHeaders, listSpec(SchemaHeaderField), null); const needsUnsetCategory = this.childDocs.some(d => !d[this.notetakingCategoryField] && !columnHeaders?.find(sh => sh.heading === 'unset')); - if (needsUnsetCategory) { + if (needsUnsetCategory || columnHeaders === undefined || columnHeaders.length === 0) { if (columnHeaders) columnHeaders.push(new SchemaHeaderField('unset', undefined, undefined, 1)); else this.dataDoc.columnHeaders = new List(); } @@ -68,10 +69,10 @@ export class CollectionNoteTakingView extends CollectionSubView() { return this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.HeaderMargin); } @computed get xMargin() { - return NumCast(this.layoutDoc._xMargin, 2 * Math.min(this.gridGap, 0.05 * this.props.PanelWidth())); + return NumCast(this.layoutDoc._xMargin, 5); } @computed get yMargin() { - return this.props.yPadding || NumCast(this.layoutDoc._yMargin, 5); + return NumCast(this.layoutDoc._yMargin, 5); } @computed get gridGap() { return NumCast(this.layoutDoc._gridGap, 10); @@ -86,13 +87,13 @@ export class CollectionNoteTakingView extends CollectionSubView() { } // maxColWidth returns the maximum column width, which is slightly less than the total available space. @computed get maxColWidth() { - return this.props.PanelWidth() - 2 * this.xMargin; + return this.props.PanelWidth(); } // availableWidth is the total amount of non-divider width. Since widths are stored relatively, // we use availableWidth to convert from a percentage to a pixel count. @computed get availableWidth() { const numDividers = this.numGroupColumns - 1; - return this.maxColWidth - numDividers * this.dividerWidth; + return this.maxColWidth - numDividers * this.DividerWidth; } // children is passed as a prop to the NoteTakingField, which uses this function @@ -295,12 +296,9 @@ export class CollectionNoteTakingView extends CollectionSubView() { const heading = !d[this.notetakingCategoryField] ? 'unset' : Field.toString(d[this.notetakingCategoryField] as Field); const existingHeader = this.columnHeaders.find(sh => sh.heading === heading); const existingWidth = existingHeader?.width ? existingHeader.width : 0; - const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth - 2 * this.xMargin : this.maxColWidth - 2 * this.xMargin; - if (d.type === DocumentType.RTF) { - return maxWidth; - } - const width = d[WidthSym](); - return width < maxWidth ? width : maxWidth; + const maxWidth = existingWidth > 0 ? existingWidth * this.availableWidth : this.maxColWidth; + const width = d.fitWidth ? maxWidth : d[WidthSym](); + return Math.min(maxWidth - CollectionNoteTakingViewColumn.ColumnMargin, width < maxWidth ? width : maxWidth); } // how to get the height of a document. Nothing special here. @@ -312,8 +310,6 @@ export class CollectionNoteTakingView extends CollectionSubView() { const nw = Doc.NativeWidth(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[WidthSym]() : 0); const nh = Doc.NativeHeight(childLayoutDoc, childDataDoc) || (!(childLayoutDoc._fitWidth || this.props.childFitWidth?.(d)) ? d[HeightSym]() : 0); if (nw && nh) { - // const colWid = this.columnWidth / this.numGroupColumns; - // const docWid = this.layoutDoc._columnsFill ? colWid : Math.min(this.getDocWidth(d), colWid); const docWid = this.getDocWidth(d); return Math.min(maxHeight, (docWid * nh) / nw); } @@ -390,7 +386,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { let colStartXCoord = 0; for (let i = 0; i < numColumns; i++) { coords.push(colStartXCoord); - colStartXCoord += this.columnHeaders[i].width * this.availableWidth + this.dividerWidth; + colStartXCoord += this.columnHeaders[i].width * this.availableWidth + this.DividerWidth; } coords.push(this.PanelWidth); for (let i = 0; i < numColumns; i++) { @@ -543,7 +539,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { numGroupColumns={this.numGroupColumns} gridGap={this.gridGap} pivotField={this.notetakingCategoryField} - dividerWidth={this.dividerWidth} + dividerWidth={this.DividerWidth} maxColWidth={this.maxColWidth} availableWidth={this.availableWidth} PanelWidth={this.PanelWidth} @@ -576,7 +572,9 @@ export class CollectionNoteTakingView extends CollectionSubView() { } const columnHeaders = Cast(this.props.Document.columnHeaders, listSpec(SchemaHeaderField), null); const newColWidth = 1 / (this.numGroupColumns + 1); - return value && columnHeaders?.push(new SchemaHeaderField(value, undefined, undefined, newColWidth)) && this.resizeColumns(true, newColWidth, this.columnHeaders.length - 1) ? true : false; + value && columnHeaders?.push(new SchemaHeaderField(value, undefined, undefined, newColWidth)) && this.resizeColumns(true, newColWidth, this.columnHeaders.length - 1); + this.dataDoc.columnHeaders = new List(columnHeaders.map(header => header[Copy]())); + return true; }; onContextMenu = (e: React.MouseEvent): void => { @@ -621,10 +619,10 @@ export class CollectionNoteTakingView extends CollectionSubView() { @computed get buttonMenu() { const menuDoc: Doc = Cast(this.rootDoc.buttonMenuDoc, Doc, null); if (menuDoc) { - const width: number = NumCast(menuDoc._width, 30); - const height: number = NumCast(menuDoc._height, 30); + const width = NumCast(menuDoc._width, 30); + const height = NumCast(menuDoc._height, 30); return ( -
+
= React.createRef(); + public static ColumnMargin = 10; @observable _heading = this.props.headingObject ? this.props.headingObject.heading : this.props.heading; @observable _color = this.props.headingObject ? this.props.headingObject.color : '#f1efeb'; _ele: HTMLElement | null = null; @@ -153,14 +144,14 @@ export class CollectionNoteTakingViewColumn extends React.Component(acolumnHeaders.map(header => header[Copy]())); // needed for undo to work properly. otherwise we end up changing field values in the undo stack since they are shared by reference + const columnHeaders = acolumnHeaders; // new List(acolumnHeaders.map(header => header[Copy]())); // needed for undo to work properly. otherwise we end up changing field values in the undo stack since they are shared by reference const newColIndex = index > 0 ? index - 1 : 1; const newColHeader = this.props.columnHeaders ? this.props.columnHeaders[newColIndex] : undefined; const newHeading = newColHeader ? newColHeader.heading : 'unset'; this.props.docList.forEach(d => (d[this.props.pivotField] = newHeading)); const colWidth = this.props.columnHeaders ? this.props.columnHeaders[index].width : 0; columnHeaders.splice(index, 1); - Doc.GetProto(this.props.Document).columnHeaders = columnHeaders; + //Doc.GetProto(this.props.Document).columnHeaders = columnHeaders; this.props.resizeColumns(false, colWidth, index); } }; @@ -251,9 +242,7 @@ export class CollectionNoteTakingViewColumn extends React.Component
evContents} SetValue={this.headingChanged} contents={evContents} oneLine={true} />
+ {(this.props.columnHeaders?.length ?? 0) > 1 && ( + + )}
) : null; - const templatecols = `${this.columnWidth}px `; + const templatecols = this.columnWidth; const type = this.props.Document.type; return ( <> {headingView} - { -
-
- {this.props.renderChildren(this.props.docList)} -
+
+
+ {this.props.renderChildren(this.props.docList)} +
- {!this.props.chromeHidden && type !== DocumentType.PRES ? ( -
-
- -
-
- -
- {this.props.columnHeaders?.length && this.props.columnHeaders.length > 1 && ( - - )} + {!this.props.chromeHidden && type !== DocumentType.PRES ? ( +
+
+
- ) : null} -
- } +
+ +
+
+ ) : null} +
); } diff --git a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx index 8d659f790..a1309b11f 100644 --- a/src/client/views/collections/CollectionNoteTakingViewDivider.tsx +++ b/src/client/views/collections/CollectionNoteTakingViewDivider.tsx @@ -57,7 +57,6 @@ export class CollectionNoteTakingViewDivider extends React.Component
-- cgit v1.2.3-70-g09d2 From dff4b18106261af77d81211e5482a38b19b2a166 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 26 Aug 2022 09:19:15 -0400 Subject: more notetaking cleanup --- src/client/views/collections/CollectionNoteTakingView.scss | 12 +++++++++++- .../views/collections/CollectionNoteTakingViewColumn.tsx | 7 +------ 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/CollectionNoteTakingView.scss b/src/client/views/collections/CollectionNoteTakingView.scss index 5582fd391..08b13fd50 100644 --- a/src/client/views/collections/CollectionNoteTakingView.scss +++ b/src/client/views/collections/CollectionNoteTakingView.scss @@ -54,7 +54,7 @@ .collectionNoteTakingViewFieldColumn { height: 100%; display: flex; - flex-direction: colum; + overflow: hidden; } .collectionNoteTakingViewFieldColumn:hover { .collectionNoteTakingView-DocumentButtons { @@ -112,6 +112,11 @@ height: auto; } + .collectionNoteTakingView-columnStack { + height: 100%; + width: 100%; + display: inline-block; + } .collectionNoteTakingView-Nodes { width: 100%; height: 100%; @@ -123,6 +128,11 @@ left: 0; width: 100%; position: absolute; + margin: auto; + width: max-content; + height: max-content; + position: relative; + grid-auto-rows: 0px; } .collectionNoteTakingView-description { diff --git a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx index d951454ff..84d1c0205 100644 --- a/src/client/views/collections/CollectionNoteTakingViewColumn.tsx +++ b/src/client/views/collections/CollectionNoteTakingViewColumn.tsx @@ -262,19 +262,14 @@ export class CollectionNoteTakingViewColumn extends React.Component {headingView} -
+
{this.props.renderChildren(this.props.docList)}
-- cgit v1.2.3-70-g09d2 From 9263422913f30b54922f3c0d7290e36d4a509455 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 26 Aug 2022 12:43:00 -0400 Subject: extending pres trails to allow ink animation without using keyframes. --- src/client/views/MainView.tsx | 2 +- src/client/views/collections/CollectionMenu.tsx | 20 +++---- src/client/views/collections/TabDocView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 1 - .../views/nodes/CollectionFreeFormDocumentView.tsx | 10 ++-- src/client/views/nodes/trails/PresBox.tsx | 67 +++++++++++++++------- 6 files changed, 61 insertions(+), 41 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 06be4d194..515faa316 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -154,10 +154,10 @@ export class MainView extends React.Component { if (!MainView.Live) { DocServer.setPlaygroundFields([ 'dataTransition', + 'viewTransition', 'treeViewOpen', 'showSidebar', 'sidebarWidthPercent', - 'viewTransition', 'panX', 'panY', 'fitWidth', diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 0dc30e0fd..6a0f69359 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -726,18 +726,16 @@ export class CollectionFreeFormViewChrome extends React.Component { const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined; const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null); - PresBox.pinDocView(pinDoc, pinProps); + PresBox.pinDocView(pinDoc, pinProps, doc); pinDoc.onClick = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)'); Doc.AddDocToList(curPres, 'data', pinDoc, presSelected); if (!pinProps?.audioRange && duration !== undefined) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index c44b33ed0..210370d39 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -278,7 +278,6 @@ export class CollectionFreeFormView extends CollectionSubView { - p[val.key] = Cast(`${val}-indexed`, listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : []).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as number); + p[val.key] = Cast(doc[`${val.key}-indexed`], listSpec('number'), fillIn ? [NumCast(doc[val.key], val.val)] : []).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as number); return p; }, {} as { [val: string]: Opt }); } public static getStringValues(doc: Doc, time: number) { return CollectionFreeFormDocumentView.animStringFields.reduce((p, val) => { - p[val] = Cast(`${val}-indexed`, listSpec('string'), [StrCast(doc[val])]).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as string); + p[val] = Cast(doc[`${val}-indexed`], listSpec('string'), [StrCast(doc[val])]).reduce((p, v, i) => ((i <= Math.round(time) && v !== undefined) || p === undefined ? v : p), undefined as any as string); return p; }, {} as { [val: string]: Opt }); } @@ -140,8 +140,8 @@ export class CollectionFreeFormDocumentView extends DocComponent (doc._viewTransition = doc.dataTransition = 'all 1s')); + public static gotoKeyframe(docs: Doc[], duration = 1000) { + docs.forEach(doc => (doc._viewTransition = doc.dataTransition = `all ${duration}ms`)); setTimeout( () => docs.forEach(doc => { @@ -226,7 +226,6 @@ export class CollectionFreeFormDocumentView extends DocComponent {this.props.renderCutoffProvider(this.props.Document) ? ( diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index eb40089ec..f254eaba6 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -6,7 +6,7 @@ import { observer } from 'mobx-react'; import { ColorState, SketchPicker } from 'react-color'; import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal'; import { Doc, DocListCast, DocListCastAsync, FieldResult } from '../../../../fields/Doc'; -import { InkTool } from '../../../../fields/InkField'; +import { InkField, InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; import { listSpec } from '../../../../fields/Schema'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; @@ -19,8 +19,7 @@ import { SelectionManager } from '../../../util/SelectionManager'; import { SettingsManager } from '../../../util/SettingsManager'; import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { CollectionDockingView } from '../../collections/CollectionDockingView'; -import { MarqueeViewBounds } from '../../collections/collectionFreeForm'; -import { CollectionFreeFormViewChrome } from '../../collections/CollectionMenu'; +import { CollectionFreeFormView, MarqueeViewBounds } from '../../collections/collectionFreeForm'; import { CollectionView } from '../../collections/CollectionView'; import { TabDocView } from '../../collections/TabDocView'; import { ViewBoxBaseComponent } from '../../DocComponent'; @@ -30,6 +29,8 @@ import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentVie import { FieldView, FieldViewProps } from '../FieldView'; import './PresBox.scss'; import { PresEffect, PresMovement, PresStatus } from './PresEnums'; +import { Copy } from '../../../../fields/FieldSymbols'; +import { CollectionFreeFormViewChrome } from '../../collections/CollectionMenu'; export interface PinProps { audioRange?: boolean; @@ -317,8 +318,15 @@ export class PresBox extends ViewBoxBaseComponent() { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; if (activeItem.presActiveFrame !== undefined) { + const transTime = NumCast(activeItem.presDuration, 500); const context = DocCast(DocCast(activeItem.presentationTargetDoc).context); - context && CollectionFreeFormViewChrome.gotoKeyFrame(context, NumCast(activeItem.presActiveFrame)); + if (context) { + const contextView = DocumentManager.Instance.getFirstDocumentView(context); + if (contextView?.ComponentView) { + CollectionFreeFormDocumentView.gotoKeyframe((contextView.ComponentView as CollectionFreeFormView).childDocs.slice(), transTime); + context._currentFrame = NumCast(activeItem.presActiveFrame); + } + } } if (from?.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) { DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia); @@ -353,8 +361,6 @@ export class PresBox extends ViewBoxBaseComponent() { navigateToView = (targetDoc: Doc, activeItem: Doc) => { clearTimeout(this._navTimer); const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document; - if (bestTarget) console.log(bestTarget.title, bestTarget.type); - else console.log('no best target'); if (bestTarget) this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem, false); }; @@ -363,17 +369,29 @@ export class PresBox extends ViewBoxBaseComponent() { const pannable = [DocumentType.IMG].includes(target.type as any) || (target.type === DocumentType.COL && target._viewType === CollectionViewType.Freeform); const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(target.type as any); const clippable = [DocumentType.COMPARISON].includes(target.type as any); - return { scrollable, pannable, temporal, clippable }; + const dataview = [DocumentType.INK].includes(target.type as any) && target.activeFrame === undefined; + return { scrollable, pannable, temporal, clippable, dataview }; } // navigates to the bestTarget document by making sure it is on screen, // then it applies the view specs stored in activeItem to @action static navigateToDoc(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) { - bestTarget._viewTransition = activeItem.presTransition ? `transform ${activeItem.presTransition}ms` : 'all 0.5s'; - const { scrollable, pannable, temporal, clippable } = this.pinDataTypes(bestTarget); + const transTime = NumCast(activeItem.presDuration, 500); + const presTransitionTime = `all ${transTime}ms`; + bestTarget._viewTransition = presTransitionTime; + const { scrollable, pannable, temporal, clippable, dataview } = this.pinDataTypes(bestTarget); if (clippable) bestTarget._clipWidth = activeItem.presPinClipWidth; if (temporal) bestTarget._currentTimecode = activeItem.presStartTime; if (scrollable) bestTarget._scrollTop = activeItem.presPinViewScroll; + if (dataview) { + bestTarget._dataTransition = presTransitionTime; + bestTarget.data = (activeItem.presData as any as InkField)[Copy](); + bestTarget.x = NumCast(activeItem.presX); + bestTarget.y = NumCast(activeItem.presY); + bestTarget.width = NumCast(activeItem.presWidth); + bestTarget.height = NumCast(activeItem.presHeight); + } + if (pannable) { const contentBounds = Cast(activeItem.contentBounds, listSpec('number')); if (contentBounds) { @@ -389,14 +407,17 @@ export class PresBox extends ViewBoxBaseComponent() { bestTarget._viewScale = activeItem.presPinViewScale; } } - return setTimeout(() => (bestTarget._viewTransition = undefined), activeItem.presTransition ? NumCast(activeItem.presTransition) + 10 : 510); + return setTimeout(() => { + bestTarget._viewTransition = undefined; + if (dataview) bestTarget._dataTransition = undefined; + }, transTime + 10); } /// copies values from the targetDoc (which is the prototype of the pinDoc) to /// reserved fields on the pinDoc so that those values can be restored to the /// target doc when navigating to it. @action - static pinDocView(pinDoc: Doc, pinProps: PinProps | undefined) { + static pinDocView(pinDoc: Doc, pinProps: PinProps | undefined, targetDoc: Doc) { if (pinProps?.pinWithView) { // If pinWithView option set then update scale and x / y props of slide const bounds = pinProps.pinWithView.bounds; @@ -407,13 +428,20 @@ export class PresBox extends ViewBoxBaseComponent() { pinDoc.contentBounds = new List([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]); } if (pinProps?.pinDocView) { - const { scrollable, pannable, temporal, clippable } = this.pinDataTypes(pinDoc); - pinDoc.presPinView = (pinProps?.pinWithView ? true : false) || scrollable || temporal || pannable || clippable; + const { scrollable, pannable, temporal, clippable, dataview } = this.pinDataTypes(pinDoc); + pinDoc.presPinView = (pinProps?.pinWithView ? true : false) || scrollable || temporal || pannable || clippable || dataview || pinProps.activeFrame !== undefined; if (scrollable) pinDoc.presPinViewScroll = pinDoc._scrollTop; - else if (clippable) pinDoc.presPinClipWidth = pinDoc._clipWidth; - else if (temporal) pinDoc.presEndTime = NumCast((pinDoc.presStartTime = pinDoc._currentTimecode)) + 0.1; - else if (pannable) { + if (clippable) pinDoc.presPinClipWidth = pinDoc._clipWidth; + if (temporal) pinDoc.presEndTime = NumCast((pinDoc.presStartTime = pinDoc._currentTimecode)) + 0.1; + if (dataview) { + pinDoc.presData = (targetDoc.data as InkField)[Copy](); + pinDoc.presX = NumCast(targetDoc.x); + pinDoc.presY = NumCast(targetDoc.y); + pinDoc.presWidth = NumCast(targetDoc.width); + pinDoc.presHeight = NumCast(targetDoc.height); + } + if (pannable) { const panX = NumCast(pinDoc._panX); const panY = NumCast(pinDoc._panY); const pw = NumCast(pinProps.panelWidth); @@ -493,8 +521,6 @@ export class PresBox extends ViewBoxBaseComponent() { // After navigating to the document, if it is added as a presPinView then it will // adjust the pan and scale to that of the pinView when it was added. if (activeItem.presPinView) { - console.log(targetDoc.title); - console.log('presPinView in PresBox.tsx:420'); // if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target this.navigateToView(targetDoc, activeItem); } @@ -866,10 +892,9 @@ export class PresBox extends ViewBoxBaseComponent() { //Regular click @action selectElement = async (doc: Doc) => { - const context = Cast(doc.context, Doc, null); this.gotoDocument(this.childDocs.indexOf(doc), this.activeItem); - if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(context), 0); - else this.updateCurrentPresentation(context); + if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(DocCast(doc.context)), 0); + else this.updateCurrentPresentation(DocCast(doc.context)); }; //Command click -- cgit v1.2.3-70-g09d2 From 7278aaa71a13f57cdc371bd771f5fcc6419707b7 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 26 Aug 2022 13:25:23 -0400 Subject: added x,y,w,h view pinning for all documents. --- src/client/views/DocumentButtonBar.tsx | 3 +- src/client/views/nodes/trails/PresBox.tsx | 59 +++++++++++++++--------- src/client/views/nodes/trails/PresElementBox.tsx | 15 +++++- 3 files changed, 52 insertions(+), 25 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 40fc8dae6..76e2d64a2 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -26,6 +26,7 @@ import { DashFieldView } from './nodes/formattedText/DashFieldView'; import { GoogleRef } from './nodes/formattedText/FormattedTextBox'; import { TemplateMenu } from './TemplateMenu'; import React = require('react'); +import { DocumentType } from '../documents/DocumentTypes'; const higflyout = require('@hig/flyout'); export const { anchorPoints } = higflyout; export const Flyout = higflyout.default; @@ -243,7 +244,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV .views() .filter(v => v) .map(dv => dv!.rootDoc); - TabDocView.PinDoc(docs, { pinDocView: true, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) }); + TabDocView.PinDoc(docs, { pinDocView: !docs.some(doc => !e.shiftKey && doc.type === DocumentType.RTF), activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) }); }}>
diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index f254eaba6..eb6dad327 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -6,8 +6,10 @@ import { observer } from 'mobx-react'; import { ColorState, SketchPicker } from 'react-color'; import { Bounce, Fade, Flip, LightSpeed, Roll, Rotate, Zoom } from 'react-reveal'; import { Doc, DocListCast, DocListCastAsync, FieldResult } from '../../../../fields/Doc'; +import { Copy } from '../../../../fields/FieldSymbols'; import { InkField, InkTool } from '../../../../fields/InkField'; import { List } from '../../../../fields/List'; +import { ObjectField } from '../../../../fields/ObjectField'; import { listSpec } from '../../../../fields/Schema'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents } from '../../../../Utils'; @@ -29,8 +31,6 @@ import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentVie import { FieldView, FieldViewProps } from '../FieldView'; import './PresBox.scss'; import { PresEffect, PresMovement, PresStatus } from './PresEnums'; -import { Copy } from '../../../../fields/FieldSymbols'; -import { CollectionFreeFormViewChrome } from '../../collections/CollectionMenu'; export interface PinProps { audioRange?: boolean; @@ -370,7 +370,8 @@ export class PresBox extends ViewBoxBaseComponent() { const temporal = [DocumentType.AUDIO, DocumentType.VID].includes(target.type as any); const clippable = [DocumentType.COMPARISON].includes(target.type as any); const dataview = [DocumentType.INK].includes(target.type as any) && target.activeFrame === undefined; - return { scrollable, pannable, temporal, clippable, dataview }; + const textview = [DocumentType.RTF].includes(target.type as any) && target.activeFrame === undefined; + return { scrollable, pannable, temporal, clippable, dataview, textview }; } // navigates to the bestTarget document by making sure it is on screen, // then it applies the view specs stored in activeItem to @@ -378,20 +379,18 @@ export class PresBox extends ViewBoxBaseComponent() { static navigateToDoc(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) { const transTime = NumCast(activeItem.presDuration, 500); const presTransitionTime = `all ${transTime}ms`; + const { scrollable, pannable, temporal, clippable, dataview, textview } = this.pinDataTypes(bestTarget); bestTarget._viewTransition = presTransitionTime; - const { scrollable, pannable, temporal, clippable, dataview } = this.pinDataTypes(bestTarget); + bestTarget._dataTransition = dataview || textview ? presTransitionTime : undefined; + bestTarget.x = NumCast(activeItem.presX); + bestTarget.y = NumCast(activeItem.presY); + bestTarget.width = NumCast(activeItem.presWidth); + bestTarget.height = NumCast(activeItem.presHeight); if (clippable) bestTarget._clipWidth = activeItem.presPinClipWidth; if (temporal) bestTarget._currentTimecode = activeItem.presStartTime; if (scrollable) bestTarget._scrollTop = activeItem.presPinViewScroll; - if (dataview) { - bestTarget._dataTransition = presTransitionTime; - bestTarget.data = (activeItem.presData as any as InkField)[Copy](); - bestTarget.x = NumCast(activeItem.presX); - bestTarget.y = NumCast(activeItem.presY); - bestTarget.width = NumCast(activeItem.presWidth); - bestTarget.height = NumCast(activeItem.presHeight); - } - + if (dataview) Doc.GetProto(bestTarget).data = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; + if (textview) Doc.GetProto(bestTarget).text = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; if (pannable) { const contentBounds = Cast(activeItem.contentBounds, listSpec('number')); if (contentBounds) { @@ -409,7 +408,7 @@ export class PresBox extends ViewBoxBaseComponent() { } return setTimeout(() => { bestTarget._viewTransition = undefined; - if (dataview) bestTarget._dataTransition = undefined; + if (dataview || textview) bestTarget._dataTransition = undefined; }, transTime + 10); } @@ -428,19 +427,18 @@ export class PresBox extends ViewBoxBaseComponent() { pinDoc.contentBounds = new List([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]); } if (pinProps?.pinDocView) { - const { scrollable, pannable, temporal, clippable, dataview } = this.pinDataTypes(pinDoc); - pinDoc.presPinView = (pinProps?.pinWithView ? true : false) || scrollable || temporal || pannable || clippable || dataview || pinProps.activeFrame !== undefined; + const { scrollable, pannable, temporal, clippable, dataview, textview } = this.pinDataTypes(pinDoc); + pinDoc.presPinView = (pinProps?.pinWithView ? true : false) || scrollable || temporal || pannable || clippable || dataview || textview || pinProps.activeFrame !== undefined; + pinDoc.presX = NumCast(targetDoc.x); + pinDoc.presY = NumCast(targetDoc.y); + pinDoc.presWidth = NumCast(targetDoc.width); + pinDoc.presHeight = NumCast(targetDoc.height); if (scrollable) pinDoc.presPinViewScroll = pinDoc._scrollTop; if (clippable) pinDoc.presPinClipWidth = pinDoc._clipWidth; if (temporal) pinDoc.presEndTime = NumCast((pinDoc.presStartTime = pinDoc._currentTimecode)) + 0.1; - if (dataview) { - pinDoc.presData = (targetDoc.data as InkField)[Copy](); - pinDoc.presX = NumCast(targetDoc.x); - pinDoc.presY = NumCast(targetDoc.y); - pinDoc.presWidth = NumCast(targetDoc.width); - pinDoc.presHeight = NumCast(targetDoc.height); - } + if (textview) pinDoc.presData = targetDoc.text instanceof ObjectField ? targetDoc.text[Copy]() : targetDoc.text; + if (dataview) pinDoc.presData = targetDoc.data instanceof ObjectField ? targetDoc.data[Copy]() : targetDoc.data; if (pannable) { const panX = NumCast(pinDoc._panX); const panY = NumCast(pinDoc._panY); @@ -512,9 +510,24 @@ export class PresBox extends ViewBoxBaseComponent() { // openInTab(targetDoc); } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) { LightboxView.SetLightboxDoc(undefined); + const transTime = NumCast(activeItem.presDuration, 500); + const presTransitionTime = `all ${transTime}ms`; + targetDoc._dataTransition = presTransitionTime; + targetDoc.x = NumCast(activeItem.presX); + targetDoc.y = NumCast(activeItem.presY); + targetDoc.width = NumCast(activeItem.presWidth); + targetDoc.height = NumCast(activeItem.presHeight); await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) { LightboxView.SetLightboxDoc(undefined); + + const transTime = NumCast(activeItem.presDuration, 500); + const presTransitionTime = `all ${transTime}ms`; + targetDoc._dataTransition = presTransitionTime; + targetDoc.x = NumCast(activeItem.presX); + targetDoc.y = NumCast(activeItem.presY); + targetDoc.width = NumCast(activeItem.presWidth); + targetDoc.height = NumCast(activeItem.presHeight); //awaiting jump so that new scale can be found, since jumping is async await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right } diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 91196ca21..38a87c34b 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -3,7 +3,7 @@ import { Tooltip } from '@material-ui/core'; import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../../fields/Doc'; -import { Id } from '../../../../fields/FieldSymbols'; +import { Copy, Id } from '../../../../fields/FieldSymbols'; import { List } from '../../../../fields/List'; import { Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils'; @@ -24,6 +24,8 @@ import { PresBox } from './PresBox'; import './PresElementBox.scss'; import { PresMovement } from './PresEnums'; import React = require('react'); +import { InkField } from '../../../../fields/InkField'; +import { RichTextField } from '../../../../fields/RichTextField'; /** * This class models the view a document added to presentation will have in the presentation. * It involves some functionality for its buttons and options. @@ -320,6 +322,12 @@ export class PresElementBox extends ViewBoxBaseComponent() { case DocumentType.RTF: const scroll = targetDoc._scrollTop; activeItem.presPinViewScroll = scroll; + if (targetDoc.type === DocumentType.RTF) { + activeItem.presData = targetDoc.text instanceof RichTextField ? targetDoc.text[Copy]() : targetDoc.text; + } + break; + case DocumentType.INK: + activeItem.presData = targetDoc.data instanceof InkField ? targetDoc.data[Copy]() : targetDoc.data; break; case DocumentType.VID: case DocumentType.AUDIO: @@ -337,6 +345,11 @@ export class PresElementBox extends ViewBoxBaseComponent() { activeItem.presPinViewY = y; activeItem.presPinViewScale = scale; } + + activeItem.presX = NumCast(targetDoc.x); + activeItem.presY = NumCast(targetDoc.y); + activeItem.presWidth = NumCast(targetDoc.width); + activeItem.presHeight = NumCast(targetDoc.height); }; @computed get recordingIsInOverlay() { -- cgit v1.2.3-70-g09d2 From b1044d1d79c1e06769f74df514e12557426b67be Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 26 Aug 2022 16:16:21 -0400 Subject: trying to clean up transition times for presbox / jumptoDoc, etc. --- src/client/util/DocumentManager.ts | 18 +- .../views/collections/CollectionNoteTakingView.tsx | 2 +- .../views/collections/CollectionStackingView.tsx | 5 +- src/client/views/collections/TabDocView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 5 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 1 + src/client/views/nodes/trails/PresBox.tsx | 662 +++++++++------------ src/client/views/nodes/trails/PresElementBox.tsx | 9 - 8 files changed, 287 insertions(+), 417 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 52b643c04..2ca5d1095 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -154,7 +154,7 @@ export class DocumentManager { CollectionDockingView.AddSplit(doc, 'right'); finished?.(); }; - public jumpToDocument = async ( + public jumpToDocument = ( targetDoc: Doc, // document to display willZoom: boolean, // whether to zoom doc to take up most of screen createViewFunc = DocumentManager.addView, // how to create a view of the doc if it doesn't exist @@ -165,8 +165,8 @@ export class DocumentManager { finished?: () => void, originalTarget?: Doc, noSelect?: boolean, - presZoom?: number - ): Promise => { + presZoomScale?: number + ): void => { originalTarget = originalTarget ?? targetDoc; const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView; const docView = getFirstDocView(targetDoc, originatingDoc); @@ -207,7 +207,7 @@ export class DocumentManager { finished?.(); }; const annoContainerView = (!wasHidden || resolvedTarget !== annotatedDoc) && annotatedDoc && getFirstDocView(annotatedDoc); - const contextDocs = docContext.length ? await DocListCastAsync(docContext[0].data) : undefined; + const contextDocs = docContext.length ? DocListCast(docContext[0].data) : undefined; const contextDoc = contextDocs?.find(doc => Doc.AreProtosEqual(doc, targetDoc) || Doc.AreProtosEqual(doc, annotatedDoc)) ? docContext.lastElement() : undefined; const targetDocContext = contextDoc || annotatedDoc; const targetDocContextView = (targetDocContext && getFirstDocView(targetDocContext)) || (wasHidden && annoContainerView); // if we have an annotation container and the target was hidden, then try again because we just un-hid the document above @@ -218,7 +218,7 @@ export class DocumentManager { annoContainerView.focus(targetDoc, { originalTarget, willZoom, - scale: presZoom, + scale: presZoomScale, afterFocus: (didFocus: boolean) => new Promise(res => { focusAndFinish(true); @@ -237,7 +237,7 @@ export class DocumentManager { focusView.focus(originalTarget ?? targetDoc, { originalTarget, willZoom, - scale: presZoom, + scale: presZoomScale, afterFocus: (didFocus: boolean) => new Promise(res => { focusAndFinish(forceDidFocus || didFocus); @@ -265,7 +265,9 @@ export class DocumentManager { afterFocus: async () => { targetDocContext._viewTransition = undefined; if (targetDocContext.layoutKey === 'layout_icon') { - targetDocContextView.iconify(() => this.jumpToDocument(resolvedTarget ?? targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc, finished, originalTarget, noSelect, presZoom)); + targetDocContextView.iconify(() => + this.jumpToDocument(resolvedTarget ?? targetDoc, willZoom, createViewFunc, docContext, linkDoc, closeContextIfNotFound, originatingDoc, finished, originalTarget, noSelect, presZoomScale) + ); } return ViewAdjustment.doNothing; }, @@ -309,7 +311,7 @@ export class DocumentManager { const docContextView = this.getFirstDocumentView(docContext[0]); if (docContextView) { return docContextView.iconify(() => - this.jumpToDocument(targetDoc, willZoom, createViewFunc, docContext.slice(1, docContext.length), linkDoc, closeContextIfNotFound, originatingDoc, finished, originalTarget, noSelect, presZoom) + this.jumpToDocument(targetDoc, willZoom, createViewFunc, docContext.slice(1, docContext.length), linkDoc, closeContextIfNotFound, originatingDoc, finished, originalTarget, noSelect, presZoomScale) ); } } diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 615141485..92c0bc341 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -201,7 +201,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { const top = found.getBoundingClientRect().top; const localTop = this.props.ScreenToLocalTransform().transformPoint(0, top); if (Math.floor(localTop[1]) !== 0) { - smoothScroll((focusSpeed = doc.presTransition || doc.presTransition === 0 ? NumCast(doc.presTransition) : 500), this._mainCont!, localTop[1] + this._mainCont!.scrollTop); + smoothScroll((focusSpeed = NumCast(doc.focusSpeed, 500)), this._mainCont!, localTop[1] + this._mainCont!.scrollTop); } } const endFocus = async (moved: boolean) => (options?.afterFocus ? options?.afterFocus(moved) : ViewAdjustment.doNothing); diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index 71834607c..7f142727c 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -259,7 +259,7 @@ export class CollectionStackingView extends CollectionSubView options?.afterFocus?.(moved) ?? ViewAdjustment.doNothing; @@ -494,8 +494,7 @@ export class CollectionStackingView extends CollectionSubView { if (targInd === -1) { this.addDocument(docs); - } - else { + } else { const childDocs = this.childDocList; if (childDocs) { childDocs.splice(targInd, 0, ...docs); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index f7b48adf6..49228a808 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -366,7 +366,7 @@ export class TabDocView extends React.Component { focusFunc = (doc: Doc, options?: DocFocusOptions) => { const shrinkwrap = options?.originalTarget === this._document && this.view?.ComponentView?.shrinkWrap; if (shrinkwrap && this._document) { - const focusSpeed = 1000; + const focusSpeed = NumCast(this._document.focusSpeed, 500); shrinkwrap(); this._document._viewTransition = `transform ${focusSpeed}ms`; setTimeout( diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 210370d39..ede113a9f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1146,7 +1146,7 @@ export class CollectionFreeFormView extends CollectionSubView() { constructor(props: any) { super(props); if ((Doc.ActivePresentation = this.rootDoc)) runInAction(() => (PresBox.Instance = this)); - this.props.Document.presentationFieldKey = this.fieldKey; // provide info to the presElement script so that it can look up rendering information about the presBox } @observable public static Instance: PresBox; @@ -127,12 +126,7 @@ export class PresBox extends ViewBoxBaseComponent() { @observable _treeViewMap: Map = new Map(); @computed get tagDocs() { - const tagDocs: Doc[] = []; - for (const doc of this.childDocs) { - const tagDoc = Cast(doc.presentationTargetDoc, Doc, null); - tagDocs.push(tagDoc); - } - return tagDocs; + return this.childDocs.map(doc => Cast(doc.presentationTargetDoc, Doc, null)); } @computed get itemIndex() { return NumCast(this.rootDoc._itemIndex); @@ -145,11 +139,11 @@ export class PresBox extends ViewBoxBaseComponent() { } @computed get scrollable(): boolean { if (this.targetDoc.type === DocumentType.PDF || this.targetDoc.type === DocumentType.WEB || this.targetDoc.type === DocumentType.RTF || this.targetDoc._viewType === CollectionViewType.Stacking) return true; - else return false; + return false; } @computed get panable(): boolean { if ((this.targetDoc.type === DocumentType.COL && this.targetDoc._viewType === CollectionViewType.Freeform) || this.targetDoc.type === DocumentType.IMG) return true; - else return false; + return false; } @computed get selectedDocumentView() { if (SelectionManager.Views().length) return SelectionManager.Views()[0]; @@ -202,8 +196,7 @@ export class PresBox extends ViewBoxBaseComponent() { @action updateCurrentPresentation = (pres?: Doc) => { - if (pres) Doc.ActivePresentation = pres; - else Doc.ActivePresentation = this.rootDoc; + Doc.ActivePresentation = pres ?? this.rootDoc; document.removeEventListener('keydown', PresBox.keyEventsWrapper, true); document.addEventListener('keydown', PresBox.keyEventsWrapper, true); this._presKeyEventsActive = true; @@ -318,7 +311,7 @@ export class PresBox extends ViewBoxBaseComponent() { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; if (activeItem.presActiveFrame !== undefined) { - const transTime = NumCast(activeItem.presDuration, 500); + const transTime = NumCast(activeItem.presTransition, 500); const context = DocCast(DocCast(activeItem.presentationTargetDoc).context); if (context) { const contextView = DocumentManager.Instance.getFirstDocumentView(context); @@ -340,12 +333,8 @@ export class PresBox extends ViewBoxBaseComponent() { } if (targetDoc) { Doc.linkFollowHighlight(targetDoc.annotationOn instanceof Doc ? [targetDoc, targetDoc.annotationOn] : targetDoc); - targetDoc && - runInAction(() => { - if (activeItem.presMovement === PresMovement.Jump) targetDoc.focusSpeed = 0; - else targetDoc.focusSpeed = activeItem.presTransition ? activeItem.presTransition : 500; - }); - setTimeout(() => (targetDoc.focusSpeed = 500), this.activeItem.presTransition ? NumCast(this.activeItem.presTransition) + 10 : 510); + targetDoc && runInAction(() => (targetDoc.focusSpeed = activeItem.presMovement === PresMovement.Jump ? 0 : NumCast(activeItem.presTransition, 500))); + setTimeout(() => (targetDoc.focusSpeed = undefined), NumCast(targetDoc.focusSpeed) + 10); } if (targetDoc?.lastFrame !== undefined) { targetDoc._currentFrame = 0; @@ -356,14 +345,6 @@ export class PresBox extends ViewBoxBaseComponent() { this.onHideDocument(); //Handles hide after/before } }); - - _navTimer!: NodeJS.Timeout; - navigateToView = (targetDoc: Doc, activeItem: Doc) => { - clearTimeout(this._navTimer); - const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document; - if (bestTarget) this._navTimer = PresBox.navigateToDoc(bestTarget, activeItem, false); - }; - static pinDataTypes(target: Doc) { const scrollable = [DocumentType.PDF, DocumentType.RTF, DocumentType.WEB].includes(target.type as any) || target._viewType === CollectionViewType.Stacking; const pannable = [DocumentType.IMG].includes(target.type as any) || (target.type === DocumentType.COL && target._viewType === CollectionViewType.Freeform); @@ -373,43 +354,29 @@ export class PresBox extends ViewBoxBaseComponent() { const textview = [DocumentType.RTF].includes(target.type as any) && target.activeFrame === undefined; return { scrollable, pannable, temporal, clippable, dataview, textview }; } - // navigates to the bestTarget document by making sure it is on screen, - // then it applies the view specs stored in activeItem to + @action - static navigateToDoc(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) { - const transTime = NumCast(activeItem.presDuration, 500); + static restoreTargetDocView(bestTarget: Doc, activeItem: Doc, jumpToDoc: boolean) { + const transTime = NumCast(activeItem.presTransition, 500); const presTransitionTime = `all ${transTime}ms`; const { scrollable, pannable, temporal, clippable, dataview, textview } = this.pinDataTypes(bestTarget); bestTarget._viewTransition = presTransitionTime; - bestTarget._dataTransition = dataview || textview ? presTransitionTime : undefined; - bestTarget.x = NumCast(activeItem.presX); - bestTarget.y = NumCast(activeItem.presY); - bestTarget.width = NumCast(activeItem.presWidth); - bestTarget.height = NumCast(activeItem.presHeight); if (clippable) bestTarget._clipWidth = activeItem.presPinClipWidth; if (temporal) bestTarget._currentTimecode = activeItem.presStartTime; if (scrollable) bestTarget._scrollTop = activeItem.presPinViewScroll; if (dataview) Doc.GetProto(bestTarget).data = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; if (textview) Doc.GetProto(bestTarget).text = activeItem.presData instanceof ObjectField ? activeItem.presData[Copy]() : activeItem.presData; if (pannable) { - const contentBounds = Cast(activeItem.contentBounds, listSpec('number')); + bestTarget._panX = activeItem.presPinViewX; + bestTarget._panY = activeItem.presPinViewY; + bestTarget._viewScale = activeItem.presPinViewScale; + const contentBounds = Cast(activeItem.presPinViewBounds, listSpec('number')); if (contentBounds) { - bestTarget._panX = (contentBounds[0] + contentBounds[2]) / 2; - bestTarget._panY = (contentBounds[1] + contentBounds[3]) / 2; const dv = DocumentManager.Instance.getDocumentView(bestTarget); - if (dv) { - bestTarget._viewScale = Math.min(dv.props.PanelHeight() / (contentBounds[3] - contentBounds[1]), dv.props.PanelWidth() / (contentBounds[2] - contentBounds[0])); - } - } else { - bestTarget._panX = activeItem.presPinViewX; - bestTarget._panY = activeItem.presPinViewY; - bestTarget._viewScale = activeItem.presPinViewScale; + dv && (bestTarget._viewScale = Math.min(dv.props.PanelHeight() / (contentBounds[3] - contentBounds[1]), dv.props.PanelWidth() / (contentBounds[2] - contentBounds[0]))); } } - return setTimeout(() => { - bestTarget._viewTransition = undefined; - if (dataview || textview) bestTarget._dataTransition = undefined; - }, transTime + 10); + return setTimeout(() => (bestTarget._viewTransition = undefined), transTime + 10); } /// copies values from the targetDoc (which is the prototype of the pinDoc) to @@ -424,7 +391,7 @@ export class PresBox extends ViewBoxBaseComponent() { pinDoc.presPinViewX = bounds.left + bounds.width / 2; pinDoc.presPinViewY = bounds.top + bounds.height / 2; pinDoc.presPinViewScale = pinProps.pinWithView.scale; - pinDoc.contentBounds = new List([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]); + pinDoc.presPinViewBounds = new List([bounds.left, bounds.top, bounds.left + bounds.width, bounds.top + bounds.height]); } if (pinProps?.pinDocView) { const { scrollable, pannable, temporal, clippable, dataview, textview } = this.pinDataTypes(pinDoc); @@ -446,7 +413,7 @@ export class PresBox extends ViewBoxBaseComponent() { const ph = NumCast(pinProps.panelHeight); const ps = NumCast(pinDoc._viewScale); if (pw && ph && ps) { - pinDoc.contentBounds = new List([panX - pw / 2 / ps, panY - ph / 2 / ps, panX + pw / 2 / ps, panY + ph / 2 / ps]); + pinDoc.presPinViewBounds = new List([panX - pw / 2 / ps, panY - ph / 2 / ps, panX + pw / 2 / ps, panY + ph / 2 / ps]); } pinDoc.presPinViewX = panX; pinDoc.presPinViewY = panY; @@ -455,6 +422,7 @@ export class PresBox extends ViewBoxBaseComponent() { } } + _navTimer!: NodeJS.Timeout; /** * This method makes sure that cursor navigates to the element that * has the option open and last in the group. @@ -504,38 +472,31 @@ export class PresBox extends ViewBoxBaseComponent() { finished?.(); } }; - // If openDocument is selected then it should open the document for the user - if (activeItem.openDocument) { - LightboxView.SetLightboxDoc(targetDoc); - // openInTab(targetDoc); - } else if (curDoc.presMovement === PresMovement.Pan && targetDoc) { - LightboxView.SetLightboxDoc(undefined); - const transTime = NumCast(activeItem.presDuration, 500); + if (activeItem.presPinView && DocCast(targetDoc.context)?._currentFrame === undefined) { + const transTime = NumCast(activeItem.presTransition, 500); const presTransitionTime = `all ${transTime}ms`; targetDoc._dataTransition = presTransitionTime; - targetDoc.x = NumCast(activeItem.presX); - targetDoc.y = NumCast(activeItem.presY); - targetDoc.width = NumCast(activeItem.presWidth); - targetDoc.height = NumCast(activeItem.presHeight); - await DocumentManager.Instance.jumpToDocument(targetDoc, false, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true); // documents open in new tab instead of on right - } else if ((curDoc.presMovement === PresMovement.Zoom || curDoc.presMovement === PresMovement.Jump) && targetDoc) { + targetDoc.x = NumCast(activeItem.presX, NumCast(targetDoc.x)); + targetDoc.y = NumCast(activeItem.presY, NumCast(targetDoc.y)); + targetDoc.width = NumCast(activeItem.presWidth, NumCast(targetDoc.width)); + targetDoc.height = NumCast(activeItem.presHeight, NumCast(targetDoc.height)); + setTimeout(() => (targetDoc._dataTransition = undefined), transTime + 10); + } + // If openDocument is selected then it should open the document for the user + if (activeItem.openDocument) { + LightboxView.SetLightboxDoc(targetDoc); // openInTab(targetDoc); + } else if (targetDoc && curDoc.presMovement !== PresMovement.None && targetDoc) { LightboxView.SetLightboxDoc(undefined); - - const transTime = NumCast(activeItem.presDuration, 500); - const presTransitionTime = `all ${transTime}ms`; - targetDoc._dataTransition = presTransitionTime; - targetDoc.x = NumCast(activeItem.presX); - targetDoc.y = NumCast(activeItem.presY); - targetDoc.width = NumCast(activeItem.presWidth); - targetDoc.height = NumCast(activeItem.presHeight); - //awaiting jump so that new scale can be found, since jumping is async - await DocumentManager.Instance.jumpToDocument(targetDoc, true, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); // documents open in new tab instead of on right + const zooming = curDoc.presMovement !== PresMovement.Pan; + DocumentManager.Instance.jumpToDocument(targetDoc, zooming, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, includesDoc || tab ? undefined : resetSelection, undefined, true, NumCast(curDoc.presZoom)); } // After navigating to the document, if it is added as a presPinView then it will // adjust the pan and scale to that of the pinView when it was added. if (activeItem.presPinView) { // if targetDoc is not displayed but one of its aliases is, then we need to modify that alias, not the original target - this.navigateToView(targetDoc, activeItem); + clearTimeout(this._navTimer); + const bestTarget = DocumentManager.Instance.getFirstDocumentView(targetDoc)?.props.Document; + if (bestTarget) this._navTimer = PresBox.restoreTargetDocView(bestTarget, activeItem, false); } }; @@ -1488,12 +1449,8 @@ export class PresBox extends ViewBoxBaseComponent() { style={{ display: targetDoc.type === DocumentType.AUDIO ? 'none' : 'block' }} className={'toolbar-slider'} id="duration-slider" - onPointerDown={() => { - this._batch = UndoManager.StartBatch('presDuration'); - }} - onPointerUp={() => { - if (this._batch) this._batch.end(); - }} + onPointerDown={() => (this._batch = UndoManager.StartBatch('presDuration'))} + onPointerUp={() => this._batch?.end()} onChange={(e: React.ChangeEvent) => { e.stopPropagation(); this.setDurationTime(e.target.value); @@ -1650,28 +1607,12 @@ export class PresBox extends ViewBoxBaseComponent() { }); }; - @computed get mediaStopSlides() { - const activeItem: Doc = this.activeItem; - const list = this.childDocs.map((doc, i) => { - if (i > this.itemIndex) { - return ( - - ); - } - }); - return list; - } - @computed get mediaOptionsDropdown() { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; const clipStart: number = NumCast(activeItem.clipStart); const clipEnd: number = NumCast(activeItem.clipEnd); - const duration = Math.round(NumCast(activeItem[`${Doc.LayoutFieldKey(activeItem)}-duration`]) * 10); const mediaStopDocInd: number = NumCast(activeItem.mediaStopDoc); - const mediaStopDocStr: string = mediaStopDocInd ? mediaStopDocInd + '. ' + this.childDocs[mediaStopDocInd - 1].title : ''; if (activeItem && targetDoc) { return (
@@ -1842,55 +1783,53 @@ export class PresBox extends ViewBoxBaseComponent() { @computed get newDocumentToolbarDropdown() { return ( -
-
e.stopPropagation()} - onPointerUp={e => e.stopPropagation()} - onPointerDown={e => e.stopPropagation()}> -
-
{ - this.layout = 'blank'; - this.createNewSlide(this.layout); - })} - /> -
{ - this.layout = 'title'; - this.createNewSlide(this.layout); - })}> -
Title
-
Subtitle
-
-
{ - this.layout = 'header'; - this.createNewSlide(this.layout); - })}> -
- Section header -
+
e.stopPropagation()} + onPointerUp={e => e.stopPropagation()} + onPointerDown={e => e.stopPropagation()}> +
+
{ + this.layout = 'blank'; + this.createNewSlide(this.layout); + })} + /> +
{ + this.layout = 'title'; + this.createNewSlide(this.layout); + })}> +
Title
+
Subtitle
+
+
{ + this.layout = 'header'; + this.createNewSlide(this.layout); + })}> +
+ Section header
-
{ - this.layout = 'content'; - this.createNewSlide(this.layout); - })}> -
- Title -
-
Text goes here
+
+
{ + this.layout = 'content'; + this.createNewSlide(this.layout); + })}> +
+ Title
+
Text goes here
@@ -1904,73 +1843,71 @@ export class PresBox extends ViewBoxBaseComponent() { @computed get newDocumentDropdown() { return ( -
-
e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> -
- Slide Title:

- { - e.stopPropagation(); - e.preventDefault(); - runInAction(() => (this.title = e.target.value)); - }}> +
e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> +
+ Slide Title:

+ { + e.stopPropagation(); + e.preventDefault(); + runInAction(() => (this.title = e.target.value)); + }}> +
+
+ Choose type: +
+
(this.addFreeform = !this.addFreeform))}> + Text +
+
(this.addFreeform = !this.addFreeform))}> + Freeform +
-
- Choose type: -
-
(this.addFreeform = !this.addFreeform))}> - Text -
-
(this.addFreeform = !this.addFreeform))}> - Freeform +
+
+ Preset layouts: +
+
(this.layout = 'blank'))} /> +
(this.layout = 'title'))}> +
Title
+
Subtitle
+
+
(this.layout = 'header'))}> +
+ Section header
-
-
- Preset layouts: -
-
(this.layout = 'blank'))} /> -
(this.layout = 'title'))}> -
Title
-
Subtitle
+
(this.layout = 'content'))}> +
+ Title
-
(this.layout = 'header'))}> -
- Section header -
+
Text goes here
+
+
(this.layout = 'twoColumns'))}> +
+ Title
-
(this.layout = 'content'))}> -
- Title -
-
Text goes here
+
+ Column one text
-
(this.layout = 'twoColumns'))}> -
- Title -
-
- Column one text -
-
- Column two text -
+
+ Column two text
-
(this.openLayouts = !this.openLayouts))}> - -
-
-
this.createNewSlide(this.layout, this.title, this.addFreeform)}> - Create New Slide -
+
(this.openLayouts = !this.openLayouts))}> + +
+
+
+
this.createNewSlide(this.layout, this.title, this.addFreeform)}> + Create New Slide
@@ -2105,115 +2042,109 @@ export class PresBox extends ViewBoxBaseComponent() { const activeFontColor = targetDoc['pres-text-color'] ? StrCast(targetDoc['pres-text-color']) : 'Black'; const viewedFontColor = targetDoc['pres-text-viewed-color'] ? StrCast(targetDoc['pres-text-viewed-color']) : 'Black'; return ( -
-
e.stopPropagation()} - onPointerUp={e => e.stopPropagation()} - onPointerDown={e => e.stopPropagation()}> -
- {this.stringType} selected -
-
- Contents -
-
- Edit -
-
-
-
Active text color
-
{ - this.openActiveColorPicker = !this.openActiveColorPicker; - })}>
+
e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> +
+ {this.stringType} selected +
+
+ Contents
- {this.activeColorPicker} -
-
Viewed font color
-
(this.openViewedColorPicker = !this.openViewedColorPicker))}>
+
+ Edit
- {this.viewedColorPicker} +
+
+
Active text color
-
- Zoom -
-
- Edit -
+ className="ribbon-colorBox" + style={{ backgroundColor: activeFontColor, height: 15, width: 15 }} + onClick={action(() => { + this.openActiveColorPicker = !this.openActiveColorPicker; + })}>
+
+ {this.activeColorPicker} +
+
Viewed font color
+
(this.openViewedColorPicker = !this.openViewedColorPicker))}>
+
+ {this.viewedColorPicker} +
+
+ Zoom
-
-
- Scroll -
-
- Edit -
+
+ Edit
-
- Frames -
-
-
{ - e.stopPropagation(); - this.prevKeyframe(targetDoc, activeItem); - }}> - -
-
(targetDoc.keyFrameEditing = !targetDoc.keyFrameEditing))}> - {NumCast(targetDoc._currentFrame)} -
-
{ - e.stopPropagation(); - this.nextKeyframe(targetDoc, activeItem); - }}> - -
-
- -
{'Last frame'}
- - }> -
{NumCast(targetDoc.lastFrame)}
-
+
+
+ Scroll
-
- {this.frameListHeader} - {this.frameList} +
+ Edit
-
console.log(' TODO: play frames')}> - Play +
+
+
+ Frames +
+
+
{ + e.stopPropagation(); + this.prevKeyframe(targetDoc, activeItem); + }}> + +
+
(targetDoc.keyFrameEditing = !targetDoc.keyFrameEditing))}> + {NumCast(targetDoc._currentFrame)} +
+
{ + e.stopPropagation(); + this.nextKeyframe(targetDoc, activeItem); + }}> + +
+ +
{'Last frame'}
+ + }> +
{NumCast(targetDoc.lastFrame)}
+
+
+
+ {this.frameListHeader} + {this.frameList} +
+
console.log(' TODO: play frames')}> + Play
@@ -2233,7 +2164,6 @@ export class PresBox extends ViewBoxBaseComponent() { @undoBatch @action switchPresented = (color: ColorState) => { - const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; const val = String(color.hex); targetDoc['pres-text-viewed-color'] = val; @@ -2241,25 +2171,21 @@ export class PresBox extends ViewBoxBaseComponent() { }; @computed get activeColorPicker() { - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; return !this.openActiveColorPicker ? null : ( ); } @computed get viewedColorPicker() { - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; return !this.openViewedColorPicker ? null : ( ); } @@ -2422,10 +2348,7 @@ export class PresBox extends ViewBoxBaseComponent() { }; @observable - toggleDisplayMovement = (doc: Doc) => { - if (doc.displayMovement) doc.displayMovement = false; - else doc.displayMovement = true; - }; + toggleDisplayMovement = (doc: Doc) => (doc.displayMovement = !doc.displayMovement); @action checkList = (doc: Doc, list: any): number => { @@ -2538,24 +2461,13 @@ export class PresBox extends ViewBoxBaseComponent() { } }; - @computed get moreInfoDropdown() { - return
; - } - @computed get toolbarWidth(): number { - const width = this.props.PanelWidth(); - return width; + return this.props.PanelWidth(); } @action - toggleProperties = () => { - if (SettingsManager.propertiesWidth > 0) { - SettingsManager.propertiesWidth = 0; - } else { - SettingsManager.propertiesWidth = 250; - } - }; + toggleProperties = () => (SettingsManager.propertiesWidth = SettingsManager.propertiesWidth > 0 ? 0 : 250); @computed get toolbar() { const propIcon = SettingsManager.propertiesWidth > 0 ? 'angle-double-right' : 'angle-double-left'; @@ -2677,10 +2589,7 @@ export class PresBox extends ViewBoxBaseComponent() { } @action - getList = (list: any): List => { - const x: List = list; - return x; - }; + getList = (list: any): List => list; @action updateList = (list: any): List => { @@ -2709,12 +2618,7 @@ export class PresBox extends ViewBoxBaseComponent() {
  Frames {this.panable ? Panable : this.scrollable ? Scrollable : null}
- -
{'Add frame by example'}
- - }> + {'Add frame by example'}
}>
{ @@ -2724,12 +2628,7 @@ export class PresBox extends ViewBoxBaseComponent() { e.stopPropagation()} />
- -
{'Edit in collection'}
- - }> + {'Edit in collection'}
}>
{ @@ -2746,7 +2645,6 @@ export class PresBox extends ViewBoxBaseComponent() { @computed get frameList() { const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; const frameList: List = this.getList(activeItem.frameList); if (frameList) { const frameItems = frameList.map(value =>
); @@ -2755,18 +2653,14 @@ export class PresBox extends ViewBoxBaseComponent() { } @computed get playButtonFrames() { - const targetDoc: Doc = this.targetDoc; - return ( - <> - {this.targetDoc ? ( -
= 0 ? 'inline-flex' : 'none' }}> -
{NumCast(targetDoc._currentFrame)}
-
-
{NumCast(targetDoc.lastFrame)}
-
- ) : null} - - ); + const targetDoc = this.targetDoc; + return this.targetDoc ? ( +
= 0 ? 'inline-flex' : 'none' }}> +
{NumCast(targetDoc._currentFrame)}
+
+
{NumCast(targetDoc.lastFrame)}
+
+ ) : null; } @computed get playButtons() { @@ -2775,12 +2669,7 @@ export class PresBox extends ViewBoxBaseComponent() { // Case 1: There are still other frames and should go through all frames before going to next slide return (
- -
{'Loop'}
- - }> + {'Loop'}
}>
(this.layoutDoc.presLoop = !this.layoutDoc.presLoop)}>
@@ -2798,12 +2687,7 @@ export class PresBox extends ViewBoxBaseComponent() { }}>
- -
{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}
- - }> + {this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}
}>
@@ -2821,12 +2705,7 @@ export class PresBox extends ViewBoxBaseComponent() {
- -
{'Click to return to 1st slide'}
- - }> + {'Click to return to 1st slide'}
}>
this.gotoDocument(0, this.activeItem)}> 1
@@ -2931,12 +2810,7 @@ export class PresBox extends ViewBoxBaseComponent() { return DocListCast(Doc.MyOverlayDocs?.data).includes(this.rootDoc) ? (
e.stopPropagation()}>
- -
{'Loop'}
- - }> + {'Loop'}
}>
() {
setupMoveUpEvents(this, e, returnFalse, returnFalse, this.prevClicked, false, false)}>
- -
{this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}
- - }> + {this.layoutDoc.presStatus === PresStatus.Autoplay ? 'Pause' : 'Autoplay'}
}>
setupMoveUpEvents(this, e, returnFalse, returnFalse, this.startOrPause, false, false)}>
@@ -2962,12 +2831,7 @@ export class PresBox extends ViewBoxBaseComponent() {
- -
{'Click to return to 1st slide'}
- - }> + {'Click to return to 1st slide'}
}>
setupMoveUpEvents(this, e, returnFalse, returnFalse, () => this.gotoDocument(0, this.activeItem), false, false)}> 1
@@ -3033,5 +2897,17 @@ ScriptingGlobals.add(function navigateToDoc(bestTarget: Doc, activeItem: Doc) { CollectionDockingView.AddSplit(doc, 'right'); finished?.(); }; - DocumentManager.Instance.jumpToDocument(bestTarget, true, openInTab, srcContext ? [srcContext] : [], undefined, undefined, undefined, () => PresBox.navigateToDoc(bestTarget, activeItem, true), undefined, true, NumCast(activeItem.presZoom)); + DocumentManager.Instance.jumpToDocument( + bestTarget, + true, + openInTab, + srcContext ? [srcContext] : [], + undefined, + undefined, + undefined, + () => PresBox.restoreTargetDocView(bestTarget, activeItem, true), + undefined, + true, + NumCast(activeItem.presZoom) + ); }); diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 38a87c34b..c78828a78 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -162,15 +162,6 @@ export class PresElementBox extends ViewBoxBaseComponent() { )); return groupSlides; } - @computed get duration() { - let durationInS: number; - if (this.rootDoc.type === DocumentType.AUDIO || this.rootDoc.type === DocumentType.VID) { - durationInS = NumCast(this.rootDoc.presEndTime) - NumCast(this.rootDoc.presStartTime); - durationInS = Math.round(durationInS * 10) / 10; - } else if (this.rootDoc.presDuration) durationInS = NumCast(this.rootDoc.presDuration) / 1000; - else durationInS = 2; - return 'D: ' + durationInS + 's'; - } @computed get transition() { let transitionInS: number; -- cgit v1.2.3-70-g09d2 From 3f5cbb9ae99b7ed33fa09c1d3cf5f27414881c00 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 26 Aug 2022 16:34:15 -0400 Subject: from last --- src/client/views/nodes/WebBox.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 6c2e42f86..b9e8e7c6e 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -285,7 +285,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { const windowHeight = this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); const scrollTo = doc.unrendered ? NumCast(doc.y) : Utils.scrollIntoView(NumCast(doc.y), doc[HeightSym](), NumCast(this.props.layoutDoc._scrollTop), windowHeight, 0.1 * windowHeight, NumCast(this.props.Document.scrollHeight)); if (scrollTo !== undefined && scrollTo !== this.props.layoutDoc._scrollTop) { - focusSpeed = 500; - if (!this._pdfViewer) this._initialScroll = scrollTo; - else if (smooth) smoothScroll(focusSpeed, mainCont, scrollTo); + else if (smooth) smoothScroll((focusSpeed = NumCast(doc.focusSpeed, 500)), mainCont, scrollTo); else this._mainCont.current?.scrollTo({ top: Math.abs(scrollTo || 0) }); } } else { -- cgit v1.2.3-70-g09d2 From a4ce2913b8a15cdd4670002a4a74f1d86601348e Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 26 Aug 2022 18:34:41 -0400 Subject: a bunch of mostly decorative cleanup to presBox --- src/client/views/PropertiesView.tsx | 6 +- src/client/views/collections/TabDocView.tsx | 2 +- src/client/views/nodes/trails/PresBox.tsx | 558 +++++++---------------- src/client/views/nodes/trails/PresElementBox.tsx | 2 +- src/client/views/nodes/trails/PresEnums.ts | 42 +- 5 files changed, 186 insertions(+), 424 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index 33f17047b..2708c561d 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -58,7 +58,7 @@ export class PropertiesView extends React.Component { } @computed get selectedDocumentView() { if (SelectionManager.Views().length) return SelectionManager.Views()[0]; - if (PresBox.Instance?._selectedArray.size) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc); + if (PresBox.Instance?.selectedArray.size) return DocumentManager.Instance.getDocumentView(PresBox.Instance.rootDoc); return undefined; } @computed get isPres(): boolean { @@ -1612,7 +1612,7 @@ export class PropertiesView extends React.Component { ); } if (this.isPres) { - const selectedItem: boolean = PresBox.Instance?._selectedArray.size > 0; + const selectedItem: boolean = PresBox.Instance?.selectedArray.size > 0; const type = PresBox.Instance.activeItem?.type; return (
@@ -1622,7 +1622,7 @@ export class PropertiesView extends React.Component {
{this.editableTitle}
-
{PresBox.Instance?._selectedArray.size} selected
+
{PresBox.Instance?.selectedArray.size} selected
{PresBox.Instance?.listOfSelected}
diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 49228a808..bead5825c 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -239,7 +239,7 @@ export class TabDocView extends React.Component { 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 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); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index ade098917..ac68ea281 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -12,7 +12,7 @@ import { List } from '../../../../fields/List'; import { ObjectField } from '../../../../fields/ObjectField'; import { listSpec } from '../../../../fields/Schema'; import { BoolCast, Cast, DocCast, NumCast, StrCast } from '../../../../fields/Types'; -import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents } from '../../../../Utils'; +import { emptyFunction, returnFalse, returnOne, returnTrue, setupMoveUpEvents, StopEvent } from '../../../../Utils'; import { Docs } from '../../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DocumentManager } from '../../../util/DocumentManager'; @@ -86,6 +86,7 @@ export class PresBox extends ViewBoxBaseComponent() { } private _disposers: { [name: string]: IReactionDisposer } = {}; + public selectedArray = new ObservableSet(); constructor(props: any) { super(props); @@ -93,6 +94,7 @@ export class PresBox extends ViewBoxBaseComponent() { } @observable public static Instance: PresBox; + @observable static startMarquee: boolean = false; // onclick "+ new slide" in presentation mode, set as true, then when marquee selection finish, onPointerUp automatically triggers PinWithView @observable _isChildActive = false; @observable _moveOnFromAudio: boolean = true; @@ -103,14 +105,13 @@ export class PresBox extends ViewBoxBaseComponent() { @observable _dragArray: HTMLElement[] = []; @observable _pathBoolean: boolean = false; @observable _expandBoolean: boolean = false; - - @observable static startMarquee: boolean = false; // onclick "+ new slide" in presentation mode, set as true, then when marquee selection finish, onPointerUp automatically triggers PinWithView - @observable private transitionTools: boolean = false; - @observable private newDocumentTools: boolean = false; - @observable private progressivizeTools: boolean = false; - @observable private openMovementDropdown: boolean = false; - @observable private openEffectDropdown: boolean = false; - @observable private presentTools: boolean = false; + @observable _transitionTools: boolean = false; + @observable _newDocumentTools: boolean = false; + @observable _progressivizeTools: boolean = false; + @observable _openMovementDropdown: boolean = false; + @observable _openEffectDropdown: boolean = false; + @observable _presentTools: boolean = false; + @observable _treeViewMap: Map = new Map(); @computed get isTreeOrStack() { return [CollectionViewType.Tree, CollectionViewType.Stacking].includes(StrCast(this.layoutDoc._viewType) as any); } @@ -123,8 +124,6 @@ export class PresBox extends ViewBoxBaseComponent() { @computed get childDocs() { return DocListCast(this.rootDoc[this.presFieldKey]); } - @observable _treeViewMap: Map = new Map(); - @computed get tagDocs() { return this.childDocs.map(doc => Cast(doc.presentationTargetDoc, Doc, null)); } @@ -137,22 +136,21 @@ export class PresBox extends ViewBoxBaseComponent() { @computed get targetDoc() { return Cast(this.activeItem?.presentationTargetDoc, Doc, null); } - @computed get scrollable(): boolean { + @computed get scrollable() { if (this.targetDoc.type === DocumentType.PDF || this.targetDoc.type === DocumentType.WEB || this.targetDoc.type === DocumentType.RTF || this.targetDoc._viewType === CollectionViewType.Stacking) return true; return false; } - @computed get panable(): boolean { + @computed get panable() { if ((this.targetDoc.type === DocumentType.COL && this.targetDoc._viewType === CollectionViewType.Freeform) || this.targetDoc.type === DocumentType.IMG) return true; return false; } @computed get selectedDocumentView() { if (SelectionManager.Views().length) return SelectionManager.Views()[0]; - if (this._selectedArray.size) return DocumentManager.Instance.getDocumentView(this.rootDoc); + if (this.selectedArray.size) return DocumentManager.Instance.getDocumentView(this.rootDoc); } - @computed get isPres(): boolean { + @computed get isPres() { document.removeEventListener('keydown', PresBox.keyEventsWrapper, true); if (this.selectedDoc?.type === DocumentType.PRES) { - document.removeEventListener('keydown', PresBox.keyEventsWrapper, true); document.addEventListener('keydown', PresBox.keyEventsWrapper, true); return true; } @@ -161,10 +159,9 @@ export class PresBox extends ViewBoxBaseComponent() { @computed get selectedDoc() { return this.selectedDocumentView?.rootDoc; } - _selectedArray = new ObservableSet(); - clearSelectedArray = () => this._selectedArray.clear(); - addToSelectedArray = (doc: Doc) => this._selectedArray.add(doc); - removeFromSelectedArray = (doc: Doc) => this._selectedArray.delete(doc); + clearSelectedArray = () => this.selectedArray.clear(); + addToSelectedArray = (doc: Doc) => this.selectedArray.add(doc); + removeFromSelectedArray = (doc: Doc) => this.selectedArray.delete(doc); _unmounting = false; @action @@ -448,7 +445,7 @@ export class PresBox extends ViewBoxBaseComponent() { this.layoutDoc.presCollection = srcContext; } const presStatus = this.rootDoc.presStatus; - const selViewCache = Array.from(this._selectedArray); + const selViewCache = Array.from(this.selectedArray); const dragViewCache = Array.from(this._dragArray); const eleViewCache = Array.from(this._eleArray); const self = this; @@ -756,27 +753,11 @@ export class PresBox extends ViewBoxBaseComponent() { } }); - setMovementName = action((movement: any, activeItem: Doc): string => { - let output: string = 'none'; - switch (movement) { - case PresMovement.Zoom: - output = 'Pan & Zoom'; - break; //Pan and zoom - case PresMovement.Pan: - output = 'Pan'; - break; //Pan - case PresMovement.Jump: - output = 'Jump cut'; - break; //Jump Cut - case PresMovement.None: - output = 'None'; - break; //None - default: - output = 'Zoom'; - activeItem.presMovement = 'zoom'; - break; //default set as zoom + movementName = action((activeItem: Doc) => { + if (![PresMovement.Zoom, PresMovement.Pan, PresMovement.Jump, PresMovement.None].includes(StrCast(activeItem.presMovement) as any)) { + activeItem.presMovement = 'zoom'; } - return output; + return StrCast(activeItem.presMovement); }); whenChildContentsActiveChanged = action((isActive: boolean) => this.props.whenChildContentsActiveChanged((this._isChildActive = isActive))); @@ -822,16 +803,13 @@ export class PresBox extends ViewBoxBaseComponent() { /** * For sorting the array so that the order is maintained when it is dropped. */ - @action - sortArray = (): Doc[] => { - return this.childDocs.filter(doc => this._selectedArray.has(doc)); - }; + sortArray = () => this.childDocs.filter(doc => this.selectedArray.has(doc)); /** * Method to get the list of selected items in the order in which they have been selected */ @computed get listOfSelected() { - return Array.from(this._selectedArray).map((doc: Doc, index: any) => { + return Array.from(this.selectedArray).map((doc: Doc, index: any) => { const curDoc = Cast(doc, Doc, null); const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null); if (curDoc && curDoc === this.activeItem) @@ -874,24 +852,18 @@ export class PresBox extends ViewBoxBaseComponent() { //Command click @action multiSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => { - if (!this._selectedArray.has(doc)) { + if (!this.selectedArray.has(doc)) { this.addToSelectedArray(doc); this._eleArray.push(ref); this._dragArray.push(drag); } else { this.removeFromSelectedArray(doc); - this.removeFromArray(this._eleArray, doc); - this.removeFromArray(this._dragArray, doc); + this._eleArray.splice(this._eleArray.indexOf(ref)); + this._dragArray.splice(this._dragArray.indexOf(drag)); } this.selectPres(); }; - removeFromArray = (arr: any[], val: any) => { - const index: number = arr.indexOf(val); - const ret: any[] = arr.splice(index, 1); - arr = ret; - }; - //Shift click @action shiftSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => { @@ -924,9 +896,7 @@ export class PresBox extends ViewBoxBaseComponent() { else this.regularSelect(doc, ref, drag, focus); }; - static keyEventsWrapper = (e: KeyboardEvent) => { - PresBox.Instance.keyEvents(e); - }; + static keyEventsWrapper = (e: KeyboardEvent) => PresBox.Instance.keyEvents(e); // Key for when the presentaiton is active @action @@ -940,7 +910,7 @@ export class PresBox extends ViewBoxBaseComponent() { if (this.layoutDoc.presStatus === 'edit') { undoBatch( action(() => { - for (const doc of this._selectedArray) { + for (const doc of this.selectedArray) { this.removeDocument(doc); } this.clearSelectedArray(); @@ -1027,13 +997,7 @@ export class PresBox extends ViewBoxBaseComponent() { } }; - getAllIndexes = (arr: Doc[], val: Doc): number[] => { - const indexes = []; - for (let i = 0; i < arr.length; i++) { - arr[i] === val && indexes.push(i); - } - return indexes; - }; + getAllIndexes = (arr: Doc[], val: Doc) => arr.map((doc, i) => (doc === val ? i : -1)).filter(i => i !== -1); // Adds the index in the pres path graphically @computed get order() { @@ -1152,7 +1116,7 @@ export class PresBox extends ViewBoxBaseComponent() { if (change) timeInMS += change; if (timeInMS < 100) timeInMS = 100; if (timeInMS > 10000) timeInMS = 10000; - this._selectedArray.forEach(doc => (doc.presTransition = timeInMS)); + this.selectedArray.forEach(doc => (doc.presTransition = timeInMS)); }; // Converts seconds to ms and updates presTransition @@ -1161,7 +1125,7 @@ export class PresBox extends ViewBoxBaseComponent() { if (change) scale += change; if (scale < 0.01) scale = 0.01; if (scale > 1.5) scale = 1.5; - this._selectedArray.forEach(doc => (doc.presZoom = scale)); + this.selectedArray.forEach(doc => (doc.presZoom = scale)); }; // Converts seconds to ms and updates presDuration @@ -1170,100 +1134,43 @@ export class PresBox extends ViewBoxBaseComponent() { if (change) timeInMS += change; if (timeInMS < 100) timeInMS = 100; if (timeInMS > 20000) timeInMS = 20000; - this._selectedArray.forEach(doc => (doc.presDuration = timeInMS)); + this.selectedArray.forEach(doc => (doc.presDuration = timeInMS)); }; /** * When the movement dropdown is changes */ @undoBatch - updateMovement = action((movement: any, all?: boolean) => { - (all ? this.childDocs : this._selectedArray).forEach(doc => { - switch (movement) { - case PresMovement.Zoom: //Pan and zoom - doc.presMovement = PresMovement.Zoom; - break; - case PresMovement.Pan: //Pan - doc.presMovement = PresMovement.Pan; - break; - case PresMovement.Jump: //Jump Cut - doc.presJump = true; - doc.presMovement = PresMovement.Jump; - break; - case PresMovement.None: - default: - doc.presMovement = PresMovement.None; - break; - } - }); - }); + updateMovement = action((movement: PresMovement, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presMovement = movement))); @undoBatch @action updateHideBefore = (activeItem: Doc) => { activeItem.presHideBefore = !activeItem.presHideBefore; - this._selectedArray.forEach(doc => (doc.presHideBefore = activeItem.presHideBefore)); + this.selectedArray.forEach(doc => (doc.presHideBefore = activeItem.presHideBefore)); }; @undoBatch @action updateHideAfter = (activeItem: Doc) => { activeItem.presHideAfter = !activeItem.presHideAfter; - this._selectedArray.forEach(doc => (doc.presHideAfter = activeItem.presHideAfter)); + this.selectedArray.forEach(doc => (doc.presHideAfter = activeItem.presHideAfter)); }; @undoBatch @action updateOpenDoc = (activeItem: Doc) => { activeItem.openDocument = !activeItem.openDocument; - this._selectedArray.forEach(doc => { - doc.openDocument = activeItem.openDocument; - }); + this.selectedArray.forEach(doc => (doc.openDocument = activeItem.openDocument)); }; @undoBatch @action - updateEffectDirection = (effect: any, all?: boolean) => { - (all ? this.childDocs : this._selectedArray).forEach(doc => { - const tagDoc = doc; // Cast(doc.presentationTargetDoc, Doc, null); - switch (effect) { - case PresEffect.Left: - tagDoc.presEffectDirection = PresEffect.Left; - break; - case PresEffect.Right: - tagDoc.presEffectDirection = PresEffect.Right; - break; - case PresEffect.Top: - tagDoc.presEffectDirection = PresEffect.Top; - break; - case PresEffect.Bottom: - tagDoc.presEffectDirection = PresEffect.Bottom; - break; - case PresEffect.Center: - default: - tagDoc.presEffectDirection = PresEffect.Center; - break; - } - }); - }; + updateEffectDirection = (effect: PresEffect, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffectDirection = effect)); @undoBatch @action - updateEffect = (effect: any, all?: boolean) => { - (all ? this.childDocs : this._selectedArray).forEach(doc => { - const tagDoc = doc; //Cast(doc.presentationTargetDoc, Doc, null); - //prettier-ignore - switch (effect) { - default: - case PresEffect.None: tagDoc.presEffect = PresEffect.None; break; - case PresEffect.Bounce: tagDoc.presEffect = PresEffect.Bounce; break; - case PresEffect.Fade: tagDoc.presEffect = PresEffect.Fade; break; - case PresEffect.Flip: tagDoc.presEffect = PresEffect.Flip; break; - case PresEffect.Roll: tagDoc.presEffect = PresEffect.Roll; break; - case PresEffect.Rotate: tagDoc.presEffect = PresEffect.Rotate; break; - } - }); - }; + updateEffect = (effect: PresEffect, all?: boolean) => (all ? this.childDocs : this.selectedArray).forEach(doc => (doc.presEffect = effect)); _batch: UndoManager.Batch | undefined = undefined; @@ -1272,6 +1179,46 @@ export class PresBox extends ViewBoxBaseComponent() { const targetDoc: Doc = this.targetDoc; const isPresCollection: boolean = targetDoc === this.layoutDoc.presCollection; const isPinWithView: boolean = BoolCast(activeItem.presPinView); + const presEffect = (effect: PresEffect) => ( +
this.updateEffect(effect)}> + {effect} +
+ ); + const presMovement = (movement: PresMovement) => ( +
this.updateMovement(movement)}> + {movement} +
+ ); + const presDirection = (diretion: PresEffect, icon: string, gridColumn: number, gridRow: number, opts: object) => { + const color = this.activeItem.presEffectDirection === diretion || (diretion === PresEffect.Center && !this.activeItem.presEffectDirection) ? Colors.LIGHT_BLUE : 'black'; + return ( + {diretion}
}> +
this.updateEffectDirection(diretion)}> + {icon ? : null} +
+ + ); + }; + const inputter = (min: string, step: string, max: string, value: number, active: boolean, change: (val: string) => void) => { + return ( + (this._batch = UndoManager.StartBatch('pres slider'))} + onPointerUp={() => this._batch?.end()} + onChange={e => { + e.stopPropagation(); + change(e.target.value); + }} + /> + ); + }; if (activeItem && targetDoc) { const type = targetDoc.type; const transitionSpeed = activeItem.presTransition ? NumCast(activeItem.presTransition) / 1000 : 0.5; @@ -1282,13 +1229,13 @@ export class PresBox extends ViewBoxBaseComponent() { activeItem.presMovement = activeItem.presMovement ? activeItem.presMovement : 'Zoom'; return (
e.stopPropagation()} - onPointerUp={e => e.stopPropagation()} + className={`presBox-ribbon ${this._transitionTools && this.layoutDoc.presStatus === PresStatus.Edit ? 'active' : ''}`} + onPointerDown={StopEvent} + onPointerUp={StopEvent} onClick={action(e => { e.stopPropagation(); - this.openMovementDropdown = false; - this.openEffectDropdown = false; + this._openMovementDropdown = false; + this._openEffectDropdown = false; })}>
Movement @@ -1301,24 +1248,16 @@ export class PresBox extends ViewBoxBaseComponent() { className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); - this.openMovementDropdown = !this.openMovementDropdown; + this._openMovementDropdown = !this._openMovementDropdown; })} - style={{ borderBottomLeftRadius: this.openMovementDropdown ? 0 : 5, border: this.openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}> - {this.setMovementName(activeItem.presMovement, activeItem)} - -
e.stopPropagation()} style={{ display: this.openMovementDropdown ? 'grid' : 'none' }}> -
e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.None)}> - None -
-
e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Zoom)}> - Pan {'&'} Zoom -
-
e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Pan)}> - Pan -
-
e.stopPropagation()} onClick={() => this.updateMovement(PresMovement.Jump)}> - Jump cut -
+ style={{ borderBottomLeftRadius: this._openMovementDropdown ? 0 : 5, border: this._openMovementDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}> + {this.movementName(activeItem)} + +
+ {presMovement(PresMovement.None)} + {presMovement(PresMovement.Zoom)} + {presMovement(PresMovement.Pan)} + {presMovement(PresMovement.Jump)}
)} @@ -1336,21 +1275,7 @@ export class PresBox extends ViewBoxBaseComponent() {
- (this._batch = UndoManager.StartBatch('presZoom'))} - onPointerUp={() => this._batch?.end()} - onChange={(e: React.ChangeEvent) => { - e.stopPropagation(); - this.setZoom(e.target.value); - }} - /> + {inputter('0', '1', '150', zoom, activeItem.presMovement === PresMovement.Zoom, this.setZoom)}
Movement Speed
@@ -1365,21 +1290,7 @@ export class PresBox extends ViewBoxBaseComponent() {
- (this._batch = UndoManager.StartBatch('presTransition'))} - onPointerUp={() => this._batch?.end()} - onChange={(e: React.ChangeEvent) => { - e.stopPropagation(); - this.setTransitionTime(e.target.value); - }} - /> + {inputter('0.1', '0.1', '10', transitionSpeed, [PresMovement.Pan, PresMovement.Zoom].includes(activeItem.presMovement as any), this.setTransitionTime)}
Fast
Medium
@@ -1390,35 +1301,20 @@ export class PresBox extends ViewBoxBaseComponent() { Visibility {'&'} Duration
{isPresCollection ? null : ( - -
{'Hide before presented'}
- - }> + {'Hide before presented'}
}>
this.updateHideBefore(activeItem)}> Hide before
)} {isPresCollection ? null : ( - -
{'Hide after presented'}
- - }> + {'Hide after presented'}
}>
this.updateHideAfter(activeItem)}> Hide after
)} - -
{'Open in lightbox view'}
- - }> + {'Open in lightbox view'}
}>
this.updateOpenDoc(activeItem)}> Lightbox
@@ -1440,22 +1336,7 @@ export class PresBox extends ViewBoxBaseComponent() {
- (this._batch = UndoManager.StartBatch('presDuration'))} - onPointerUp={() => this._batch?.end()} - onChange={(e: React.ChangeEvent) => { - e.stopPropagation(); - this.setDurationTime(e.target.value); - }} - /> + {inputter('0.1', '0.1', '20', duration, targetDoc.type !== DocumentType.AUDIO, this.setDurationTime)}
Short
Medium
@@ -1471,98 +1352,30 @@ export class PresBox extends ViewBoxBaseComponent() { className="presBox-dropdown" onClick={action(e => { e.stopPropagation(); - this.openEffectDropdown = !this.openEffectDropdown; + this._openEffectDropdown = !this._openEffectDropdown; })} - style={{ borderBottomLeftRadius: this.openEffectDropdown ? 0 : 5, border: this.openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}> + style={{ borderBottomLeftRadius: this._openEffectDropdown ? 0 : 5, border: this._openEffectDropdown ? `solid 2px ${Colors.MEDIUM_BLUE}` : 'solid 1px black' }}> {effect.toString()} - -
e.stopPropagation()}> -
e.stopPropagation()} - onClick={() => this.updateEffect(PresEffect.None)}> - None -
-
e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Fade)}> - Fade In -
-
e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Flip)}> - Flip -
-
e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Rotate)}> - Rotate -
-
e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Bounce)}> - Bounce -
-
e.stopPropagation()} onClick={() => this.updateEffect(PresEffect.Roll)}> - Roll -
+ +
e.stopPropagation()}> + {presEffect(PresEffect.None)} + {presEffect(PresEffect.Fade)} + {presEffect(PresEffect.Flip)} + {presEffect(PresEffect.Rotate)} + {presEffect(PresEffect.Bounce)} + {presEffect(PresEffect.Roll)}
Effect direction
-
{this.effectDirection}
+
{StrCast(this.activeItem.presEffectDirection)}
- {'Enter from left'}
}> -
this.updateEffectDirection(PresEffect.Left)}> - -
- - {'Enter from right'}
}> -
this.updateEffectDirection(PresEffect.Right)}> - -
- - -
{'Enter from top'}
- - }> -
this.updateEffectDirection(PresEffect.Top)}> - -
-
- -
{'Enter from bottom'}
- - }> -
this.updateEffectDirection(PresEffect.Bottom)}> - -
-
- -
{'Enter from center'}
- - }> -
this.updateEffectDirection(PresEffect.Center)}>
-
+ {presDirection(PresEffect.Left, 'angle-right', 1, 2, {})} + {presDirection(PresEffect.Right, 'angle-left', 3, 2, {})} + {presDirection(PresEffect.Top, 'angle-down', 2, 1, {})} + {presDirection(PresEffect.Bottom, 'angle-up', 2, 3, {})} + {presDirection(PresEffect.Center, '', 2, 2, { width: 10, height: 10, alignSelf: 'center' })}
)} @@ -1576,34 +1389,18 @@ export class PresBox extends ViewBoxBaseComponent() { } } - @computed get effectDirection() { - // prettier-ignore - switch (this.activeItem.presEffectDirection) { - case 'left': return 'Enter from left'; - case 'right': return 'Enter from right'; - case 'top': return'Enter from top'; - case 'bottom': return 'Enter from bottom'; - } - return 'Enter from center'; - } - @undoBatch @action applyTo = (array: Doc[]) => { - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; - this.updateMovement(activeItem.presMovement, true); - this.updateEffect(activeItem.presEffect, true); - this.updateEffectDirection(activeItem.presEffectDirection, true); - array.forEach(doc => { - const curDoc = Cast(doc, Doc, null); - const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null); - if (tagDoc && targetDoc) { - curDoc.presTransition = activeItem.presTransition; - curDoc.presDuration = activeItem.presDuration; - curDoc.presHideBefore = activeItem.presHideBefore; - curDoc.presHideAfter = activeItem.presHideAfter; - } + this.updateMovement(this.activeItem.presMovement as PresMovement, true); + this.updateEffect(this.activeItem.presEffect as PresEffect, true); + this.updateEffectDirection(this.activeItem.presEffectDirection as PresEffect, true); + const { presTransition, presDuration, presHideBefore, presHideAfter } = this.activeItem; + array.forEach(curDoc => { + curDoc.presTransition = presTransition; + curDoc.presDuration = presDuration; + curDoc.presHideBefore = presHideBefore; + curDoc.presHideAfter = presHideAfter; }); }; @@ -1785,7 +1582,7 @@ export class PresBox extends ViewBoxBaseComponent() { return (
e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> @@ -1956,7 +1753,7 @@ export class PresBox extends ViewBoxBaseComponent() { // Dropdown that appears when the user wants to begin presenting (either minimize or sidebar view) @computed get presentDropdown() { return ( -
e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> +
e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
() { const activeFontColor = targetDoc['pres-text-color'] ? StrCast(targetDoc['pres-text-color']) : 'Black'; const viewedFontColor = targetDoc['pres-text-viewed-color'] ? StrCast(targetDoc['pres-text-viewed-color']) : 'Black'; return ( -
e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}> +
e.stopPropagation()} onPointerUp={e => e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
{this.stringType} selected
() { @undoBatch @action switchActive = (color: ColorState) => { - const activeItem: Doc = this.activeItem; - const targetDoc: Doc = this.targetDoc; - const val = String(color.hex); - targetDoc['pres-text-color'] = val; + this.targetDoc['pres-text-color'] = String(color.hex); return true; }; @undoBatch @action switchPresented = (color: ColorState) => { - const targetDoc: Doc = this.targetDoc; - const val = String(color.hex); - targetDoc['pres-text-viewed-color'] = val; + this.targetDoc['pres-text-viewed-color'] = String(color.hex); return true; }; @@ -2228,12 +2020,11 @@ export class PresBox extends ViewBoxBaseComponent() { //Toggle whether the user edits or not @action editScrollProgressivize = (e: React.MouseEvent) => { - const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; if (!targetDoc.editScrollProgressivize) { if (!targetDoc.scrollProgressivize) { targetDoc.scrollProgressivize = true; - activeItem.scrollProgressivize = true; + this.activeItem.scrollProgressivize = true; } targetDoc.editScrollProgressivize = true; } else { @@ -2245,8 +2036,7 @@ export class PresBox extends ViewBoxBaseComponent() { @action progressivizeScroll = (e: React.MouseEvent) => { e.stopPropagation(); - const activeItem: Doc = this.activeItem; - activeItem.scrollProgressivize = !activeItem.scrollProgressivize; + this.activeItem.scrollProgressivize = !this.activeItem.scrollProgressivize; const targetDoc: Doc = this.targetDoc; targetDoc.scrollProgressivize = !targetDoc.scrollProgressivize; // CollectionFreeFormDocumentView.setupScroll(targetDoc, NumCast(targetDoc._currentFrame)); @@ -2353,13 +2143,14 @@ export class PresBox extends ViewBoxBaseComponent() { @action checkList = (doc: Doc, list: any): number => { const x: List = list; - if (x && x.length >= NumCast(doc._currentFrame) + 1) { + if (x?.length >= NumCast(doc._currentFrame) + 1) { return x[NumCast(doc._currentFrame)]; } else if (x) { x.length = NumCast(doc._currentFrame) + 1; x[NumCast(doc._currentFrame)] = x[NumCast(doc._currentFrame) - 1]; return x[NumCast(doc._currentFrame)]; - } else return 100; + } + return 100; }; @computed get progressivizeChildDocs() { @@ -2413,26 +2204,14 @@ export class PresBox extends ViewBoxBaseComponent() { } @action - nextAppearFrame = (doc: Doc, i: number): void => { - // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); - // const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); - const appearFrame = Cast(doc.appearFrame, 'number', null); - if (appearFrame === undefined) { - doc.appearFrame = 0; - } - doc.appearFrame = appearFrame + 1; + nextAppearFrame = (doc: Doc, i: number) => { + doc.appearFrame = (Cast(doc.appearFrame, 'number', null) ?? 0) + 1; this.updateOpacityList(doc['opacity-indexed'], NumCast(doc.appearFrame)); }; @action - prevAppearFrame = (doc: Doc, i: number): void => { - // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); - // const targetDoc = Cast(activeItem?.presentationTargetDoc, Doc, null); - const appearFrame = Cast(doc.appearFrame, 'number', null); - if (appearFrame === undefined) { - doc.appearFrame = 0; - } - doc.appearFrame = Math.max(0, appearFrame - 1); + prevAppearFrame = (doc: Doc, i: number) => { + doc.appearFrame = Math.max(0, (Cast(doc.appearFrame, 'number', null) ?? 0) - 1); this.updateOpacityList(doc['opacity-indexed'], NumCast(doc.appearFrame)); }; @@ -2483,12 +2262,7 @@ export class PresBox extends ViewBoxBaseComponent() {
*/} - -
{'View paths'}
- - }> + {'View paths'}
}>
1 && this.layoutDoc.presCollection ? 1 : 0.3, color: this._pathBoolean ? Colors.MEDIUM_BLUE : 'white', width: isMini ? '100%' : undefined }} className={'toolbar-button'} @@ -2507,22 +2281,12 @@ export class PresBox extends ViewBoxBaseComponent() {
*/} - -
{presKeyEvents ? 'Keys are active' : 'Keys are not active - click anywhere on the presentation trail to activate keys'}
- - }> + {presKeyEvents ? 'Keys are active' : 'Keys are not active - click anywhere on the presentation trail to activate keys'}
}>
- -
{propTitle}
- - }> + {propTitle}
}>
0 ? activeColor : inactiveColor }} />
@@ -2545,14 +2309,14 @@ export class PresBox extends ViewBoxBaseComponent() {
{isMini ? null : ( { - e.stopPropagation(); - this._scrubbing = true; - })} - onChange={(e: React.ChangeEvent) => this.setPlayheadTime(Number(e.target.value))} - onPointerUp={action((e: React.PointerEvent) => { - e.stopPropagation(); - this._scrubbing = false; - })} - /> -
- ) : ( -
/
- )} - -
{formatTime(this.timeline.clipDuration)}
-
- )} - -
- -
- - {!this._fullScreen && width > 300 && ( -
- -
- )} - - {!this._fullScreen && width > 300 && ( -
- -
- )} - -
{ - e.stopPropagation(); - this.toggleMute(); - }}> - -
- {width > 300 && ( - e.stopPropagation()} - onChange={(e: React.ChangeEvent) => this.setVolume(Number(e.target.value))} - /> - )} - - {!this._fullScreen && this.heightPercent !== 100 && width > 300 && ( - <> -
- -
- { - e.stopPropagation(); - }} - onChange={(e: React.ChangeEvent) => { - this.zoom(Number(e.target.value)); - }} - /> - - )} - - ); - } - // renders CollectionStackedTimeline @computed get renderTimeline() { return ( @@ -1149,6 +1043,112 @@ export class VideoBox extends ViewBoxAnnotatableComponent ); } + + @computed get UIButtons() { + const bounds = this.props.docViewPath().lastElement().getBounds(); + const width = (bounds?.right || 0) - (bounds?.left || 0); + const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0); + return ( + <> +
+ +
+ + {this.timeline && width > 150 && ( +
+
{formatTime(curTime)}
+ + {this._fullScreen || (this.heightPercent === 100 && width > 200) ? ( +
+ { + e.stopPropagation(); + this._scrubbing = true; + })} + onChange={(e: React.ChangeEvent) => this.setPlayheadTime(Number(e.target.value))} + onPointerUp={action((e: React.PointerEvent) => { + e.stopPropagation(); + this._scrubbing = false; + })} + /> +
+ ) : ( +
/
+ )} + +
{formatTime(this.timeline.clipDuration)}
+
+ )} + +
+ +
+ + {!this._fullScreen && width > 300 && ( +
+ +
+ )} + + {!this._fullScreen && width > 300 && ( +
+ +
+ )} + +
{ + e.stopPropagation(); + this.toggleMute(); + }}> + +
+ {width > 300 && ( + e.stopPropagation()} + onChange={(e: React.ChangeEvent) => this.setVolume(Number(e.target.value))} + /> + )} + + {!this._fullScreen && this.heightPercent !== 100 && width > 300 && ( + <> +
+ +
+ { + e.stopPropagation(); + }} + onChange={(e: React.ChangeEvent) => { + this.zoom(Number(e.target.value)); + }} + /> + + )} + + ); + } } VideoBox._nativeControls = false; diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index 787e331c5..0b6e18743 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -86,7 +86,7 @@ export default class UploadManager extends ApiManager { const videoId = JSON.parse(payload).videoId; const results: Upload.FileResponse[] = []; const result = await DashUploadUtils.uploadYoutube(videoId); - result && !(result.result instanceof Error) && results.push(result); + result && results.push(result); _success(res, results); resolve(); }); diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index ef7192ecc..28e26e51e 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -17,8 +17,6 @@ import { resolvedServerUrl } from './server_Initialization'; import { AcceptableMedia, Upload } from './SharedMediaTypes'; import request = require('request-promise'); import formidable = require('formidable'); -import { file } from 'jszip'; -import { csvParser } from './DataVizUtils'; const { exec } = require('child_process'); const parse = require('pdf-parse'); const ffmpeg = require('fluent-ffmpeg'); @@ -102,16 +100,41 @@ export namespace DashUploadUtils { } export function uploadYoutube(videoId: string): Promise { - console.log('UPLOAD ' + videoId); return new Promise>((res, rej) => { - exec('youtube-dl -o ' + (videoId + '.mp4') + ' https://www.youtube.com/watch?v=' + videoId + ' -f "best[filesize<50M]"', (error: any, stdout: any, stderr: any) => { - if (error) console.log(`error: ${error.message}`); - else if (stderr) console.log(`stderr: ${stderr}`); - else { - console.log(`stdout: ${stdout}`); - const data = { size: 0, path: videoId + '.mp4', name: videoId, type: 'video/mp4' }; - const file = { ...data, toJSON: () => ({ ...data, filename: data.path.replace(/.*\//, ''), mtime: null, length: 0, mime: '', toJson: () => undefined as any }) }; - res(MoveParsedFile(file, Directory.videos)); + console.log('Uploading YouTube video: ' + videoId); + exec('youtube-dl -o ' + (videoId + '.mp4') + ' ' + videoId + ' -f "bestvideo[filesize<5M]+bestaudio/bestvideo+bestaudio"', (error: any, stdout: any, stderr: any) => { + if (error) { + console.log(`error: Error: ${error.message}`); + res({ + source: { + size: 0, + path: videoId, + name: videoId, + type: '', + toJSON: () => ({ name: videoId, path: videoId }), + }, + result: { name: 'failed youtube query', message: `Could not upload YouTube video (${videoId}). Error: ${error.message}` }, + }); + } else if (stderr) { + console.log(`stderr: StdError: ${stderr}`); + res({ + source: { + size: 0, + path: videoId, + name: videoId, + type: '', + toJSON: () => ({ name: videoId, path: videoId }), + }, + result: { name: 'failed youtube query', message: `Could not upload YouTube video (${videoId}). Error: ${stderr}` }, + }); + } else { + exec('youtube-dl -o ' + (videoId + '.mp4') + ' ' + videoId + ' --get-duration', (error: any, stdout: any, stderr: any) => { + const time = Array.from(stdout.trim().split(':')).reverse(); + const duration = (time.length > 2 ? Number(time[2]) * 1000 * 60 : 0) + (time.length > 1 ? Number(time[1]) * 60 : 0) + (time.length > 0 ? Number(time[0]) : 0); + const data = { size: 0, path: videoId + '.mp4', name: videoId, type: 'video/mp4' }; + const file = { ...data, toJSON: () => ({ ...data, filename: data.path.replace(/.*\//, ''), mtime: duration.toString(), mime: '', toJson: () => undefined as any }) }; + res(MoveParsedFile(file, Directory.videos)); + }); } }); }); @@ -352,7 +375,7 @@ export namespace DashUploadUtils { * @param suffix If the file doesn't have a suffix and you want to provide it one * to appear in the new location */ - export async function MoveParsedFile(file: formidable.File, destination: Directory, suffix: string | undefined = undefined, text?: string): Promise { + export async function MoveParsedFile(file: formidable.File, destination: Directory, suffix: string | undefined = undefined, text?: string, duration?: number): Promise { const { path: sourcePath } = file; let name = path.basename(sourcePath); suffix && (name += suffix); @@ -368,6 +391,7 @@ export namespace DashUploadUtils { agnostic: getAccessPaths(destination, name), }, rawText: text, + duration, }, }); }); diff --git a/src/server/SharedMediaTypes.ts b/src/server/SharedMediaTypes.ts index cde95526f..7db1c2dae 100644 --- a/src/server/SharedMediaTypes.ts +++ b/src/server/SharedMediaTypes.ts @@ -2,36 +2,45 @@ import { ExifData } from 'exif'; import { File } from 'formidable'; export namespace AcceptableMedia { - export const gifs = [".gif"]; - export const pngs = [".png"]; - export const jpgs = [".jpg", ".jpeg"]; - export const webps = [".webp"]; - export const tiffs = [".tiff"]; + export const gifs = ['.gif']; + export const pngs = ['.png']; + export const jpgs = ['.jpg', '.jpeg']; + export const webps = ['.webp']; + export const tiffs = ['.tiff']; export const imageFormats = [...pngs, ...jpgs, ...gifs, ...webps, ...tiffs]; - export const videoFormats = [".mov", ".mp4", ".quicktime", ".mkv", ".x-matroska;codecs=avc1"]; - export const applicationFormats = [".pdf"]; - export const audioFormats = [".wav", ".mp3", ".mpeg", ".flac", ".au", ".aiff", ".m4a", ".webm"]; + export const videoFormats = ['.mov', '.mp4', '.quicktime', '.mkv', '.x-matroska;codecs=avc1']; + export const applicationFormats = ['.pdf']; + export const audioFormats = ['.wav', '.mp3', '.mpeg', '.flac', '.au', '.aiff', '.m4a', '.webm']; } export namespace Upload { - export function isImageInformation(uploadResponse: Upload.FileInformation): uploadResponse is Upload.ImageInformation { - return "nativeWidth" in uploadResponse; + return 'nativeWidth' in uploadResponse; + } + + export function isVideoInformation(uploadResponse: Upload.FileInformation): uploadResponse is Upload.VideoInformation { + return 'duration' in uploadResponse; } export interface FileInformation { accessPaths: AccessPathInfo; rawText?: string; + duration?: number; } - export type FileResponse = { source: File, result: T | Error }; + export type FileResponse = { source: File; result: T | Error }; export type ImageInformation = FileInformation & InspectionResults; + export type VideoInformation = FileInformation & VideoResults; + export interface AccessPathInfo { - [suffix: string]: { client: string, server: string }; + [suffix: string]: { client: string; server: string }; } + export interface VideoResults { + duration: number; + } export interface InspectionResults { source: string; requestable: string; @@ -44,8 +53,7 @@ export namespace Upload { } export interface EnrichedExifData { - data: ExifData & ExifData["gps"]; + data: ExifData & ExifData['gps']; error?: string; } - -} \ No newline at end of file +} -- cgit v1.2.3-70-g09d2 From acee01619d91b9ac88d78d0eee02cbd33414ad8b Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 7 Sep 2022 09:11:06 -0400 Subject: no pointer events on timeline time to prevent flicker --- src/client/views/collections/CollectionStackedTimeline.scss | 1 + 1 file changed, 1 insertion(+) (limited to 'src/client/views') diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss index c296e1172..aa8502c20 100644 --- a/src/client/views/collections/CollectionStackedTimeline.scss +++ b/src/client/views/collections/CollectionStackedTimeline.scss @@ -143,5 +143,6 @@ transform: translate(0, -100%); font-weight: bold; + pointer-events: none; } } -- cgit v1.2.3-70-g09d2 From 2d8f763d763080fd52e940abb1a98b41e2da23fd Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 7 Sep 2022 14:28:44 -0400 Subject: fixed dragging stackedTimeline entries so that they appear during drag. fixed cursors for video/pdf/stackedTimeline to be a little more clear. fixed initial corner resize of pdfs. fixed videobox marquee drag when viewScale is undefined. --- src/client/util/DragManager.ts | 9 +++++++-- src/client/views/DocumentDecorations.tsx | 3 +++ src/client/views/MarqueeAnnotator.scss | 9 ++++----- src/client/views/MarqueeAnnotator.tsx | 3 +-- .../views/collections/CollectionStackedTimeline.scss | 4 +++- .../views/collections/CollectionStackedTimeline.tsx | 4 +++- .../collections/collectionFreeForm/MarqueeView.scss | 20 +++++++++----------- src/client/views/nodes/VideoBox.tsx | 4 ++-- 8 files changed, 32 insertions(+), 24 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index cec158d23..664933de0 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -357,10 +357,13 @@ export namespace DragManager { let rot = 0; const docsToDrag = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnchorAnnoDragData ? [dragData.dragDocument] : []; const dragElements = eles.map(ele => { + let useDim = false; if (ele?.parentElement?.parentElement?.parentElement?.className === 'collectionFreeFormDocumentView-container') { ele = ele.parentElement.parentElement.parentElement; const rotStr = ele.style.transform.replace(/.*rotate\(([-0-9.]*)deg\).*/, '$1'); if (rotStr) rot = Number(rotStr); + } else { + useDim = true; } if (rot < 0) rot += 360; if (!ele.parentNode) dragDiv.appendChild(ele); @@ -402,6 +405,8 @@ export namespace DragManager { xs.push(((0 - minx) / (maxx - minx)) * rect.width + rect.left); ys.push(((0 - miny) / (maxy - miny)) * rect.height + rect.top); scalings.push(scaling); + const width = useDim ? getComputedStyle(ele).width : ''; + const height = useDim ? getComputedStyle(ele).height : ''; Object.assign(dragElement.style, { opacity: '0.7', position: 'absolute', @@ -414,8 +419,8 @@ export namespace DragManager { borderRadius: getComputedStyle(ele).borderRadius, zIndex: globalCssVariables.contextMenuZindex, transformOrigin: '0 0', - width: '', - height: '', + width, + height, transform: `translate(${xs[0]}px, ${ys[0]}px) rotate(${rot}deg) scale(${scaling})`, }); dragLabel.style.transform = `translate(${xs[0]}px, ${ys[0] - 20}px)`; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3589e014a..a79f727a7 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -476,6 +476,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if (e.ctrlKey && !Doc.NativeHeight(docView.props.Document)) docView.toggleNativeDimensions(); if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { const doc = Document(docView.rootDoc); + if (doc.nativeHeightUnfrozen && !NumCast(doc.nativeHeight)) { + doc._nativeHeight = (NumCast(doc._height) / NumCast(doc._width, 1)) * docView.nativeWidth; + } const nwidth = docView.nativeWidth; const nheight = docView.nativeHeight; let docheight = doc._height || 0; diff --git a/src/client/views/MarqueeAnnotator.scss b/src/client/views/MarqueeAnnotator.scss index c90d48a65..5c65f35e9 100644 --- a/src/client/views/MarqueeAnnotator.scss +++ b/src/client/views/MarqueeAnnotator.scss @@ -1,12 +1,11 @@ - .marqueeAnnotator-annotationBox { position: absolute; background-color: rgba(245, 230, 95, 0.616); } - .marqueeAnnotator-dragBox { - position:absolute; + position: absolute; background-color: transparent; - opacity: 0.1; -} \ No newline at end of file + opacity: 0.2; + cursor: default; +} diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index f90ad8bb5..d9a989309 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -271,8 +271,7 @@ export class MarqueeAnnotator extends React.Component { width: `${this._width}px`, height: `${this._height}px`, border: `${this._width === 0 ? '' : '2px dashed black'}`, - opacity: 0.2, - }}>
+ }}/> ); } } diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss index aa8502c20..5a107d2ca 100644 --- a/src/client/views/collections/CollectionStackedTimeline.scss +++ b/src/client/views/collections/CollectionStackedTimeline.scss @@ -10,6 +10,7 @@ border-width: 0 2px 0 2px; &:hover { + cursor: default; .collectionStackedTimeline-hover { display: block; } @@ -109,14 +110,15 @@ height: 100%; width: 10px; pointer-events: all; - cursor: ew-resize; z-index: 100; } .collectionStackedTimeline-resizer { right: 0; + cursor: e-resize; } .collectionStackedTimeline-left-resizer { left: 0; + cursor: w-resize; } } diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 2543624d3..b29abf083 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -244,6 +244,7 @@ export class CollectionStackedTimeline extends CollectionSubView DocumentManager.Instance.getDocumentView(anchor)?.select(false)); } (!isClick || !wasSelecting) && (this._markerEnd = undefined); + this._timelineWrapper && (this._timelineWrapper.style.cursor = ''); }), (e, doubleTap) => { if (e.button !== 2) { @@ -561,7 +563,7 @@ export class CollectionStackedTimeline extends CollectionSubView
e.stopPropagation()} onScroll={this.setScroll} onMouseMove={e => this.isContentActive() && this.onHover(e)} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss index 41e4d6b6a..e0f5cbe5b 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss @@ -1,16 +1,14 @@ - .marqueeView { position: inherit; - top:0; - left:0; - width:100%; - height:100%; + top: 0; + left: 0; + width: 100%; + height: 100%; overflow: hidden; border-radius: inherit; user-select: none; } - .marqueeView:focus-within { overflow: hidden; } @@ -22,13 +20,13 @@ border-color: black; pointer-events: none; .marquee-legend { - bottom:-18px; - left:0; + bottom: -18px; + left: 0; position: absolute; font-size: 9; - white-space:nowrap; + white-space: nowrap; } .marquee-legend::after { - content: "Press for lasso" + content: 'Press for lasso'; } -} \ No newline at end of file +} diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index bfb8c1528..5a3594ffc 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -551,7 +551,7 @@ export class VideoBox extends ViewBoxAnnotatableComponentLoading
) : ( -
+
this._fullScreen && e.stopPropagation()}> {this._fullScreen && (
{ - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) { + if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._viewScale, 1) === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) { setupMoveUpEvents( this, e, -- cgit v1.2.3-70-g09d2 From ddc5cc18d006203df204d1ee21cec31cf8a5421a Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 7 Sep 2022 15:05:12 -0400 Subject: fixed context menu placement so it won't be partially off screen. --- src/client/views/ContextMenu.tsx | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/ContextMenu.tsx b/src/client/views/ContextMenu.tsx index c9908b4b0..6a530e3ae 100644 --- a/src/client/views/ContextMenu.tsx +++ b/src/client/views/ContextMenu.tsx @@ -118,27 +118,11 @@ export class ContextMenu extends React.Component { static readonly buffer = 20; get pageX() { - const x = this._pageX; - if (x < 0) { - return 0; - } - const width = this._width; - if (x + width > window.innerWidth - ContextMenu.buffer) { - return window.innerWidth - ContextMenu.buffer - width; - } - return x; + return this._pageX + this._width > window.innerWidth - ContextMenu.buffer ? window.innerWidth - ContextMenu.buffer - this._width : Math.max(0, this._pageX); } get pageY() { - const y = this._pageY; - if (y < 0) { - return 0; - } - const height = this._height; - if (y + height > window.innerHeight - ContextMenu.buffer) { - return window.innerHeight - ContextMenu.buffer - height; - } - return y; + return this._pageY + this._height > window.innerHeight - ContextMenu.buffer ? window.innerHeight - ContextMenu.buffer - this._height : Math.max(0, this._pageY); } _onDisplay?: () => void = undefined; @@ -223,7 +207,15 @@ export class ContextMenu extends React.Component { render() { return !this._display ? null : ( -
+
{ + if (r) { + this._width = Number(getComputedStyle(r).width.replace('px', '')); + this._height = Number(getComputedStyle(r).height.replace('px', '')); + } + })} + style={{ left: this.pageX, ...(this._yRelativeToTop ? { top: this.pageY } : { bottom: this.pageY }) }}> {!this.itemsNeedSearch ? null : ( -- cgit v1.2.3-70-g09d2 From 71f9850387c2719ed5b050db4130fa15852cb38d Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 8 Sep 2022 13:30:36 -0400 Subject: fixed pinning viewScale --- src/client/views/nodes/trails/PresBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 50a77ce57..1439ae5d5 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -420,7 +420,7 @@ export class PresBox extends ViewBoxBaseComponent() { const panY = NumCast(pinDoc._panY); const pw = NumCast(pinProps.panelWidth); const ph = NumCast(pinProps.panelHeight); - const ps = NumCast(pinDoc._viewScale); + const ps = NumCast(pinDoc._viewScale, 1); if (pw && ph && ps) { pinDoc.presPinViewBounds = new List([panX - pw / 2 / ps, panY - ph / 2 / ps, panX + pw / 2 / ps, panY + ph / 2 / ps]); } -- cgit v1.2.3-70-g09d2 From 0454279c9845fc64dc2c77aeb6bca9a5fee573f6 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 8 Sep 2022 13:34:55 -0400 Subject: added rotation to preselement saved parameters. --- src/client/views/nodes/trails/PresBox.tsx | 2 ++ src/client/views/nodes/trails/PresElementBox.tsx | 1 + 2 files changed, 3 insertions(+) (limited to 'src/client/views') diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 1439ae5d5..beebca973 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -407,6 +407,7 @@ export class PresBox extends ViewBoxBaseComponent() { pinDoc.presPinView = (pinProps?.pinWithView ? true : false) || scrollable || temporal || pannable || clippable || dataview || textview || pinProps.activeFrame !== undefined; pinDoc.presX = NumCast(targetDoc.x); pinDoc.presY = NumCast(targetDoc.y); + pinDoc.presRot = NumCast(targetDoc.jitterRotation); pinDoc.presWidth = NumCast(targetDoc.width); pinDoc.presHeight = NumCast(targetDoc.height); @@ -487,6 +488,7 @@ export class PresBox extends ViewBoxBaseComponent() { targetDoc._dataTransition = presTransitionTime; targetDoc.x = NumCast(activeItem.presX, NumCast(targetDoc.x)); targetDoc.y = NumCast(activeItem.presY, NumCast(targetDoc.y)); + targetDoc.jitterRotation = NumCast(activeItem.presRot, NumCast(targetDoc.jitterRotation)); targetDoc.width = NumCast(activeItem.presWidth, NumCast(targetDoc.width)); targetDoc.height = NumCast(activeItem.presHeight, NumCast(targetDoc.height)); setTimeout(() => (targetDoc._dataTransition = undefined), transTime + 10); diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index e4fab7528..d88ff45fa 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -337,6 +337,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { activeItem.presX = NumCast(targetDoc.x); activeItem.presY = NumCast(targetDoc.y); + activeItem.presRot = NumCast(targetDoc.jitterRotation); activeItem.presWidth = NumCast(targetDoc.width); activeItem.presHeight = NumCast(targetDoc.height); }; -- cgit v1.2.3-70-g09d2 From 637dd4dd36a74e49c653aa6e265a0db41797de00 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 8 Sep 2022 13:43:02 -0400 Subject: made presbox slide changes undoable --- src/client/views/nodes/trails/PresBox.tsx | 1 + 1 file changed, 1 insertion(+) (limited to 'src/client/views') diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index beebca973..847617378 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -312,6 +312,7 @@ export class PresBox extends ViewBoxBaseComponent() { //The function that is called when a document is clicked or reached through next or back. //it'll also execute the necessary actions if presentation is playing. + @undoBatch public gotoDocument = action((index: number, from?: Doc, group?: boolean) => { Doc.UnBrushAllDocs(); -- cgit v1.2.3-70-g09d2 From 2c5942d76ad6e9b5874b98658b7c5af59cdfa367 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 8 Sep 2022 14:38:38 -0400 Subject: fixed font menu settings to be accurate of current selection. --- src/client/documents/Documents.ts | 16 ++++-- src/client/util/CurrentUserUtils.ts | 4 +- src/client/views/nodes/button/FontIconBox.tsx | 36 ++++--------- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 +- .../views/nodes/formattedText/RichTextMenu.tsx | 62 +++++++++------------- 5 files changed, 49 insertions(+), 73 deletions(-) (limited to 'src/client/views') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 7111cb233..57a24b304 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1386,11 +1386,17 @@ export namespace DocUtils { scripts && Object.keys(scripts).map(key => { if (ScriptCast(doc[key])?.script.originalScript !== scripts[key] && scripts[key]) { - doc[key] = ScriptField.MakeScript( - scripts[key], - { dragData: DragManager.DocumentDragData.name, value: 'any', scriptContext: 'any', thisContainer: Doc.name, documentView: Doc.name, heading: Doc.name, checked: 'boolean', containingTreeView: Doc.name }, - { _readOnly_: true } - ); + doc[key] = ScriptField.MakeScript(scripts[key], { + dragData: DragManager.DocumentDragData.name, + value: 'any', + _readOnly_: 'boolean', + scriptContext: 'any', + thisContainer: Doc.name, + documentView: Doc.name, + heading: Doc.name, + checked: 'boolean', + containingTreeView: Doc.name, + }); } }); funcs && diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 25852c6ce..9d3f19e50 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -623,8 +623,8 @@ export class CurrentUserUtils { return [ { title: "Font", toolTip: "Font", width: 100, btnType: ButtonType.DropdownList, ignoreClick: true, scripts: {script: 'setFont(value, _readOnly_)'}, btnList: new List(["Roboto", "Roboto Mono", "Nunito", "Times New Roman", "Arial", "Georgia", "Comic Sans MS", "Tahoma", "Impact", "Crimson Text"]) }, - { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, ignoreClick: true, scripts: {script: '{ return setFontSize(value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions }, - { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, scripts: {script: '{ return setFontColor(value, _readOnly_); }'}}, + { title: "Size", toolTip: "Font size", width: 75, btnType: ButtonType.NumberButton, ignoreClick: true, scripts: {script: '{ return setFontSize(value, _readOnly_);}'}, numBtnMax: 200, numBtnMin: 0, numBtnType: NumButtonType.DropdownOptions }, + { title: "Color", toolTip: "Font color", btnType: ButtonType.ColorButton, icon: "font", ignoreClick: true, scripts: {script: '{ return setFontColor(value, _readOnly_);}'}}, { title: "Bold", toolTip: "Bold (Ctrl+B)", btnType: ButtonType.ToggleButton, icon: "bold", scripts: {onClick: '{ return toggleBold(_readOnly_); }'} }, { title: "Italic", toolTip: "Italic (Ctrl+I)", btnType: ButtonType.ToggleButton, icon: "italic", scripts: {onClick: '{ return toggleItalic(_readOnly_);}'} }, { title: "Under", toolTip: "Underline (Ctrl+U)", btnType: ButtonType.ToggleButton, icon: "underline", scripts: {onClick: '{ return toggleUnderline(_readOnly_);}'} }, diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index c72b5ca9b..4a6099fb3 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -576,27 +576,19 @@ ScriptingGlobals.add(function toggleOverlay(checkResult?: boolean) { // toggle: Set overlay status of selected document ScriptingGlobals.add(function setFont(font: string, checkResult?: boolean) { - SelectionManager.Docs().map(doc => (doc._fontFamily = font)); - const editorView = RichTextMenu.Instance.TextView?.EditorView; - if (checkResult) { - return StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); - } - if (editorView) RichTextMenu.Instance.setFontFamily(font); - else Doc.UserDoc().fontFamily = font; + if (checkResult) return RichTextMenu.Instance?.fontFamily; + font && RichTextMenu.Instance.setFontFamily(font); }); ScriptingGlobals.add(function getActiveTextInfo(info: 'family' | 'size' | 'color' | 'highlight') { const editorView = RichTextMenu.Instance.TextView?.EditorView; const style = editorView?.state && RichTextMenu.Instance.getActiveFontStylesOnSelection(); + // prettier-ignore 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]; } }); @@ -621,14 +613,8 @@ ScriptingGlobals.add(function setBulletList(mapStyle: 'bullet' | 'decimal', chec // toggle: Set overlay status of selected document ScriptingGlobals.add(function setFontColor(color?: string, checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; - - if (checkResult) { - return editorView ? RichTextMenu.Instance.fontColor : Doc.UserDoc().fontColor; - } - - if (editorView) color && RichTextMenu.Instance.setColor(color, editorView, editorView?.dispatch); - else Doc.UserDoc().fontColor = color; + if (checkResult) return RichTextMenu.Instance.fontColor; + color && RichTextMenu.Instance.setColor(color); }); // toggle: Set overlay status of selected document @@ -650,14 +636,12 @@ ScriptingGlobals.add(function setFontHighlight(color?: string, checkResult?: boo // toggle: Set overlay status of selected document ScriptingGlobals.add(function setFontSize(size: string | number, checkResult?: boolean) { - const editorView = RichTextMenu.Instance?.TextView?.EditorView; if (checkResult) { return RichTextMenu.Instance?.fontSize.replace('px', ''); } if (typeof size === 'number') size = size.toString(); if (size && Number(size).toString() === size) size += 'px'; - if (editorView) RichTextMenu.Instance.setFontSize(size); - else Doc.UserDoc()._fontSize = size; + RichTextMenu.Instance.setFontSize(size); }); ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 81ac45521..314696251 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1387,10 +1387,10 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent mark.type === schema.marks.user_mark)) { + if (this._editorView) { this._editorView.state.storedMarks = [ ...(this._editorView.state.storedMarks ?? []), - schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) }), + ...(!this._editorView.state.storedMarks?.some(mark => mark.type === schema.marks.user_mark) ? [schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })] : []), ...(Doc.UserDoc().fontColor !== 'transparent' && Doc.UserDoc().fontColor ? [schema.mark(schema.marks.pFontColor, { color: StrCast(Doc.UserDoc().fontColor) })] : []), ...(Doc.UserDoc().fontStyle === 'italics' ? [schema.mark(schema.marks.em)] : []), ...(Doc.UserDoc().textDecoration === 'underline' ? [schema.mark(schema.marks.underline)] : []), diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 0cbe60c0c..42a204e1d 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -64,6 +64,7 @@ export class RichTextMenu extends AntimodeMenu { super(props); runInAction(() => { RichTextMenu.Instance = this; + this.updateMenu(undefined, undefined, props); this._canFade = false; this.Pinned = true; }); @@ -103,13 +104,12 @@ export class RichTextMenu extends AntimodeMenu { return; } this.view = view; - if (!view || !view.hasFocus()) { - return; - } props && (this.editorProps = props); // Don't do anything if the document/selection didn't change - if (lastState?.doc.eq(view.state.doc) && lastState.selection.eq(view.state.selection)) return; + if (view && view.hasFocus()) { + if (lastState?.doc.eq(view.state.doc) && lastState.selection.eq(view.state.selection)) return; + } // update active marks const activeMarks = this.getActiveMarksOnSelection(); @@ -124,9 +124,9 @@ export class RichTextMenu extends AntimodeMenu { this.activeListType = this.getActiveListStyle(); this._activeAlignment = this.getActiveAlignment(); - this._activeFontFamily = !activeFamilies.length ? 'Arial' : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various'; - this._activeFontSize = !activeSizes.length ? StrCast(this.TextView.Document.fontSize, StrCast(Doc.UserDoc().fontSize, '10px')) : activeSizes[0]; - this._activeFontColor = !activeColors.length ? 'black' : activeColors.length > 0 ? String(activeColors[0]) : '...'; + this._activeFontFamily = !activeFamilies.length ? StrCast(this.TextView?.Document.fontFamily, StrCast(Doc.UserDoc().fontFamily, 'Arial')) : activeFamilies.length === 1 ? String(activeFamilies[0]) : 'various'; + this._activeFontSize = !activeSizes.length ? StrCast(this.TextView?.Document.fontSize, StrCast(Doc.UserDoc().fontSize, '10px')) : activeSizes[0]; + this._activeFontColor = !activeColors.length ? StrCast(this.TextView?.Document.fontColor, StrCast(Doc.UserDoc().fontColor, 'black')) : activeColors.length > 0 ? String(activeColors[0]) : '...'; this.activeHighlightColor = !activeHighlights.length ? '' : activeHighlights.length > 0 ? String(activeHighlights[0]) : '...'; // update link in current selection @@ -279,28 +279,15 @@ export class RichTextMenu extends AntimodeMenu { this._superscriptActive = false; activeMarks.forEach(mark => { + // prettier-ignore switch (mark.name) { - case 'noAutoLinkAnchor': - this._noLinkActive = true; - break; - case 'strong': - this._boldActive = true; - break; - case 'em': - this._italicsActive = true; - break; - case 'underline': - this._underlineActive = true; - break; - case 'strikethrough': - this._strikethroughActive = true; - break; - case 'subscript': - this._subscriptActive = true; - break; - case 'superscript': - this._superscriptActive = true; - break; + case 'noAutoLinkAnchor': this._noLinkActive = true; break; + case 'strong': this._boldActive = true; break; + case 'em': this._italicsActive = true; break; + case 'underline': this._underlineActive = true; break; + case 'strikethrough': this._strikethroughActive = true; break; + case 'subscript': this._subscriptActive = true; break; + case 'superscript': this._superscriptActive = true; break; } }); } @@ -342,14 +329,13 @@ export class RichTextMenu extends AntimodeMenu { if (this.view.state.selection.from === 1 && this.view.state.selection.empty && (!this.view.state.doc.nodeAt(1) || !this.view.state.doc.nodeAt(1)?.marks.some(m => m.type.name === fontSize))) { this.TextView.dataDoc.fontSize = fontSize; this.view.focus(); - this.updateMenu(this.view, undefined, this.props); } else { const fmark = this.view.state.schema.marks.pFontSize.create({ fontSize }); this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true); this.view.focus(); - this.updateMenu(this.view, undefined, this.props); } - } + } else Doc.UserDoc()._fontSize = fontSize; + this.updateMenu(this.view, undefined, this.props); }; setFontFamily = (family: string) => { @@ -357,8 +343,8 @@ export class RichTextMenu extends AntimodeMenu { const fmark = this.view.state.schema.marks.pFontFamily.create({ family: family }); this.setMark(fmark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(fmark)), true); this.view.focus(); - this.updateMenu(this.view, undefined, this.props); - } + } else Doc.UserDoc()._fontFamily = family; + this.updateMenu(this.view, undefined, this.props); }; setHighlight(color: String, view: EditorView, dispatch: any) { @@ -368,13 +354,13 @@ export class RichTextMenu extends AntimodeMenu { this.setMark(highlightMark, view.state, dispatch, false); } - setColor(color: String, view: EditorView, dispatch: any) { + setColor(color: string) { if (this.view) { - const colorMark = view.state.schema.mark(view.state.schema.marks.pFontColor, { color }); + const colorMark = this.view.state.schema.mark(this.view.state.schema.marks.pFontColor, { color }); this.setMark(colorMark, this.view.state, (tx: any) => this.view!.dispatch(tx.addStoredMark(colorMark)), true); - view.focus(); - this.updateMenu(this.view, undefined, this.props); - } + this.view.focus(); + } else Doc.UserDoc().fontColor = color; + this.updateMenu(this.view, undefined, this.props); } // TODO: remove doesn't work -- cgit v1.2.3-70-g09d2 From a9e1c702a8009ac5924b9deef0a50b2016c20efc Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 8 Sep 2022 16:29:27 -0400 Subject: made timelineHeightpercent and currentTimecode palyground fields so that two people sharing a doc have independent control over playing and timeline view --- src/client/views/MainView.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/client/views') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 515faa316..45281ed69 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -158,6 +158,8 @@ export class MainView extends React.Component { 'treeViewOpen', 'showSidebar', 'sidebarWidthPercent', + 'currentTimecode', + 'timelineHeightPercent', 'panX', 'panY', 'fitWidth', -- cgit v1.2.3-70-g09d2 From d8753c8de6cc9ef9d4bfdb87f11605f0ef80929c Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 8 Sep 2022 17:30:17 -0400 Subject: fixed maintaining selections after ctrl-a + setting a font property --- src/client/views/nodes/formattedText/RichTextMenu.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 42a204e1d..c548e211b 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -144,13 +144,8 @@ export class RichTextMenu extends AntimodeMenu { const tr = updateBullets(state.tr.setNodeMarkup(state.selection.from, node.type, attrs), state.schema); dispatch(tr.setSelection(new NodeSelection(tr.doc.resolve(state.selection.from)))); } else if (dontToggle) { - toggleMark(mark.type, mark.attrs)(state, (tx: any) => { - const { from, $from, to, empty } = tx.selection; - if (!tx.doc.rangeHasMark(from, to, mark.type)) { - // hack -- should have just set the mark in the first place - toggleMark(mark.type, mark.attrs)({ tr: tx, doc: tx.doc, selection: tx.selection, storedMarks: tx.storedMarks }, dispatch); - } else dispatch(tx); - }); + const tr = state.tr.addMark(state.selection.from, state.selection.to, mark); + dispatch(tr.setSelection(new TextSelection(tr.doc.resolve(state.selection.from), tr.doc.resolve(state.selection.to)))); // bcz: need to redo the selection because ctrl-a selections disappear otherwise } else { toggleMark(mark.type, mark.attrs)(state, dispatch); } @@ -198,7 +193,8 @@ export class RichTextMenu extends AntimodeMenu { const state = this.view.state; const pos = this.view.state.selection.$from; const marks: Mark[] = [...(state.storedMarks ?? [])]; - if (state.selection.empty) { + if (state.storedMarks !== null) { + } else if (state.selection.empty) { const ref_node = this.reference_node(pos); marks.push(...(ref_node !== this.view.state.doc && ref_node?.isText ? Array.from(ref_node.marks) : [])); } else { -- cgit v1.2.3-70-g09d2 From ccf9c8099bc2b4685071efe117f4a4a40d1eb5e6 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 8 Sep 2022 18:13:44 -0400 Subject: fixed groups to automatically fit to box when not in a collection. fixed computing group bounds to not use zoomScaling --- .../collectionFreeForm/CollectionFreeFormView.tsx | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 947bd8aaa..039407e77 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -128,7 +128,7 @@ export class CollectionFreeFormView extends CollectionSubView { this._firstRender = false; - this._disposers.layoutComputation = reaction( - () => this.doLayoutComputation, - elements => (this._layoutElements = elements || []), - { fireImmediately: true, name: 'doLayout' } - ); this._marqueeRef.current?.addEventListener('dashDragAutoScroll', this.onDragAutoScroll as any); @@ -1547,10 +1542,10 @@ export class CollectionFreeFormView extends CollectionSubView this.doLayoutComputation, + elements => (this._layoutElements = elements || []), + { fireImmediately: true, name: 'doLayout' } + ); }) ); } -- cgit v1.2.3-70-g09d2 From 77cc4a22480d3d5773f70f661052404fed016219 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 8 Sep 2022 18:22:47 -0400 Subject: stop playing videos when lightbox doc changes. --- src/client/views/LightboxView.tsx | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/client/views') diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 99d50b4a2..d5f5dabb6 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -12,6 +12,7 @@ import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; import { Transform } from '../util/Transform'; import { CollectionDockingView } from './collections/CollectionDockingView'; +import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline'; import { TabDocView } from './collections/TabDocView'; import './LightboxView.scss'; import { DocumentView } from './nodes/DocumentView'; @@ -56,6 +57,11 @@ export class LightboxView extends React.Component { const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen'); } + CollectionStackedTimeline.CurrentlyPlaying.forEach(doc => { + DocumentManager.Instance.getAllDocumentViews(doc).forEach(dv => { + dv.ComponentView?.Pause?.(); + }); + }); //TabDocView.PinDoc(doc, { hidePresBox: true }); this._history ? this._history.push({ doc, target }) : (this._history = [{ doc, target }]); if (doc !== LightboxView.LightboxDoc) { -- cgit v1.2.3-70-g09d2 From 5e0ababf6d323ff599fa469693d5a6b20e438baf Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 11 Sep 2022 13:18:35 -0400 Subject: fixed crash when selecting ink strokes --- src/client/views/nodes/formattedText/RichTextMenu.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index c548e211b..9faaa0f3a 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -154,7 +154,7 @@ export class RichTextMenu extends AntimodeMenu { // finds font sizes and families in selection getActiveAlignment() { - if (this.view && this.TextView.props.isSelected(true)) { + if (this.view && this.TextView?.props.isSelected(true)) { const path = (this.view.state.selection.$from as any).path; for (let i = path.length - 3; i < path.length && i >= 0; i -= 3) { if (path[i]?.type === this.view.state.schema.nodes.paragraph || path[i]?.type === this.view.state.schema.nodes.heading) { @@ -167,7 +167,7 @@ export class RichTextMenu extends AntimodeMenu { // finds font sizes and families in selection getActiveListStyle() { - if (this.view && this.TextView.props.isSelected(true)) { + if (this.view && this.TextView?.props.isSelected(true)) { const path = (this.view.state.selection.$from as any).path; for (let i = 0; i < path.length; i += 3) { if (path[i].type === this.view.state.schema.nodes.ordered_list) { @@ -189,7 +189,7 @@ export class RichTextMenu extends AntimodeMenu { const activeSizes: string[] = []; const activeColors: string[] = []; const activeHighlights: string[] = []; - if (this.TextView.props.isSelected(true)) { + if (this.TextView?.props.isSelected(true)) { const state = this.view.state; const pos = this.view.state.selection.$from; const marks: Mark[] = [...(state.storedMarks ?? [])]; @@ -222,7 +222,7 @@ export class RichTextMenu extends AntimodeMenu { //finds all active marks on selection in given group getActiveMarksOnSelection() { let activeMarks: MarkType[] = []; - if (!this.view || !this.TextView.props.isSelected(true)) return activeMarks; + if (!this.view || !this.TextView?.props.isSelected(true)) return activeMarks; const markGroup = [schema.marks.noAutoLinkAnchor, schema.marks.strong, schema.marks.em, schema.marks.underline, schema.marks.strikethrough, schema.marks.superscript, schema.marks.subscript]; if (this.view.state.storedMarks) return this.view.state.storedMarks.map(mark => mark.type); @@ -412,7 +412,7 @@ export class RichTextMenu extends AntimodeMenu { } align = (view: EditorView, dispatch: any, alignment: 'left' | 'right' | 'center') => { - if (this.TextView.props.isSelected(true)) { + if (this.TextView?.props.isSelected(true)) { var tr = view.state.tr; view.state.doc.nodesBetween(view.state.selection.from, view.state.selection.to, (node, pos, parent, index) => { if ([schema.nodes.paragraph, schema.nodes.heading].includes(node.type)) { -- cgit v1.2.3-70-g09d2 From a0b595c00111404e9a4fb6b9a926ef22c6e2979b Mon Sep 17 00:00:00 2001 From: bobzel Date: Sun, 11 Sep 2022 13:27:44 -0400 Subject: fixed fontFamily menu for ink strokes --- .../views/nodes/formattedText/RichTextMenu.tsx | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/formattedText/RichTextMenu.tsx b/src/client/views/nodes/formattedText/RichTextMenu.tsx index 9faaa0f3a..6c6d26af5 100644 --- a/src/client/views/nodes/formattedText/RichTextMenu.tsx +++ b/src/client/views/nodes/formattedText/RichTextMenu.tsx @@ -183,13 +183,11 @@ export class RichTextMenu extends AntimodeMenu { // finds font sizes and families in selection getActiveFontStylesOnSelection() { - if (!this.view) return { activeFamilies: [], activeSizes: [], activeColors: [], activeHighlights: [] }; - - const activeFamilies: string[] = []; - const activeSizes: string[] = []; - const activeColors: string[] = []; - const activeHighlights: string[] = []; - if (this.TextView?.props.isSelected(true)) { + const activeFamilies = new Set(); + const activeSizes = new Set(); + const activeColors = new Set(); + const activeHighlights = new Set(); + if (this.view && this.TextView?.props.isSelected(true)) { const state = this.view.state; const pos = this.view.state.selection.$from; const marks: Mark[] = [...(state.storedMarks ?? [])]; @@ -203,13 +201,13 @@ export class RichTextMenu extends AntimodeMenu { }); } marks.forEach(m => { - m.type === state.schema.marks.pFontFamily && activeFamilies.push(m.attrs.family); - m.type === state.schema.marks.pFontColor && activeColors.push(m.attrs.color); - m.type === state.schema.marks.pFontSize && activeSizes.push(m.attrs.fontSize); - m.type === state.schema.marks.marker && activeHighlights.push(String(m.attrs.highlight)); + m.type === state.schema.marks.pFontFamily && activeFamilies.add(m.attrs.family); + m.type === state.schema.marks.pFontColor && activeColors.add(m.attrs.color); + m.type === state.schema.marks.pFontSize && activeSizes.add(m.attrs.fontSize); + m.type === state.schema.marks.marker && activeHighlights.add(String(m.attrs.highlight)); }); } - return { activeFamilies, activeSizes, activeColors, activeHighlights }; + return { activeFamilies: Array.from(activeFamilies), activeSizes: Array.from(activeSizes), activeColors: Array.from(activeColors), activeHighlights: Array.from(activeHighlights) }; } getMarksInSelection(state: EditorState) { -- cgit v1.2.3-70-g09d2 From 3993c034210f13e95717a3d417323e14bd8417b9 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 12 Sep 2022 09:33:25 -0400 Subject: fixed lightbox exception when no media is playing --- src/client/views/LightboxView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index d5f5dabb6..25354a09d 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -57,7 +57,7 @@ export class LightboxView extends React.Component { const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); l && (Cast(l.anchor2, Doc, null).backgroundColor = 'lightgreen'); } - CollectionStackedTimeline.CurrentlyPlaying.forEach(doc => { + CollectionStackedTimeline.CurrentlyPlaying?.forEach(doc => { DocumentManager.Instance.getAllDocumentViews(doc).forEach(dv => { dv.ComponentView?.Pause?.(); }); -- cgit v1.2.3-70-g09d2 From f847c6d554f9dcecbd6c3024f712510f341daf67 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 12 Sep 2022 11:36:15 -0400 Subject: made annotation pen available in lightbox. --- src/client/views/GestureOverlay.scss | 5 +- src/client/views/GestureOverlay.tsx | 43 +++++----- src/client/views/LightboxView.scss | 80 ++++++++++++------- src/client/views/LightboxView.tsx | 101 ++++++++++++++---------- src/client/views/MainView.tsx | 48 +++++------ src/client/views/collections/CollectionMenu.tsx | 1 - src/client/views/nodes/DocumentView.tsx | 9 +-- 7 files changed, 159 insertions(+), 128 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/GestureOverlay.scss b/src/client/views/GestureOverlay.scss index f5cbbffb1..bfe2d5c64 100644 --- a/src/client/views/GestureOverlay.scss +++ b/src/client/views/GestureOverlay.scss @@ -1,8 +1,9 @@ .gestureOverlay-cont { - width: 100vw; - height: 100vh; + width: 100%; + height: 100%; position: absolute; touch-action: none; + top: 0; .pointerBubbles { width: 100%; diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 6f28ef9eb..850688e7e 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -1,7 +1,6 @@ import React = require('react'); import * as fitCurve from 'fit-curve'; -import { action, computed, observable, runInAction } from 'mobx'; -import { observer } from 'mobx-react'; +import { action, computed, observable, runInAction, trace } from 'mobx'; import { Doc } from '../../fields/Doc'; import { InkData, InkTool } from '../../fields/InkField'; import { List } from '../../fields/List'; @@ -41,9 +40,13 @@ import { RadialMenu } from './nodes/RadialMenu'; import HorizontalPalette from './Palette'; import { Touchable } from './Touchable'; import TouchScrollableMenu, { TouchScrollableMenuItem } from './TouchScrollableMenu'; +import { observer } from 'mobx-react'; +interface GestureOverlayProps { + isActive: boolean; +} @observer -export class GestureOverlay extends Touchable { +export class GestureOverlay extends Touchable { static Instance: GestureOverlay; @observable public InkShape: string = ''; @@ -83,7 +86,7 @@ export class GestureOverlay extends Touchable { protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; - constructor(props: Readonly<{}>) { + constructor(props: any) { super(props); GestureOverlay.Instance = this; @@ -495,7 +498,7 @@ export class GestureOverlay extends Touchable { } this._strokes = []; - this._points = []; + this._points.length = 0; this._possibilities = []; document.removeEventListener('touchend', this.handleHandUp); } @@ -619,14 +622,14 @@ export class GestureOverlay extends Touchable { // get the two targets at the ends of the line const ep1 = this._points[0]; - const ep2 = this._points[this._points.length - 1]; + const ep2 = this._points.lastElement(); const target1 = document.elementFromPoint(ep1.X, ep1.Y); const target2 = document.elementFromPoint(ep2.X, ep2.Y); const ge = new CustomEvent('dashOnGesture', { bubbles: true, detail: { - points: this._points, + points: this._points.slice(), gesture: GestureUtils.Gestures.Line, bounds: B, }, @@ -652,8 +655,8 @@ export class GestureOverlay extends Touchable { if (this.Tool !== ToolglassTools.None && xInGlass && yInGlass) { switch (this.Tool) { case ToolglassTools.InkToText: - this._strokes.push(new Array(...this._points)); - this._points = []; + this._strokes.push(this._points.slice()); + this._points.length = 0; CognitiveServices.Inking.Appliers.InterpretStrokes(this._strokes).then(results => { const wordResults = results.filter((r: any) => r.category === 'line'); const possibilities: string[] = []; @@ -675,7 +678,7 @@ export class GestureOverlay extends Touchable { break; case ToolglassTools.IgnoreGesture: this.dispatchGesture(GestureUtils.Gestures.Stroke); - this._points = []; + this._points.length = 0; break; } } @@ -683,7 +686,7 @@ export class GestureOverlay extends Touchable { else if (this.InkShape) { this.makePolygon(this.InkShape, false); this.dispatchGesture(GestureUtils.Gestures.Stroke); - this._points = []; + this._points.length = 0; if (!CollectionFreeFormViewChrome.Instance?._keepPrimitiveMode) { this.InkShape = ''; Doc.ActiveTool = InkTool.None; @@ -743,15 +746,16 @@ export class GestureOverlay extends Touchable { (controlPoints[0].X - controlPoints.lastElement().X) * (controlPoints[0].X - controlPoints.lastElement().X) + (controlPoints[0].Y - controlPoints.lastElement().Y) * (controlPoints[0].Y - controlPoints.lastElement().Y) ); if (controlPoints.length > 4 && dist < 10) controlPoints[controlPoints.length - 1] = controlPoints[0]; - this._points = controlPoints; + this._points.length = 0; + this._points.push(...controlPoints); this.dispatchGesture(GestureUtils.Gestures.Stroke); // TODO: nda - check inks to group here checkInksToGroup(); } - this._points = []; + this._points.length = 0; } } else { - this._points = []; + this._points.length = 0; } CollectionFreeFormViewChrome.Instance?.primCreated(); }; @@ -803,7 +807,7 @@ export class GestureOverlay extends Touchable { } } } - this._points = []; + this._points.length = 0; switch (shape) { //must push an extra point in the end so InteractionUtils knows pointer is up. //must be (points[0].X,points[0]-1) @@ -922,7 +926,7 @@ export class GestureOverlay extends Touchable { new CustomEvent('dashOnGesture', { bubbles: true, detail: { - points: stroke ?? this._points, + points: stroke ?? this._points.slice(), gesture: gesture as any, bounds: this.getBounds(stroke ?? this._points), text: data, @@ -1067,8 +1071,8 @@ export class GestureOverlay extends Touchable { render() { return ( -
- {this.showMobileInkOverlay ? : <>} +
+ {this.showMobileInkOverlay ? : null} {this.elements}
+ }} + />
); diff --git a/src/client/views/LightboxView.scss b/src/client/views/LightboxView.scss index 5d42cd97f..ae5dc9902 100644 --- a/src/client/views/LightboxView.scss +++ b/src/client/views/LightboxView.scss @@ -1,35 +1,55 @@ - - .lightboxView-navBtn { - margin: auto; - position: absolute; - right: 10; - top: 10; - background: transparent; - border-radius: 8; - color:white; - opacity: 0.7; - width: 35; - &:hover { - opacity: 1; - } +.lightboxView-navBtn { + margin: auto; + position: absolute; + right: 10; + top: 10; + background: transparent; + border-radius: 8; + color: white; + opacity: 0.7; + width: 25; + flex-direction: column; + display: flex; + &:hover { + opacity: 1; } - .lightboxView-tabBtn { - margin: auto; - position: absolute; - right: 35; - top: 10; - background: transparent; - border-radius: 8; - color:white; - opacity: 0.7; - width: 35; - &:hover { - opacity: 1; - } +} +.lightboxView-tabBtn { + margin: auto; + position: absolute; + right: 38; + top: 10; + background: transparent; + border-radius: 8; + color: white; + opacity: 0.7; + width: 25; + flex-direction: column; + display: flex; + &:hover { + opacity: 1; + } +} +.lightboxView-penBtn { + margin: auto; + position: absolute; + right: 70; + top: 10; + background: transparent; + border-radius: 8; + color: white; + opacity: 0.7; + width: 25; + flex-direction: column; + display: flex; + &:hover { + opacity: 1; } +} .lightboxView-frame { - position: absolute; - top: 0; left: 0; + position: absolute; + top: 0; + left: 0; width: 100%; height: 100%; background: #000000bb; @@ -51,4 +71,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 25354a09d..5613e82fb 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -4,6 +4,7 @@ import { observer } from 'mobx-react'; import 'normalize.css'; import * as React from 'react'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; +import { InkTool } from '../../fields/InkField'; import { Cast, NumCast, StrCast } from '../../fields/Types'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue } from '../../Utils'; import { DocUtils } from '../documents/Documents'; @@ -14,6 +15,7 @@ import { Transform } from '../util/Transform'; import { CollectionDockingView } from './collections/CollectionDockingView'; import { CollectionStackedTimeline } from './collections/CollectionStackedTimeline'; import { TabDocView } from './collections/TabDocView'; +import { GestureOverlay } from './GestureOverlay'; import './LightboxView.scss'; import { DocumentView } from './nodes/DocumentView'; import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider'; @@ -267,45 +269,48 @@ export class LightboxView extends React.Component { clipPath: `path('${Doc.UserDoc().renderStyle === 'comic' ? wavyBorderPath(this.lightboxWidth(), this.lightboxHeight()) : undefined}')`, }}> {/* TODO:glr This is where it would go*/} - { - LightboxView._docView = r !== null ? r : undefined; - r && - setTimeout( - action(() => { - const target = LightboxView._docTarget; - const doc = LightboxView._doc; - const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target); - if (doc === r.props.Document && (!target || target === doc)) r.ComponentView?.shrinkWrap?.(); - //else target?.focus(target, { willZoom: true, scale: 0.9, instant: true }); // bcz: why was this here? it breaks smooth navigation in lightbox using 'next' button - }) - ); - })} - Document={LightboxView.LightboxDoc} - DataDoc={undefined} - LayoutTemplate={LightboxView.LightboxDocTemplate} - addDocument={undefined} - isDocumentActive={returnFalse} - isContentActive={returnTrue} - addDocTab={this.addDocTab} - pinToPres={TabDocView.PinDoc} - rootSelected={returnTrue} - docViewPath={returnEmptyDoclist} - docFilters={this.docFilters} - removeDocument={undefined} - styleProvider={DefaultStyleProvider} - ScreenToLocalTransform={this.lightboxScreenToLocal} - PanelWidth={this.lightboxWidth} - PanelHeight={this.lightboxHeight} - focus={DocUtils.DefaultFocus} - whenChildContentsActiveChanged={emptyFunction} - bringToFront={emptyFunction} - docRangeFilters={returnEmptyFilter} - searchFilterDocs={returnEmptyDoclist} - ContainingCollectionView={undefined} - ContainingCollectionDoc={undefined} - renderDepth={0} - /> + + + { + LightboxView._docView = r !== null ? r : undefined; + r && + setTimeout( + action(() => { + const target = LightboxView._docTarget; + const doc = LightboxView._doc; + const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target); + if (doc === r.props.Document && (!target || target === doc)) r.ComponentView?.shrinkWrap?.(); + //else target?.focus(target, { willZoom: true, scale: 0.9, instant: true }); // bcz: why was this here? it breaks smooth navigation in lightbox using 'next' button + }) + ); + })} + Document={LightboxView.LightboxDoc} + DataDoc={undefined} + LayoutTemplate={LightboxView.LightboxDocTemplate} + addDocument={undefined} + isDocumentActive={returnFalse} + isContentActive={returnTrue} + addDocTab={this.addDocTab} + pinToPres={TabDocView.PinDoc} + rootSelected={returnTrue} + docViewPath={returnEmptyDoclist} + docFilters={this.docFilters} + removeDocument={undefined} + styleProvider={DefaultStyleProvider} + ScreenToLocalTransform={this.lightboxScreenToLocal} + PanelWidth={this.lightboxWidth} + PanelHeight={this.lightboxHeight} + focus={DocUtils.DefaultFocus} + whenChildContentsActiveChanged={emptyFunction} + bringToFront={emptyFunction} + docRangeFilters={returnEmptyFilter} + searchFilterDocs={returnEmptyDoclist} + ContainingCollectionView={undefined} + ContainingCollectionDoc={undefined} + renderDepth={0} + /> +
{this.navBtn( @@ -332,6 +337,15 @@ export class LightboxView extends React.Component { this.future()?.length.toString() )} +
{ + e.stopPropagation(); + LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth; + }}> + +
{
{ e.stopPropagation(); - LightboxView.LightboxDoc!._fitWidth = !LightboxView.LightboxDoc!._fitWidth; + Doc.ActiveTool = Doc.ActiveTool === InkTool.Pen ? InkTool.None : InkTool.Pen; }}> - +
); diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 45281ed69..d7b526d22 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -86,7 +86,7 @@ export class MainView extends React.Component { return this._hideUI ? 0 : 27; } // 27 comes form lm.config.defaultConfig.dimensions.headerHeight in goldenlayout.js @computed private get topOfDashUI() { - return this._hideUI ? 0 : Number(DASHBOARD_SELECTOR_HEIGHT.replace('px', '')); + return this._hideUI || LightboxView.LightboxDoc ? 0 : Number(DASHBOARD_SELECTOR_HEIGHT.replace('px', '')); } @computed private get topOfHeaderBarDoc() { return this.topOfDashUI; @@ -610,7 +610,7 @@ export class MainView extends React.Component { @computed get dockingContent() { return ( - +
1 ? locationFields[1] : ''; if (doc.dockingConfig) return DashboardView.openDashboard(doc); + // prettier-ignore switch (locationFields[0]) { - case 'dashboard': - return DashboardView.openDashboard(doc); - case 'close': - return CollectionDockingView.CloseSplit(doc, locationParams); - case 'fullScreen': - return CollectionDockingView.OpenFullScreen(doc); - case 'lightbox': - return LightboxView.AddDocTab(doc, location); - case 'toggle': - return CollectionDockingView.ToggleSplit(doc, locationParams); - case 'inPlace': - case 'add': default: - return CollectionDockingView.AddSplit(doc, locationParams); + case 'inPlace': + case 'add': return CollectionDockingView.AddSplit(doc, locationParams); + case 'dashboard': return DashboardView.openDashboard(doc); + case 'close': return CollectionDockingView.CloseSplit(doc, locationParams); + case 'fullScreen': return CollectionDockingView.OpenFullScreen(doc); + case 'lightbox': return LightboxView.AddDocTab(doc, location); + case 'toggle': return CollectionDockingView.ToggleSplit(doc, locationParams); } }; @@ -716,7 +711,7 @@ export class MainView extends React.Component { @computed get leftMenuPanel() { return ( -
+
: null} {((page: string) => { + // prettier-ignore switch (page) { - case 'dashboard': default: - return ( - <> -
- -
- {this.mainDashboardArea} - - ); - case 'home': - return ; + case 'dashboard': return (<> +
+ +
+ {this.mainDashboardArea} + ); + case 'home': return ; } })(Doc.ActivePage)} @@ -1020,7 +1012,7 @@ export class MainView extends React.Component { {this.snapLines}
- +
); } diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 6a0f69359..46e8494ab 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -34,7 +34,6 @@ import { CollectionFreeFormDocumentView } from '../nodes/CollectionFreeFormDocum import { DocumentView } from '../nodes/DocumentView'; import { FormattedTextBox } from '../nodes/formattedText/FormattedTextBox'; import { RichTextMenu } from '../nodes/formattedText/RichTextMenu'; -import { PresBox } from '../nodes/trails/PresBox'; import { DefaultStyleProvider } from '../StyleProvider'; import { CollectionDockingView } from './CollectionDockingView'; import { CollectionLinearView } from './collectionLinear'; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 113574a64..e628c2e44 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1,8 +1,9 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Tooltip } from '@material-ui/core'; import { action, computed, IReactionDisposer, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, HeightSym, Opt, StrListCast, WidthSym } from '../../../fields/Doc'; +import { AclAdmin, AclEdit, AclPrivate, DataSym, Doc, DocListCast, Field, Opt, StrListCast, WidthSym } from '../../../fields/Doc'; import { Document } from '../../../fields/documentSchemas'; import { Id } from '../../../fields/FieldSymbols'; import { InkTool } from '../../../fields/InkField'; @@ -10,7 +11,7 @@ import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { AudioField } from '../../../fields/URLField'; import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; import { MobileInterface } from '../../../mobile/MobileInterface'; @@ -20,6 +21,7 @@ import { DocServer } from '../../DocServer'; import { Docs, DocUtils } from '../../documents/Documents'; import { CollectionViewType, DocumentType } from '../../documents/DocumentTypes'; import { Networking } from '../../Network'; +import { DictationManager } from '../../util/DictationManager'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager, dropActionType } from '../../util/DragManager'; import { InteractionUtils } from '../../util/InteractionUtils'; @@ -37,7 +39,6 @@ import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; import { DocComponent } from '../DocComponent'; import { EditableView } from '../EditableView'; -import { InkingStroke } from '../InkingStroke'; import { LightboxView } from '../LightboxView'; import { StyleProp } from '../StyleProvider'; import { CollectionFreeFormDocumentView } from './CollectionFreeFormDocumentView'; @@ -52,8 +53,6 @@ import { RadialMenu } from './RadialMenu'; import { ScriptingBox } from './ScriptingBox'; import { PresBox } from './trails/PresBox'; import React = require('react'); -import { DictationManager } from '../../util/DictationManager'; -import { Tooltip } from '@material-ui/core'; const { Howl } = require('howler'); interface Window { -- cgit v1.2.3-70-g09d2 From 13e0ac912beeab64a859b3463953774f3f1676f1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 12 Sep 2022 12:09:03 -0400 Subject: fixed h1 style for use in text boxes with #,## etc markdown. made %[tix!] text tags reset user_mark to current time. --- src/client/views/DictationOverlay.tsx | 60 ++++++++++-------- src/client/views/LightboxView.tsx | 1 - src/client/views/MainView.scss | 5 ++ src/client/views/MainView.tsx | 1 + src/client/views/PreviewCursor.scss | 5 +- src/client/views/PreviewCursor.tsx | 3 +- .../views/nodes/formattedText/RichTextRules.ts | 10 ++- src/debug/Viewer.tsx | 72 +++++++++++----------- 8 files changed, 86 insertions(+), 71 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/DictationOverlay.tsx b/src/client/views/DictationOverlay.tsx index f4f96da8a..0bdcdc303 100644 --- a/src/client/views/DictationOverlay.tsx +++ b/src/client/views/DictationOverlay.tsx @@ -1,9 +1,8 @@ import { computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import "normalize.css"; import * as React from 'react'; import { DictationManager } from '../util/DictationManager'; -import "./Main.scss"; +import './Main.scss'; import { MainViewModal } from './MainViewModal'; @observer @@ -29,44 +28,53 @@ export class DictationOverlay extends React.Component { this.dictationOverlayVisible = false; this.dictationSuccess = undefined; DictationOverlay.Instance.hasActiveModal = false; - setTimeout(() => this.dictatedPhrase = DictationManager.placeholder, 500); + setTimeout(() => (this.dictatedPhrase = DictationManager.placeholder), 500); }, duration); - } + }; public cancelDictationFade = () => { if (this.overlayTimeout) { clearTimeout(this.overlayTimeout); this.overlayTimeout = undefined; } - } + }; - @computed public get dictatedPhrase() { return this._dictationState; } - @computed public get dictationSuccess() { return this._dictationSuccessState; } - @computed public get dictationOverlayVisible() { return this._dictationDisplayState; } - @computed public get isListening() { return this._dictationListeningState; } + @computed public get dictatedPhrase() { + return this._dictationState; + } + @computed public get dictationSuccess() { + return this._dictationSuccessState; + } + @computed public get dictationOverlayVisible() { + return this._dictationDisplayState; + } + @computed public get isListening() { + return this._dictationListeningState; + } - public set dictatedPhrase(value: string) { runInAction(() => this._dictationState = value); } - public set dictationSuccess(value: boolean | undefined) { runInAction(() => this._dictationSuccessState = value); } - public set dictationOverlayVisible(value: boolean) { runInAction(() => this._dictationDisplayState = value); } - public set isListening(value: DictationManager.Controls.ListeningUIStatus) { runInAction(() => this._dictationListeningState = value); } + public set dictatedPhrase(value: string) { + runInAction(() => (this._dictationState = value)); + } + public set dictationSuccess(value: boolean | undefined) { + runInAction(() => (this._dictationSuccessState = value)); + } + public set dictationOverlayVisible(value: boolean) { + runInAction(() => (this._dictationDisplayState = value)); + } + public set isListening(value: DictationManager.Controls.ListeningUIStatus) { + runInAction(() => (this._dictationListeningState = value)); + } render() { const success = this.dictationSuccess; const result = this.isListening && !this.isListening.interim ? DictationManager.placeholder : `"${this.dictatedPhrase}"`; const dialogueBoxStyle = { - background: success === undefined ? "gainsboro" : success ? "lawngreen" : "red", - borderColor: this.isListening ? "red" : "black", - fontStyle: "italic" + background: success === undefined ? 'gainsboro' : success ? 'lawngreen' : 'red', + borderColor: this.isListening ? 'red' : 'black', + fontStyle: 'italic', }; const overlayStyle = { - backgroundColor: this.isListening ? "red" : "darkslategrey" + backgroundColor: this.isListening ? 'red' : 'darkslategrey', }; - return (); + return ; } -} \ No newline at end of file +} diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index 5613e82fb..cb5094f4b 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -1,7 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; -import 'normalize.css'; import * as React from 'react'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; import { InkTool } from '../../fields/InkField'; diff --git a/src/client/views/MainView.scss b/src/client/views/MainView.scss index da79d2992..c5ac1cf52 100644 --- a/src/client/views/MainView.scss +++ b/src/client/views/MainView.scss @@ -1,5 +1,10 @@ @import 'global/globalCssVariables'; @import 'nodeModuleOverrides'; +h1, +.h1 { + // reverts change to h1 made by normalize.css + font-size: 36px; +} .dash-tooltip { font-size: 11px; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d7b526d22..24dae8816 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -5,6 +5,7 @@ import * as fa from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { action, computed, configure, observable, reaction, runInAction } from 'mobx'; import { observer } from 'mobx-react'; +import 'normalize.css'; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { Doc, DocListCast, Opt } from '../../fields/Doc'; diff --git a/src/client/views/PreviewCursor.scss b/src/client/views/PreviewCursor.scss index 60b7d14a0..7be765ea9 100644 --- a/src/client/views/PreviewCursor.scss +++ b/src/client/views/PreviewCursor.scss @@ -1,11 +1,10 @@ - .previewCursor-Dark, .previewCursor { color: black; position: absolute; transform-origin: left top; top: 0; - left:0; + left: 0; pointer-events: none; opacity: 1; z-index: 1001; @@ -13,4 +12,4 @@ .previewCursor-Dark { color: white; -} \ No newline at end of file +} diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 4c17d5a97..d56d2a310 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -1,10 +1,9 @@ import { action, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; -import 'normalize.css'; import * as React from 'react'; import { Doc } from '../../fields/Doc'; import { Cast, NumCast, StrCast } from '../../fields/Types'; -import { emptyFunction, returnFalse } from '../../Utils'; +import { returnFalse } from '../../Utils'; import { DocServer } from '../DocServer'; import { Docs, DocumentOptions, DocUtils } from '../documents/Documents'; import { Transform } from '../util/Transform'; diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 2097b321f..2eb62c38d 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -343,8 +343,14 @@ export class RichTextRules { const node = (state.doc.resolve(start) as any).nodeAfter; if (node?.marks.findIndex((m: any) => m.type === schema.marks.user_tag) !== -1) return state.tr.removeMark(start, end, schema.marks.user_tag); - - return node ? state.tr.addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: tag, modified: Math.round(Date.now() / 1000 / 60) })) : state.tr; + if (node?.marks.findIndex((m: any) => m.type === schema.marks.user_mark) !== -1) { + } + return node + ? state.tr + .removeMark(start, end, schema.marks.user_mark) + .addMark(start, end, schema.marks.user_mark.create({ userid: Doc.CurrentUserEmail, modified: Math.floor(Date.now() / 1000) })) + .addMark(start, end, schema.marks.user_tag.create({ userid: Doc.CurrentUserEmail, tag: tag, modified: Math.round(Date.now() / 1000 / 60) })) + : state.tr; }), new InputRule(new RegExp(/%\(/), (state, match, start, end) => { diff --git a/src/debug/Viewer.tsx b/src/debug/Viewer.tsx index ee7dd1fc1..02038c426 100644 --- a/src/debug/Viewer.tsx +++ b/src/debug/Viewer.tsx @@ -1,5 +1,4 @@ import { action, configure, observable, ObservableMap, Lambda } from 'mobx'; -import "normalize.css"; import * as React from 'react'; import * as ReactDOM from 'react-dom'; import { observer } from 'mobx-react'; @@ -21,7 +20,6 @@ URLField; ScriptField; CursorField; - function applyToDoc(doc: { [index: string]: FieldResult }, key: string, scriptString: string): boolean; function applyToDoc(doc: { [index: number]: FieldResult }, key: number, scriptString: string): boolean; function applyToDoc(doc: any, key: string | number, scriptString: string): boolean { @@ -37,11 +35,11 @@ function applyToDoc(doc: any, key: string | number, scriptString: string): boole } configure({ - enforceActions: "observed" + enforceActions: 'observed', }); @observer -class ListViewer extends React.Component<{ field: List }>{ +class ListViewer extends React.Component<{ field: List }> { @observable expanded = false; @@ -49,14 +47,16 @@ class ListViewer extends React.Component<{ field: List }>{ onClick = (e: React.MouseEvent) => { this.expanded = !this.expanded; e.stopPropagation(); - } + }; render() { let content; if (this.expanded) { content = (
- {this.props.field.map((field, index) => applyToDoc(this.props.field, index, value)} />)} + {this.props.field.map((field, index) => ( + applyToDoc(this.props.field, index, value)} /> + ))}
); } else { @@ -66,7 +66,7 @@ class ListViewer extends React.Component<{ field: List }>{
{content} -
+
); } } @@ -80,7 +80,7 @@ class DocumentViewer extends React.Component<{ field: Doc }> { onClick = (e: React.MouseEvent) => { this.expanded = !this.expanded; e.stopPropagation(); - } + }; render() { let content; @@ -96,10 +96,7 @@ class DocumentViewer extends React.Component<{ field: Doc }> { }); content = (
- Document ({this.props.field[Id]}) -
- {fields} -
+ Document ({this.props.field[Id]})
{fields}
); } else { @@ -109,24 +106,23 @@ class DocumentViewer extends React.Component<{ field: Doc }> {
{content} -
+
); } } @observer -class DebugViewer extends React.Component<{ field: FieldResult, setValue(value: string): boolean }> { - +class DebugViewer extends React.Component<{ field: FieldResult; setValue(value: string): boolean }> { render() { let content; const field = this.props.field; if (field instanceof List) { - content = (); + content = ; } else if (field instanceof Doc) { - content = (); - } else if (typeof field === "string") { + content = ; + } else if (typeof field === 'string') { content =

"{field}"

; - } else if (typeof field === "number" || typeof field === "boolean") { + } else if (typeof field === 'number' || typeof field === 'boolean') { content =

{field}

; } else if (field instanceof RichTextField) { content =

RTF: {field.Data}

; @@ -153,28 +149,30 @@ class Viewer extends React.Component { @action inputOnChange = (e: React.ChangeEvent) => { this.idToAdd = e.target.value; - } + }; @action onKeyPress = (e: React.KeyboardEvent) => { - if (e.key === "Enter") { - DocServer.GetRefField(this.idToAdd).then(action((field: any) => { - if (field !== undefined) { - this.fields.push(field); - } - })); - this.idToAdd = ""; + if (e.key === 'Enter') { + DocServer.GetRefField(this.idToAdd).then( + action((field: any) => { + if (field !== undefined) { + this.fields.push(field); + } + }) + ); + this.idToAdd = ''; } - } + }; render() { return ( <> - +
- {this.fields.map((field, index) => false}>)} + {this.fields.map((field, index) => ( + false}> + ))}
); @@ -182,11 +180,11 @@ class Viewer extends React.Component { } (async function () { - await DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, "viewer"); - ReactDOM.render(( -
+ await DocServer.init(window.location.protocol, window.location.hostname, resolvedPorts.socket, 'viewer'); + ReactDOM.render( +
-
), +
, document.getElementById('root') ); -})(); \ No newline at end of file +})(); -- cgit v1.2.3-70-g09d2 From 51a4c0978cdc65a4a16bc9f03c7e4eff551769af Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 12 Sep 2022 13:12:39 -0400 Subject: fixed autosizing text with header tags. --- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 314696251..5cb805f80 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1697,7 +1697,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent p + Number(getComputedStyle(child).height.replace('px', '')), margins); + const proseHeight = !this.ProseRef + ? 0 + : children.reduce((p, child) => p + Number(getComputedStyle(child).height.replace('px', '')) + Number(getComputedStyle(child).marginTop.replace('px', '')) + Number(getComputedStyle(child).marginBottom.replace('px', '')), margins); const scrollHeight = this.ProseRef && Math.min(NumCast(this.layoutDoc.docMaxAutoHeight, proseHeight), proseHeight); if (this.props.setHeight && scrollHeight && this.props.renderDepth && !this.props.dontRegisterView) { // if top === 0, then the text box is growing upward (as the overlay caption) which doesn't contribute to the height computation -- cgit v1.2.3-70-g09d2 From 4315a0378bc54ae9eaa684d416839f635c38e865 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 12 Sep 2022 16:18:42 -0400 Subject: fixed rotating UI for video boxes. fixed generating error response for unsupported video/audio formats. --- src/client/views/DocumentDecorations.tsx | 8 +++---- src/client/views/nodes/VideoBox.tsx | 38 +++++++++++++++++++++----------- src/server/DashUploadUtils.ts | 3 +++ 3 files changed, 31 insertions(+), 18 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index a79f727a7..832d0a35c 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -738,11 +738,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P )} - {useRotation && ( -
e.preventDefault()}> - {'⟲'} -
- )} +
e.preventDefault()}> + {'⟲'} +
{useRounding && (
{ - const bounds = this.props.docViewPath().lastElement().getBounds(); - const left = bounds?.left || 0; - const right = bounds?.right || 0; - const top = bounds?.top || 0; - const height = (bounds?.bottom || 0) - top; - const width = Math.max(right - left, 100); - const uiHeight = Math.max(25, Math.min(50, height / 10)); - const uiMargin = Math.min(10, height / 20); - const vidHeight = (height * this.heightPercent) / 100; - const yPos = top + vidHeight - uiHeight - uiMargin; - const xPos = uiHeight / vidHeight > 0.4 ? right + 10 : left + 10; + const xf = this.props.ScreenToLocalTransform().inverse(); + const height = this.props.PanelHeight(); + const vidHeight = (height * this.heightPercent) / 100 / this.scaling(); + const vidWidth = this.props.PanelWidth() / this.scaling(); + const uiHeight = 25; + const uiMargin = 10; + const yBot = xf.transformPoint(0, vidHeight)[1]; + // prettier-ignore + const yMid = (xf.transformPoint(0, 0)[1] + + xf.transformPoint(0, height / this.scaling())[1]) / 2; + const xPos = xf.transformPoint(vidWidth / 2, 0)[0]; + const xRight = xf.transformPoint(vidWidth, 0)[0]; const opacity = this._scrubbing ? 0.3 : this._controlsVisible ? 1 : 0; - return this._fullScreen || right - left < 50 ? null : ( + return this._fullScreen || (xRight - xPos) * 2 < 50 ? null : (
-
+
{this.UIButtons}
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index e94ef8534..8cf657da4 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -142,6 +142,7 @@ export namespace DashUploadUtils { const result = await UploadImage(path, basename(path)); return { source: file, result }; } + return { source: file, result: { name: 'Unsupported image format', message: `Could not upload unsupported file (${name}). Please convert to an .jpg` } }; case 'video': if (format.includes('x-matroska')) { console.log('case video'); @@ -179,6 +180,7 @@ export namespace DashUploadUtils { if (videoFormats.includes(format)) { return MoveParsedFile(file, Directory.videos); } + return { source: file, result: { name: 'Unsupported video format', message: `Could not upload unsupported file (${name}). Please convert to an .mp4` } }; case 'application': if (applicationFormats.includes(format)) { return UploadPdf(file); @@ -191,6 +193,7 @@ export namespace DashUploadUtils { if (audioFormats.includes(format)) { return UploadAudio(file, format); } + return { source: file, result: { name: 'Unsupported audio format', message: `Could not upload unsupported file (${name}). Please convert to an .mp3` } }; case 'text': if (types[1] == 'csv') { return UploadCsv(file); -- cgit v1.2.3-70-g09d2 From a2820ffc0c9fd8323fa2f9a9ae5334da4c72283b Mon Sep 17 00:00:00 2001 From: Jenny Yu Date: Mon, 12 Sep 2022 19:16:57 -0400 Subject: breaking: made trails belong to dashboard instead of userdoc --- src/client/util/CurrentUserUtils.ts | 29 +++++++++++++++++--- src/client/util/SharingManager.tsx | 2 +- src/client/views/DashboardView.tsx | 41 +++++++++++++++++++++++++---- src/client/views/MainView.tsx | 12 +++++++-- src/client/views/collections/TabDocView.tsx | 11 ++++++-- src/fields/Doc.ts | 8 +++--- 6 files changed, 86 insertions(+), 17 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 6c80cf0f4..5fbecd741 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -1,3 +1,4 @@ +import { forOwn } from "lodash"; import { reaction } from "mobx"; import * as rp from 'request-promise'; import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc"; @@ -5,7 +6,7 @@ import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; import { listSpec } from "../../fields/Schema"; -import { ScriptField } from "../../fields/ScriptField"; +import { ComputedField, ScriptField } from "../../fields/ScriptField"; import { Cast, DateCast, DocCast, PromiseValue, StrCast } from "../../fields/Types"; import { nullAudio } from "../../fields/URLField"; import { SetCachedGroups, SharingPermissions } from "../../fields/util"; @@ -278,7 +279,8 @@ export class CurrentUserUtils { /// returns descriptions needed to buttons for the left sidebar to open up panes displaying different collections of documents static leftSidebarMenuBtnDescriptions(doc: Doc):{title:string, target:Doc, icon:string, scripts:{[key:string]:any}, funcs?:{[key:string]:any}}[] { - const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length.toString())"; + const badgeValue = "((len) => len && len !== '0' ? len: undefined)(docList(self.target.data).filter(doc => !docList(self.target.viewed).includes(doc)).length.toString())"; + const getActiveDashTrails = "(() => Doc.ActiveDashboard?.myTrails)"; return [ { title: "Dashboards", target: this.setupDashboards(doc, "myDashboards"), icon: "desktop", }, { title: "Search", target: this.setupSearcher(doc, "mySearcher"), icon: "search", }, @@ -287,9 +289,17 @@ export class CurrentUserUtils { { title: "Imports", target: this.setupImportSidebar(doc, "myImports"), icon: "upload", }, { title: "Recently Closed", target: this.setupRecentlyClosed(doc, "myRecentlyClosed"), icon: "archive", }, { title: "Shared Docs", target: Doc.MySharedDocs, icon: "users", funcs:{badgeValue:badgeValue}}, - { title: "Trails", target: this.setupTrails(doc, "myTrails"), icon: "pres-trail", }, + { title: "Trails", target: Doc.UserDoc(), icon: "pres-trail", funcs: {target: getActiveDashTrails}}, { title: "User Doc View", target: this.setupUserDocView(doc, "myUserDocView"), icon: "address-card",funcs: {hidden: "IsNoviceMode()"} }, ].map(tuple => ({...tuple, scripts:{onClick: 'selectMainMenu(self)'}})); + + + // Doc.UserDoc().myButtons.trailsBtn.target = Doc.ActiveDashboard.myTrails; + // const foo = new Doc(); + // foo.a = 3; + // foo.a = "bob"; + // foo.a = new List([1]); // = [] + // foo.a = ComputedField.MakeFunction("() =>'hello'"); // () => "hello"; } /// the empty panel that is filled with whichever left menu button's panel has been selected @@ -450,13 +460,19 @@ export class CurrentUserUtils { } /// initializes the left sidebar Trails pane + /// This creates button Document which has a target that is the collection of trails + /// the collection of trails is stored on the Doc.UserDoc() in the field 'myTrails" static setupTrails(doc: Doc, field:string) { + // TODO: how to change this place so that I access the trail associated with the active dashboard + + // this section iscreating the button document itself === myTrails = new Button var myTrails = DocCast(doc[field]); const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "New trail", toolTip: "Create new trail", btnType: ButtonType.ClickButton, buttonText: "New trail", icon: "plus", system: true }; const reqdBtnScript = {onClick: `createNewPresentation()`}; - const newTrailButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myTrails?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript); + const newTrailButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(doc[field]), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript); + // this section creates the collection of trails which will be storedon the user Doc and as the target of the button === Doc.UserDoc().myTrails = button const reqdOpts:DocumentOptions = { title: "My Trails", _showTitle: "title", _height: 100, treeViewHideTitle: true, _fitWidth: true, _gridGap: 5, _forceActive: true, childDropAction: "alias", @@ -466,7 +482,12 @@ export class CurrentUserUtils { _lockedPosition: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", system: true, explainer: "All of the trails that you have created will appear here." }; + // instead of assigninbg Doc.UserDoc().myrails we want to assign Doc.AxtiveDashboard.myTrails + // but we don't wanbt to create the list of trails here-- but rathger in createDashbarod + // myTrail myTrails = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts); + + const contextMenuScripts = [reqdBtnScript.onClick]; if (Cast(myTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { myTrails.contextMenuScripts = new List(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); diff --git a/src/client/util/SharingManager.tsx b/src/client/util/SharingManager.tsx index 793027ea1..f3ff842fb 100644 --- a/src/client/util/SharingManager.tsx +++ b/src/client/util/SharingManager.tsx @@ -373,7 +373,7 @@ export class SharingManager extends React.Component<{}> { if (!uniform) dropdownValues.unshift('-multiple-'); if (override) dropdownValues.unshift('None'); return dropdownValues - .filter(permission => !Doc.noviceMode || ![SharingPermissions.View, SharingPermissions.SelfEdit].includes(permission as any)) + .filter(permission => !Doc.noviceMode || ![SharingPermissions.SelfEdit].includes(permission as any)) .map(permission => (
{this.switchMenuView} {this.inkMenu} - +
-- cgit v1.2.3-70-g09d2 From 743f4ab3a65babedb30b8ae9575e9b3583e52b3d Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 13 Sep 2022 12:51:37 -0400 Subject: fixed activePresentation to work properly with multiple dashboards. fixed undoing PinDoc. --- src/client/util/CurrentUserUtils.ts | 8 -- src/client/views/DashboardView.tsx | 24 +++--- src/client/views/MainView.tsx | 9 +-- src/client/views/collections/TabDocView.tsx | 114 ++++++++++++++-------------- src/client/views/nodes/trails/PresBox.tsx | 14 +--- src/fields/Doc.ts | 6 +- 6 files changed, 78 insertions(+), 97 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c2cf5dae0..7419750b1 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -336,14 +336,6 @@ export class CurrentUserUtils { { title: "Trails", target: Doc.UserDoc(), icon: "pres-trail", funcs: {target: getActiveDashTrails}}, { title: "User Doc View", target: this.setupUserDocView(doc, "myUserDocView"), icon: "address-card",funcs: {hidden: "IsNoviceMode()"} }, ].map(tuple => ({...tuple, scripts:{onClick: 'selectMainMenu(self)'}})); - - - // Doc.UserDoc().myButtons.trailsBtn.target = Doc.ActiveDashboard.myTrails; - // const foo = new Doc(); - // foo.a = 3; - // foo.a = "bob"; - // foo.a = new List([1]); // = [] - // foo.a = ComputedField.MakeFunction("() =>'hello'"); // () => "hello"; } /// the empty panel that is filled with whichever left menu button's panel has been selected diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 478234eb0..d1926951d 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -257,7 +257,6 @@ export class DashboardView extends React.Component { }; public static createNewDashboard = (id?: string, name?: string) => { - // const presentation = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true); const dashboards = Doc.MyDashboards; const dashboardCount = DocListCast(dashboards.data).length + 1; const freeformOptions: DocumentOptions = { @@ -269,7 +268,7 @@ export class DashboardView extends React.Component { _backgroundGridShow: true, title: `Untitled Tab 1`, }; - const title = name ? name : `Dashboard ${dashboardCount}`; + const title = name ?? `Dashboard ${dashboardCount}`; const freeformDoc = Doc.GuestTarget || Docs.Create.FreeformDocument([], freeformOptions); const dashboardDoc = Docs.Create.StandardCollectionDockingDocument([{ doc: freeformDoc, initialWidth: 600 }], { title: title }, id, 'row'); freeformDoc.context = dashboardDoc; @@ -280,9 +279,6 @@ export class DashboardView extends React.Component { dashboardDoc['pane-count'] = 1; Doc.AddDocToList(dashboards, 'data', dashboardDoc); - // open this new dashboard - Doc.ActiveDashboard = dashboardDoc; - Doc.ActivePage = 'dashboard'; // this section is creating the button document itself === myTrails = new Button const reqdBtnOpts: DocumentOptions = { @@ -301,10 +297,9 @@ export class DashboardView extends React.Component { const reqdBtnScript = { onClick: `createNewPresentation()` }; const myTrailsBtn = DocUtils.AssignScripts(Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript); - // createa a list of presentations (as a tree view collection) and store i on the new dashboard - // instead of assigninbg Doc.UserDoc().myrails we want to assign Doc.AxtiveDashboard.myTrails - // but we don't wanbt to create the list of trails here-- but rather in createDashbarod - // myTrail + // createa a list of presentations (as a tree view collection) and store it on the new dashboard + // instead of assigning Doc.UserDoc().myrails we want to assign Doc.AxtiveDashboard.myTrails + // but we don't want to create the list of trails here-- but rather in createDashboard const reqdOpts: DocumentOptions = { title: 'My Trails', _showTitle: 'title', @@ -327,12 +322,15 @@ export class DashboardView extends React.Component { system: true, explainer: 'All of the trails that you have created will appear here.', }; - const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeViewChildDoubleClick: 'openPresentation(documentView.rootDoc)' }); + dashboardDoc.myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeViewChildDoubleClick: 'openPresentation(documentView.rootDoc)' }); - dashboardDoc.myTrails = myTrails; + // open this new dashboard + Doc.ActiveDashboard = dashboardDoc; + Doc.ActivePage = 'dashboard'; + Doc.ActivePresentation = undefined; const contextMenuScripts = [reqdBtnScript.onClick]; - if (Cast(myTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { - myTrails.contextMenuScripts = new List(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); + if (Cast(Doc.MyTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { + Doc.MyTrails.contextMenuScripts = new List(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); } }; } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 79f83b386..09ab49d1c 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -524,17 +524,14 @@ export class MainView extends React.Component { createNewPresentation = () => { const pres = Docs.Create.PresDocument({ title: 'Untitled Trail', _viewType: CollectionViewType.Stacking, _fitWidth: true, _width: 400, _height: 500, targetDropAction: 'alias', _chromeHidden: true, boxShadow: '0 0' }); CollectionDockingView.AddSplit(pres, 'left'); - - const myTrails = Doc.ActiveDashboard!.myTrails as Doc - console.log(Doc.ActiveDashboard!.myTrails) - Doc.AddDocToList(myTrails, "trails", pres) - Doc.ActivePresentation = pres + Doc.MyTrails && Doc.AddDocToList(Doc.MyTrails, 'data', pres); // Doc.MyTrails should be created in createDashboard + Doc.ActivePresentation = pres; }; @action openPresentation = (pres: Doc) => { CollectionDockingView.AddSplit(pres, 'left'); - Doc.ActivePresentation = pres; + Doc.MyTrails && (Doc.ActivePresentation = pres); Doc.AddDocToList(Doc.MyTrails, 'data', pres); this.closeFlyout(); }; diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 7522affa7..042d39285 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -213,75 +213,75 @@ export class TabDocView extends React.Component { public static PinDoc(docs: Doc | Doc[], pinProps?: PinProps) { const docList = docs instanceof Doc ? [docs] : docs; - let curPres = Doc.ActivePresentation; - console.log(curPres); - if (!curPres) { - curPres = Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true); + const batch = UndoManager.StartBatch('pinning doc'); + const curPres = Doc.ActivePresentation ?? Doc.MakeCopy(Doc.UserDoc().emptyPresentation as Doc, true); + + if (!Doc.ActivePresentation) { + Doc.AddDocToList(Doc.MyTrails, 'data', curPres); Doc.ActivePresentation = curPres; } - 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(); // 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); + 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(); // 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); - PresBox.pinDocView(pinDoc, pinProps, doc); - 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?.activeFrame !== undefined) { - pinDoc.presActiveFrame = pinProps?.activeFrame; - pinDoc.title = doc.title + ' (move)'; - pinDoc.presMovement = PresMovement.Pan; - } - if (pinDoc.isInkMask) { - pinDoc.presHideAfter = true; - pinDoc.presHideBefore = true; - pinDoc.presMovement = PresMovement.None; - } - if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; - PresBox.Instance?.clearSelectedArray(); - pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); //Update selected array - }); + PresBox.pinDocView(pinDoc, pinProps, doc); + 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?.activeFrame !== undefined) { + pinDoc.presActiveFrame = pinProps?.activeFrame; + pinDoc.title = doc.title + ' (move)'; + pinDoc.presMovement = PresMovement.Pan; + } + if (pinDoc.isInkMask) { + pinDoc.presHideAfter = true; + pinDoc.presHideBefore = true; + pinDoc.presMovement = PresMovement.None; + } + if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; + PresBox.Instance?.clearSelectedArray(); + pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); //Update selected array + }); if ( - CollectionDockingView.Instance && - !Array.from(CollectionDockingView.Instance.tabMap) + !Array.from(CollectionDockingView.Instance?.tabMap ?? []) .map(d => d.DashDoc) .includes(curPres) ) { const docs = Cast(Doc.MyOverlayDocs.data, listSpec(Doc), []); if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1); - CollectionDockingView.AddSplit(curPres, 'right'); + CollectionDockingView.AddSplit(curPres, 'left'); 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() { diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 82b8a8f90..1325a9d67 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -88,11 +88,6 @@ export class PresBox extends ViewBoxBaseComponent() { private _disposers: { [name: string]: IReactionDisposer } = {}; public selectedArray = new ObservableSet(); - constructor(props: any) { - super(props); - if ((Doc.ActivePresentation = this.rootDoc)) runInAction(() => (PresBox.Instance = this)); - } - @observable public static Instance: PresBox; @observable static startMarquee: boolean = false; // onclick "+ new slide" in presentation mode, set as true, then when marquee selection finish, onPointerUp automatically triggers PinWithView @@ -199,9 +194,6 @@ export class PresBox extends ViewBoxBaseComponent() { this.layoutDoc._gridGap = 0; this.layoutDoc._yMargin = 0; this.turnOffEdit(true); - if (Doc.MyTrails) { - DocListCastAsync(Doc.MyTrails.data).then(pres => !pres?.includes(this.rootDoc) && Doc.AddDocToList(Doc.MyTrails, 'data', this.rootDoc)); - } this._disposers.selection = reaction( () => SelectionManager.Views(), views => views.some(view => view.props.Document === this.rootDoc) && this.updateCurrentPresentation() @@ -2322,7 +2314,7 @@ export class PresBox extends ViewBoxBaseComponent() { const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; return ( -
+
{isMini ? null : ( this.setNewDashboardName((e.target as any).value)} /> - - +
+
Create New Dashboard
+
+ Title + this.setNewDashboardName((e.target as any).value)} /> +
+
+ Background + { + this.newDashboardColor = color; + }} /> +
+
+
); } @@ -142,11 +164,19 @@ export class DashboardView extends React.Component {
+
this.selectDashboardGroup(DashboardGroup.MyDashboards)}> My Dashboards @@ -180,14 +210,26 @@ export class DashboardView extends React.Component { this._downY = e.clientY; }} onClick={e => { + e.preventDefault(); + e.stopPropagation(); this.onContextMenu(dashboard, e); }}> - + } + />
); })} +
{ + this.setNewDashboardName(''); + }}> + + +
); @@ -257,7 +299,7 @@ export class DashboardView extends React.Component { } }; - public static createNewDashboard = (id?: string, name?: string) => { + public static createNewDashboard = (id?: string, name?: string, background?: string) => { const dashboards = Doc.MyDashboards; const dashboardCount = DocListCast(dashboards.data).length + 1; const freeformOptions: DocumentOptions = { @@ -267,6 +309,7 @@ export class DashboardView extends React.Component { _height: 1000, _fitWidth: true, _backgroundGridShow: true, + backgroundColor: background, title: `Untitled Tab 1`, }; const title = name ? name : `Dashboard ${dashboardCount}`; diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 26718d695..d07e255e2 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -65,6 +65,7 @@ import { PreviewCursor } from './PreviewCursor'; import { PropertiesView } from './PropertiesView'; import { DashboardStyleProvider, DefaultStyleProvider } from './StyleProvider'; import { TopBar } from './topbar/TopBar'; +import 'browndash-components/dist/styles/global.min.css'; const _global = (window /* browser */ || global) /* node */ as any; @observer diff --git a/src/client/views/global/globalCssVariables.scss b/src/client/views/global/globalCssVariables.scss index c5968d9a7..8a75aeef8 100644 --- a/src/client/views/global/globalCssVariables.scss +++ b/src/client/views/global/globalCssVariables.scss @@ -39,7 +39,7 @@ $small-text: 9px; // misc values $border-radius: 0.3em; $search-thumnail-size: 130; -$topbar-height: 32px; +$topbar-height: 37px; $antimodemenu-height: 36px; // dragged items diff --git a/src/client/views/topbar/TopBar.scss b/src/client/views/topbar/TopBar.scss index d415e9367..a1131b92e 100644 --- a/src/client/views/topbar/TopBar.scss +++ b/src/client/views/topbar/TopBar.scss @@ -13,7 +13,12 @@ align-items: center; height: $topbar-height; background-color: $dark-gray; + border-bottom: $standard-border; + padding: 0px 10px; cursor: default; + display: flex; + justify-content: center; + width: 100%; .topbar-inner-container { display: flex; @@ -21,6 +26,7 @@ position: relative; display: grid; grid-auto-columns: 33.3% 33.3% 33.3%; + width: 100%; align-items: center; // &:first-child { @@ -70,10 +76,8 @@ align-items: center; gap: 5px; - .topbar-dashboards { - display: flex; - flex-direction: row; - gap: 5px; + .topbar-dashboard-header { + font-weight: 600; } } @@ -96,6 +100,27 @@ width: fit-content; gap: 5px; + .logo-container { + font-size: 15; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -o-user-select: none; + user-select: none; + + .logo { + background-color: transparent; + width: 25px; + height: 25px; + margin-right: 5px; + + } + } + .topBar-icon:hover { background-color: $close-red; } diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 096b2561f..bbdb4621e 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -1,15 +1,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { Tooltip } from '@material-ui/core'; -import { MdBugReport } from 'react-icons/md'; -import { action } from 'mobx'; +import { Button, FontSize, IconButton, Size } from 'browndash-components'; +import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { AclAdmin, Doc } from '../../../fields/Doc'; +import { FaBug, FaCamera } from 'react-icons/fa'; +import { AclAdmin, Doc, DocListCast } from '../../../fields/Doc'; import { StrCast } from '../../../fields/Types'; import { GetEffectiveAcl } from '../../../fields/util'; -import { Utils } from '../../../Utils'; import { DocumentManager } from '../../util/DocumentManager'; -import { SelectionManager } from '../../util/SelectionManager'; +import { ReportManager } from '../../util/ReportManager'; import { SettingsManager } from '../../util/SettingsManager'; import { SharingManager } from '../../util/SharingManager'; import { UndoManager } from '../../util/UndoManager'; @@ -19,7 +18,7 @@ import { DashboardView } from '../DashboardView'; import { Borders, Colors } from '../global/globalEnums'; import { MainView } from '../MainView'; import './TopBar.scss'; -import { ReportManager } from '../../util/ReportManager'; + /** * ABOUT: This is the topbar in Dash, which included the current Dashboard as well as access to information on the user @@ -34,36 +33,73 @@ export class TopBar extends React.Component { }); }; - render() { - const activeDashboard = Doc.ActiveDashboard; - return ( - //TODO:glr Add support for light / dark mode -
-
-
- {activeDashboard ? ( - <> -
{ - ContextMenu.Instance.addItem({ description: 'Logout', event: () => window.location.assign(Utils.prepend('/logout')), icon: 'edit' }); - ContextMenu.Instance.displayMenu(e.clientX + 5, e.clientY + 10); - }}> - {Doc.CurrentUserEmail} -
-
- -
- - ) : null} -
-
-
activeDashboard && SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}> - {activeDashboard ? StrCast(activeDashboard.title) : 'Dash'} -
-
{ + @observable textColor: string = Colors.LIGHT_GRAY; + @observable backgroundColor: string = Colors.DARK_GRAY; + + /** + * Returns the left hand side of the topbar. + * This side of the topbar contains the different modes. + * The modes include: + * - Explore mode + * - Tracking mode + */ + @computed get topbarLeft() { + return ( +
+ {Doc.ActiveDashboard ? } isCircle={true} hoverStyle="gray" color={this.textColor} /> : +
+ dash logo + browndash +
+ } + {Doc.ActiveDashboard &&
+ ); + } + + /** + * Returns the center of the topbar + * This part of the topbar contains everything related to the current dashboard including: + * - Selection of dashboards + * - Creating a new dashboard + * - Taking a snapshot of a dashboard + */ + @computed get topbarCenter() { + const myDashboards = DocListCast(Doc.MyDashboards.data); + const activeDashboard = Doc.ActiveDashboard; + // const dashboardItems = myDashboards.map(board => { + // const boardTitle = StrCast(board.title); + // console.log(boardTitle); + // return { + // text: boardTitle, + // onClick: () => DashboardView.openDashboard(board), + // val: board, + // }; + // }); + return activeDashboard ? ( +
+
- Browsing mode for directly navigating to documents
} placement="bottom"> -
(MainView.Instance._exploreMode = !MainView.Instance._exploreMode))}> - -
- -
-
- {Doc.ActiveDashboard ? ( -
{ - SharingManager.Instance.open(undefined, activeDashboard); - }}> - {GetEffectiveAcl(Doc.GetProto(Doc.ActiveDashboard)) === AclAdmin ? 'Share' : 'view original'} -
- ) : null} -
ReportManager.Instance.open()}> - -
-
window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')}> - -
-
SettingsManager.Instance.open()}> - -
-
+ }} + /> +
+ ) : null; + } + + /** + * Returns the right hand side of the topbar. + * This part of the topbar includes information about the current user, + * and allows the user to access their account settings etc. + */ + @computed get topbarRight() { + + return ( +
+ ReportManager.Instance.open()} + icon={} + /> + window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')} + icon={} + /> + SettingsManager.Instance.open()} + icon={} + /> + {/*
+ ); + } + + // render() { + // const activeDashboard = Doc.ActiveDashboard; + // return ( + // //TODO:glr Add support for light / dark mode + //
+ //
+ //
+ // {activeDashboard ? ( + // <> + //
{ + // ContextMenu.Instance.addItem({ description: 'Logout', event: () => window.location.assign(Utils.prepend('/logout')), icon: 'edit' }); + // ContextMenu.Instance.displayMenu(e.clientX + 5, e.clientY + 10); + // }}> + // {Doc.CurrentUserEmail} + //
+ //
+ // + //
+ // + // ) : null} + //
+ //
+ //
activeDashboard && SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(activeDashboard)!, false)}> + // {activeDashboard ? StrCast(activeDashboard.title) : 'Dash'} + //
+ //
{ + // const dashView = activeDashboard && DocumentManager.Instance.getDocumentView(activeDashboard); + // ContextMenu.Instance.addItem({ description: 'Open Dashboard View', event: this.navigateToHome, icon: 'edit' }); + // ContextMenu.Instance.addItem({ + // description: 'Snapshot Dashboard', + // event: async () => { + // const batch = UndoManager.StartBatch('snapshot'); + // await DashboardView.snapshotDashboard(); + // batch.end(); + // }, + // icon: 'edit', + // }); + // dashView?.showContextMenu(e.clientX + 20, e.clientY + 30); + // }}> + // + //
+ // Browsing mode for directly navigating to documents
} placement="bottom"> + //
(MainView.Instance._exploreMode = !MainView.Instance._exploreMode))}> + // + //
+ // + //
+ //
+ // {Doc.ActiveDashboard ? ( + //
{ + // SharingManager.Instance.open(undefined, activeDashboard); + // }}> + // {GetEffectiveAcl(Doc.GetProto(Doc.ActiveDashboard)) === AclAdmin ? 'Share' : 'view original'} + //
+ // ) : null} + //
ReportManager.Instance.open()}> + // + //
+ //
window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')}> + // + //
+ //
SettingsManager.Instance.open()}> + // + //
+ //
+ //
+ //
+ // ); + // } + render() { + return ( + //TODO:glr Add support for light / dark mode +
+
+ {this.topbarLeft} + {this.topbarCenter} + {this.topbarRight}
); -- cgit v1.2.3-70-g09d2 From 37be2477782b0b372c16d46df3f9634ebf3677fe Mon Sep 17 00:00:00 2001 From: Geireann Lindfield Roberts <60007097+geireann@users.noreply.github.com> Date: Wed, 21 Sep 2022 01:43:03 -0400 Subject: restyled document decorations and changed novice mode to exclude `data` in treeView --- src/client/views/DocumentButtonBar.tsx | 2 +- src/client/views/DocumentDecorations.scss | 189 ++++++++++++++------- src/client/views/DocumentDecorations.tsx | 13 +- src/client/views/_nodeModuleOverrides.scss | 4 +- .../views/collections/CollectionDockingView.scss | 8 + .../views/collections/CollectionTreeView.scss | 1 + src/client/views/collections/TabDocView.tsx | 4 +- src/client/views/collections/TreeView.scss | 20 ++- src/client/views/collections/TreeView.tsx | 4 +- src/client/views/global/globalCssVariables.scss | 4 + src/client/views/nodes/button/FontIconBox.tsx | 2 +- src/client/views/nodes/trails/PresBox.tsx | 16 +- src/client/views/topbar/TopBar.tsx | 2 +- 13 files changed, 178 insertions(+), 91 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 35c0b9a7d..0bfe6e87a 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -434,7 +434,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
) : null} -
{this.recordButton}
+ {Doc.noviceMode ? null :
{this.recordButton}
} { Doc.noviceMode ? null :
{this.templateButton}
/*
diff --git a/src/client/views/DocumentDecorations.scss b/src/client/views/DocumentDecorations.scss index b490278c3..2e8d31478 100644 --- a/src/client/views/DocumentDecorations.scss +++ b/src/client/views/DocumentDecorations.scss @@ -22,6 +22,129 @@ $resizeHandler: 8px; grid-template-columns: $resizeHandler 1fr $resizeHandler; pointer-events: none; + .documentDecorations-topbar { + display: flex; + grid-column-start: 1; + grid-column-end: 4; + flex-direction: row; + gap: 2px; + + + .documentDecorations-openButton { + display: flex; + align-items: center; + justify-content: center; + background: #3ce312; + border: solid 1.5px #0a620a; + color: #3ce312; + transition: 0.1s ease; + font-size: 11px; + opacity: 1; + pointer-events: all; + width: 20px; + height: 20px; + min-width: 20px; + border-radius: 100%; + cursor: pointer; + + &:hover { + color: #02600d; + } + } + + .documentDecorations-closeButton { + display: flex; + align-items: center; + justify-content: center; + background: #fb9d75; + border: solid 1.5px #a94442; + color: #fb9d75; + transition: 0.1s ease; + opacity: 1; + pointer-events: all; + width: 20px; + height: 20px; + min-width: 20px; + border-radius: 100%; + cursor: pointer; + + &:hover { + color: #a94442; + } + + > svg { + margin: 0; + } + } + + .documentDecorations-minimizeButton { + display: flex; + align-items: center; + justify-content: center; + background: #ffdd00; + border: solid 1.5px #a94442; + color: #ffdd00; + transition: 0.1s ease; + font-size: 11px; + opacity: 1; + grid-column: 2; + pointer-events: all; + width: 20px; + height: 20px; + min-width: 20px; + border-radius: 100%; + cursor: pointer; + + &:hover { + color: #a94442; + } + + > svg { + margin: 0; + } + } + + .documentDecorations-title-Dark, + .documentDecorations-title { + opacity: 1; + width: calc(100% - 60px); // = margin-left + margin-right + grid-column: 3; + pointer-events: auto; + overflow: hidden; + text-align: center; + display: flex; + height: 20px; + border-radius: 8px; + outline: none; + border: none; + + .documentDecorations-titleSpan, + .documentDecorations-titleSpan-Dark { + width: 100%; + border-radius: 8px; + background: $light-gray; + display: inline-block; + cursor: move; + } + .documentDecorations-titleSpan-Dark { + background: hsla(0, 0%, 0%, 0.412); + } + } + + .documentDecorations-title-Dark { + color: white; + background: black; + } + + .documentDecorations-titleBackground { + background: $light-gray; + border-radius: 8px; + width: 100%; + height: 100%; + position: absolute; + } + } + .documentDecorations-centerCont { grid-column: 2; background: none; @@ -225,76 +348,12 @@ $resizeHandler: 8px; cursor: ew-resize; } - .documentDecorations-title-Dark, - .documentDecorations-title { - opacity: 1; - width: calc(100% - 8px); // = margin-left + margin-right - grid-column: 2; - grid-column-end: 2; - pointer-events: auto; - overflow: hidden; - text-align: center; - display: flex; - margin-left: 6px; // closeButton width (14) - leftColumn width (8) - margin-right: 2px; - height: 20px; - position: absolute; - border-radius: 8px; - background: rgba(159, 159, 159, 0.1); - - .documentDecorations-titleSpan, - .documentDecorations-titleSpan-Dark { - width: 100%; - border-radius: 8px; - background: #ffffffa0; - position: absolute; - display: inline-block; - cursor: move; - } - .documentDecorations-titleSpan-Dark { - background: hsla(0, 0%, 0%, 0.412); - } - } - .documentDecorations-title-Dark { - color: white; - background: black; - } - - .documentDecorations-titleBackground { - background: #ffffffcf; - border-radius: 8px; - width: 100%; - height: 100%; - position: absolute; - } - .focus-visible { margin-left: 0px; } } -.documentDecorations-openButton { - display: flex; - align-items: center; - opacity: 1; - grid-column-start: 3; - pointer-events: all; - cursor: pointer; -} -.documentDecorations-closeButton { - display: flex; - align-items: center; - opacity: 1; - grid-column: 1; - pointer-events: all; - width: 14px; - cursor: pointer; - - > svg { - margin: 0; - } -} .documentDecorations-background { background: lightblue; @@ -329,7 +388,7 @@ $resizeHandler: 8px; justify-content: center; align-items: center; gap: 5px; - background: $medium-gray; + background: $light-gray; } .linkButtonWrapper { diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 0fc33bdea..95025bf70 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -719,9 +719,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P width: bounds.r - bounds.x + this._resizeBorderWidth + 'px', height: bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight + 'px', }}> - {hideDeleteButton ?
: topBtn('close', this.hasIcons ? 'times' : 'window-maximize', undefined, e => this.onCloseClick(this.hasIcons ? true : undefined), 'Close')} - {titleArea} - {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Tab (ctrl: as alias, shift: in new collection)')} +
+ {hideDeleteButton ?
: topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')} + {hideDeleteButton ?
: topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')} + {titleArea} + {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Tab (ctrl: as alias, shift: in new collection)')} +
{hideResizers ? null : ( <>
e.preventDefault()} /> @@ -738,9 +741,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P )} -
e.preventDefault()}> + {!Doc.noviceMode &&
e.preventDefault()}> {'⟲'} -
+
} {useRounding && (
li { + height: 27px !important; opacity: 1; transform: scale(1); } diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index c0561e42c..a182a72c5 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -104,6 +104,7 @@ text-overflow: ellipsis; white-space: pre-wrap; min-width: 10px; + grid-column: 2; } .docContainer-system { font-variant: all-small-caps; diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index a82bd2dc8..98121f423 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -56,7 +56,9 @@ export class TabDocView extends React.Component { 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))); + let tabColor = StrCast(this._document?._backgroundColor, StrCast(this._document?.backgroundColor, DefaultStyleProvider(this._document, undefined, StyleProp.BackgroundColor))); + if (tabColor === 'transparent') return 'black'; + return tabColor; } @computed get tabTextColor() { return this._document?.type === DocumentType.PRES ? 'black' : StrCast(this._document?._color, StrCast(this._document?.color, DefaultStyleProvider(this._document, undefined, StyleProp.Color))); diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss index ce87e6f89..cfb97709b 100644 --- a/src/client/views/collections/TreeView.scss +++ b/src/client/views/collections/TreeView.scss @@ -53,13 +53,16 @@ } .bullet { + grid-column: 1; + display: flex; + justify-content: center; + align-items: center; position: relative; width: $TREE_BULLET_WIDTH; + min-height: 20px; color: $medium-gray; - margin-top: 3px; - // transform: scale(1.3, 1.3); // bcz: why was this here? It makes displaying images in the treeView for documents that have icons harder. border: #80808030 1px solid; - border-radius: 4px; + border-radius: 5px; } } @@ -113,7 +116,15 @@ .treeView-header-editing, .treeView-header { border: transparent 1px solid; - display: flex; + display: grid; + align-items: center; + grid-auto-columns: 22px auto 22px; + width: 100%; + border-radius: 5px; + + &:hover { + background-color: #bdddf5; + } //align-items: center; ::-webkit-scrollbar { @@ -140,6 +151,7 @@ .treeView-rightButtons { display: flex; + grid-column: 3; align-items: center; margin-left: 0.25rem; opacity: 0.75; diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 833e364b3..b489b5214 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -736,7 +736,7 @@ export class TreeView extends React.Component { }} /> )} - {this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? null : ( + {Doc.noviceMode ? null : this.doc.treeViewExpandedViewLock || Doc.IsSystem(this.doc) ? null : ( {this.treeViewExpandedView} @@ -953,7 +953,7 @@ export class TreeView extends React.Component {
() { icon = 'globe-asia'; text = 'User Default'; } - noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking]; + noviceList = [CollectionViewType.Freeform, CollectionViewType.Schema, CollectionViewType.Stacking, CollectionViewType.NoteTaking]; } else if (script?.script.originalScript.startsWith('setFont')) { const editorView = RichTextMenu.Instance?.TextView?.EditorView; text = StrCast((editorView ? RichTextMenu.Instance : Doc.UserDoc()).fontFamily); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index ab59e6112..7f0f13437 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -2321,8 +2321,8 @@ export class PresBox extends ViewBoxBaseComponent() { const mode = StrCast(this.rootDoc._viewType) as CollectionViewType; const isMini: boolean = this.toolbarWidth <= 100; return ( -
- {isMini ? null : ( +
+ {isMini || Doc.noviceMode ? null : ( )}
@@ -2653,14 +2651,14 @@ export class PresBox extends ViewBoxBaseComponent() { ) : null}
- { + {/* { // if the document type is a presentation, then the collection stacking view has a "+ new slide" button at the bottom of the stack {'Click on document to pin to presentaiton or make a marquee selection to pin your desired view'}
}> - } + } */}
); diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index bbdb4621e..2fe0c6e79 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -52,7 +52,7 @@ export class TopBar extends React.Component { browndash
} - {Doc.ActiveDashboard &&
); @@ -163,20 +160,19 @@ export class DashboardView extends React.Component { <>
-
+
this.selectDashboardGroup(DashboardGroup.MyDashboards)}> My Dashboards @@ -214,18 +210,15 @@ export class DashboardView extends React.Component { e.stopPropagation(); this.onContextMenu(dashboard, e); }}> - } - /> + } />
); })} -
{ +
{ this.setNewDashboardName(''); }}> + @@ -324,6 +317,15 @@ export class DashboardView extends React.Component { Doc.AddDocToList(dashboards, 'data', dashboardDoc); + DashboardView.SetupDashboardTrails(dashboardDoc); + + // open this new dashboard + Doc.ActiveDashboard = dashboardDoc; + Doc.ActivePage = 'dashboard'; + Doc.ActivePresentation = undefined; + }; + + public static SetupDashboardTrails(dashboardDoc: Doc) { // this section is creating the button document itself === myTrails = new Button const reqdBtnOpts: DocumentOptions = { _forceActive: true, @@ -368,15 +370,11 @@ export class DashboardView extends React.Component { }; dashboardDoc.myTrails = new PrefetchProxy(DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeViewChildDoubleClick: 'openPresentation(documentView.rootDoc)' })); - // open this new dashboard - Doc.ActiveDashboard = dashboardDoc; - Doc.ActivePage = 'dashboard'; - Doc.ActivePresentation = undefined; const contextMenuScripts = [reqdBtnScript.onClick]; if (Cast(Doc.MyTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { Doc.MyTrails.contextMenuScripts = new List(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); } - }; + } } export function AddToList(MySharedDocs: Doc, arg1: string, dash: any) { diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 6435fdd0f..25fe5fe43 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -430,7 +430,8 @@ export class CollectionDockingView extends CollectionSubView() { return newtab; }); const copy = Docs.Create.DockDocument(newtabs, json, { title: incrementTitleCopy(StrCast(doc.title)) }); - return DashboardView.openDashboard(await copy); + DashboardView.SetupDashboardTrails(copy); + return DashboardView.openDashboard(copy); } @action diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 2fe0c6e79..7bc7ba3ed 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -19,7 +19,6 @@ import { Borders, Colors } from '../global/globalEnums'; import { MainView } from '../MainView'; import './TopBar.scss'; - /** * ABOUT: This is the topbar in Dash, which included the current Dashboard as well as access to information on the user * and settings and help buttons. Future scope for this bar is to include the collaborators that are on the same Dashboard. @@ -44,88 +43,94 @@ export class TopBar extends React.Component { * - Tracking mode */ @computed get topbarLeft() { - return ( + return (
- {Doc.ActiveDashboard ? } isCircle={true} hoverStyle="gray" color={this.textColor} /> : -
- dash logo - browndash -
- } - {Doc.ActiveDashboard && !Doc.noviceMode &&
); } - /** + /** * Returns the center of the topbar * This part of the topbar contains everything related to the current dashboard including: * - Selection of dashboards * - Creating a new dashboard * - Taking a snapshot of a dashboard */ - @computed get topbarCenter() { - const myDashboards = DocListCast(Doc.MyDashboards.data); - const activeDashboard = Doc.ActiveDashboard; - // const dashboardItems = myDashboards.map(board => { - // const boardTitle = StrCast(board.title); - // console.log(boardTitle); - // return { - // text: boardTitle, - // onClick: () => DashboardView.openDashboard(board), - // val: board, - // }; - // }); - return activeDashboard ? ( -
-
- ) : null; - } - - /** - * Returns the right hand side of the topbar. - * This part of the topbar includes information about the current user, - * and allows the user to access their account settings etc. - */ - @computed get topbarRight() { - - return ( -
- ReportManager.Instance.open()} - icon={} - /> + /> + )} +
+ ) : null; + } + + /** + * Returns the right hand side of the topbar. + * This part of the topbar includes information about the current user, + * and allows the user to access their account settings etc. + */ + @computed get topbarRight() { + return ( +
+ ReportManager.Instance.open()} icon={} /> } /> {/*
- ); - } +
+ ); + } // render() { // const activeDashboard = Doc.ActiveDashboard; -- cgit v1.2.3-70-g09d2 From 510f9145fa1bffd72aaee1c861e2013a0cad42ca Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 21 Sep 2022 12:06:07 -0400 Subject: fixed following links to toggle alias if it's displayed when actual target isn't. changed cropping images to leave behind an outline not, a solid rectangle. double clicking on link buttons no longer opens them in lightbox view. --- src/client/util/DocumentManager.ts | 2 +- src/client/views/nodes/DocumentView.tsx | 2 +- src/client/views/nodes/ImageBox.tsx | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 2ca5d1095..3a6c43773 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -171,7 +171,7 @@ export class DocumentManager { const getFirstDocView = LightboxView.LightboxDoc ? DocumentManager.Instance.getLightboxDocumentView : DocumentManager.Instance.getFirstDocumentView; const docView = getFirstDocView(targetDoc, originatingDoc); const annotatedDoc = Cast(targetDoc.annotationOn, Doc, null); - const resolvedTarget = targetDoc.type === DocumentType.MARKER ? annotatedDoc ?? targetDoc : targetDoc; // if target is a marker, then focus toggling should apply to the document it's on since the marker itself doesn't have a hidden field + const resolvedTarget = targetDoc.type === DocumentType.MARKER ? annotatedDoc ?? docView?.rootDoc ?? targetDoc : docView?.rootDoc ?? targetDoc; // if target is a marker, then focus toggling should apply to the document it's on since the marker itself doesn't have a hidden field var wasHidden = resolvedTarget.hidden; if (wasHidden) { runInAction(() => { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f24ceb5ae..0e485e2f9 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -582,7 +582,7 @@ export class DocumentViewInternal extends DocComponent (func().result?.select === true ? this.props.select(false) : ''), 'on double click'); - } else if (!Doc.IsSystem(this.rootDoc)) { + } else if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isLinkButton) { UndoManager.RunInBatch(() => LightboxView.AddDocTab(this.rootDoc, 'lightbox', this.props.LayoutTemplate?.(), this.props.addDocTab), 'double tap'); SelectionManager.DeselectAll(); Doc.UnBrushDoc(this.props.Document); diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 9590bcb15..11f221448 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -136,6 +136,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent { if (!region) return; const cropping = Doc.MakeCopy(region, true); + Doc.GetProto(region).backgroundColor = 'transparent'; Doc.GetProto(region).lockedPosition = true; Doc.GetProto(region).title = 'region:' + this.rootDoc.title; Doc.GetProto(region).isPushpin = true; -- cgit v1.2.3-70-g09d2 From 0046f6eab6ec28b509398b5184922296014a66d0 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 21 Sep 2022 13:24:02 -0400 Subject: added cropping for videos. made images render at full res when selected. --- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/VideoBox.tsx | 53 +++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 11f221448..d3d68d835 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -74,7 +74,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent (this._curSuffix = this.fieldKey === 'icon' ? '_m' : forceFull ? '_o' : scrSize < 0.25 ? '_s' : scrSize < 0.5 ? '_m' : scrSize < 0.8 || !selected ? '_l' : '_o'), + ({ forceFull, scrSize, selected }) => (this._curSuffix = selected ? '_o' : this.fieldKey === 'icon' ? '_m' : forceFull ? '_o' : scrSize < 0.25 ? '_s' : scrSize < 0.5 ? '_m' : scrSize < 0.8 ? '_l' : '_o'), { fireImmediately: true, delay: 1000 } ); this._disposers.path = reaction( diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index b45ee7f6e..ac472aa89 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -30,6 +30,7 @@ import { StyleProp } from '../StyleProvider'; import { FieldView, FieldViewProps } from './FieldView'; import { RecordingBox } from './RecordingBox'; import './VideoBox.scss'; +import { ObjectField } from '../../../fields/ObjectField'; const path = require('path'); /** @@ -397,7 +398,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { const aspect = this.player!.videoWidth / (this.player!.videoHeight || 1); - if (aspect) { + if (aspect && !this.dataDoc.viewScaleMin) { Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth); Doc.SetNativeHeight(this.dataDoc, this.player!.videoHeight); this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect; @@ -570,7 +571,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent this.Play()} @@ -986,6 +987,53 @@ export class VideoBox extends ViewBoxAnnotatableComponent; } + crop = (region: Doc | undefined, addCrop?: boolean) => { + if (!region) return; + const cropping = Doc.MakeCopy(region, true); + Doc.GetProto(region).backgroundColor = 'transparent'; + Doc.GetProto(region).lockedPosition = true; + Doc.GetProto(region).title = 'region:' + this.rootDoc.title; + Doc.GetProto(region).isPushpin = true; + region._timecodeToHide = region._timecodeToShow; + this.addDocument(region); + const anchx = NumCast(cropping.x); + const anchy = NumCast(cropping.y); + const anchw = NumCast(cropping._width); + const anchh = NumCast(cropping._height); + const viewScale = NumCast(this.rootDoc[this.fieldKey + '-nativeWidth']) / anchw; + cropping.title = 'crop: ' + this.rootDoc.title; + cropping.x = NumCast(this.rootDoc.x) + NumCast(this.rootDoc._width); + cropping.y = NumCast(this.rootDoc.y); + cropping._width = anchw * (this.props.NativeDimScaling?.() || 1); + cropping._height = anchh * (this.props.NativeDimScaling?.() || 1); + cropping.timecodeToHide = undefined; + cropping.timecodeToShow = undefined; + cropping.isLinkButton = undefined; + const croppingProto = Doc.GetProto(cropping); + croppingProto.annotationOn = undefined; + croppingProto.isPrototype = true; + croppingProto.proto = Cast(this.rootDoc.proto, Doc, null)?.proto; // set proto of cropping's data doc to be IMAGE_PROTO + croppingProto.type = DocumentType.VID; + croppingProto.layout = VideoBox.LayoutString('data'); + croppingProto.data = ObjectField.MakeCopy(this.rootDoc[this.fieldKey] as ObjectField); + croppingProto['data-nativeWidth'] = anchw; + croppingProto['data-nativeHeight'] = anchh; + croppingProto.currentTimecode = this.layoutDoc._currentTimecode; + croppingProto.viewScale = viewScale; + croppingProto.viewScaleMin = viewScale; + croppingProto.panX = anchx / viewScale; + croppingProto.panY = anchy / viewScale; + croppingProto.panXMin = anchx / viewScale; + croppingProto.panXMax = anchw / viewScale; + croppingProto.panYMin = anchy / viewScale; + croppingProto.panYMax = anchh / viewScale; + if (addCrop) { + DocUtils.MakeLink({ doc: region }, { doc: cropping }, 'cropped image', ''); + } + this.props.bringToFront(cropping); + return cropping; + }; + savedAnnotations = () => this._savedAnnotations; render() { const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding); @@ -1048,6 +1096,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent )} {this.renderTimeline} -- cgit v1.2.3-70-g09d2 From 4484aff465c8f4f242b98d31558483eff245ab95 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 21 Sep 2022 13:31:20 -0400 Subject: no video ui for cropped videos --- src/client/views/nodes/VideoBox.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index ac472aa89..aabe3eb25 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -398,7 +398,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent { const aspect = this.player!.videoWidth / (this.player!.videoHeight || 1); - if (aspect && !this.dataDoc.viewScaleMin) { + if (aspect && !this.isCropped) { Doc.SetNativeWidth(this.dataDoc, this.player!.videoWidth); Doc.SetNativeHeight(this.dataDoc, this.player!.videoHeight); this.layoutDoc._height = NumCast(this.layoutDoc._width) / aspect; @@ -571,7 +571,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent this.Play()} @@ -928,7 +928,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent
; } + @computed get isCropped() { + return this.dataDoc.viewScaleMin; // bcz: hack to identify a cropped video + } crop = (region: Doc | undefined, addCrop?: boolean) => { if (!region) return; const cropping = Doc.MakeCopy(region, true); -- cgit v1.2.3-70-g09d2 From 87aaeef9b9be2a1faab1cc7a7cf35792d3c01252 Mon Sep 17 00:00:00 2001 From: Geireann Lindfield Roberts <60007097+geireann@users.noreply.github.com> Date: Wed, 21 Sep 2022 13:49:51 -0400 Subject: added resize back in --- src/client/views/DocumentDecorations.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 95025bf70..797730c10 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -29,6 +29,8 @@ import { DocumentView } from './nodes/DocumentView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { ImageBox } from './nodes/ImageBox'; import React = require('react'); +import { IconButton } from 'browndash-components'; +import { FaUndo } from 'react-icons/fa'; @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> { @@ -719,12 +721,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P width: bounds.r - bounds.x + this._resizeBorderWidth + 'px', height: bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight + 'px', }}> -
+ {hideResizers ? null : (
{hideDeleteButton ?
: topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')} {hideDeleteButton ?
: topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')} {titleArea} {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Tab (ctrl: as alias, shift: in new collection)')} -
+
)} {hideResizers ? null : ( <>
e.preventDefault()} /> @@ -741,8 +743,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P )} - {!Doc.noviceMode &&
e.preventDefault()}> - {'⟲'} + {useRotation &&
e.preventDefault()}> + } isCircle={true} hoverStyle={"lighten"} backgroundColor={Colors.DARK_GRAY} color={Colors.LIGHT_GRAY}/>
} {useRounding && ( -- cgit v1.2.3-70-g09d2 From f13bb74b7354fce077c1888c2fe11059cb61d5e2 Mon Sep 17 00:00:00 2001 From: Geireann Lindfield Roberts <60007097+geireann@users.noreply.github.com> Date: Wed, 21 Sep 2022 15:00:33 -0400 Subject: fixed homepage bug --- src/client/views/DashboardView.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 68d975a17..911b53945 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -83,8 +83,10 @@ export class DashboardView extends React.Component { @undoBatch createNewDashboard = async (name: string, background?: string) => { + setTimeout(() => { + this.abortCreateNewDashboard(); + }, 100); DashboardView.createNewDashboard(undefined, name, background); - this.abortCreateNewDashboard(); }; @computed -- cgit v1.2.3-70-g09d2 From dc47762c9da3c867c5d76e23e32293fd0abeb5f4 Mon Sep 17 00:00:00 2001 From: mehekj Date: Wed, 21 Sep 2022 15:49:33 -0400 Subject: fixed image size on paste --- src/client/views/PreviewCursor.tsx | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index d56d2a310..119476210 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -6,6 +6,7 @@ import { Cast, NumCast, StrCast } from '../../fields/Types'; import { returnFalse } from '../../Utils'; import { DocServer } from '../DocServer'; import { Docs, DocumentOptions, DocUtils } from '../documents/Documents'; +import { ImageUtils } from '../util/Import & Export/ImageUtils'; import { Transform } from '../util/Transform'; import { undoBatch, UndoManager } from '../util/UndoManager'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; @@ -109,16 +110,16 @@ export class PreviewCursor extends React.Component<{}> { const re: any = / - PreviewCursor._addDocument( - Docs.Create.ImageDocument(arr[1], { - _width: 300, - title: arr[1], - x: newPoint[0], - y: newPoint[1], - }) - ) - )(); + undoBatch(() => { + const doc = Docs.Create.ImageDocument(arr[1], { + _width: 300, + title: arr[1], + x: newPoint[0], + y: newPoint[1], + }); + ImageUtils.ExtractExif(doc); + PreviewCursor._addDocument(doc); + })(); } else if (e.clipboardData.items.length) { const batch = UndoManager.StartBatch('collection view drop'); const files: File[] = []; -- cgit v1.2.3-70-g09d2 From 1c8007f5354e24420250359071b93f6b8526f009 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 21 Sep 2022 15:58:48 -0400 Subject: fixed pres box sliders. --- src/client/views/nodes/trails/PresBox.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 7f0f13437..40cda4f7e 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -1232,7 +1232,10 @@ export class PresBox extends ViewBoxBaseComponent() { max={max} value={value} className={`toolbar-slider ${active ? '' : 'none'}`} - onPointerDown={() => (this._batch = UndoManager.StartBatch('pres slider'))} + onPointerDown={e => { + this._batch = UndoManager.StartBatch('pres slider'); + e.stopPropagation(); + }} onPointerUp={() => this._batch?.end()} onChange={e => { e.stopPropagation(); -- cgit v1.2.3-70-g09d2 From 6f135d3bd98e31489e6ca5738e3546c31959aeb6 Mon Sep 17 00:00:00 2001 From: Geireann Lindfield Roberts <60007097+geireann@users.noreply.github.com> Date: Wed, 21 Sep 2022 16:36:23 -0400 Subject: open trail on right and capitalize header buttons --- src/client/util/CurrentUserUtils.ts | 8 ++++---- src/client/views/MainView.tsx | 4 ++-- src/client/views/collections/TabDocView.tsx | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index c6a3959ee..8b21a5365 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -663,10 +663,10 @@ export class CurrentUserUtils { { title: "Fill", icon: "fill-drip", toolTip: "Background Fill Color",btnType: ButtonType.ColorButton, funcs: {hidden: '!SelectionManager_selectedDocType()'}, ignoreClick: true, width: 20, scripts: { script: 'return setBackgroundColor(value, _readOnly_)'}}, // Only when a document is selected { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, funcs: {hidden: '!SelectionManager_selectedDocType()'}, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'}}, { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform", true)'}, scripts: { onClick: 'toggleOverlay(_readOnly_)'}}, // Only when floating document is selected in freeform - { title: "Text", icon: "text", toolTip: "Text functions", subMenu: CurrentUserUtils.textTools(), funcs: {hidden: 'false', linearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.RTF}")`} }, // Always available - { title: "Ink", icon: "ink", toolTip: "Ink functions", subMenu: CurrentUserUtils.inkTools(), funcs: {hidden: 'false', inearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.INK}")`} }, // Always available - { title: "Web", icon: "web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), funcs: {hidden: `!SelectionManager_selectedDocType("${DocumentType.WEB}")`, linearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.WEB}")`, } }, // Only when Web is selected - { title: "Schema", icon: "schema", toolTip: "Schema functions", subMenu: CurrentUserUtils.schemaTools(), funcs: {hidden: `!SelectionManager_selectedDocType(undefined, "${CollectionViewType.Schema}")`, linearViewIsExpanded: `SelectionManager_selectedDocType(undefined, "${CollectionViewType.Schema}")`} } // Only when Schema is selected + { title: "Text", icon: "Text", toolTip: "Text functions", subMenu: CurrentUserUtils.textTools(), funcs: {hidden: 'false', linearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.RTF}")`} }, // Always available + { title: "Ink", icon: "Ink", toolTip: "Ink functions", subMenu: CurrentUserUtils.inkTools(), funcs: {hidden: 'false', inearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.INK}")`} }, // Always available + { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), funcs: {hidden: `!SelectionManager_selectedDocType("${DocumentType.WEB}")`, linearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.WEB}")`, } }, // Only when Web is selected + { title: "Schema", icon: "Schema", toolTip: "Schema functions", subMenu: CurrentUserUtils.schemaTools(), funcs: {hidden: `!SelectionManager_selectedDocType(undefined, "${CollectionViewType.Schema}")`, linearViewIsExpanded: `SelectionManager_selectedDocType(undefined, "${CollectionViewType.Schema}")`} } // Only when Schema is selected ]; } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d07e255e2..857a5522c 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -535,7 +535,7 @@ export class MainView extends React.Component { @action createNewPresentation = () => { const pres = Doc.MakeCopy(Doc.UserDoc().emptyTrail as Doc, true); - CollectionDockingView.AddSplit(pres, 'left'); + CollectionDockingView.AddSplit(pres, 'right'); Doc.MyTrails && Doc.AddDocToList(Doc.MyTrails, 'data', pres); // Doc.MyTrails should be created in createDashboard Doc.ActivePresentation = pres; }; @@ -543,7 +543,7 @@ export class MainView extends React.Component { @action openPresentation = (pres: Doc) => { if (pres.type === DocumentType.PRES) { - CollectionDockingView.AddSplit(pres, 'left'); + CollectionDockingView.AddSplit(pres, 'right'); Doc.MyTrails && (Doc.ActivePresentation = pres); Doc.AddDocToList(Doc.MyTrails, 'data', pres); this.closeFlyout(); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 98121f423..2d8055ba9 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -280,7 +280,7 @@ export class TabDocView extends React.Component { ) { const docs = Cast(Doc.MyOverlayDocs.data, listSpec(Doc), []); if (docs.includes(curPres)) docs.splice(docs.indexOf(curPres), 1); - CollectionDockingView.AddSplit(curPres, 'left'); + 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 -- cgit v1.2.3-70-g09d2 From 69719257b8275cb19d63960a6ec552a0c118d988 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 21 Sep 2022 16:43:54 -0400 Subject: fixed navigating to cropped video regions. --- .../collections/CollectionStackedTimeline.tsx | 27 ++++++++++++++++++++-- src/client/views/nodes/VideoBox.tsx | 17 ++++++++++---- 2 files changed, 38 insertions(+), 6 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index b29abf083..c694e17fb 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -2,7 +2,7 @@ import React = require('react'); import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { computedFn } from 'mobx-utils'; -import { Doc, DocListCast, StrListCast } from '../../../fields/Doc'; +import { Doc, DocListCast, Opt, StrListCast } from '../../../fields/Doc'; import { Id } from '../../../fields/FieldSymbols'; import { List } from '../../../fields/List'; import { listSpec } from '../../../fields/Schema'; @@ -23,11 +23,13 @@ import { AudioWaveform } from '../AudioWaveform'; import { CollectionSubView } from '../collections/CollectionSubView'; import { Colors } from '../global/globalEnums'; import { LightboxView } from '../LightboxView'; -import { DocAfterFocusFunc, DocFocusFunc, DocumentView, DocumentViewProps } from '../nodes/DocumentView'; +import { DocAfterFocusFunc, DocFocusFunc, DocumentView, DocumentViewProps, DocumentViewSharedProps } from '../nodes/DocumentView'; import { LabelBox } from '../nodes/LabelBox'; import './CollectionStackedTimeline.scss'; import { VideoBox } from '../nodes/VideoBox'; import { ImageField } from '../../../fields/URLField'; +import { StyleProp } from '../StyleProvider'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; export type CollectionStackedTimelineProps = { Play: () => void; @@ -693,6 +695,7 @@ interface StackedTimelineAnchorProps { width: number; height: number; toTimeline: (screen_delta: number, width: number) => number; + styleProvider?: (doc: Opt, props: Opt, property: string) => any; playLink: (linkDoc: Doc) => void; setTime: (time: number) => void; startTag: string; @@ -803,6 +806,25 @@ class StackedTimelineAnchor extends React.Component return [resetTitle]; }; + innerStyleProvider = (doc: Opt, props: Opt, property: string): any => { + if (property === StyleProp.Decorations && doc && NumCast(doc.timecodeToHide) - NumCast(doc.timecodeToShow) < 0.0002) { + return ( +
+ { + LinkFollower.FollowLink(undefined, doc, props as DocumentViewSharedProps, e.altKey); + e.stopPropagation(); + }} + size="lg" + /> +
+ ); + } + return this.props.styleProvider?.(doc, props, property); + }; + // renders anchor LabelBox renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) { const anchor = observable({ view: undefined as any }); @@ -819,6 +841,7 @@ class StackedTimelineAnchor extends React.Component ref={action((r: DocumentView | null) => (anchor.view = r))} Document={mark} DataDoc={undefined} + styleProvider={this.innerStyleProvider} renderDepth={this.props.renderDepth + 1} LayoutTemplate={undefined} LayoutTemplateString={LabelBox.LayoutStringWithTitle('data', this.computeTitle())} diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index aabe3eb25..4bcd79641 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -948,6 +948,14 @@ export class VideoBox extends ViewBoxAnnotatableComponent { + if (doc !== this.rootDoc) { + const showTime = Cast(doc._timecodeToShow, 'number', null); + showTime !== undefined && setTimeout(() => this.Seek(showTime), 100); + return 0.1; + } + }; + // renders CollectionStackedTimeline @computed get renderTimeline() { return ( @@ -981,15 +989,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent ); } + @computed get isCropped() { + return this.dataDoc.videoCrop; // bcz: hack to identify a cropped video + } // renders annotation layer @computed get annotationLayer() { return
; } - @computed get isCropped() { - return this.dataDoc.viewScaleMin; // bcz: hack to identify a cropped video - } crop = (region: Doc | undefined, addCrop?: boolean) => { if (!region) return; const cropping = Doc.MakeCopy(region, true); @@ -997,7 +1005,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent Date: Wed, 21 Sep 2022 18:18:31 -0400 Subject: fixed issues with deleting last stack, or deleting stacks and leaving only row/cols in goldenLayout --- src/client/goldenLayout.js | 13 ++++++++----- src/client/views/collections/CollectionDockingView.tsx | 3 ++- 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'src/client/views') diff --git a/src/client/goldenLayout.js b/src/client/goldenLayout.js index e5cd50de2..dd11e6466 100644 --- a/src/client/goldenLayout.js +++ b/src/client/goldenLayout.js @@ -3263,11 +3263,6 @@ const canDelete = rowOrCol && !rowOrCol.isRoot && (rowOrCol.contentItems.length > 1 || (parRowOrCol && parRowOrCol.contentItems.length > 1)); // bcz: added test for last stack if (canDelete) { rowOrCol.removeChild(stack); - if (rowOrCol.contentItems.length === 1 && parRowOrCol.contentItems.length === 1 && !parRowOrCol.isRoot) { - saveScrollTops(rowOrCol.contentItems[0].element); - parRowOrCol.replaceChild(rowOrCol, rowOrCol.contentItems[0]); - restoreScrollTops(rowOrCol.contentItems[0].element); - } } } }, @@ -4061,6 +4056,14 @@ lm.items.AbstractContentItem.prototype.removeChild.call(this, contentItem, keepChild); if (this.contentItems.length === 1 && this.config.isClosable === true) { + if (["row","column"].includes(this.contentItems[0].type) || ["row","column"].includes(this.parent.type)) { + let parent = this.parent; + let correctRowOrCol = this.contentItems[0]; + saveScrollTops(correctRowOrCol.element); + parent.replaceChild(this, correctRowOrCol); + restoreScrollTops(correctRowOrCol.element); + } + // bcz: this has the effect of removing children from the DOM and then re-adding them above where they were before. // in the case of things like an iFrame with a YouTube video, the video will reload for now reason. So let's try leaving these "empty" rows alone. // childItem = this.contentItems[0]; diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 25fe5fe43..c6ac5ee0c 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -477,6 +477,7 @@ export class CollectionDockingView extends CollectionSubView() { }; stackCreated = (stack: any) => { + stack = stack.header ? stack : stack.origin; stack.header?.element.on('mousedown', (e: any) => { const dashboard = Doc.ActiveDashboard; if (dashboard && e.target === stack.header?.element[0] && e.button === 2) { @@ -499,7 +500,7 @@ export class CollectionDockingView extends CollectionSubView() { .click( action(() => { //if (confirm('really close this?')) { - if (!stack.parent.parent.isRoot || stack.parent.contentItems.length > 1) { + if ((!stack.parent.isRoot && !stack.parent.parent.isRoot) || stack.parent.contentItems.length > 1) { stack.remove(); } else { alert('cant delete the last stack'); -- cgit v1.2.3-70-g09d2 From d40a8e4506672f9d0ad505409f4b181d73762c28 Mon Sep 17 00:00:00 2001 From: mehekj Date: Wed, 21 Sep 2022 23:23:37 -0400 Subject: fixed video duration in presbox media controls --- src/client/views/nodes/VideoBox.tsx | 1 + src/client/views/nodes/trails/PresBox.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 4bcd79641..f7f558bb4 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -405,6 +405,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent() { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; const clipStart: number = NumCast(activeItem.clipStart); - const clipEnd: number = NumCast(activeItem.clipEnd); + const clipEnd: number = NumCast(activeItem.clipEnd, NumCast(activeItem.duration)); const mediaStopDocInd: number = NumCast(activeItem.mediaStopDoc); if (activeItem && targetDoc) { return ( -- cgit v1.2.3-70-g09d2 From 13a0c0c505edd4b00f9d7b5aed78237c4d23a3d1 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 22 Sep 2022 09:59:00 -0400 Subject: remove overlaydocs when switching dashboards. --- src/client/views/topbar/TopBar.tsx | 87 +++----------------------------------- src/fields/Doc.ts | 2 + 2 files changed, 7 insertions(+), 82 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 7bc7ba3ed..7e728306c 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -80,8 +80,6 @@ export class TopBar extends React.Component { * - Taking a snapshot of a dashboard */ @computed get topbarCenter() { - const myDashboards = DocListCast(Doc.MyDashboards.data); - const activeDashboard = Doc.ActiveDashboard; // const dashboardItems = myDashboards.map(board => { // const boardTitle = StrCast(board.title); // console.log(boardTitle); @@ -91,10 +89,10 @@ export class TopBar extends React.Component { // val: board, // }; // }); - return activeDashboard ? ( + return Doc.ActiveDashboard ? (
- // ); - // } render() { return ( //TODO:glr Add support for light / dark mode diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 74a3d8cf2..a3c742f28 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -239,6 +239,8 @@ export class Doc extends RefField { return DocCast(Doc.UserDoc().activeDashboard); } public static set ActiveDashboard(val: Doc | undefined) { + const overlays = Cast(Doc.MyOverlayDocs.data, listSpec(Doc), null); + overlays && (overlays.length = 0); Doc.UserDoc().activeDashboard = val; } public static set ActiveTool(tool: InkTool) { -- cgit v1.2.3-70-g09d2 From 0bf2c263c82eb08a8f9545967c5907fc00be9a9f Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 22 Sep 2022 11:32:38 -0400 Subject: fixed presenting backward in presBox. fixed context menu to appear above other widgets --- src/client/views/ContextMenu.scss | 2 +- src/client/views/DocumentDecorations.tsx | 25 +++++++++++++++---------- src/client/views/global/globalCssVariables.scss | 2 +- src/client/views/nodes/trails/PresBox.tsx | 25 +++++++++++++++---------- 4 files changed, 32 insertions(+), 22 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/ContextMenu.scss b/src/client/views/ContextMenu.scss index 1e6a377de..cbe14060a 100644 --- a/src/client/views/ContextMenu.scss +++ b/src/client/views/ContextMenu.scss @@ -3,7 +3,7 @@ .contextMenu-cont { position: absolute; display: flex; - z-index: 100000; + z-index: $contextMenu-zindex; box-shadow: 0px 3px 4px rgba(0, 0, 0, 30%); flex-direction: column; background: whitesmoke; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 797730c10..4675571c2 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -31,6 +31,7 @@ import { ImageBox } from './nodes/ImageBox'; import React = require('react'); import { IconButton } from 'browndash-components'; import { FaUndo } from 'react-icons/fa'; +import { VideoBox } from './nodes/VideoBox'; @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> { @@ -678,7 +679,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P bounds.b = Math.max(bounds.y, Math.max(topBounds, Math.min(window.innerHeight, bounds.b + this._resizeBorderWidth / 2 + this._linkBoxHeight) - this._resizeBorderWidth / 2 - this._linkBoxHeight)); // Rotation constants: Only allow rotation on ink and images - const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox; + const useRotation = seldoc.ComponentView instanceof InkingStroke || seldoc.ComponentView instanceof ImageBox || seldoc.ComponentView instanceof VideoBox; const rotation = NumCast(seldoc.rootDoc._jitterRotation); const resizerScheme = colorScheme ? 'documentDecorations-resizer' + colorScheme : ''; @@ -721,12 +722,14 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P width: bounds.r - bounds.x + this._resizeBorderWidth + 'px', height: bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight + 'px', }}> - {hideResizers ? null : (
- {hideDeleteButton ?
: topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')} - {hideDeleteButton ?
: topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')} - {titleArea} - {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Tab (ctrl: as alias, shift: in new collection)')} -
)} + {hideResizers ? null : ( +
+ {hideDeleteButton ?
: topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')} + {hideDeleteButton ?
: topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')} + {titleArea} + {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Tab (ctrl: as alias, shift: in new collection)')} +
+ )} {hideResizers ? null : ( <>
e.preventDefault()} /> @@ -743,9 +746,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P )} - {useRotation &&
e.preventDefault()}> - } isCircle={true} hoverStyle={"lighten"} backgroundColor={Colors.DARK_GRAY} color={Colors.LIGHT_GRAY}/> -
} + {useRotation && ( +
e.preventDefault()}> + } isCircle={true} hoverStyle={'lighten'} backgroundColor={Colors.DARK_GRAY} color={Colors.LIGHT_GRAY} /> +
+ )} {useRounding && (
() { //TODO: al: it seems currently that tempMedia doesn't stop onslidechange after clicking the button; the time the tempmedia stop depends on the start & end time // TODO: to handle child slides (entering into subtrail and exiting), also the next() and back() functions // No more frames in current doc and next slide is defined, therefore move to next slide - nextSlide = (activeNext: Doc) => { - let nextSelected = this.itemIndex + 1; + nextSlide = (slideNum?: number) => { + let nextSelected = slideNum ?? this.itemIndex + 1; this.gotoDocument(nextSelected, this.activeItem); for (nextSelected = nextSelected + 1; nextSelected < this.childDocs.length; nextSelected++) { if (this.childDocs[nextSelected].groupWithUp) { @@ -261,10 +261,12 @@ export class PresBox extends ViewBoxBaseComponent() { this.nextInternalFrame(targetDoc, activeItem); } else if (this.childDocs[this.itemIndex + 1] !== undefined) { // Case 2: No more frames in current doc and next slide is defined, therefore move to next slide - this.nextSlide(activeNext); + const slides = DocListCast(this.rootDoc[StrCast(this.presFieldKey, 'data')]); + const curLast = this.selectedArray.size ? Math.max(...Array.from(this.selectedArray).map(d => slides.indexOf(DocCast(d)))) : this.itemIndex; + this.nextSlide(curLast + 1); } else if (this.childDocs[this.itemIndex + 1] === undefined && (this.layoutDoc.presLoop || this.layoutDoc.presStatus === PresStatus.Edit)) { // Case 3: Last slide and presLoop is toggled ON or it is in Edit mode - this.gotoDocument(0, this.activeItem); + this.nextSlide(0); } }; @@ -280,8 +282,8 @@ export class PresBox extends ViewBoxBaseComponent() { let prevSelected = this.itemIndex; // Functionality for group with up let didZoom = activeItem.presMovement; - for (; !didZoom && prevSelected > 0 && this.childDocs[prevSelected].groupButton; prevSelected--) { - didZoom = this.childDocs[prevSelected].presMovement; + for (; prevSelected > 0 && this.childDocs[Math.max(0, prevSelected - 1)].groupWithUp; prevSelected--) { + didZoom = didZoom === 'none' ? this.childDocs[prevSelected].presMovement : didZoom; } if (lastFrame !== undefined && curFrame >= 1) { // Case 1: There are still other frames and should go through all frames before going to previous slide @@ -289,7 +291,8 @@ export class PresBox extends ViewBoxBaseComponent() { } else if (activeItem && this.childDocs[this.itemIndex - 1] !== undefined) { // Case 2: There are no other frames so it should go to the previous slide prevSelected = Math.max(0, prevSelected - 1); - this.gotoDocument(prevSelected, activeItem); + this.nextSlide(prevSelected); + this.rootDoc._itemIndex = prevSelected; if (NumCast(prevTargetDoc.lastFrame) > 0) prevTargetDoc._currentFrame = NumCast(prevTargetDoc.lastFrame); } else if (this.childDocs[this.itemIndex - 1] === undefined && this.layoutDoc.presLoop) { // Case 3: Pres loop is on so it should go to the last slide @@ -2458,12 +2461,13 @@ export class PresBox extends ViewBoxBaseComponent() {
{ + onClick={e => { this.back(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } + e.stopPropagation(); }}>
@@ -2475,18 +2479,19 @@ export class PresBox extends ViewBoxBaseComponent() {
{ + onClick={e => { this.next(); if (this._presTimer) { clearTimeout(this._presTimer); this.layoutDoc.presStatus = PresStatus.Manual; } + e.stopPropagation(); }}>
{'Click to return to 1st slide'}
}> -
this.gotoDocument(0, this.activeItem)}> +
this.nextSlide(0)}> 1
-- cgit v1.2.3-70-g09d2 From 735e81f31cca1fdc054b9c28c950ceafa04e039a Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 22 Sep 2022 12:02:59 -0400 Subject: minipres opacity bumped up to 0.5 --- src/client/views/nodes/trails/PresBox.scss | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss index a0a2dd4f8..7069d2742 100644 --- a/src/client/views/nodes/trails/PresBox.scss +++ b/src/client/views/nodes/trails/PresBox.scss @@ -1,4 +1,4 @@ -@import "../../global/globalCssVariables"; +@import '../../global/globalCssVariables'; .presBox-cont { cursor: auto; @@ -231,8 +231,7 @@ margin-top: 10px; } - @media screen and (-webkit-min-device-pixel-ratio:0) { - + @media screen and (-webkit-min-device-pixel-ratio: 0) { .multiThumb-slider { display: grid; background-color: $white; @@ -330,8 +329,6 @@ } } - - .slider-headers { position: relative; display: grid; @@ -382,7 +379,6 @@ border-bottom: solid 2px $medium-gray; } - .ribbon-textInput { border-radius: 2px; height: 20px; @@ -467,7 +463,6 @@ font-weight: 500; position: relative; - .ribbon-final-button { cursor: pointer; position: relative; @@ -687,7 +682,6 @@ max-width: 200px; overflow: visible; - .presBox-dropdownOption { cursor: pointer; font-size: 11; @@ -716,7 +710,7 @@ width: 85%; min-width: max-content; display: block; - background: #FFFFFF; + background: #ffffff; border: 0.5px solid #979797; box-sizing: border-box; box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25); @@ -741,7 +735,7 @@ padding-top: 5px; padding-bottom: 5px; border: solid 1px $black; - // overflow: auto; + // overflow: auto; ::-webkit-scrollbar { -webkit-appearance: none; @@ -953,8 +947,6 @@ min-width: 150px; } - - select { background: $dark-gray; color: $white; @@ -999,8 +991,6 @@ } } - - .collectionViewBaseChrome-viewPicker { min-width: 50; width: 5%; @@ -1080,7 +1070,7 @@ position: absolute; top: 0; left: 0; - opacity: 0.1; + opacity: 0.5; transition: all 0.4s; color: $white; width: 100%; @@ -1165,8 +1155,6 @@ .presPanel-button-text:hover { background-color: $medium-gray; } - - } // .miniPres { @@ -1241,4 +1229,4 @@ // background-color: #5a5a5a; // } // } -// } \ No newline at end of file +// } -- cgit v1.2.3-70-g09d2 From 4f1427343be65fcf00389570107f436cee4c66c5 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 22 Sep 2022 12:11:56 -0400 Subject: fixed creating new dashboards to open them --- src/client/views/DashboardView.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 911b53945..11bb0c6a8 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -370,11 +370,12 @@ export class DashboardView extends React.Component { system: true, explainer: 'All of the trails that you have created will appear here.', }; - dashboardDoc.myTrails = new PrefetchProxy(DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeViewChildDoubleClick: 'openPresentation(documentView.rootDoc)' })); + const myTrails = DocUtils.AssignScripts(Docs.Create.TreeDocument([], reqdOpts), { treeViewChildDoubleClick: 'openPresentation(documentView.rootDoc)' }); + dashboardDoc.myTrails = new PrefetchProxy(myTrails); const contextMenuScripts = [reqdBtnScript.onClick]; - if (Cast(Doc.MyTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { - Doc.MyTrails.contextMenuScripts = new List(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); + if (Cast(myTrails.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { + myTrails.contextMenuScripts = new List(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); } } } -- cgit v1.2.3-70-g09d2 From 40cc7455e853d306ee2750c51308d095dc2970ef Mon Sep 17 00:00:00 2001 From: mehekj Date: Thu, 22 Sep 2022 12:27:54 -0400 Subject: fixed media range sliders in presBox --- src/client/views/nodes/trails/PresBox.scss | 1 + src/client/views/nodes/trails/PresBox.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss index 7069d2742..34df415ac 100644 --- a/src/client/views/nodes/trails/PresBox.scss +++ b/src/client/views/nodes/trails/PresBox.scss @@ -288,6 +288,7 @@ height: 10px; -webkit-appearance: none; margin-top: -1px; + background: transparent; } .toolbar-slider::-webkit-slider-thumb { diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 69f817d79..de19f831d 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -1430,7 +1430,7 @@ export class PresBox extends ViewBoxBaseComponent() { const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; const clipStart: number = NumCast(activeItem.clipStart); - const clipEnd: number = NumCast(activeItem.clipEnd, NumCast(activeItem.duration)); + const clipEnd: number = NumCast(activeItem.clipEnd, NumCast(activeItem[Doc.LayoutFieldKey(activeItem) + '-duration'])); const mediaStopDocInd: number = NumCast(activeItem.mediaStopDoc); if (activeItem && targetDoc) { return ( -- cgit v1.2.3-70-g09d2 From 38e56a0eb6d965535be748b5bf7befed6edbc4ee Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 22 Sep 2022 12:28:52 -0400 Subject: fixed webboxes not to render below webbox when something is dragged. --- src/client/views/nodes/WebBox.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index e7b188961..8288810b1 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -948,7 +948,10 @@ export class WebBox extends ViewBoxAnnotatableComponent +
Date: Thu, 22 Sep 2022 13:10:56 -0400 Subject: added more things to novice mode --- src/client/views/nodes/ImageBox.tsx | 2 +- src/client/views/nodes/trails/PresElementBox.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index d3d68d835..486c9c48c 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -340,7 +340,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent )}
- {this.considerDownloadIcon} + {!Doc.noviceMode && this.considerDownloadIcon} {this.considerGooglePhotosLink()}
diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index e3f93c637..e6d08cd53 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -511,11 +511,11 @@ export class PresElementBox extends ViewBoxBaseComponent() { V
- {this.recordingIsInOverlay ? 'Hide Recording' : `${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}
}> + {!Doc.noviceMode && {this.recordingIsInOverlay ? 'Hide Recording' : `${PresElementBox.videoIsRecorded(activeItem) ? 'Show' : 'Start'} recording`}
}>
(this.recordingIsInOverlay ? this.hideRecording(e, true) : this.startRecording(e, activeItem))} style={{ fontWeight: 700 }}> e.stopPropagation()} />
- + } {activeItem.groupWithUp ? 'Ungroup' : 'Group with up'}
}>
Date: Thu, 22 Sep 2022 13:26:45 -0400 Subject: added headers to novice mode! --- src/client/views/nodes/DocumentView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 0e485e2f9..f18fd8024 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1259,7 +1259,7 @@ export class DocumentViewInternal extends DocComponent users.user.email === this.dataDoc.author)?.sharingDoc.userColor, Doc.UserDoc().showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : 'rgba(0,0,0,0.4)' ); - const titleView = !showTitle ? null : ( + const titleView = !showTitle || Doc.noviceMode ? null : (
Date: Thu, 22 Sep 2022 16:00:17 -0400 Subject: fixed error messages on loading to not generate temporary error message when there is no error. --- src/client/documents/Documents.ts | 22 ++++++---------------- src/client/views/collections/CollectionSubView.tsx | 2 -- src/client/views/nodes/LoadingBox.tsx | 12 +++++------- 3 files changed, 11 insertions(+), 25 deletions(-) (limited to 'src/client/views') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 029c3033d..f046af684 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -908,7 +908,7 @@ export namespace Docs { export function ColorDocument(options: DocumentOptions = {}) { return InstanceFromProto(Prototypes.get(DocumentType.COLOR), '', options); } - export function LoadingDocument(file: File | string, options: DocumentOptions, ytString?: string) { + export function LoadingDocument(file: File | string, options: DocumentOptions) { return InstanceFromProto(Prototypes.get(DocumentType.LOADING), undefined, { _height: 150, _width: 200, title: typeof file == 'string' ? file : file.name, ...options }, undefined, ''); } @@ -1758,7 +1758,6 @@ export namespace DocUtils { const full = { ...options, _width: 400, title: name }; const pathname = Utils.prepend(result.accessPaths.agnostic.client); const doc = await DocUtils.DocumentFromType(type, pathname, full, rootDoc); - rootDoc && (rootDoc.isLoading = undefined) && Doc.removeCurrentlyLoading(rootDoc); if (doc) { const proto = Doc.GetProto(doc); proto.text = result.rawText; @@ -1790,6 +1789,9 @@ export namespace DocUtils { if (Upload.isVideoInformation(result)) { proto['data-duration'] = result.duration; } + if (rootDoc) { + Doc.removeCurrentlyLoading(rootDoc); + } generatedDocuments.push(doc); } } @@ -1818,17 +1820,6 @@ export namespace DocUtils { return tbox; } - export async function uploadYoutubeVideo(videoId: string, options: DocumentOptions) { - const generatedDocuments: Doc[] = []; - for (const { - source: { name, type }, - result, - } of await Networking.UploadYoutubeToServer(videoId)) { - name && processFileupload(generatedDocuments, name, type, result, options); - } - return generatedDocuments; - } - export function uploadYoutubeVideoLoading(videoId: string, options: DocumentOptions, overwriteDoc?: Doc) { const generatedDocuments: Doc[] = []; Networking.UploadYoutubeToServer(videoId).then(upfiles => { @@ -1839,7 +1830,7 @@ export namespace DocUtils { if ((result as any).message) { if (overwriteDoc) { overwriteDoc.isLoading = false; - overwriteDoc.errorMessage = (result as any).message; + overwriteDoc.loadingError = (result as any).message; Doc.removeCurrentlyLoading(overwriteDoc); } } else name && processFileupload(generatedDocuments, name, type, result, options, overwriteDoc); @@ -1867,8 +1858,7 @@ export namespace DocUtils { } = upfiles.lastElement() ?? { source: { name: '', type: '' }, result: { message: 'upload failed' } }; if ((result as any).message) { if (overwriteDoc) { - overwriteDoc.isLoading = false; - overwriteDoc.errorMessage = (result as any).message; + overwriteDoc.loadingError = (result as any).message; Doc.removeCurrentlyLoading(overwriteDoc); } } else name && type && processFileupload(generatedDocuments, name, type, result, options, overwriteDoc); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 3ae965af0..30759b766 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -464,14 +464,12 @@ export function CollectionSubView(moreProps?: X) { if (typeof files === 'string') { const loading = Docs.Create.LoadingDocument(files, options); generatedDocuments.push(loading); - loading.isLoading = true; Doc.addCurrentlyLoading(loading); DocUtils.uploadYoutubeVideoLoading(files, {}, loading); } else { generatedDocuments.push( ...files.map(file => { const loading = Docs.Create.LoadingDocument(file, options); - loading.isLoading = true; Doc.addCurrentlyLoading(loading); DocUtils.uploadFileToDoc(file, {}, loading); return loading; diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index 220cd0880..53390328f 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -1,4 +1,3 @@ -import { observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import ReactLoading from 'react-loading'; @@ -36,19 +35,18 @@ export class LoadingBox extends ViewBoxAnnotatableComponent() { } componentDidMount() { - if (!Doc.CurrentlyLoading || !Doc.CurrentlyLoading.includes(this.rootDoc)) { - this.rootDoc.isLoading = false; - this.rootDoc.errorMessage = 'Upload was interrupted, please try again'; + if (!Doc.CurrentlyLoading?.includes(this.rootDoc)) { + this.rootDoc.loadingError = 'Upload interrupted, please try again'; } } render() { return ( -
+
-

{this.rootDoc.isLoading ? 'Loading (can take several minutes):' : StrCast(this.rootDoc.errorMessage, 'Error Loading File:')}

+

{StrCast(this.rootDoc.loadingError, 'Loading (can take several minutes):')}

{StrCast(this.rootDoc.title)} - {!this.rootDoc.isLoading ? null : } + {this.rootDoc.loadingError ? null : }
); -- cgit v1.2.3-70-g09d2 From 357247845341ef5e92d74883aeea093351d18490 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 23 Sep 2022 15:29:22 -0400 Subject: added progress messages to youtube uploads --- src/client/Network.ts | 9 +++++++++ src/client/views/nodes/LoadingBox.tsx | 16 ++++++++++++++- src/server/ApiManagers/UploadManager.ts | 15 ++++++++++++++ src/server/DashUploadUtils.ts | 35 +++++++++------------------------ 4 files changed, 48 insertions(+), 27 deletions(-) (limited to 'src/client/views') diff --git a/src/client/Network.ts b/src/client/Network.ts index 996eb35d8..19eff3b3b 100644 --- a/src/client/Network.ts +++ b/src/client/Network.ts @@ -62,4 +62,13 @@ export namespace Networking { const response = await fetch('/uploadYoutubeVideo', parameters); return response.json(); } + export async function QueryYoutubeProgress(videoId: string): Promise<{ progress: string }> { + const parameters = { + method: 'POST', + body: JSON.stringify({ videoId }), + json: true, + }; + const response = await fetch('/queryYoutubeProgress', parameters); + return response.json(); + } } diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index 53390328f..8c5255f80 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -1,8 +1,10 @@ +import { action, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import ReactLoading from 'react-loading'; import { Doc } from '../../../fields/Doc'; import { StrCast } from '../../../fields/Types'; +import { Networking } from '../../Network'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { FieldView, FieldViewProps } from './FieldView'; import './LoadingBox.scss'; @@ -34,17 +36,29 @@ export class LoadingBox extends ViewBoxAnnotatableComponent() { return FieldView.LayoutString(LoadingBox, fieldKey); } + _timer: any; + @observable progress = ''; componentDidMount() { if (!Doc.CurrentlyLoading?.includes(this.rootDoc)) { this.rootDoc.loadingError = 'Upload interrupted, please try again'; + } else { + const updateFunc = async () => { + const result = await Networking.QueryYoutubeProgress(StrCast(this.rootDoc.title)); + runInAction(() => (this.progress = result.progress)); + this._timer = setTimeout(updateFunc, 1000); + }; + this._timer = setTimeout(updateFunc, 1000); } } + componentWillUnmount() { + clearTimeout(this._timer); + } render() { return (
-

{StrCast(this.rootDoc.loadingError, 'Loading (can take several minutes):')}

+

{StrCast(this.rootDoc.loadingError, 'Loading ' + (this.progress.replace('[download]', '') || '(can take several minutes)'))}

{StrCast(this.rootDoc.title)} {this.rootDoc.loadingError ? null : }
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index 76cf36d44..fe4c475c9 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -106,6 +106,21 @@ export default class UploadManager extends ApiManager { }, }); + register({ + method: Method.POST, + subscription: '/queryYoutubeProgress', + secureHandler: async ({ req, res }) => { + return new Promise(async resolve => { + req.addListener('data', args => { + const payload = String.fromCharCode.apply(String, args); + const videoId = JSON.parse(payload).videoId; + _success(res, { progress: DashUploadUtils.QueryYoutubeProgress(videoId) }); + resolve(); + }); + }); + }, + }); + register({ method: Method.POST, subscription: new RouteSubscriber('youtubeScreenshot'), diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index 2c549cc9f..45e88d8cc 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -117,6 +117,12 @@ export namespace DashUploadUtils { }; } + export function QueryYoutubeProgress(videoId: string) { + return uploadProgress.get(videoId) ?? 'failed'; + } + + let uploadProgress = new Map(); + export function uploadYoutube(videoId: string): Promise { return new Promise>((res, rej) => { console.log('Uploading YouTube video: ' + videoId); @@ -124,6 +130,7 @@ export namespace DashUploadUtils { const path = name.replace(/^-/, '__') + '.mp4'; const finalPath = serverPathToFile(Directory.videos, path); if (existsSync(finalPath)) { + uploadProgress.set(videoId, 'computing duration'); exec(`yt-dlp -o ${finalPath} "https://www.youtube.com/watch?v=${videoId}" --get-duration`, (error: any, stdout: any, stderr: any) => { const time = Array.from(stdout.trim().split(':')).reverse(); const duration = (time.length > 2 ? Number(time[2]) * 1000 * 60 : 0) + (time.length > 1 ? Number(time[1]) * 60 : 0) + (time.length > 0 ? Number(time[0]) : 0); @@ -132,9 +139,7 @@ export namespace DashUploadUtils { } else { const ytdlp = spawn(`yt-dlp`, ['-o', path, videoId, '-f', 'mp4']); - ytdlp.stdout.on('data', function (data: any) { - console.log('stdout: ' + data.toString()); // bcz: somehow channel this back to the client... - }); + ytdlp.stdout.on('data', (data: any) => uploadProgress.set(videoId, data.toString())); let errors = ''; ytdlp.stderr.on('data', (data: any) => (errors = data.toString())); @@ -153,6 +158,7 @@ export namespace DashUploadUtils { result: { name: 'failed youtube query', message: `Could not upload video. ${errors}` }, }); } else { + uploadProgress.set(videoId, 'computing duration'); exec(`yt-dlp-o ${path} "https://www.youtube.com/watch?v=${videoId}" --get-duration`, (error: any, stdout: any, stderr: any) => { const time = Array.from(stdout.trim().split(':')).reverse(); const duration = (time.length > 2 ? Number(time[2]) * 1000 * 60 : 0) + (time.length > 1 ? Number(time[1]) * 60 : 0) + (time.length > 0 ? Number(time[0]) : 0); @@ -162,29 +168,6 @@ export namespace DashUploadUtils { }); } }); - // exec(`yt-dlp -o ${path} "https://www.youtube.com/watch?v=${videoId}" -f "mp4"`, (error: any, stdout: any, stderr: any) => { - // if (error) { - // console.log(`error: Error: ${error.message}`); - // res({ - // source: { - // size: 0, - // path, - // name, - // type: '', - // toJSON: () => ({ name, path }), - // }, - // result: { name: 'failed youtube query', message: `Could not upload YouTube video (${videoId}). Error: ${error.message}` }, - // }); - // } else { - // exec(`yt-dlp-o ${path} "https://www.youtube.com/watch?v=${videoId}" --get-duration`, (error: any, stdout: any, stderr: any) => { - // const time = Array.from(stdout.trim().split(':')).reverse(); - // const duration = (time.length > 2 ? Number(time[2]) * 1000 * 60 : 0) + (time.length > 1 ? Number(time[1]) * 60 : 0) + (time.length > 0 ? Number(time[0]) : 0); - // const data = { size: 0, path, name, type: 'video/mp4' }; - // const file = { ...data, toJSON: () => ({ ...data, filename: data.path.replace(/.*\//, ''), mtime: duration.toString(), mime: '', toJson: () => undefined as any }) }; - // res(MoveParsedFile(file, Directory.videos)); - // }); - // } - // }); } }); } -- cgit v1.2.3-70-g09d2 From ddc7c4019eff459be9e81583aa956720c687818d Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 23 Sep 2022 16:08:31 -0400 Subject: fixed pinWithVIew for videos to capture timecode. fixed formatting of time labels in presBox panel. --- src/client/views/collections/TabDocView.tsx | 6 +++--- src/client/views/nodes/trails/PresBox.scss | 2 +- src/client/views/nodes/trails/PresBox.tsx | 20 +++++++++++--------- 3 files changed, 15 insertions(+), 13 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 2d8055ba9..35edbe684 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -249,15 +249,15 @@ export class TabDocView extends React.Component { const presSelected: Doc | undefined = presArray && size ? presArray[size - 1] : undefined; const duration = NumCast(doc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], null); - PresBox.pinDocView(pinDoc, pinProps, doc); - 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); } + PresBox.pinDocView(pinDoc, pinProps, doc); + pinDoc.onClick = ScriptField.MakeFunction('navigateToDoc(self.presentationTargetDoc, self)'); + Doc.AddDocToList(curPres, 'data', pinDoc, presSelected); //save position if (pinProps?.activeFrame !== undefined) { pinDoc.presActiveFrame = pinProps?.activeFrame; diff --git a/src/client/views/nodes/trails/PresBox.scss b/src/client/views/nodes/trails/PresBox.scss index 34df415ac..4d60a02f1 100644 --- a/src/client/views/nodes/trails/PresBox.scss +++ b/src/client/views/nodes/trails/PresBox.scss @@ -352,8 +352,8 @@ .slider-number { border-radius: 3px; - width: 30px; margin: auto; + overflow: hidden; } } diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index de19f831d..3d9c38186 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -1447,9 +1447,9 @@ export class PresBox extends ViewBoxBaseComponent() {
e.stopPropagation()} onChange={action((e: React.ChangeEvent) => { activeItem.presStartTime = Number(e.target.value); @@ -1473,9 +1473,9 @@ export class PresBox extends ViewBoxBaseComponent() { e.stopPropagation()} - style={{ textAlign: 'center', width: 30, height: 15, fontSize: 10 }} + style={{ textAlign: 'center', width: '100%', height: 15, fontSize: 10 }} type="number" - value={NumCast(activeItem.presEndTime)} + value={NumCast(activeItem.presEndTime).toFixed(2)} onChange={action((e: React.ChangeEvent) => { activeItem.presEndTime = Number(e.target.value); })} @@ -1493,13 +1493,14 @@ export class PresBox extends ViewBoxBaseComponent() { style={{ gridColumn: 1, gridRow: 1 }} className={`toolbar-slider ${'end'}`} id="toolbar-slider" - onPointerDown={() => { + onPointerDown={e => { this._batch = UndoManager.StartBatch('presEndTime'); const endBlock = document.getElementById('endTime'); if (endBlock) { endBlock.style.color = Colors.LIGHT_GRAY; endBlock.style.backgroundColor = Colors.MEDIUM_BLUE; } + e.stopPropagation(); }} onPointerUp={() => { this._batch?.end(); @@ -1523,13 +1524,14 @@ export class PresBox extends ViewBoxBaseComponent() { style={{ gridColumn: 1, gridRow: 1 }} className={`toolbar-slider ${'start'}`} id="toolbar-slider" - onPointerDown={() => { + onPointerDown={e => { this._batch = UndoManager.StartBatch('presStartTime'); const startBlock = document.getElementById('startTime'); if (startBlock) { startBlock.style.color = Colors.LIGHT_GRAY; startBlock.style.backgroundColor = Colors.MEDIUM_BLUE; } + e.stopPropagation(); }} onPointerUp={() => { this._batch?.end(); @@ -1545,10 +1547,10 @@ export class PresBox extends ViewBoxBaseComponent() { }} />
-
-
{clipStart} s
+
+
{clipStart.toFixed(2)} s
-
{clipEnd} s
+
{clipEnd.toFixed(2)} s
-- cgit v1.2.3-70-g09d2 From d182a3462a06ea58c4a0c937190aaa66eced0c01 Mon Sep 17 00:00:00 2001 From: bobzel Date: Sat, 24 Sep 2022 01:57:34 -0400 Subject: Fixed: treeView now doesn't get pointer events if it's not active. fixed layout of treeview for pres box. fixed horiz/vert scrolling for trees. fixed not adding Loading docs to recently closed. --- src/client/views/DocComponent.tsx | 3 +- src/client/views/StyleProvider.tsx | 2 +- .../views/collections/CollectionTreeView.scss | 3 - .../views/collections/CollectionTreeView.tsx | 73 ++++++++++++---------- src/client/views/collections/TreeView.scss | 3 +- src/client/views/collections/TreeView.tsx | 1 - src/client/views/nodes/trails/PresBox.tsx | 12 ++-- 7 files changed, 52 insertions(+), 45 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 465bb40f0..043a83d16 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -7,6 +7,7 @@ import { Cast, ScriptCast } from '../../fields/Types'; import { denormalizeEmail, distributeAcls, GetEffectiveAcl, inheritParentAcls, SharingPermissions } from '../../fields/util'; import { returnFalse } from '../../Utils'; import { DocUtils } from '../documents/Documents'; +import { DocumentType } from '../documents/DocumentTypes'; import { InteractionUtils } from '../util/InteractionUtils'; import { UndoManager } from '../util/UndoManager'; import { DocumentView } from './nodes/DocumentView'; @@ -162,7 +163,7 @@ export function ViewBoxAnnotatableComponent

() doc.context = undefined; if (recent) { Doc.RemoveDocFromList(recent, 'data', doc); - Doc.AddDocToList(recent, 'data', doc, undefined, true, true); + doc.type !== DocumentType.LOADING && Doc.AddDocToList(recent, 'data', doc, undefined, true, true); } }); this.isAnyChildContentActive() && this.props.select(false); diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 3a55b7de1..8b256923a 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -271,7 +271,7 @@ export function DefaultStyleProvider(doc: Opt, props: Opt this._mainEle; @@ -111,7 +111,7 @@ export class CollectionTreeView extends CollectionSubView { if (!this._isDisposing) { const titleHeight = !this._titleRef ? this.marginTop() : Number(getComputedStyle(this._titleRef).height.replace('px', '')); - const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), this.marginBot()); + const bodyHeight = Array.from(this.refList).reduce((p, r) => p + Number(getComputedStyle(r).height.replace('px', '')), this.marginBot()) + 6; this.layoutDoc._autoHeightMargins = bodyHeight; this.props.setHeight?.(bodyHeight + titleHeight); } @@ -298,7 +298,11 @@ export class CollectionTreeView extends CollectionSubView (this._titleRef = r)}> +

(this._titleRef = r) && (this._titleHeight = r.getBoundingClientRect().height * this.props.ScreenToLocalTransform().Scale))} + key={this.doc[Id]} + style={!this.outlineMode ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}}> {this.outlineMode ? this.documentTitle : this.editableTitle}
); @@ -378,40 +382,45 @@ export class CollectionTreeView extends CollectionSubView (!this.props.isContentActive() && !SnappingManager.GetIsDragging() ? 'none' : undefined); const titleBar = this.props.treeViewHideTitle || this.doc.treeViewHideTitle ? null : this.titleBar; return [ -
- {titleBar} +
+ {!this.buttonMenu && !this.noviceExplainer ? null : ( +
+ {this.buttonMenu} + {this.noviceExplainer} +
+ )}
- {!this.buttonMenu && !this.noviceExplainer ? null : ( -
r && (this._explainerHeight = r.getBoundingClientRect().height))}> - {this.buttonMenu} - {this.noviceExplainer} -
- )} + ...(!titleBar ? { paddingLeft: this.marginX(), paddingTop: this.marginTop() } : {}), + overflow: 'auto', + width: '100%', + height: '100%', + }}> + {titleBar}
e.stopPropagation()} - onDrop={this.onTreeDrop} - ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}> -
    {this.treeViewElements}
+ onContextMenu={this.onContextMenu}> +
e.stopPropagation()} + onDrop={this.onTreeDrop} + ref={r => !this.doc.treeViewHasOverlay && r && this.createTreeDropTarget(r)}> +
    {this.treeViewElements}
+
, diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss index cfb97709b..57bb5274d 100644 --- a/src/client/views/collections/TreeView.scss +++ b/src/client/views/collections/TreeView.scss @@ -115,10 +115,9 @@ .treeView-header-editing, .treeView-header { + display: flex; // needed for PresBox's treeView border: transparent 1px solid; - display: grid; align-items: center; - grid-auto-columns: 22px auto 22px; width: 100%; border-radius: 5px; diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index b489b5214..c34a6faaa 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -953,7 +953,6 @@ export class TreeView extends React.Component {
() { const isMini: boolean = this.toolbarWidth <= 100; return (
- {isMini || Doc.noviceMode ? null : ( + {isMini ? null : ( )}
@@ -2636,7 +2638,7 @@ export class PresBox extends ViewBoxBaseComponent() { {this.toolbar} {this.newDocumentToolbarDropdown}
-
+
{mode !== CollectionViewType.Invalid ? ( Date: Mon, 26 Sep 2022 10:31:54 -0400 Subject: Update PresBox.tsx fixed pinwithview videos so end time is clip's end time. --- src/client/views/nodes/trails/PresBox.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 3d9c38186..1a4ffa24f 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -421,7 +421,11 @@ export class PresBox extends ViewBoxBaseComponent() { pinDoc.presPinViewScroll = pinDoc._scrollTop; } if (clippable) pinDoc.presPinClipWidth = pinDoc._clipWidth; - if (temporal) pinDoc.presEndTime = NumCast((pinDoc.presStartTime = pinDoc._currentTimecode)) + 0.1; + if (temporal) { + pinDoc.presStartTime = pinDoc._currentTimecode; + const duration = NumCast(pinDoc[`${Doc.LayoutFieldKey(pinDoc)}-duration`], NumCast(pinDoc.presStartTime) + 0.1); + pinDoc.presEndTime = NumCast(pinDoc.clipEnd, duration); + } if (textview) pinDoc.presData = targetDoc.text instanceof ObjectField ? targetDoc.text[Copy]() : targetDoc.text; if (dataview) pinDoc.presData = targetDoc.data instanceof ObjectField ? targetDoc.data[Copy]() : targetDoc.data; if (pannable || scrollable) { -- cgit v1.2.3-70-g09d2 From d14cece23d1dac76d3bc1643e05b5b51071466ca Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 26 Sep 2022 11:14:50 -0400 Subject: added explore mode option to lightbox view. turned of ink and explore mode when leaving light box. --- src/client/views/LightboxView.scss | 16 ++++++++++++++++ src/client/views/LightboxView.tsx | 14 ++++++++++++++ .../collectionFreeForm/CollectionFreeFormView.tsx | 8 +++----- 3 files changed, 33 insertions(+), 5 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/LightboxView.scss b/src/client/views/LightboxView.scss index ae5dc9902..f86a1d211 100644 --- a/src/client/views/LightboxView.scss +++ b/src/client/views/LightboxView.scss @@ -46,6 +46,22 @@ opacity: 1; } } +.lightboxView-exploreBtn { + margin: auto; + position: absolute; + right: 100; + top: 10; + background: transparent; + border-radius: 8; + color: white; + opacity: 0.7; + width: 25; + flex-direction: column; + display: flex; + &:hover { + opacity: 1; + } +} .lightboxView-frame { position: absolute; top: 0; diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index e3e8403df..e8b8a3eaa 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -16,6 +16,7 @@ import { CollectionStackedTimeline } from './collections/CollectionStackedTimeli import { TabDocView } from './collections/TabDocView'; import { GestureOverlay } from './GestureOverlay'; import './LightboxView.scss'; +import { MainView } from './MainView'; import { DocumentView } from './nodes/DocumentView'; import { DefaultStyleProvider, wavyBorderPath } from './StyleProvider'; @@ -53,6 +54,8 @@ export class LightboxView extends React.Component { if (!doc) { this._docFilters && (this._docFilters.length = 0); this._future = this._history = []; + Doc.ActiveTool = InkTool.None; + MainView.Instance._exploreMode = false; } else { if (doc) { const l = DocUtils.MakeLinkToActiveAudio(() => doc).lastElement(); @@ -292,6 +295,7 @@ export class LightboxView extends React.Component { isContentActive={returnTrue} addDocTab={this.addDocTab} pinToPres={TabDocView.PinDoc} + onBrowseClick={MainView.Instance.exploreMode} rootSelected={returnTrue} docViewPath={returnEmptyDoclist} docFilters={this.docFilters} @@ -367,6 +371,16 @@ export class LightboxView extends React.Component { }}>
+
{ + e.stopPropagation(); + MainView.Instance._exploreMode = !MainView.Instance._exploreMode; + })}> + +
); } diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 3cc425745..e9d0826cd 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1101,10 +1101,7 @@ export class CollectionFreeFormView extends CollectionSubView (this._viewTransition = 0)), - (this._viewTransition = transitionTime) - ); // set transition to be smooth, then reset + this._viewTransition = transitionTime; const screenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]); this.layoutDoc[this.scaleFieldKey] = scale; const newScreenXY = this.getTransform().inverse().transformPoint(docpt[0], docpt[1]); @@ -1112,6 +1109,7 @@ export class CollectionFreeFormView extends CollectionSubView(res => setTimeout(() => res(runInAction(() => (this._viewTransition = 0))), this._viewTransition)); // set transition to be smooth, then reset } focusDocument = (doc: Doc, options?: DocFocusOptions) => { @@ -2237,7 +2235,7 @@ export function CollectionBrowseClick(dv: DocumentView, clientX: number, clientY const selfFfview = dv.ComponentView instanceof CollectionFreeFormView ? dv.ComponentView : undefined; const parFfview = dv.props.CollectionFreeFormDocumentView?.().props.CollectionFreeFormView; const ffview = selfFfview && selfFfview.rootDoc[selfFfview.props.scaleField || '_viewScale'] !== 0.5 ? selfFfview : parFfview; // if focus doc is a freeform that is not at it's default 0.5 scale, then zoom out on it. Otherwise, zoom out on the parent ffview - ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5); + await ffview?.zoomSmoothlyAboutPt(ffview.getTransform().transformPoint(clientX, clientY), 0.5); } return ViewAdjustment.doNothing; }, -- cgit v1.2.3-70-g09d2 From 5529fe6aa9d0a894bb6d88460930583fb2a85e46 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 26 Sep 2022 11:20:26 -0400 Subject: merged tree view fixes to enable tree view in presbox and fix various scrolling and contentActive issues. --- src/client/views/collections/CollectionTreeView.scss | 1 - 1 file changed, 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index fe148fbb5..1aef29b2f 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -61,7 +61,6 @@ .editableView-container-editing { display: block; text-overflow: ellipsis; - font-size: 1vw; white-space: nowrap; } } -- cgit v1.2.3-70-g09d2 From d98db79837171d364d7897ac9b7bc6771af2a2a7 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 26 Sep 2022 11:44:20 -0400 Subject: added onClick capability to linearView subMenu buttons so that inkTools could turn pen off when opening/closing. --- src/client/util/CurrentUserUtils.ts | 8 +++++--- .../collectionLinear/CollectionLinearView.tsx | 20 ++++++++++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 8b21a5365..fdac3c00a 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -3,6 +3,7 @@ import { reaction } from "mobx"; import * as rp from 'request-promise'; import { Doc, DocListCast, DocListCastAsync, Opt } from "../../fields/Doc"; import { Id } from "../../fields/FieldSymbols"; +import { InkTool } from "../../fields/InkField"; import { List } from "../../fields/List"; import { PrefetchProxy } from "../../fields/Proxy"; import { RichTextField } from "../../fields/RichTextField"; @@ -664,7 +665,7 @@ export class CurrentUserUtils { { title: "Header", icon: "heading", toolTip: "Header Color", btnType: ButtonType.ColorButton, funcs: {hidden: '!SelectionManager_selectedDocType()'}, ignoreClick: true, scripts: { script: 'return setHeaderColor(value, _readOnly_)'}}, { title: "Overlay", icon: "layer-group", toolTip: "Overlay", btnType: ButtonType.ToggleButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform", true)'}, scripts: { onClick: 'toggleOverlay(_readOnly_)'}}, // Only when floating document is selected in freeform { title: "Text", icon: "Text", toolTip: "Text functions", subMenu: CurrentUserUtils.textTools(), funcs: {hidden: 'false', linearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.RTF}")`} }, // Always available - { title: "Ink", icon: "Ink", toolTip: "Ink functions", subMenu: CurrentUserUtils.inkTools(), funcs: {hidden: 'false', inearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.INK}")`} }, // Always available + { title: "Ink", icon: "Ink", toolTip: "Ink functions", subMenu: CurrentUserUtils.inkTools(), funcs: {hidden: 'false', linearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.INK}")`}, scripts: { onClick: 'setInkToolDefaults()'} }, // Always available { title: "Web", icon: "Web", toolTip: "Web functions", subMenu: CurrentUserUtils.webTools(), funcs: {hidden: `!SelectionManager_selectedDocType("${DocumentType.WEB}")`, linearViewIsExpanded: `SelectionManager_selectedDocType("${DocumentType.WEB}")`, } }, // Only when Web is selected { title: "Schema", icon: "Schema", toolTip: "Schema functions", subMenu: CurrentUserUtils.schemaTools(), funcs: {hidden: `!SelectionManager_selectedDocType(undefined, "${CollectionViewType.Schema}")`, linearViewIsExpanded: `SelectionManager_selectedDocType(undefined, "${CollectionViewType.Schema}")`} } // Only when Schema is selected ]; @@ -698,13 +699,13 @@ export class CurrentUserUtils { return this.setupContextMenuButton(params, menuBtnDoc); } else { const reqdSubMenuOpts = { ...OmitKeys(params, ["scripts", "funcs", "subMenu"]).omit, - childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: true, + childDontRegisterViews: true, flexGap: 0, _height: 30, ignoreClick: params.scripts?.onClick ? false : true, linearViewSubMenu: true, linearViewExpandable: true, }; const items = params.subMenu?.map(sub => this.setupContextMenuButton(sub, DocListCast(menuBtnDoc?.data).find(doc => doc.title === sub.title)) ); return DocUtils.AssignScripts( - DocUtils.AssignDocField(ctxtMenuBtnsDoc, StrCast(params.title), (opts) => this.linearButtonList(opts, items??[]), reqdSubMenuOpts, items), undefined, params.funcs); + DocUtils.AssignDocField(ctxtMenuBtnsDoc, StrCast(params.title), (opts) => this.linearButtonList(opts, items??[]), reqdSubMenuOpts, items), params.scripts, params.funcs); } }); return DocUtils.AssignOpts(ctxtMenuBtnsDoc, reqdCtxtOpts, ctxtMenuBtns); @@ -937,3 +938,4 @@ ScriptingGlobals.add(function openPresentation(pres:Doc) { return MainView.Insta ScriptingGlobals.add(function createNewFolder() { return MainView.Instance.createNewFolder(); }, "creates a new folder in myFiles when called"); ScriptingGlobals.add(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, "returns all the links to the document or its annotations", "(doc: any)"); ScriptingGlobals.add(function importDocument() { return CurrentUserUtils.importDocument(); }, "imports files from device directly into the import sidebar"); +ScriptingGlobals.add(function setInkToolDefaults() { Doc.ActiveTool = InkTool.None; }); \ No newline at end of file diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx index 0d7d67dd8..f6eb2fce4 100644 --- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx +++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx @@ -198,13 +198,7 @@ export class CollectionLinearView extends CollectionSubView() {
{!expandable ? null : ( - -
{BoolCast(this.props.Document.linearViewIsExpanded) ? 'Close' : 'Open'}
- - } - placement="top"> + {BoolCast(this.props.Document.linearViewIsExpanded) ? 'Close' : 'Open'}
} placement="top"> {menuOpener} )} @@ -213,7 +207,17 @@ export class CollectionLinearView extends CollectionSubView() { type="checkbox" checked={BoolCast(this.props.Document.linearViewIsExpanded)} ref={this.addMenuToggle} - onChange={action(() => (this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked))} + onChange={action(e => { + ScriptCast(this.Document.onClick).script.run({ + this: this.layoutDoc, + self: this.rootDoc, + _readOnly_: false, + scriptContext: this.props.scriptContext, + thisContainer: this.props.ContainingCollectionDoc, + documentView: this.props.docViewPath().lastElement(), + }); + this.props.Document.linearViewIsExpanded = this.addMenuToggle.current!.checked; + })} />
Date: Tue, 27 Sep 2022 14:56:28 -0400 Subject: fixed so that following link to video doesn't zoom the video (works the same way as PDFs or web pages -- if the document is 'scrollable', then don't zoom in on it as well). --- src/client/documents/Documents.ts | 2 +- src/client/util/DocumentManager.ts | 4 +- .../views/collections/CollectionNoteTakingView.tsx | 2 +- .../collections/CollectionStackedTimeline.tsx | 6 +- .../views/collections/CollectionStackingView.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 8 +-- src/client/views/nodes/DocumentView.tsx | 83 +++++++++++----------- 8 files changed, 55 insertions(+), 54 deletions(-) (limited to 'src/client/views') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f046af684..fb68fba38 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1307,7 +1307,7 @@ export namespace DocUtils { }); } - export function DefaultFocus(doc: Doc, options?: DocFocusOptions) { + export function DefaultFocus(doc: Doc, options: DocFocusOptions) { options?.afterFocus?.(false); } diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 3a6c43773..50190061a 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -228,7 +228,7 @@ export class DocumentManager { ); return; } else if (!docView && targetDoc.type !== DocumentType.MARKER) { - annoContainerView.focus(targetDoc); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below + annoContainerView.focus(targetDoc, {}); // this allows something like a PDF view to remove its doc filters to expose the target so that it can be found in the retry code below } } if (focusView) { @@ -337,7 +337,7 @@ export function DocFocusOrOpen(doc: Doc, collectionDoc?: Doc) { const showDoc = context || doc; const bestAlias = showDoc === Doc.GetProto(showDoc) ? DocListCast(showDoc.aliases).find(doc => !doc.context && doc.author === Doc.CurrentUserEmail) : showDoc; - CollectionDockingView.AddSplit(bestAlias ? bestAlias : Doc.MakeAlias(showDoc), 'right') && context && setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc)); + CollectionDockingView.AddSplit(bestAlias ? bestAlias : Doc.MakeAlias(showDoc), 'right') && context && setTimeout(() => DocumentManager.Instance.getDocumentView(Doc.GetProto(doc))?.focus(doc, {})); } } ScriptingGlobals.add(DocFocusOrOpen); diff --git a/src/client/views/collections/CollectionNoteTakingView.tsx b/src/client/views/collections/CollectionNoteTakingView.tsx index 92c0bc341..b0f64ed60 100644 --- a/src/client/views/collections/CollectionNoteTakingView.tsx +++ b/src/client/views/collections/CollectionNoteTakingView.tsx @@ -193,7 +193,7 @@ export class CollectionNoteTakingView extends CollectionSubView() { }; // let's dive in and get the actual document we want to drag/move around - focusDocument = (doc: Doc, options?: DocFocusOptions) => { + focusDocument = (doc: Doc, options: DocFocusOptions) => { Doc.BrushDoc(doc); let focusSpeed = 0; const found = this._mainCont && Array.from(this._mainCont.getElementsByClassName('documentView-node')).find((node: any) => node.id === doc[Id]); diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index c694e17fb..7bf798656 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -23,7 +23,7 @@ import { AudioWaveform } from '../AudioWaveform'; import { CollectionSubView } from '../collections/CollectionSubView'; import { Colors } from '../global/globalEnums'; import { LightboxView } from '../LightboxView'; -import { DocAfterFocusFunc, DocFocusFunc, DocumentView, DocumentViewProps, DocumentViewSharedProps } from '../nodes/DocumentView'; +import { DocFocusFunc, DocFocusOptions, DocumentView, DocumentViewProps, DocumentViewSharedProps } from '../nodes/DocumentView'; import { LabelBox } from '../nodes/LabelBox'; import './CollectionStackedTimeline.scss'; import { VideoBox } from '../nodes/VideoBox'; @@ -828,9 +828,9 @@ class StackedTimelineAnchor extends React.Component // renders anchor LabelBox renderInner = computedFn(function (this: StackedTimelineAnchor, mark: Doc, script: undefined | (() => ScriptField), doublescript: undefined | (() => ScriptField), screenXf: () => Transform, width: () => number, height: () => number) { const anchor = observable({ view: undefined as any }); - const focusFunc = (doc: Doc, willZoom?: boolean, scale?: number, afterFocus?: DocAfterFocusFunc, docTransform?: Transform) => { + const focusFunc = (doc: Doc, options: DocFocusOptions) => { this.props.playLink(mark); - this.props.focus(doc, { willZoom, scale, afterFocus, docTransform }); + this.props.focus(doc, options); }; return { anchor, diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index cc006c734..ba29b1d6f 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -251,7 +251,7 @@ export class CollectionStackingView extends CollectionSubView { + focusDocument = (doc: Doc, options: DocFocusOptions) => { Doc.BrushDoc(doc); let focusSpeed = 0; diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 35edbe684..b1f57f3fd 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -367,7 +367,7 @@ export class TabDocView extends React.Component { return NumCast(Cast(PresBox.Instance.childDocs[PresBox.Instance.itemIndex].presentationTargetDoc, Doc, null)._currentFrame); }; @action - focusFunc = (doc: Doc, options?: DocFocusOptions) => { + focusFunc = (doc: Doc, options: DocFocusOptions) => { const shrinkwrap = options?.originalTarget === this._document && this.view?.ComponentView?.shrinkWrap; if (options?.willZoom !== false && shrinkwrap && this._document) { const focusSpeed = NumCast(this._document.focusSpeed, 500); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index e9d0826cd..7b6944e1e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1112,7 +1112,7 @@ export class CollectionFreeFormView extends CollectionSubView(res => setTimeout(() => res(runInAction(() => (this._viewTransition = 0))), this._viewTransition)); // set transition to be smooth, then reset } - focusDocument = (doc: Doc, options?: DocFocusOptions) => { + focusDocument = (doc: Doc, options: DocFocusOptions) => { const state = HistoryUtil.getState(); // TODO This technically isn't correct if type !== "doc", as @@ -1131,13 +1131,13 @@ export class CollectionFreeFormView extends CollectionSubView new Promise(res => setTimeout(async () => res(await endFocus(didMove || didFocus)), Math.max(0, focusSpeed - (Date.now() - startTime)))), diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f18fd8024..ba061ce8f 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -80,7 +80,7 @@ export interface DocFocusOptions { instant?: boolean; // whether focus should happen instantly (as opposed to smooth zoom) } export type DocAfterFocusFunc = (notFocused: boolean) => Promise; -export type DocFocusFunc = (doc: Doc, options?: DocFocusOptions) => void; +export type DocFocusFunc = (doc: Doc, options: DocFocusOptions) => void; export type StyleProviderFunc = (doc: Opt, props: Opt, property: string) => any; export interface DocComponentView { updateIcon?: () => void; // updates the icon representation of the document @@ -534,7 +534,7 @@ export class DocumentViewInternal extends DocComponent { + focus = (anchor: Doc, options: DocFocusOptions) => { LightboxView.SetCookie(StrCast(anchor['cookies-set'])); // copying over VIEW fields immediately allows the view type to switch to create the right _componentView Array.from(Object.keys(Doc.GetProto(anchor))) @@ -1259,46 +1259,47 @@ export class DocumentViewInternal extends DocComponent users.user.email === this.dataDoc.author)?.sharingDoc.userColor, Doc.UserDoc().showTitle && [DocumentType.RTF, DocumentType.COL].includes(this.rootDoc.type as any) ? StrCast(Doc.SharingDoc().userColor) : 'rgba(0,0,0,0.4)' ); - const titleView = !showTitle || Doc.noviceMode ? null : ( -
- field.trim()) - .map(field => targetDoc[field]?.toString()) - .join('\\')} - display={'block'} - fontSize={10} - GetValue={() => (showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle)} - SetValue={undoBatch((input: string) => { - if (input?.startsWith('#')) { - if (this.props.showTitle) { - this.rootDoc._showTitle = input?.substring(1) ? input.substring(1) : undefined; + const titleView = + !showTitle || Doc.noviceMode ? null : ( +
+ field.trim()) + .map(field => targetDoc[field]?.toString()) + .join('\\')} + display={'block'} + fontSize={10} + GetValue={() => (showTitle.split(';').length === 1 ? showTitle + '=' + Field.toString(targetDoc[showTitle.split(';')[0]] as any as Field) : '#' + showTitle)} + SetValue={undoBatch((input: string) => { + if (input?.startsWith('#')) { + if (this.props.showTitle) { + this.rootDoc._showTitle = input?.substring(1) ? input.substring(1) : undefined; + } else { + Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : 'creationDate'; + } } else { - Doc.UserDoc().showTitle = input?.substring(1) ? input.substring(1) : 'creationDate'; + var value = input.replace(new RegExp(showTitle + '='), '') as string | number; + if (showTitle !== 'title' && Number(value).toString() === value) value = Number(value); + if (showTitle.includes('Date') || showTitle === 'author') return true; + Doc.SetInPlace(targetDoc, showTitle, value, true); } - } else { - var value = input.replace(new RegExp(showTitle + '='), '') as string | number; - if (showTitle !== 'title' && Number(value).toString() === value) value = Number(value); - if (showTitle.includes('Date') || showTitle === 'author') return true; - Doc.SetInPlace(targetDoc, showTitle, value, true); - } - return true; - })} - /> -
- ); + return true; + })} + /> +
+ ); return this.props.hideTitle || (!showTitle && !showCaption) ? ( this.contents ) : ( @@ -1557,7 +1558,7 @@ export class DocumentView extends React.Component { } toggleNativeDimensions = () => this.docView && Doc.toggleNativeDimensions(this.layoutDoc, this.docView.NativeDimScaling, this.props.PanelWidth(), this.props.PanelHeight()); - focus = (doc: Doc, options?: DocFocusOptions) => this.docView?.focus(doc, options); + focus = (doc: Doc, options: DocFocusOptions) => this.docView?.focus(doc, options); getBounds = () => { if (!this.docView || !this.docView.ContentDiv || this.props.Document.presBox || this.docView.props.treeViewDoc || Doc.AreProtosEqual(this.props.Document, Doc.UserDoc())) { return undefined; -- cgit v1.2.3-70-g09d2 From 31ac3ec644547bd4a3450820f9a69ebcfd17661e Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 3 Oct 2022 13:19:14 -0400 Subject: fixed client crash on text menu for ink strokes. fixed circle rubber banding. fixed vertical position of text in ink stroke --- src/client/util/InteractionUtils.tsx | 53 ++++++---------------- src/client/views/InkingStroke.tsx | 15 +++--- .../collectionLinear/CollectionLinearView.tsx | 2 +- 3 files changed, 21 insertions(+), 49 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 4af51b9a0..85700da37 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -230,58 +230,33 @@ export namespace InteractionUtils { points.push({ X: right, Y: bottom }); points.push({ X: left, Y: bottom }); points.push({ X: left, Y: top }); + break; case 'triangle': - // points.push({ X: left, Y: bottom }); - // points.push({ X: right, Y: bottom }); - // points.push({ X: (right + left) / 2, Y: top }); - // points.push({ X: left, Y: bottom }); - - points.push({ X: left, Y: bottom }); points.push({ X: left, Y: bottom }); - - points.push({ X: right, Y: bottom }); - points.push({ X: right, Y: bottom }); points.push({ X: right, Y: bottom }); - points.push({ X: right, Y: bottom }); - - points.push({ X: (right + left) / 2, Y: top }); - points.push({ X: (right + left) / 2, Y: top }); points.push({ X: (right + left) / 2, Y: top }); - points.push({ X: (right + left) / 2, Y: top }); - - points.push({ X: left, Y: bottom }); points.push({ X: left, Y: bottom }); + break; case 'circle': const centerX = (Math.max(left, right) + Math.min(left, right)) / 2; const centerY = (Math.max(top, bottom) + Math.min(top, bottom)) / 2; const radius = Math.max(centerX - Math.min(left, right), centerY - Math.min(top, bottom)); - if (centerX - Math.min(left, right) < centerY - Math.min(top, bottom)) { - for (var y = Math.min(top, bottom); y < Math.max(top, bottom); y++) { - const x = Math.sqrt(Math.pow(radius, 2) - Math.pow(y - centerY, 2)) + centerX; - points.push({ X: x, Y: y }); - } - for (var y = Math.max(top, bottom); y > Math.min(top, bottom); y--) { - const x = Math.sqrt(Math.pow(radius, 2) - Math.pow(y - centerY, 2)) + centerX; - const newX = centerX - (x - centerX); - points.push({ X: newX, Y: y }); - } - points.push({ X: Math.sqrt(Math.pow(radius, 2) - Math.pow(Math.min(top, bottom) - centerY, 2)) + centerX, Y: Math.min(top, bottom) }); - } else { - for (var x = Math.min(left, right); x < Math.max(left, right); x++) { - const y = Math.sqrt(Math.pow(radius, 2) - Math.pow(x - centerX, 2)) + centerY; - points.push({ X: x, Y: y }); - } - for (var x = Math.max(left, right); x > Math.min(left, right); x--) { - const y = Math.sqrt(Math.pow(radius, 2) - Math.pow(x - centerX, 2)) + centerY; - const newY = centerY - (y - centerY); - points.push({ X: x, Y: newY }); - } - points.push({ X: Math.min(left, right), Y: Math.sqrt(Math.pow(radius, 2) - Math.pow(Math.min(left, right) - centerX, 2)) + centerY }); + for (var x = Math.min(left, right); x < Math.max(left, right); x++) { + const y = Math.sqrt(Math.pow(radius, 2) - Math.pow(x - centerX, 2)) + centerY; + points.push({ X: x, Y: y }); } + for (var x = Math.max(left, right); x > Math.min(left, right); x--) { + const y = Math.sqrt(Math.pow(radius, 2) - Math.pow(x - centerX, 2)) + centerY; + const newY = centerY - (y - centerY); + points.push({ X: x, Y: newY }); + } + points.push({ X: Math.min(left, right), Y: Math.sqrt(Math.pow(radius, 2) - Math.pow(Math.min(left, right) - centerX, 2)) + centerY }); + break; + case 'line': points.push({ X: left, Y: top }); points.push({ X: right, Y: bottom }); - return points; + break; } return points; } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index dae1c10bb..b83ba97e7 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -24,8 +24,8 @@ import React = require('react'); import { action, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, HeightSym, WidthSym } from '../../fields/Doc'; -import { InkData, InkField, InkTool } from '../../fields/InkField'; -import { BoolCast, Cast, FieldValue, NumCast, RTFCast, StrCast } from '../../fields/Types'; +import { InkData, InkField } from '../../fields/InkField'; +import { BoolCast, Cast, NumCast, RTFCast, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; import { OmitKeys, returnFalse, setupMoveUpEvents } from '../../Utils'; import { CognitiveServices } from '../cognitive_services/CognitiveServices'; @@ -35,20 +35,17 @@ import { Transform } from '../util/Transform'; import { UndoManager } from '../util/UndoManager'; import { ContextMenu } from './ContextMenu'; import { ViewBoxBaseComponent } from './DocComponent'; +import { INK_MASK_SIZE } from './global/globalCssVariables.scss'; import { Colors } from './global/globalEnums'; import { InkControlPtHandles, InkEndPtHandles } from './InkControlPtHandles'; +import './InkStroke.scss'; import { InkStrokeProperties } from './InkStrokeProperties'; import { InkTangentHandles } from './InkTangentHandles'; import { DocComponentView } from './nodes/DocumentView'; import { FieldView, FieldViewProps } from './nodes/FieldView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; -import { INK_MASK_SIZE } from './global/globalCssVariables.scss'; -import './InkStroke.scss'; -import Color = require('color'); -import { ComputedField } from '../../fields/ScriptField'; -import { listSpec } from '../../fields/Schema'; -import { List } from '../../fields/List'; import { StyleProp } from './StyleProvider'; +import Color = require('color'); @observer export class InkingStroke extends ViewBoxBaseComponent() { @@ -460,7 +457,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { width: this.layoutDoc[WidthSym](), transform: `scale(${this.props.NativeDimScaling?.() || 1})`, transformOrigin: 'top left', - top: (this.props.PanelHeight() - (lineHeightGuess * fsize + 20) * (this.props.NativeDimScaling?.() || 1)) / 2, + //top: (this.props.PanelHeight() - (lineHeightGuess * fsize + 20) * (this.props.NativeDimScaling?.() || 1)) / 2, }}> { - ScriptCast(this.Document.onClick).script.run({ + ScriptCast(this.Document.onClick)?.script.run({ this: this.layoutDoc, self: this.rootDoc, _readOnly_: false, -- cgit v1.2.3-70-g09d2 From aaef5f4693b5eb6aae429f5c8caaf82838e9c65e Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 3 Oct 2022 15:12:14 -0400 Subject: added a reset menu item for docking views in their context menu. fixed being able to edit dockingConfig in keyValue by quoting with ' instead of ". If there's no dockingConfig, make one. --- src/client/util/CurrentUserUtils.ts | 8 +- src/client/views/DashboardView.tsx | 85 ++++++++++++++++++++++ .../views/collections/CollectionDockingView.tsx | 56 +++++++------- src/fields/Doc.ts | 5 +- 4 files changed, 120 insertions(+), 34 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index fdac3c00a..ad14ab141 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -475,15 +475,15 @@ export class CurrentUserUtils { _showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true, contextMenuLabels: new List(["Create New Dashboard"]), contextMenuIcons: new List(["plus"]), - childContextMenuLabels: new List(["Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard"]),// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters - childContextMenuIcons: new List(["chalkboard", "tv", "camera", "users", "times"]), // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters + childContextMenuLabels: new List(["Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard", "Reset Dashboard"]),// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters + childContextMenuIcons: new List(["chalkboard", "tv", "camera", "users", "times", "trash"]), // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files." }; myDashboards = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts); const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`; const contextMenuScripts = [newDashboard]; - const childContextMenuScripts = [toggleDarkTheme, `toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(self)`, 'removeDashboard(self)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters - const childContextMenuFilters = ['!IsNoviceMode()', '!IsNoviceMode()', '!IsNoviceMode()', undefined as any, undefined as any];// entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuScripts + const childContextMenuScripts = [toggleDarkTheme, `toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(self)`, 'removeDashboard(self)', 'resetDashboard(self)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters + const childContextMenuFilters = ['!IsNoviceMode()', '!IsNoviceMode()', '!IsNoviceMode()', undefined as any, undefined as any, undefined as any];// entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuScripts if (Cast(myDashboards.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { myDashboards.contextMenuScripts = new List(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); } diff --git a/src/client/views/DashboardView.tsx b/src/client/views/DashboardView.tsx index 11bb0c6a8..192e55431 100644 --- a/src/client/views/DashboardView.tsx +++ b/src/client/views/DashboardView.tsx @@ -294,6 +294,88 @@ export class DashboardView extends React.Component { } }; + public static resetDashboard = (dashboard: Doc) => { + const config = StrCast(dashboard.dockingConfig); + const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g); + const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) ?? []; + + const components = + docids.map(docid => ({ + type: 'component', + component: 'DocumentFrameRenderer', + title: 'Untitled Tab 1', + width: 600, + props: { + documentId: docid, + }, + componentName: 'lm-react-component', + isClosable: true, + reorderEnabled: true, + componentState: null, + })) ?? []; + const reset = { + isClosable: true, + reorderEnabled: true, + title: '', + openPopouts: [], + maximisedItemId: null, + settings: { + hasHeaders: true, + constrainDragToContainer: true, + reorderEnabled: true, + selectionEnabled: false, + popoutWholeStack: false, + blockedPopoutsThrowError: true, + closePopoutsOnUnload: true, + showPopoutIcon: true, + showMaximiseIcon: true, + showCloseIcon: true, + responsiveMode: 'onload', + tabOverlapAllowance: 0, + reorderOnTabMenuClick: false, + tabControlOffset: 10, + }, + dimensions: { + borderWidth: 3, + borderGrabWidth: 5, + minItemHeight: 10, + minItemWidth: 20, + headerHeight: 27, + dragProxyWidth: 300, + dragProxyHeight: 200, + }, + labels: { + close: 'close', + maximise: 'maximise', + minimise: 'minimise', + popout: 'new tab', + popin: 'pop in', + tabDropdown: 'additional tabs', + }, + content: [ + { + type: 'row', + isClosable: true, + reorderEnabled: true, + title: '', + content: [ + { + type: 'stack', + width: 100, + isClosable: true, + reorderEnabled: true, + title: '', + activeItemIndex: 0, + content: components, + }, + ], + }, + ], + }; + Doc.SetInPlace(dashboard, 'dockingConfig', JSON.stringify(reset), true); + return reset; + }; + public static createNewDashboard = (id?: string, name?: string, background?: string) => { const dashboards = Doc.MyDashboards; const dashboardCount = DocListCast(dashboards.data).length + 1; @@ -393,6 +475,9 @@ ScriptingGlobals.add(function shareDashboard(dashboard: Doc) { ScriptingGlobals.add(function removeDashboard(dashboard: Doc) { DashboardView.removeDashboard(dashboard); }, 'Remove Dashboard from Dashboards'); +ScriptingGlobals.add(function resetDashboard(dashboard: Doc) { + DashboardView.resetDashboard(dashboard); +}, 'move all dashboard tabs to single stack'); ScriptingGlobals.add(function addToDashboards(dashboard: Doc) { DashboardView.openDashboard(Doc.MakeAlias(dashboard)); }, 'adds Dashboard to set of Dashboards'); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index c6ac5ee0c..40a5876e0 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -281,38 +281,36 @@ export class CollectionDockingView extends CollectionSubView() { this.stateChanged(); return true; } - setupGoldenLayout = async () => { - 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('"', '')) ?? []; - await Promise.all(docids.map(id => DocServer.GetRefField(id))); - - if (this._goldenLayout) { - if (config === JSON.stringify(this._goldenLayout.toConfig())) { - return; - } else { - try { - this._goldenLayout.unbind('tabCreated', this.tabCreated); - this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); - this._goldenLayout.unbind('stackCreated', this.stackCreated); - } catch (e) {} - } - this.tabMap.clear(); - this._goldenLayout.destroy(); + const config = StrCast(this.props.Document.dockingConfig, JSON.stringify(DashboardView.resetDashboard(this.props.Document))); + const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g); + const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) ?? []; + + await Promise.all(docids.map(id => DocServer.GetRefField(id))); + + if (this._goldenLayout) { + if (config === JSON.stringify(this._goldenLayout.toConfig())) { + return; + } else { + try { + this._goldenLayout.unbind('tabCreated', this.tabCreated); + this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); + this._goldenLayout.unbind('stackCreated', this.stackCreated); + } catch (e) {} } - const glay = (this._goldenLayout = new GoldenLayout(JSON.parse(config))); - glay.on('tabCreated', this.tabCreated); - glay.on('tabDestroyed', this.tabDestroyed); - glay.on('stackCreated', this.stackCreated); - glay.registerComponent('DocumentFrameRenderer', TabDocView); - glay.container = this._containerRef.current; - glay.init(); - glay.root.layoutManager.on('itemDropped', this.tabItemDropped); - glay.root.layoutManager.on('dragStart', this.tabDragStart); - glay.root.layoutManager.on('activeContentItemChanged', this.stateChanged); + this.tabMap.clear(); + this._goldenLayout.destroy(); } + const glay = (this._goldenLayout = new GoldenLayout(JSON.parse(config))); + glay.on('tabCreated', this.tabCreated); + glay.on('tabDestroyed', this.tabDestroyed); + glay.on('stackCreated', this.stackCreated); + glay.registerComponent('DocumentFrameRenderer', TabDocView); + glay.container = this._containerRef.current; + glay.init(); + glay.root.layoutManager.on('itemDropped', this.tabItemDropped); + glay.root.layoutManager.on('dragStart', this.tabDragStart); + glay.root.layoutManager.on('activeContentItemChanged', this.stateChanged); }; componentDidMount: () => void = () => { diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index a3c742f28..ea3c122a4 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -33,7 +33,10 @@ export namespace Field { return !Field.IsField(field) ? '' : (onDelegate ? '=' : '') + (field instanceof ComputedField ? `:=${field.script.originalScript}` : Field.toScriptString(field)); } export function toScriptString(field: Field): string { - if (typeof field === 'string') return `"${field}"`; + if (typeof field === 'string') { + if (field.startsWith('{"')) return `'${field}'`; // bcz: hack ... want to quote the string the right way. if there are nested "'s, then use ' instead of ". In this case, test for the start of a JSON string of the format {"property": ... } and use outer 's instead of "s + return `"${field}"`; + } if (typeof field === 'number' || typeof field === 'boolean') return String(field); if (field === undefined || field === null) return 'null'; return field[ToScriptString](); -- cgit v1.2.3-70-g09d2 From 8e8cca45755426e2921e2de78556ad60d79a98a8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 4 Oct 2022 11:54:55 -0400 Subject: fix to show close and open icons of hideResizers documents. fixes for native size control of text and freeform collections. fixes for lightbox view of native size collections. fix for 'false' script. --- src/client/documents/Documents.ts | 1 + src/client/views/DocumentDecorations.tsx | 24 ++++++++++------------ .../collectionFreeForm/CollectionFreeFormView.tsx | 5 ++++- src/fields/Doc.ts | 6 ++++++ src/fields/ScriptField.ts | 2 +- 5 files changed, 23 insertions(+), 15 deletions(-) (limited to 'src/client/views') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index fb68fba38..cd647f5e8 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -396,6 +396,7 @@ export namespace Docs { _yMargin: 10, nativeDimModifiable: true, treeViewGrowsHorizontally: true, + nativeHeightUnfrozen: true, forceReflow: true, links: '@links(self)', }, diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 4675571c2..d5db45494 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -1,8 +1,10 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; +import { IconButton } from 'browndash-components'; import { action, computed, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; +import { FaUndo } from 'react-icons/fa'; import { DateField } from '../../fields/DateField'; import { AclAdmin, AclEdit, DataSym, Doc, DocListCast, Field, HeightSym, WidthSym } from '../../fields/Doc'; import { Document } from '../../fields/documentSchemas'; @@ -28,10 +30,8 @@ import { LightboxView } from './LightboxView'; import { DocumentView } from './nodes/DocumentView'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; import { ImageBox } from './nodes/ImageBox'; -import React = require('react'); -import { IconButton } from 'browndash-components'; -import { FaUndo } from 'react-icons/fa'; import { VideoBox } from './nodes/VideoBox'; +import React = require('react'); @observer export class DocumentDecorations extends React.Component<{ PanelWidth: number; PanelHeight: number; boundsLeft: number; boundsTop: number }, { value: string }> { @@ -503,7 +503,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } let actualdW = Math.max(width + dW * scale, 20); let actualdH = Math.max(height + dH * scale, 20); - const fixedAspect = nwidth && nheight && (!doc._fitWidth || e.ctrlKey || doc.nativeHeightUnfrozen); + const fixedAspect = nwidth && nheight && (!doc._fitWidth || e.ctrlKey || doc.nativeHeightUnfrozen || doc.nativeDimModifiable); if (fixedAspect) { if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop) || !modifyNativeDim)) || dragRight) { if (dragRight && modifyNativeDim) { @@ -534,7 +534,7 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P } else doc._height = actualdH; } } else { - const maxHeight = 0; //Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling(); + const maxHeight = doc.nativHeightUnfrozen || !nheight ? 0 : Math.max(nheight, NumCast(doc.scrollHeight, NumCast(doc[docView.LayoutFieldKey + '-scrollHeight']))) * docView.NativeDimScaling(); dH && (doc._height = actualdH > maxHeight && maxHeight ? maxHeight : actualdH); dW && (doc._width = actualdW); dH && (doc._autoHeight = false); @@ -722,14 +722,12 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P width: bounds.r - bounds.x + this._resizeBorderWidth + 'px', height: bounds.b - bounds.y + this._resizeBorderWidth + this._titleHeight + 'px', }}> - {hideResizers ? null : ( -
- {hideDeleteButton ?
: topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')} - {hideDeleteButton ?
: topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')} - {titleArea} - {hideOpenButton ? null : topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Tab (ctrl: as alias, shift: in new collection)')} -
- )} +
+ {hideDeleteButton ?
: topBtn('close', 'times', undefined, e => this.onCloseClick(true), 'Close')} + {hideResizers || hideDeleteButton ?
: topBtn('minimize', 'window-maximize', undefined, e => this.onCloseClick(undefined), 'Minimize')} + {hideResizers ?
: titleArea} + {hideOpenButton ?
: topBtn('open', 'external-link-alt', this.onMaximizeDown, undefined, 'Open in Lightbox (ctrl: as alias, shift: in new collection)')} +
{hideResizers ? null : ( <>
e.preventDefault()} /> diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 7b6944e1e..93bf143df 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -156,7 +156,9 @@ export class CollectionFreeFormView extends CollectionSubView this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); elementFunc = () => this._layoutElements; shrinkWrap = () => { + if (this.props.DocumentView?.().nativeWidth) return; const vals = this.fitToContentVals; this.layoutDoc._panX = vals.bounds.cx; this.layoutDoc._panY = vals.bounds.cy; diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index ea3c122a4..0ceaff968 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -1402,11 +1402,17 @@ export namespace Doc { layoutDoc._viewScale = NumCast(layoutDoc._viewScale, 1) * contentScale; layoutDoc._nativeWidth = undefined; layoutDoc._nativeHeight = undefined; + layoutDoc._forceReflow = undefined; + layoutDoc._nativeHeightUnfrozen = undefined; + layoutDoc._nativeDimModifiable = undefined; } else { layoutDoc._autoHeight = false; if (!Doc.NativeWidth(layoutDoc)) { layoutDoc._nativeWidth = NumCast(layoutDoc._width, panelWidth); layoutDoc._nativeHeight = NumCast(layoutDoc._height, panelHeight); + layoutDoc._forceReflow = true; + layoutDoc._nativeHeightUnfrozen = true; + layoutDoc._nativeDimModifiable = true; } } }); diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 3cb50a4c0..4896c027d 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -104,7 +104,7 @@ export class ScriptField extends ObjectField { } this.rawscript = rawscript; this.setterscript = setterscript; - this.script = script ?? (CompileScript('false') as CompiledScript); + this.script = script ?? (CompileScript('false', { addReturn: true }) as CompiledScript); } // init(callback: (res: Field) => any) { -- cgit v1.2.3-70-g09d2 From 6e22c212e288eb3e69740a78732f81485c97a577 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 4 Oct 2022 16:53:01 -0400 Subject: removed gitlike options from collection context menu. removed createDashboard from context menus since it has a UI button --- src/client/util/CurrentUserUtils.ts | 21 ++++++++++------- src/client/views/collections/CollectionView.tsx | 7 ++---- src/client/views/collections/TabDocView.tsx | 30 ++++++++++++++++++------- src/client/views/collections/TreeView.tsx | 6 ++--- 4 files changed, 40 insertions(+), 24 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index ad14ab141..aa230aa44 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -462,28 +462,33 @@ export class CurrentUserUtils { static setupDashboards(doc: Doc, field:string) { var myDashboards = DocCast(doc[field]); + const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`; const newDashboard = `createNewDashboard()`; + const reqdBtnOpts:DocumentOptions = { _forceActive: true, _width: 30, _height: 30, _stayInCollection: true, _hideContextMenu: true, title: "new dashboard", btnType: ButtonType.ClickButton, toolTip: "Create new dashboard", buttonText: "New trail", icon: "plus", system: true }; const reqdBtnScript = {onClick: newDashboard,} const newDashboardButton = DocUtils.AssignScripts(DocUtils.AssignOpts(DocCast(myDashboards?.buttonMenuDoc), reqdBtnOpts) ?? Docs.Create.FontIconDocument(reqdBtnOpts), reqdBtnScript); + const contextMenuScripts = [/*newDashboard*/] as string[]; + const contextMenuLabels = [/*"Create New Dashboard"*/] as string[]; + const contextMenuIcons = [/*"plus"*/] as string[]; + const childContextMenuScripts = [toggleDarkTheme, `toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(self)`, 'removeDashboard(self)', 'resetDashboard(self)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters + const childContextMenuFilters = ['!IsNoviceMode()', '!IsNoviceMode()', '!IsNoviceMode()', undefined as any, undefined as any, undefined as any];// entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuScripts + const childContextMenuLabels = ["Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard", "Reset Dashboard"];// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters + const childContextMenuIcons = ["chalkboard", "tv", "camera", "users", "times", "trash"]; // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters const reqdOpts:DocumentOptions = { title: "My Dashboards", childHideLinkButton: true, freezeChildren: "remove|add", treeViewHideTitle: true, boxShadow: "0 0", childDontRegisterViews: true, targetDropAction: "same", treeViewType: TreeViewType.fileSystem, isFolder: true, system: true, treeViewTruncateTitleWidth: 150, ignoreClick: true, buttonMenu: true, buttonMenuDoc: newDashboardButton, childDropAction: "alias", _showTitle: "title", _height: 400, _gridGap: 5, _forceActive: true, _lockedPosition: true, - contextMenuLabels: new List(["Create New Dashboard"]), - contextMenuIcons: new List(["plus"]), - childContextMenuLabels: new List(["Toggle Dark Theme", "Toggle Comic Mode", "Snapshot Dashboard", "Share Dashboard", "Remove Dashboard", "Reset Dashboard"]),// entries must be kept in synch with childContextMenuScripts, childContextMenuIcons, and childContextMenuFilters - childContextMenuIcons: new List(["chalkboard", "tv", "camera", "users", "times", "trash"]), // entries must be kept in synch with childContextMenuScripts, childContextMenuLabels, and childContextMenuFilters + contextMenuLabels:new List(contextMenuLabels), + contextMenuIcons:new List(contextMenuIcons), + childContextMenuLabels:new List(childContextMenuLabels), + childContextMenuIcons:new List(childContextMenuIcons), explainer: "This is your collection of dashboards. A dashboard represents the tab configuration of your workspace. To manage documents as folders, go to the Files." }; myDashboards = DocUtils.AssignDocField(doc, field, (opts) => Docs.Create.TreeDocument([], opts), reqdOpts); - const toggleDarkTheme = `this.colorScheme = this.colorScheme ? undefined : "${ColorScheme.Dark}"`; - const contextMenuScripts = [newDashboard]; - const childContextMenuScripts = [toggleDarkTheme, `toggleComicMode()`, `snapshotDashboard()`, `shareDashboard(self)`, 'removeDashboard(self)', 'resetDashboard(self)']; // entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuFilters - const childContextMenuFilters = ['!IsNoviceMode()', '!IsNoviceMode()', '!IsNoviceMode()', undefined as any, undefined as any, undefined as any];// entries must be kept in synch with childContextMenuLabels, childContextMenuIcons, and childContextMenuScripts if (Cast(myDashboards.contextMenuScripts, listSpec(ScriptField), null)?.length !== contextMenuScripts.length) { myDashboards.contextMenuScripts = new List(contextMenuScripts.map(script => ScriptField.MakeFunction(script)!)); } diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index ee3f46818..d88d59205 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -155,7 +155,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent func(CollectionViewType.Map), icon: 'globe-americas' }); subItems.push({ description: 'Grid', event: () => func(CollectionViewType.Grid), icon: 'th-list' }); - if (!Doc.IsSystem(this.rootDoc) && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) { + if (!Doc.IsSystem(this.rootDoc) && this.rootDoc._viewType !== CollectionViewType.Docking && !this.rootDoc.isGroup && !this.rootDoc.annotationOn) { const existingVm = ContextMenu.Instance.findByDescription(category); const catItems = existingVm && 'subitems' in existingVm ? existingVm.subitems : []; catItems.push({ description: 'Add a Perspective...', addDivider: true, noexpand: true, subitems: subItems, icon: 'eye' }); @@ -189,7 +189,7 @@ export class CollectionView extends ViewBoxAnnotatableComponent (this.rootDoc.isInPlaceContainer = !this.rootDoc.isInPlaceContainer), icon: 'project-diagram' }); - if (!Doc.noviceMode) { + if (!Doc.noviceMode && false) { optionItems.push({ description: 'Create Branch', event: async () => this.props.addDocTab(await BranchCreate(this.rootDoc), 'add:right'), @@ -206,9 +206,6 @@ export class CollectionView extends ViewBoxAnnotatableComponent DashboardView.createNewDashboard(), icon: 'project-diagram' }); - } !options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'hand-point-right' }); diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index b1f57f3fd..9938245ea 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -96,6 +96,18 @@ export class TabDocView extends React.Component { const iconWrap = document.createElement('div'); const closeWrap = document.createElement('div'); + const getChild = () => { + 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; + } + return child; + }; + titleEle.size = StrCast(doc.title).length + 3; titleEle.value = doc.title; titleEle.onkeydown = (e: KeyboardEvent) => { @@ -119,14 +131,7 @@ export class TabDocView extends React.Component { 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; - } + const child = getChild(); simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); } else { this._activated = true; @@ -165,6 +170,15 @@ export class TabDocView extends React.Component { } }; + tab.element[0].oncontextmenu = (e: MouseEvent) => { + let child = getChild(); + if (child) { + simulateMouseClick(child, e.clientX, e.clientY + 30, e.screenX, e.screenY + 30); + e.stopPropagation(); + e.preventDefault(); + } + }; + // 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') { diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index c34a6faaa..25f57b045 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -782,7 +782,7 @@ export class TreeView extends React.Component { onChildDoubleClick = () => ScriptCast(this.props.treeView.Document.treeViewChildDoubleClick, !this.props.treeView.outlineMode ? this._openScript?.() : null); - refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document); + refocus = () => this.props.treeView.props.focus(this.props.treeView.props.Document, {}); ignoreEvent = (e: any) => { if (this.props.isContentActive(true)) { e.stopPropagation(); @@ -1037,7 +1037,7 @@ export class TreeView extends React.Component { @computed get renderBorder() { const sorting = StrCast(this.doc.treeViewSortCriterion, TreeSort.None); - const sortings = this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) as { [key: string]: { color: string; label: string } }; + const sortings = (this.props.styleProvider?.(this.doc, this.props.treeView.props, StyleProp.TreeViewSortings) ?? {}) as { [key: string]: { color: string; label: string } }; return (
{!this.treeViewOpen ? null : this.renderContent} @@ -1057,7 +1057,7 @@ export class TreeView extends React.Component { render() { TraceMobx(); const hideTitle = this.doc.treeViewHideHeader || (this.doc.treeViewHideHeaderIfTemplate && this.props.treeView.props.childLayoutTemplate?.()) || this.props.treeView.outlineMode; - return this.props.renderedIds.indexOf(this.doc[Id]) !== -1 ? ( + return this.props.renderedIds?.indexOf(this.doc[Id]) !== -1 ? ( '<' + this.doc.title + '>' // just print the title of documents we've previously rendered in this hierarchical path to avoid cycles ) : (
Date: Tue, 4 Oct 2022 17:01:13 -0400 Subject: from last --- src/client/views/nodes/DocumentView.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index ba061ce8f..145d8bf3d 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -881,9 +881,9 @@ export class DocumentViewInternal extends DocComponent SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, false)), icon: 'expand-arrows-alt' }); zorderItems.push({ description: 'Send to Back', event: () => SelectionManager.Views().forEach(dv => dv.props.bringToFront(dv.rootDoc, true)), icon: 'expand-arrows-alt' }); zorderItems.push({ @@ -891,8 +891,8 @@ export class DocumentViewInternal extends DocComponent (this.rootDoc._raiseWhenDragged = this.rootDoc._raiseWhenDragged === undefined ? false : undefined))), icon: 'expand-arrows-alt', }); + !zorders && cm.addItem({ description: 'ZOrder...', noexpand: true, subitems: zorderItems, icon: 'compass' }); } - !zorders && cm.addItem({ description: 'ZOrder...', noexpand: true, subitems: zorderItems, icon: 'compass' }); !Doc.noviceMode && onClicks.push({ description: 'Enter Portal', event: this.makeIntoPortal, icon: 'window-restore' }); !Doc.noviceMode && onClicks.push({ description: 'Toggle Detail', event: this.setToggleDetail, icon: 'concierge-bell' }); -- cgit v1.2.3-70-g09d2 From f1311ad50b925dd9f9731774f273c722c06f5cf5 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 4 Oct 2022 17:28:05 -0400 Subject: colored tree view icons red when the doc is a prototype --- src/client/views/collections/TreeView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/TreeView.tsx b/src/client/views/collections/TreeView.tsx index 25f57b045..14563f990 100644 --- a/src/client/views/collections/TreeView.tsx +++ b/src/client/views/collections/TreeView.tsx @@ -8,7 +8,7 @@ import { List } from '../../../fields/List'; import { RichTextField } from '../../../fields/RichTextField'; import { listSpec } from '../../../fields/Schema'; import { ComputedField, ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { BoolCast, Cast, DocCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, returnTrue, simulateMouseClick, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; @@ -692,7 +692,7 @@ export class TreeView extends React.Component { ) ) : ( -
+
-- cgit v1.2.3-70-g09d2 From 1a0e7817754c9fbed5be0820872e9eff3c96d9bb Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 5 Oct 2022 10:37:00 -0400 Subject: Update CollectionFreeFormView.tsx --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 93bf143df..adf573061 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -157,8 +157,8 @@ export class CollectionFreeFormView extends CollectionSubView Date: Wed, 5 Oct 2022 10:51:58 -0400 Subject: from last - fixing pan to be in center of freeform views when they have native width/heights & are fitWidth in lightbox --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index adf573061..0d061a325 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -155,9 +155,10 @@ export class CollectionFreeFormView extends CollectionSubView Date: Wed, 5 Oct 2022 11:42:13 -0400 Subject: changed ctrl-a text selection to not delete the root node so that styles will be preserved. this fixes bug with text boxes inheriting default font styles after ctrl-a and editing them. fixed some text rules to not inherit default style --- .../views/nodes/formattedText/ProsemirrorExampleTransfer.ts | 9 +++++++++ src/client/views/nodes/formattedText/RichTextRules.ts | 7 +++++-- 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts index 31552cf1b..be501329f 100644 --- a/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts +++ b/src/client/views/nodes/formattedText/ProsemirrorExampleTransfer.ts @@ -168,6 +168,15 @@ export function buildKeymap>(schema: S, props: any, mapKey bind('Alt-Enter', () => (props.onKey?.(event, props) ? true : true)); bind('Ctrl-Enter', () => (props.onKey?.(event, props) ? true : true)); + bind('Cmd-a', (state: EditorState, dispatch: (tx: Transaction) => void) => { + dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(1), state.doc.resolve(state.doc.content.size - 1)))); + return true; + }); + + bind('Ctrl-a', (state: EditorState, dispatch: (tx: Transaction) => void) => { + dispatch(state.tr.setSelection(new TextSelection(state.doc.resolve(1), state.doc.resolve(state.doc.content.size - 1)))); + return true; + }); // backspace = chainCommands(deleteSelection, joinBackward, selectNodeBackward); bind('Backspace', (state: EditorState, dispatch: (tx: Transaction) => void) => { diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 2eb62c38d..e5ea7b3b0 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -276,7 +276,7 @@ export class RichTextRules { this.Document[DataSym][fieldKey] = value === 'true' ? true : value === 'false' ? false : num ? Number(value) : value; } const fieldView = state.schema.nodes.dashField.create({ fieldKey, docid }); - return state.tr.deleteRange(start, end).insert(start, fieldView); + return state.tr.setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))).replaceSelectionWith(fieldView, true); }), // create a text display of a metadata field on this or another document, or create a hyperlink portal to another document @@ -327,7 +327,10 @@ export class RichTextRules { this.Document[DataSym].tags = `${tags + '#' + tag + ':'}`; } const fieldView = state.schema.nodes.dashField.create({ fieldKey: '#' + tag }); - return state.tr.deleteRange(start, end).insert(start, fieldView).insertText(' '); + return state.tr + .setSelection(new TextSelection(state.doc.resolve(start), state.doc.resolve(end))) + .replaceSelectionWith(fieldView, true) + .insertText(' '); }), // # heading -- cgit v1.2.3-70-g09d2 From e8c7c942fa19922b476573ad664b2bfc87191b44 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 5 Oct 2022 14:06:34 -0400 Subject: fixed uploading tif and other unsupported images to generate an error quickly with a better erorr message. --- deploy/assets/unknown-file-icon-hi.png | Bin 0 -> 32861 bytes src/client/util/request-image-size.js | 26 ++++++++++++++------------ src/client/views/nodes/ImageBox.tsx | 2 +- src/server/DashUploadUtils.ts | 11 ++++++----- 4 files changed, 21 insertions(+), 18 deletions(-) create mode 100644 deploy/assets/unknown-file-icon-hi.png (limited to 'src/client/views') diff --git a/deploy/assets/unknown-file-icon-hi.png b/deploy/assets/unknown-file-icon-hi.png new file mode 100644 index 000000000..be8a5ece0 Binary files /dev/null and b/deploy/assets/unknown-file-icon-hi.png differ diff --git a/src/client/util/request-image-size.js b/src/client/util/request-image-size.js index beb030635..502e0fbac 100644 --- a/src/client/util/request-image-size.js +++ b/src/client/util/request-image-size.js @@ -15,15 +15,18 @@ const HttpError = require('standard-http-error'); module.exports = function requestImageSize(options) { let opts = { - encoding: null + encoding: null, }; if (options && typeof options === 'object') { opts = Object.assign(options, opts); } else if (options && typeof options === 'string') { - opts = Object.assign({ - uri: options - }, opts); + opts = Object.assign( + { + uri: options, + }, + opts + ); } else { return Promise.reject(new Error('You should provide an URI string or a "request" options object.')); } @@ -38,9 +41,8 @@ module.exports = function requestImageSize(options) { return reject(new HttpError(res.statusCode, res.statusMessage)); } - let buffer = new Buffer.from([]); + let buffer = Buffer.from([]); let size; - let imageSizeError; res.on('data', chunk => { buffer = Buffer.concat([buffer, chunk]); @@ -48,8 +50,8 @@ module.exports = function requestImageSize(options) { try { size = imageSize(buffer); } catch (err) { - imageSizeError = err; - return; + reject(err); + return req.abort(); } if (size) { @@ -58,11 +60,11 @@ module.exports = function requestImageSize(options) { } }); - res.on('error', err => reject(err)); + res.on('error', reject); res.on('end', () => { if (!size) { - return reject(imageSizeError); + return reject(new Error('Image has no size')); } size.downloaded = buffer.length; @@ -70,6 +72,6 @@ module.exports = function requestImageSize(options) { }); }); - req.on('error', err => reject(err)); + req.on('error', reject); }); -}; \ No newline at end of file +}; diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 486c9c48c..959c641a8 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -231,7 +231,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent => { const metadata = await InspectImage(source); if (metadata instanceof Error) { - return metadata; + return { name: metadata.name, message: metadata.message }; } return UploadInspectedImage(metadata, filename || metadata.filename, prefix); }; @@ -395,10 +394,12 @@ export namespace DashUploadUtils { exifData, requestable: resolvedUrl, }; + // Use the request library to parse out file level image information in the headers const { headers } = await new Promise((resolve, reject) => { - request.head(resolvedUrl, (error, res) => (error ? reject(error) : resolve(res))); - }).catch(console.error); + return request.head(resolvedUrl, (error, res) => (error ? reject(error) : resolve(res))); + }).catch(reject); + try { // Compute the native width and height ofthe image with an npm module const { width: nativeWidth, height: nativeHeight } = await requestImageSize(resolvedUrl); -- cgit v1.2.3-70-g09d2 From 65286c7e488f9eed9b96923791259f5169520793 Mon Sep 17 00:00:00 2001 From: mehekj Date: Wed, 5 Oct 2022 15:42:12 -0400 Subject: fixed scrolling for note-taking view --- src/client/views/collections/CollectionNoteTakingView.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/CollectionNoteTakingView.scss b/src/client/views/collections/CollectionNoteTakingView.scss index 08b13fd50..4d1f18e54 100644 --- a/src/client/views/collections/CollectionNoteTakingView.scss +++ b/src/client/views/collections/CollectionNoteTakingView.scss @@ -52,9 +52,8 @@ } .collectionNoteTakingViewFieldColumn { - height: 100%; display: flex; - overflow: hidden; + overflow: auto; } .collectionNoteTakingViewFieldColumn:hover { .collectionNoteTakingView-DocumentButtons { -- cgit v1.2.3-70-g09d2 From 4d21b79bfb87bb5fe81950a432a031f42b99f707 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 6 Oct 2022 10:19:31 -0400 Subject: don't allow golden layout to be created if there's no dockingConfig. --- src/client/documents/Documents.ts | 4 +- .../views/collections/CollectionDockingView.tsx | 57 ++++++++++++---------- 2 files changed, 32 insertions(+), 29 deletions(-) (limited to 'src/client/views') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index cd647f5e8..a6a10d91a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1902,8 +1902,8 @@ ScriptingGlobals.add(function makeDelegate(proto: any) { return d; }); ScriptingGlobals.add(function generateLinkTitle(self: Doc) { - const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null).title : ''; - const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null).title : ''; + const anchor1title = self.anchor1 && self.anchor1 !== self ? Cast(self.anchor1, Doc, null)?.title : ''; + const anchor2title = self.anchor2 && self.anchor2 !== self ? Cast(self.anchor2, Doc, null)?.title : ''; const relation = self.linkRelationship || 'to'; return `${anchor1title} (${relation}) ${anchor2title}`; }); diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 40a5876e0..37a2d5e5d 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -282,35 +282,38 @@ export class CollectionDockingView extends CollectionSubView() { return true; } setupGoldenLayout = async () => { - const config = StrCast(this.props.Document.dockingConfig, JSON.stringify(DashboardView.resetDashboard(this.props.Document))); - const matches = config.match(/\"documentId\":\"[a-z0-9-]+\"/g); - const docids = matches?.map(m => m.replace('"documentId":"', '').replace('"', '')) ?? []; - - await Promise.all(docids.map(id => DocServer.GetRefField(id))); - - if (this._goldenLayout) { - if (config === JSON.stringify(this._goldenLayout.toConfig())) { - return; - } else { - try { - this._goldenLayout.unbind('tabCreated', this.tabCreated); - this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); - this._goldenLayout.unbind('stackCreated', this.stackCreated); - } catch (e) {} + //const config = StrCast(this.props.Document.dockingConfig, JSON.stringify(DashboardView.resetDashboard(this.props.Document))); + 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('"', '')) ?? []; + + await Promise.all(docids.map(id => DocServer.GetRefField(id))); + + if (this._goldenLayout) { + if (config === JSON.stringify(this._goldenLayout.toConfig())) { + return; + } else { + try { + this._goldenLayout.unbind('tabCreated', this.tabCreated); + this._goldenLayout.unbind('tabDestroyed', this.tabDestroyed); + this._goldenLayout.unbind('stackCreated', this.stackCreated); + } catch (e) {} + } + this.tabMap.clear(); + this._goldenLayout.destroy(); } - this.tabMap.clear(); - this._goldenLayout.destroy(); + const glay = (this._goldenLayout = new GoldenLayout(JSON.parse(config))); + glay.on('tabCreated', this.tabCreated); + glay.on('tabDestroyed', this.tabDestroyed); + glay.on('stackCreated', this.stackCreated); + glay.registerComponent('DocumentFrameRenderer', TabDocView); + glay.container = this._containerRef.current; + glay.init(); + glay.root.layoutManager.on('itemDropped', this.tabItemDropped); + glay.root.layoutManager.on('dragStart', this.tabDragStart); + glay.root.layoutManager.on('activeContentItemChanged', this.stateChanged); } - const glay = (this._goldenLayout = new GoldenLayout(JSON.parse(config))); - glay.on('tabCreated', this.tabCreated); - glay.on('tabDestroyed', this.tabDestroyed); - glay.on('stackCreated', this.stackCreated); - glay.registerComponent('DocumentFrameRenderer', TabDocView); - glay.container = this._containerRef.current; - glay.init(); - glay.root.layoutManager.on('itemDropped', this.tabItemDropped); - glay.root.layoutManager.on('dragStart', this.tabDragStart); - glay.root.layoutManager.on('activeContentItemChanged', this.stateChanged); }; componentDidMount: () => void = () => { -- cgit v1.2.3-70-g09d2 From 161e0f1bdaec29516c9398c740a585e22595bf74 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 6 Oct 2022 11:18:42 -0400 Subject: fixed warnings with scriptingbox keys --- src/client/views/LightboxView.tsx | 2 +- src/client/views/collections/CollectionDockingView.tsx | 2 ++ src/client/views/nodes/ScriptingBox.tsx | 9 ++++----- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/LightboxView.tsx b/src/client/views/LightboxView.tsx index e8b8a3eaa..bd6cea28a 100644 --- a/src/client/views/LightboxView.tsx +++ b/src/client/views/LightboxView.tsx @@ -281,7 +281,7 @@ export class LightboxView extends React.Component { action(() => { const target = LightboxView._docTarget; const doc = LightboxView._doc; - const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target); + //const targetView = target && DocumentManager.Instance.getLightboxDocumentView(target); if (doc === r.props.Document && (!target || target === doc)) r.ComponentView?.shrinkWrap?.(); //else target?.focus(target, { willZoom: true, scale: 0.9, instant: true }); // bcz: why was this here? it breaks smooth navigation in lightbox using 'next' button }) diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 37a2d5e5d..e9b41de25 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -313,6 +313,8 @@ export class CollectionDockingView extends CollectionSubView() { glay.root.layoutManager.on('itemDropped', this.tabItemDropped); glay.root.layoutManager.on('dragStart', this.tabDragStart); glay.root.layoutManager.on('activeContentItemChanged', this.stateChanged); + } else { + console.log('ERROR: no config for dashboard!!'); } }; diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index 1c9b0bc0e..4883ad538 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -397,8 +397,8 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent e.stopPropagation()} onChange={e => this.viewChanged(e, parameter)} value={typeof this.rootDoc[parameter] === 'string' ? 'S' + StrCast(this.rootDoc[parameter]) : typeof this.rootDoc[parameter] === 'number' ? 'N' + NumCast(this.rootDoc[parameter]) : 'B' + BoolCast(this.rootDoc[parameter])}> - {types.map(type => ( - @@ -666,7 +666,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent {this.compileParams.map((parameter, i) => ( -
e.key === 'Enter' && this._overlayDisposer?.()}> +
e.key === 'Enter' && this._overlayDisposer?.()}> {this.paramsNames.map((parameter: string, i: number) => ( -
e.key === 'Enter' && this._overlayDisposer?.()}> +
e.key === 'Enter' && this._overlayDisposer?.()}>
{`${parameter}:${this.paramsTypes[i]} = `}
{this.paramsTypes[i] === 'boolean' ? this.renderEnum(parameter, [true, false]) : null} @@ -805,7 +805,6 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent -- cgit v1.2.3-70-g09d2 From eb5f75785fd28acb50f1b30434e89223fff00185 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 7 Oct 2022 00:33:04 -0400 Subject: improvements to sizing webpages --- src/client/views/SidebarAnnos.tsx | 2 +- src/client/views/collections/CollectionView.tsx | 1 - src/client/views/nodes/LinkDocPreview.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 28 ++++++++++++++++++------- 4 files changed, 22 insertions(+), 11 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 90d9c3c43..12f41394d 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -73,7 +73,7 @@ export class SidebarAnnos extends React.Component { this.allMetadata.map(tag => (target[tag] = tag)); DocUtils.MakeLink({ doc: anchor }, { doc: target }, 'inline comment:comment on'); this.addDocument(target); - this._stackRef.current?.focusDocument(target); + this._stackRef.current?.focusDocument(target, {}); return target; }; makeDocUnfiltered = (doc: Doc) => { diff --git a/src/client/views/collections/CollectionView.tsx b/src/client/views/collections/CollectionView.tsx index d88d59205..dcaad5632 100644 --- a/src/client/views/collections/CollectionView.tsx +++ b/src/client/views/collections/CollectionView.tsx @@ -15,7 +15,6 @@ import { ImageUtils } from '../../util/Import & Export/ImageUtils'; import { InteractionUtils } from '../../util/InteractionUtils'; import { ContextMenu } from '../ContextMenu'; import { ContextMenuProps } from '../ContextMenuItem'; -import { DashboardView } from '../DashboardView'; import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../DocComponent'; import { FieldView, FieldViewProps } from '../nodes/FieldView'; import { CollectionCarousel3DView } from './CollectionCarousel3DView'; diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index f50524e4e..27e79a83b 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -228,7 +228,7 @@ export class LinkDocPreview extends React.Component { { const targetanchor = this._linkDoc && this._linkSrc && LinkManager.getOppositeAnchor(this._linkDoc, this._linkSrc); - targetanchor && this._targetDoc !== targetanchor && r?.focus(targetanchor); + targetanchor && this._targetDoc !== targetanchor && r?.focus(targetanchor, {}); }} Document={this._targetDoc!} moveDocument={returnFalse} diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index 8288810b1..460edb7c2 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -243,6 +243,8 @@ export class WebBox extends ViewBoxAnnotatableComponent disposer?.()); // this._iframe?.removeEventListener('wheel', this.iframeWheel, true); // this._iframe?.contentDocument?.removeEventListener("pointerup", this.iframeUp); @@ -382,6 +384,7 @@ export class WebBox extends ViewBoxAnnotatableComponent { const iframe = this._iframe; @@ -409,16 +412,25 @@ export class WebBox extends ViewBoxAnnotatableComponent (this._scrollHeight = Math.max(this._scrollHeight, iframe?.contentDocument?.body.scrollHeight || 0))), + const iframeContent = iframe?.contentDocument; + if (iframeContent) { + iframeContent.addEventListener('pointerup', this.iframeUp); + iframeContent.addEventListener('pointerdown', this.iframeDown); + const initHeights = () => { + this._scrollHeight = Math.max(this._scrollHeight, (iframeContent.body.children[0] as any)?.scrollHeight || 0); + if (this._scrollHeight) { + this.rootDoc.nativeHeight = Math.min(NumCast(this.rootDoc.nativeHeight), this._scrollHeight); + this.layoutDoc.height = Math.min(this.layoutDoc[HeightSym](), (this.layoutDoc[WidthSym]() * NumCast(this.rootDoc.nativeHeight)) / NumCast(this.rootDoc.nativeWidth)); + } + }; + initHeights(); + this._iframetimeout && clearTimeout(this._iframetimeout); + this._iframetimeout = setTimeout( + action(() => initHeights), 5000 ); iframe.setAttribute('enable-annotation', 'true'); - iframe.contentDocument.addEventListener( + iframeContent.addEventListener( 'click', undoBatch( action((e: MouseEvent) => { @@ -882,7 +894,7 @@ export class WebBox extends ViewBoxAnnotatableComponent this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)} onPointerDown={this.onMarqueeDown}> -
+
{this.content} {
{renderAnnotations(this.transparentFilter)}
} {renderAnnotations(this.opaqueFilter)} -- cgit v1.2.3-70-g09d2