From 3f1ffd2fc2f09bd6b5909ae69ef07006750f4436 Mon Sep 17 00:00:00 2001 From: Michael Foiani Date: Wed, 3 Aug 2022 13:41:52 -0400 Subject: add new icon and modal for reporting. --- src/client/views/MainView.tsx | 2 ++ src/client/views/topbar/TopBar.tsx | 6 ++++++ 2 files changed, 8 insertions(+) (limited to 'src/client/views') diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index e96f65548..a8c8fedaa 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -20,6 +20,7 @@ import { DocumentManager } from '../util/DocumentManager'; import { GroupManager } from '../util/GroupManager'; import { HistoryUtil } from '../util/History'; import { Hypothesis } from '../util/HypothesisUtils'; +import { ReportManager } from '../util/ReportManager'; import { ScriptingGlobals } from '../util/ScriptingGlobals'; import { SelectionManager } from '../util/SelectionManager'; import { ColorScheme, SettingsManager } from '../util/SettingsManager'; @@ -974,6 +975,7 @@ export class MainView extends React.Component { + diff --git a/src/client/views/topbar/TopBar.tsx b/src/client/views/topbar/TopBar.tsx index 3fc0a237e..cbcfed06f 100644 --- a/src/client/views/topbar/TopBar.tsx +++ b/src/client/views/topbar/TopBar.tsx @@ -1,5 +1,6 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; +import { MdBugReport } from 'react-icons/md'; import { action } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -18,6 +19,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 @@ -31,6 +33,7 @@ export class TopBar extends React.Component { DashboardView.closeActiveDashboard(); // bcz: if we do this, we need some other way to keep track, for user convenience, of the last dashboard in use }); }; + render() { const activeDashboard = Doc.ActiveDashboard; return ( @@ -92,6 +95,9 @@ export class TopBar extends React.Component { {GetEffectiveAcl(Doc.GetProto(Doc.ActiveDashboard)) === AclAdmin ? 'Share' : 'view original'} ) : null} +
ReportManager.Instance.open()}> + +
window.open('https://brown-dash.github.io/Dash-Documentation/', '_blank')}>
-- cgit v1.2.3-70-g09d2 From 2a1d6793d33d9c72295d23965aefd23f2004bbcb Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 8 Aug 2022 11:48:27 -0400 Subject: cleaning up link ui. linkPreviews (not from linkMenu) have an edit button. link lines work when in lightboxview. --- .../CollectionFreeFormLinkView.tsx | 5 +- .../CollectionFreeFormLinksView.tsx | 5 +- src/client/views/linking/LinkEditor.scss | 65 +++++----- src/client/views/linking/LinkEditor.tsx | 141 ++++++++++++--------- src/client/views/linking/LinkMenuItem.tsx | 89 +------------ src/client/views/nodes/DocumentLinksButton.tsx | 2 +- src/client/views/nodes/DocumentView.tsx | 16 ++- src/client/views/nodes/LinkAnchorBox.tsx | 2 +- src/client/views/nodes/LinkDocPreview.tsx | 18 ++- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 +- 10 files changed, 149 insertions(+), 198 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx index d979ef961..bf9de6760 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLinkView.tsx @@ -195,7 +195,10 @@ export class CollectionFreeFormLinkView extends React.Component> { @computed get uniqueConnections() { - return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews)).map(c => ); + return Array.from(new Set(DocumentManager.Instance.LinkedDocumentViews)) + .filter(c => !LightboxView.LightboxDoc || (LightboxView.IsLightboxDocView(c.a.docViewPath) && LightboxView.IsLightboxDocView(c.b.docViewPath))) + .map(c => ); } render() { diff --git a/src/client/views/linking/LinkEditor.scss b/src/client/views/linking/LinkEditor.scss index 1d6496d3c..b0ee4e46d 100644 --- a/src/client/views/linking/LinkEditor.scss +++ b/src/client/views/linking/LinkEditor.scss @@ -1,10 +1,11 @@ -@import "../global/globalCssVariables"; +@import '../global/globalCssVariables'; .linkEditor { width: 100%; height: auto; font-size: 13px; // TODO user-select: none; + max-width: 280px; } .linkEditor-button-back { @@ -20,19 +21,20 @@ } .linkEditor-info { - //border-bottom: 0.5px solid $light-gray; - //padding-bottom: 1px; - padding: 12px; + padding-top: 12px; padding-left: 5px; + padding-bottom: 3px; //margin-bottom: 6px; display: flex; justify-content: space-between; color: black; .linkEditor-linkedTo { - width: calc(100% - 26px); - padding-left: 5px; - padding-right: 5px; + width: calc(100% - 46px); + overflow: hidden; + position: relative; + text-overflow: ellipsis; + white-space: pre; .linkEditor-downArrow { &:hover { @@ -77,11 +79,14 @@ width: 20px; } } +.linkEditor-deleteBtn { + padding-left: 3px; +} .linkEditor-description { padding-left: 26px; - padding-right: 6.5px; padding-bottom: 3.5px; + display: flex; .linkEditor-description-label { text-decoration-color: black; @@ -96,7 +101,6 @@ //border: 1px solid grey; //border-radius: 4px; padding-left: 2px; - padding-right: 2px; //margin-right: 4px; color: black; text-decoration-color: grey; @@ -104,17 +108,13 @@ .linkEditor-description-add-button { display: inline; - /* float: right; */ border-radius: 7px; font-size: 9px; background: black; - /* padding: 3px; */ - padding-top: 4px; - padding-left: 7px; - padding-bottom: 4px; - padding-right: 8px; height: 80%; color: white; + padding: 3px; + margin-left: 3px; &:hover { cursor: pointer; @@ -146,6 +146,7 @@ padding-left: 26px; padding-right: 6.5px; padding-bottom: 15px; + display: flex; &:hover { cursor: pointer; @@ -153,12 +154,11 @@ .linkEditor-followingDropdown-label { color: black; + padding-right: 3px; } .linkEditor-followingDropdown-dropdown { - .linkEditor-followingDropdown-header { - border: 1px solid grey; border-radius: 4px; //background-color: rgb(236, 236, 236); @@ -195,27 +195,25 @@ background-color: rgb(187, 220, 231); } } - } } - - } - - - - - .linkEditor-button, .linkEditor-addbutton { - width: 18px; - height: 18px; - padding: 0; - // font-size: 12px; - border-radius: 10px; - - + width: 15%; + border-radius: 7px; + font-size: 9px; + background: black; + padding: 3px; + height: 80%; + color: white; + text-align: center; + margin: auto; + margin-left: 3px; + > svg { + margin: auto; + } &:disabled { background-color: gray; } @@ -270,7 +268,6 @@ } } - .linkEditor-dropdown { width: 100%; position: relative; @@ -334,4 +331,4 @@ .linkEditor-button { margin-left: 3px; } -} \ No newline at end of file +} diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index ba301962b..d23d38854 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -9,11 +9,12 @@ import { undoBatch } from '../../util/UndoManager'; import './LinkEditor.scss'; import { LinkRelationshipSearch } from './LinkRelationshipSearch'; import React = require('react'); +import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils'; interface LinkEditorProps { sourceDoc: Doc; linkDoc: Doc; - showLinks: () => void; + showLinks?: () => void; hideback?: boolean; } @observer @@ -22,13 +23,6 @@ export class LinkEditor extends React.Component { @observable relationship = StrCast(LinkManager.currentLink?.linkRelationship); @observable zoomFollow = StrCast(this.props.sourceDoc.followLinkZoom); @observable openDropdown: boolean = false; - @observable showInfo: boolean = false; - @computed get infoIcon() { - if (this.showInfo) { - return 'chevron-up'; - } - return 'chevron-down'; - } @observable private buttonColor: string = ''; @observable private relationshipButtonColor: string = ''; @observable private relationshipSearchVisibility: string = 'none'; @@ -36,12 +30,6 @@ export class LinkEditor extends React.Component { //@observable description = this.props.linkDoc.description ? StrCast(this.props.linkDoc.description) : "DESCRIPTION"; - @undoBatch - deleteLink = (): void => { - LinkManager.Instance.deleteLink(this.props.linkDoc); - this.props.showLinks(); - }; - @undoBatch setRelationshipValue = action((value: string) => { if (LinkManager.currentLink) { @@ -173,7 +161,7 @@ export class LinkEditor extends React.Component { //NOTE: confusingly, the classnames for the following relationship JSX elements are the same as the for the description elements for shared CSS return (
-
Link Relationship:
+
Relationship:
{ get editDescription() { return (
-
Link Description:
+
Description:
@@ -284,68 +272,99 @@ export class LinkEditor extends React.Component { ); } - @action - changeInfo = () => { - this.showInfo = !this.showInfo; + autoMove = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => (this.props.linkDoc.linkAutoMove = !this.props.linkDoc.linkAutoMove)))); + }; + + showAnchor = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => (this.props.linkDoc.hidden = !this.props.linkDoc.hidden)))); + }; + + showLink = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => (this.props.linkDoc.linkDisplay = !this.props.linkDoc.linkDisplay)))); + }; + + deleteLink = (e: React.PointerEvent): void => { + setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => LinkManager.Instance.deleteLink(this.props.linkDoc)))); }; render() { const destination = LinkManager.getOppositeAnchor(this.props.linkDoc, this.props.sourceDoc); return !destination ? null : ( -
e.stopPropagation()}> +
e.stopPropagation()} onPointerDown={e => e.stopPropagation()}>
- -
Return to link menu
- - } - placement="top"> - -
+ {!this.props.showLinks ? null : ( + Return to link menu
} placement="top"> + + + )}

Editing Link to: {StrCast(destination.proto?.title, StrCast(destination.title, 'untitled'))}

- -
Show more link information
- - } - placement="top"> -
- + Delete Link
}> +
e.stopPropagation()}> +
- {this.showInfo ? ( -
-
- {this.props.linkDoc.author ? ( -
- {' '} - Author: {StrCast(this.props.linkDoc.author)} -
- ) : null} -
-
- {this.props.linkDoc.creationDate ? ( -
- {' '} - Creation Date: - {DateCast(this.props.linkDoc.creationDate).toString()} -
- ) : null} -
-
- ) : null} +
+ {this.props.linkDoc.author ? ( + <> + {' '} + Author: {StrCast(this.props.linkDoc.author)} + + ) : null} + {this.props.linkDoc.creationDate ? ( + <> + {' '} + Creation Date: + {DateCast(this.props.linkDoc.creationDate).toString()} + + ) : null} +
{this.editDescription} {this.editRelationship} {this.editZoomFollow} +
+ Show Anchor: + {this.props.linkDoc.hidden ? 'Show Link Anchor' : 'Hide Link Anchor'}
}> +
e.stopPropagation()}> + +
+ +
+
+ Show Link Line: + {this.props.linkDoc.linkDisplay ? 'Hide Link Line' : 'Show Link Line'}
}> +
e.stopPropagation()}> + +
+ +
+
+ Freeze Anchor: + {this.props.linkDoc.linkAutoMove ? 'Click to freeze link anchor position' : 'Click to auto move link anchor'}
}> +
e.stopPropagation()}> + +
+ +
{this.followingDropdown}
); diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index ed856a4ab..60428fc98 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -125,33 +125,6 @@ export class LinkMenuItem extends React.Component { ); }; - deleteLink = (e: React.PointerEvent): void => { - setupMoveUpEvents( - this, - e, - returnFalse, - emptyFunction, - undoBatch( - action(() => { - this.props.linkDoc.linksToAnnotation && Hypothesis.deleteLink(this.props.linkDoc, this.props.sourceDoc, this.props.destinationDoc); - LinkManager.Instance.deleteLink(this.props.linkDoc); - }) - ) - ); - }; - - autoMove = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => (this.props.linkDoc.linkAutoMove = !this.props.linkDoc.linkAutoMove)))); - }; - - showLink = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => (this.props.linkDoc.linkDisplay = !this.props.linkDoc.linkDisplay)))); - }; - - showAnchor = (e: React.PointerEvent) => { - setupMoveUpEvents(this, e, returnFalse, emptyFunction, undoBatch(action(() => (this.props.linkDoc.hidden = !this.props.linkDoc.hidden)))); - }; - render() { const destinationIcon = Doc.toIcon(this.props.destinationDoc) as any as IconProp; @@ -183,7 +156,7 @@ export class LinkMenuItem extends React.Component { linkSrc: this.props.sourceDoc, linkDoc: this.props.linkDoc, showHeader: false, - location: [e.clientX, e.clientY + 20], + location: [this._drag.current?.getBoundingClientRect().right ?? 100, this._drag.current?.getBoundingClientRect().top ?? e.clientY], }) } onPointerDown={this.onLinkButtonDown}> @@ -206,69 +179,11 @@ export class LinkMenuItem extends React.Component {
- -
{this.props.linkDoc.hidden ? 'Show Link Anchor' : 'Hide Link Anchor'}
- - }> -
e.stopPropagation()}> - -
-
- - -
{this.props.linkDoc.linkDisplay ? 'Hide Link Line' : 'Show Link Line'}
- - }> -
e.stopPropagation()}> - -
-
- - -
{this.props.linkDoc.linkAutoMove ? 'Click to freeze link anchor position' : 'Click to auto move link anchor'}
- - }> -
e.stopPropagation()}> - -
-
- - -
Edit Link
- - }> + Edit Link
}>
e.stopPropagation()}>
- -
Delete Link
- - }> -
e.stopPropagation()}> - -
-
diff --git a/src/client/views/nodes/DocumentLinksButton.tsx b/src/client/views/nodes/DocumentLinksButton.tsx index 5939d1680..a37de7f69 100644 --- a/src/client/views/nodes/DocumentLinksButton.tsx +++ b/src/client/views/nodes/DocumentLinksButton.tsx @@ -307,7 +307,7 @@ export class DocumentLinksButton extends React.Component {(this.props.InMenu && (DocumentLinksButton.StartLink || this.props.StartLink)) || (!DocumentLinksButton.LinkEditorDocView && !this.props.InMenu) ? ( {title}}>{this.linkButtonInner} diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 1ee1aec5a..2524d66dd 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1070,7 +1070,7 @@ export class DocumentViewInternal extends DocComponent )} {audioView} @@ -1298,6 +1298,7 @@ export class DocumentViewInternal extends DocComponent this._isHovering; @observable _isHovering = false; @observable _: string = ''; + _hoverTimeout: any = undefined; @computed get renderDoc() { TraceMobx(); const thumb = ImageCast(this.layoutDoc['thumb-frozen'], ImageCast(this.layoutDoc.thumb))?.url?.href.replace('.png', '_m.png'); @@ -1308,8 +1309,17 @@ export class DocumentViewInternal extends DocComponent (this._isHovering = true))} - onPointerLeave={action(() => (this._isHovering = false))} + onPointerEnter={action(() => { + clearTimeout(this._hoverTimeout); + this._isHovering = true; + })} + onPointerLeave={action(() => { + clearTimeout(this._hoverTimeout); + this._hoverTimeout = setTimeout( + action(() => (this._isHovering = false)), + 500 + ); + })} style={{ background: isButton || thumb ? undefined : this.backgroundColor, opacity: this.opacity, diff --git a/src/client/views/nodes/LinkAnchorBox.tsx b/src/client/views/nodes/LinkAnchorBox.tsx index 85a8622ec..5102eae51 100644 --- a/src/client/views/nodes/LinkAnchorBox.tsx +++ b/src/client/views/nodes/LinkAnchorBox.tsx @@ -136,7 +136,7 @@ export class LinkAnchorBox extends ViewBoxBaseComponent() { return (
LinkDocPreview.SetLinkInfo({ docProps: this.props, diff --git a/src/client/views/nodes/LinkDocPreview.tsx b/src/client/views/nodes/LinkDocPreview.tsx index 85b1f8d9e..93ca22d5d 100644 --- a/src/client/views/nodes/LinkDocPreview.tsx +++ b/src/client/views/nodes/LinkDocPreview.tsx @@ -17,6 +17,7 @@ import { undoBatch } from '../../util/UndoManager'; import { DocumentView, DocumentViewSharedProps } from './DocumentView'; import './LinkDocPreview.scss'; import React = require('react'); +import { LinkEditor } from '../linking/LinkEditor'; interface LinkDocPreviewProps { linkDoc?: Doc; @@ -118,13 +119,15 @@ export class LinkDocPreview extends React.Component { } return undefined; } - deleteLink = (e: React.PointerEvent) => { + @observable _showEditor = false; + editLink = (e: React.PointerEvent): void => { + LinkManager.currentLink = this.props.linkDoc; setupMoveUpEvents( this, e, returnFalse, emptyFunction, - undoBatch(() => this._linkDoc && LinkManager.Instance.deleteLink(this._linkDoc)) + action(() => (this._showEditor = !this._showEditor)) ); }; nextHref = (e: React.PointerEvent) => { @@ -183,9 +186,9 @@ export class LinkDocPreview extends React.Component {
- Delete Link} placement="top"> -
- + Edit Link
} placement="top"> +
+
@@ -271,8 +274,9 @@ export class LinkDocPreview extends React.Component { className="linkDocPreview" ref={this._linkDocRef} onPointerDown={this.followLinkPointerDown} - style={{ left: this.props.location[0], top: this.props.location[1], width: this.width() + borders, height: this.height() + borders + (this.props.showHeader ? 37 : 0) }}> - {this.docPreview} + style={{ left: this.props.location[0], top: this.props.location[1], width: this._showEditor ? 'auto' : this.width() + borders, height: this._showEditor ? 'max-content' : this.height() + borders + (this.props.showHeader ? 37 : 0) }}> + {this._showEditor ? null : this.docPreview} + {!this._showEditor || !this._linkSrc || !this._linkDoc ? null : (this._showEditor = !this._showEditor))} />} ); } diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index a018f51c2..40b0d285c 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -24,7 +24,7 @@ import { addStyleSheet, addStyleSheetRule, clearStyleSheetRules, emptyFunction, import { GoogleApiClientUtils, Pulls, Pushes } from '../../../apis/google_docs/GoogleApiClientUtils'; import { DocServer } from '../../../DocServer'; import { Docs, DocUtils } from '../../../documents/Documents'; -import { DocumentType } from '../../../documents/DocumentTypes'; +import { CollectionViewType, DocumentType } from '../../../documents/DocumentTypes'; import { DictationManager } from '../../../util/DictationManager'; import { DocumentManager } from '../../../util/DocumentManager'; import { DragManager } from '../../../util/DragManager'; @@ -1751,7 +1751,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - const ComponentTag = tag === 'freeform' ? CollectionFreeFormView : tag === 'tree' ? CollectionTreeView : tag === 'translation' ? FormattedTextBox : CollectionStackingView; + const ComponentTag = tag === CollectionViewType.Freeform ? CollectionFreeFormView : tag === CollectionViewType.Tree ? CollectionTreeView : tag === 'translation' ? FormattedTextBox : CollectionStackingView; return ComponentTag === CollectionStackingView ? ( Date: Mon, 8 Aug 2022 12:54:06 -0400 Subject: fixed autolinking to not match partial word substrings. restored textbox dictation. --- src/client/documents/Documents.ts | 5 +- src/client/util/DictationManager.ts | 439 +++++++++++---------- src/client/views/DocumentDecorations.tsx | 1 - src/client/views/linking/LinkMenuItem.tsx | 4 +- .../views/nodes/formattedText/FormattedTextBox.tsx | 96 +++-- 5 files changed, 282 insertions(+), 263 deletions(-) (limited to 'src/client/views') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 9d00664ad..c7ea04839 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1287,10 +1287,11 @@ export namespace DocUtils { export let ActiveRecordings: { props: FieldViewProps; getAnchor: () => Doc }[] = []; - export function MakeLinkToActiveAudio(getSourceDoc: () => Doc, broadcastEvent = true) { + export function MakeLinkToActiveAudio(getSourceDoc: () => Doc | undefined, broadcastEvent = true) { broadcastEvent && runInAction(() => (DocumentManager.Instance.RecordingEvent = DocumentManager.Instance.RecordingEvent + 1)); return DocUtils.ActiveRecordings.map(audio => { - const link = DocUtils.MakeLink({ doc: getSourceDoc() }, { doc: audio.getAnchor() || audio.props.Document }, 'recording annotation:linked recording', 'recording timeline'); + const sourceDoc = getSourceDoc(); + const link = sourceDoc && DocUtils.MakeLink({ doc: sourceDoc }, { doc: audio.getAnchor() || audio.props.Document }, 'recording annotation:linked recording', 'recording timeline'); link && (link.followLinkLocation = 'add:right'); return link; }); diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index a6dcda4bc..13f036838 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -1,37 +1,35 @@ -import * as interpreter from "words-to-numbers"; +import * as interpreter from 'words-to-numbers'; // @ts-ignore bcz: how are you supposed to include these definitions since dom-speech-recognition isn't a module? -import type { } from "@types/dom-speech-recognition"; -import { Doc, Opt } from "../../fields/Doc"; -import { List } from "../../fields/List"; -import { RichTextField } from "../../fields/RichTextField"; -import { listSpec } from "../../fields/Schema"; -import { Cast, CastCtor } from "../../fields/Types"; -import { AudioField, ImageField } from "../../fields/URLField"; -import { Utils } from "../../Utils"; -import { Docs } from "../documents/Documents"; -import { DocumentType } from "../documents/DocumentTypes"; -import { DictationOverlay } from "../views/DictationOverlay"; -import { DocumentView } from "../views/nodes/DocumentView"; -import { SelectionManager } from "./SelectionManager"; -import { UndoManager } from "./UndoManager"; - +import type {} from '@types/dom-speech-recognition'; +import { Doc, Opt } from '../../fields/Doc'; +import { List } from '../../fields/List'; +import { RichTextField } from '../../fields/RichTextField'; +import { listSpec } from '../../fields/Schema'; +import { Cast, CastCtor } from '../../fields/Types'; +import { AudioField, ImageField } from '../../fields/URLField'; +import { Utils } from '../../Utils'; +import { Docs } from '../documents/Documents'; +import { DocumentType } from '../documents/DocumentTypes'; +import { DictationOverlay } from '../views/DictationOverlay'; +import { DocumentView } from '../views/nodes/DocumentView'; +import { SelectionManager } from './SelectionManager'; +import { UndoManager } from './UndoManager'; /** * This namespace provides a singleton instance of a manager that * handles the listening and text-conversion of user speech. - * + * * The basic manager functionality can be attained by the DictationManager.Controls namespace, which provide * a simple recording operation that returns the interpreted text as a string. - * + * * Additionally, however, the DictationManager also exposes the ability to execute voice commands within Dash. * It stores a default library of registered commands that can be triggered by listen()'ing for a phrase and then * passing the results into the execute() function. - * + * * In addition to compile-time default commands, you can invoke DictationManager.Commands.Register(Independent|Dependent) * to add new commands as classes or components are constructed. */ export namespace DictationManager { - /** * Some type maneuvering to access Webkit's built-in * speech recognizer. @@ -42,27 +40,26 @@ export namespace DictationManager { } } const { webkitSpeechRecognition }: CORE.IWindow = window as any as CORE.IWindow; - export const placeholder = "Listening..."; + export const placeholder = 'Listening...'; export namespace Controls { - - export const Infringed = "unable to process: dictation manager still involved in previous session"; + export const Infringed = 'unable to process: dictation manager still involved in previous session'; const browser = (() => { const identifier = navigator.userAgent.toLowerCase(); - if (identifier.indexOf("safari") >= 0) { - return "Safari"; + if (identifier.indexOf('safari') >= 0) { + return 'Safari'; } - if (identifier.indexOf("chrome") >= 0) { - return "Chrome"; + if (identifier.indexOf('chrome') >= 0) { + return 'Chrome'; } - if (identifier.indexOf("firefox") >= 0) { - return "Firefox"; + if (identifier.indexOf('firefox') >= 0) { + return 'Firefox'; } - return "Unidentified Browser"; + return 'Unidentified Browser'; })(); const unsupported = `listening is not supported in ${browser}`; - const intraSession = ". "; - const interSession = " ... "; + const intraSession = '. '; + const interSession = ' ... '; export let isListening = false; let isManuallyStopped = false; @@ -74,7 +71,7 @@ export namespace DictationManager { export type InterimResultHandler = (results: string) => any; export type ContinuityArgs = { indefinite: boolean } | false; - export type DelimiterArgs = { inter: string, intra: string }; + export type DelimiterArgs = { inter: string; intra: string }; export type ListeningUIStatus = { interim: boolean } | false; export interface ListeningOptions { @@ -105,21 +102,21 @@ export namespace DictationManager { try { results = await (pendingListen = listenImpl(options)); pendingListen = undefined; - // if (results) { - // Utils.CopyText(results); - // if (overlay) { - // DictationOverlay.Instance.isListening = false; - // const execute = options?.tryExecute; - // DictationOverlay.Instance.dictatedPhrase = execute ? results.toLowerCase() : results; - // DictationOverlay.Instance.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true; - // } - // options?.tryExecute && await DictationManager.Commands.execute(results); - // } + if (results) { + Utils.CopyText(results); + if (overlay) { + DictationOverlay.Instance.isListening = false; + const execute = options?.tryExecute; + DictationOverlay.Instance.dictatedPhrase = execute ? results.toLowerCase() : results; + DictationOverlay.Instance.dictationSuccess = execute ? await DictationManager.Commands.execute(results) : true; + } + options?.tryExecute && (await DictationManager.Commands.execute(results)); + } } catch (e: any) { console.log(e); if (overlay) { DictationOverlay.Instance.isListening = false; - DictationOverlay.Instance.dictatedPhrase = results = `dictation error: ${"error" in e ? e.error : "unknown error"}`; + DictationOverlay.Instance.dictatedPhrase = results = `dictation error: ${'error' in e ? e.error : 'unknown error'}`; DictationOverlay.Instance.dictationSuccess = false; } } finally { @@ -131,7 +128,7 @@ export namespace DictationManager { const listenImpl = (options?: Partial) => { if (!recognizer) { - console.log("DictationManager:" + unsupported); + console.log('DictationManager:' + unsupported); return unsupported; } if (isListening) { @@ -146,16 +143,18 @@ export namespace DictationManager { const intra = options?.delimiters?.intra; const inter = options?.delimiters?.inter; - recognizer.onstart = () => console.log("initiating speech recognition session..."); + recognizer.onstart = () => console.log('initiating speech recognition session...'); recognizer.interimResults = handler !== undefined; recognizer.continuous = continuous === undefined ? false : continuous !== false; - recognizer.lang = language === undefined ? "en-US" : language; + recognizer.lang = language === undefined ? 'en-US' : language; recognizer.start(); return new Promise((resolve, reject) => { - recognizer.onerror = (e: any) => { // e is SpeechRecognitionError but where is that defined? - if (!(indefinite && e.error === "no-speech")) { + recognizer.onerror = (e: any) => { + console.log('SPEECH error:', encodeURIComponent); + // e is SpeechRecognitionError but where is that defined? + if (!(indefinite && e.error === 'no-speech')) { recognizer.stop(); resolve(e); //reject(e); @@ -163,9 +162,10 @@ export namespace DictationManager { }; recognizer.onresult = (e: SpeechRecognitionEvent) => { + console.log('RESULT: ', e); current = synthesize(e, intra); let matchedTerminator: string | undefined; - if (options?.terminators && (matchedTerminator = options.terminators.find(end => current ? current.trim().toLowerCase().endsWith(end.toLowerCase()) : false))) { + if (options?.terminators && (matchedTerminator = options.terminators.find(end => (current ? current.trim().toLowerCase().endsWith(end.toLowerCase()) : false)))) { current = matchedTerminator; recognizer.abort(); return complete(); @@ -175,6 +175,7 @@ export namespace DictationManager { }; recognizer.onend = (e: Event) => { + console.log('END: ', e); if (!indefinite || isManuallyStopped) { return complete(); } @@ -187,11 +188,12 @@ export namespace DictationManager { }; const complete = () => { + console.log('COMPLETE:'); if (indefinite) { current && sessionResults.push(current); sessionResults.length && resolve(sessionResults.join(inter || interSession)); } else { - resolve(current || ""); + resolve(current || ''); } current = undefined; sessionResults = []; @@ -201,7 +203,6 @@ export namespace DictationManager { recognizer.onerror = null; recognizer.onend = null; }; - }); }; @@ -222,171 +223,173 @@ export namespace DictationManager { } return transcripts.join(delimiter || intraSession); }; - } - // export namespace Commands { - - // export const dictationFadeDuration = 2000; - - // export type IndependentAction = (target: DocumentView) => any | Promise; - // export type IndependentEntry = { action: IndependentAction, restrictTo?: DocumentType[] }; - - // export type DependentAction = (target: DocumentView, matches: RegExpExecArray) => any | Promise; - // export type DependentEntry = { expression: RegExp, action: DependentAction, restrictTo?: DocumentType[] }; - - // export const RegisterIndependent = (key: string, value: IndependentEntry) => Independent.set(key, value); - // export const RegisterDependent = (entry: DependentEntry) => Dependent.push(entry); - - // export const execute = async (phrase: string) => { - // return UndoManager.RunInBatch(async () => { - // const targets = SelectionManager.Views(); - // if (!targets || !targets.length) { - // return; - // } - - // phrase = phrase.toLowerCase(); - // const entry = Independent.get(phrase); - - // if (entry) { - // let success = false; - // const restrictTo = entry.restrictTo; - // for (const target of targets) { - // if (!restrictTo || validate(target, restrictTo)) { - // await entry.action(target); - // success = true; - // } - // } - // return success; - // } - - // for (const entry of Dependent) { - // const regex = entry.expression; - // const matches = regex.exec(phrase); - // regex.lastIndex = 0; - // if (matches !== null) { - // let success = false; - // const restrictTo = entry.restrictTo; - // for (const target of targets) { - // if (!restrictTo || validate(target, restrictTo)) { - // await entry.action(target, matches); - // success = true; - // } - // } - // return success; - // } - // } - - // return false; - // }, "Execute Command"); - // }; - - // const ConstructorMap = new Map([ - // [DocumentType.COL, listSpec(Doc)], - // [DocumentType.AUDIO, AudioField], - // [DocumentType.IMG, ImageField], - // [DocumentType.IMPORT, listSpec(Doc)], - // [DocumentType.RTF, "string"] - // ]); - - // const tryCast = (view: DocumentView, type: DocumentType) => { - // const ctor = ConstructorMap.get(type); - // if (!ctor) { - // return false; - // } - // return Cast(Doc.GetProto(view.props.Document).data, ctor) !== undefined; - // }; - - // const validate = (target: DocumentView, types: DocumentType[]) => { - // for (const type of types) { - // if (tryCast(target, type)) { - // return true; - // } - // } - // return false; - // }; - - // const interpretNumber = (number: string) => { - // const initial = parseInt(number); - // if (!isNaN(initial)) { - // return initial; - // } - // const converted = interpreter.wordsToNumbers(number, { fuzzy: true }); - // if (converted === null) { - // return NaN; - // } - // return typeof converted === "string" ? parseInt(converted) : converted; - // }; - - // const Independent = new Map([ - - // ["clear", { - // action: (target: DocumentView) => Doc.GetProto(target.props.Document).data = new List(), - // restrictTo: [DocumentType.COL] - // }], - - // ["open fields", { - // action: (target: DocumentView) => { - // const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 }); - // target.props.addDocTab(kvp, "add:right"); - // } - // }], - - // ["new outline", { - // action: (target: DocumentView) => { - // const newBox = Docs.Create.TextDocument("", { _width: 400, _height: 200, title: "My Outline", _autoHeight: true }); - // const proto = newBox.proto!; - // const prompt = "Press alt + r to start dictating here..."; - // const head = 3; - // const anchor = head + prompt.length; - // const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"ordered_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`; - // proto.data = new RichTextField(proseMirrorState); - // proto.backgroundColor = "#eeffff"; - // target.props.addDocTab(newBox, "add:right"); - // } - // }] - - // ]); - - // const Dependent = new Array( - - // { - // expression: /create (\w+) documents of type (image|nested collection)/g, - // action: (target: DocumentView, matches: RegExpExecArray) => { - // const count = interpretNumber(matches[1]); - // const what = matches[2]; - // const dataDoc = Doc.GetProto(target.props.Document); - // const fieldKey = "data"; - // if (isNaN(count)) { - // return; - // } - // for (let i = 0; i < count; i++) { - // let created: Doc | undefined; - // switch (what) { - // case "image": - // created = Docs.Create.ImageDocument("https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg"); - // break; - // case "nested collection": - // created = Docs.Create.FreeformDocument([], {}); - // break; - // } - // created && Doc.AddDocToList(dataDoc, fieldKey, created); - // } - // }, - // restrictTo: [DocumentType.COL] - // }, - - // { - // expression: /view as (freeform|stacking|masonry|schema|tree)/g, - // action: (target: DocumentView, matches: RegExpExecArray) => { - // const mode = matches[1]; - // mode && (target.props.Document._viewType = mode); - // }, - // restrictTo: [DocumentType.COL] - // } - - // ); - - // } - -} \ No newline at end of file + export namespace Commands { + export const dictationFadeDuration = 2000; + + export type IndependentAction = (target: DocumentView) => any | Promise; + export type IndependentEntry = { action: IndependentAction; restrictTo?: DocumentType[] }; + + export type DependentAction = (target: DocumentView, matches: RegExpExecArray) => any | Promise; + export type DependentEntry = { expression: RegExp; action: DependentAction; restrictTo?: DocumentType[] }; + + export const RegisterIndependent = (key: string, value: IndependentEntry) => Independent.set(key, value); + export const RegisterDependent = (entry: DependentEntry) => Dependent.push(entry); + + export const execute = async (phrase: string) => { + return UndoManager.RunInBatch(async () => { + console.log('PHRASE: ' + phrase); + const targets = SelectionManager.Views(); + if (!targets || !targets.length) { + return; + } + + phrase = phrase.toLowerCase(); + const entry = Independent.get(phrase); + + if (entry) { + let success = false; + const restrictTo = entry.restrictTo; + for (const target of targets) { + if (!restrictTo || validate(target, restrictTo)) { + await entry.action(target); + success = true; + } + } + return success; + } + + for (const entry of Dependent) { + const regex = entry.expression; + const matches = regex.exec(phrase); + regex.lastIndex = 0; + if (matches !== null) { + let success = false; + const restrictTo = entry.restrictTo; + for (const target of targets) { + if (!restrictTo || validate(target, restrictTo)) { + await entry.action(target, matches); + success = true; + } + } + return success; + } + } + + return false; + }, 'Execute Command'); + }; + + const ConstructorMap = new Map([ + [DocumentType.COL, listSpec(Doc)], + [DocumentType.AUDIO, AudioField], + [DocumentType.IMG, ImageField], + [DocumentType.IMPORT, listSpec(Doc)], + [DocumentType.RTF, 'string'], + ]); + + const tryCast = (view: DocumentView, type: DocumentType) => { + const ctor = ConstructorMap.get(type); + if (!ctor) { + return false; + } + return Cast(Doc.GetProto(view.props.Document).data, ctor) !== undefined; + }; + + const validate = (target: DocumentView, types: DocumentType[]) => { + for (const type of types) { + if (tryCast(target, type)) { + return true; + } + } + return false; + }; + + const interpretNumber = (number: string) => { + const initial = parseInt(number); + if (!isNaN(initial)) { + return initial; + } + const converted = interpreter.wordsToNumbers(number, { fuzzy: true }); + if (converted === null) { + return NaN; + } + return typeof converted === 'string' ? parseInt(converted) : converted; + }; + + const Independent = new Map([ + [ + 'clear', + { + action: (target: DocumentView) => (Doc.GetProto(target.props.Document).data = new List()), + restrictTo: [DocumentType.COL], + }, + ], + + [ + 'open fields', + { + action: (target: DocumentView) => { + const kvp = Docs.Create.KVPDocument(target.props.Document, { _width: 300, _height: 300 }); + target.props.addDocTab(kvp, 'add:right'); + }, + }, + ], + + [ + 'new outline', + { + action: (target: DocumentView) => { + const newBox = Docs.Create.TextDocument('', { _width: 400, _height: 200, title: 'My Outline', _autoHeight: true }); + const proto = newBox.proto!; + const prompt = 'Press alt + r to start dictating here...'; + const head = 3; + const anchor = head + prompt.length; + const proseMirrorState = `{"doc":{"type":"doc","content":[{"type":"ordered_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"${prompt}"}]}]}]}]},"selection":{"type":"text","anchor":${anchor},"head":${head}}}`; + proto.data = new RichTextField(proseMirrorState); + proto.backgroundColor = '#eeffff'; + target.props.addDocTab(newBox, 'add:right'); + }, + }, + ], + ]); + + const Dependent = new Array( + { + expression: /create (\w+) documents of type (image|nested collection)/g, + action: (target: DocumentView, matches: RegExpExecArray) => { + const count = interpretNumber(matches[1]); + const what = matches[2]; + const dataDoc = Doc.GetProto(target.props.Document); + const fieldKey = 'data'; + if (isNaN(count)) { + return; + } + for (let i = 0; i < count; i++) { + let created: Doc | undefined; + switch (what) { + case 'image': + created = Docs.Create.ImageDocument('https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg'); + break; + case 'nested collection': + created = Docs.Create.FreeformDocument([], {}); + break; + } + created && Doc.AddDocToList(dataDoc, fieldKey, created); + } + }, + restrictTo: [DocumentType.COL], + }, + + { + expression: /view as (freeform|stacking|masonry|schema|tree)/g, + action: (target: DocumentView, matches: RegExpExecArray) => { + const mode = matches[1]; + mode && (target.props.Document._viewType = mode); + }, + restrictTo: [DocumentType.COL], + } + ); + } +} diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 0db9eab69..7628beef2 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -491,7 +491,6 @@ 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); - console.log(fixedAspect); if (fixedAspect) { if ((Math.abs(dW) > Math.abs(dH) && ((!dragBottom && !dragTop) || !modifyNativeDim)) || dragRight) { if (dragRight && modifyNativeDim) { diff --git a/src/client/views/linking/LinkMenuItem.tsx b/src/client/views/linking/LinkMenuItem.tsx index 60428fc98..3f9db2612 100644 --- a/src/client/views/linking/LinkMenuItem.tsx +++ b/src/client/views/linking/LinkMenuItem.tsx @@ -6,14 +6,12 @@ import { observer } from 'mobx-react'; import { Doc, DocListCast } from '../../../fields/Doc'; import { Cast, StrCast } from '../../../fields/Types'; import { WebField } from '../../../fields/URLField'; -import { emptyFunction, returnFalse, setupMoveUpEvents } from '../../../Utils'; +import { emptyFunction, setupMoveUpEvents } from '../../../Utils'; import { DocumentType } from '../../documents/DocumentTypes'; import { DocumentManager } from '../../util/DocumentManager'; import { DragManager } from '../../util/DragManager'; -import { Hypothesis } from '../../util/HypothesisUtils'; import { LinkFollower } from '../../util/LinkFollower'; import { LinkManager } from '../../util/LinkManager'; -import { undoBatch } from '../../util/UndoManager'; import { DocumentView } from '../nodes/DocumentView'; import { LinkDocPreview } from '../nodes/LinkDocPreview'; import './LinkMenuItem.scss'; diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 40b0d285c..849deb04e 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -426,28 +426,26 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - const flattened = this.findInNode(this._editorView!, this._editorView!.state.doc, autoLinkTerm); - this._searchIndex = ++this._searchIndex > flattened.length - 1 ? 0 : this._searchIndex; + this.findInNode(editorView, editorView.state.doc, autoLinkTerm).forEach(sel => { const splitter = editorView.state.schema.marks.splitter.create({ id: Utils.GenerateGuid() }); - const sel = flattened[i]; - tr = tr.addMark(sel.from, sel.to, splitter); - tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => { - if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { - alink = - alink ?? - (DocListCast(this.Document.links).find(link => Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || - DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, LinkManager.AutoKeywords)!); - newAutoLinks.add(alink); - const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }]; - allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? [])); - const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: 'auto term', location: 'add:right' }); - tr = tr.addMark(pos, pos + node.nodeSize, link); - } - }); - tr = tr.removeMark(sel.from, sel.to, splitter); + if (!sel.$anchor.pos || editorView.state.doc.textBetween(sel.$anchor.pos - 1, sel.$to.pos).trim() === autoLinkTerm) { + tr = tr.addMark(sel.from, sel.to, splitter); + tr.doc.nodesBetween(sel.from, sel.to, (node: any, pos: number, parent: any) => { + if (node.firstChild === null && !node.marks.find((m: Mark) => m.type.name === schema.marks.noAutoLinkAnchor.name) && node.marks.find((m: Mark) => m.type.name === schema.marks.splitter.name)) { + alink = + alink ?? + (DocListCast(this.Document.links).find(link => Doc.AreProtosEqual(Cast(link.anchor1, Doc, null), this.rootDoc) && Doc.AreProtosEqual(Cast(link.anchor2, Doc, null), target)) || + DocUtils.MakeLink({ doc: this.props.Document }, { doc: target }, LinkManager.AutoKeywords)!); + newAutoLinks.add(alink); + const allAnchors = [{ href: Doc.localServerPath(target), title: 'a link', anchorId: this.props.Document[Id] }]; + allAnchors.push(...(node.marks.find((m: Mark) => m.type.name === schema.marks.autoLinkAnchor.name)?.attrs.allAnchors ?? [])); + const link = editorView.state.schema.marks.autoLinkAnchor.create({ allAnchors, title: 'auto term', location: 'add:right' }); + tr = tr.addMark(pos, pos + node.nodeSize, link); + } + }); + tr = tr.removeMark(sel.from, sel.to, splitter); + } }); } return tr; @@ -841,6 +839,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { + console.log('RECORD DICTATIN:'); DictationManager.Controls.listen({ interimHandler: this.setDictationContent, continuous: { indefinite: false }, @@ -853,26 +852,35 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent DictationManager.Controls.stop(!abort); setDictationContent = (value: string) => { + console.log('DICTATION CONETNT: ' + value); if (this._editorView && this._recordingStart) { + console.log('STEP 1'); if (this._break) { - const textanchor = Docs.Create.TextanchorDocument({ title: 'dictation anchor' }); - this.addDocument(textanchor); - const link = DocUtils.MakeLinkToActiveAudio(() => textanchor, false).lastElement(); - link && (Doc.GetProto(link).isDictation = true); - if (!link) return; - const audioanchor = Cast(link.anchor2, Doc, null); - if (!audioanchor) return; - audioanchor.backgroundColor = 'tan'; - const audiotag = this._editorView.state.schema.nodes.audiotag.create({ - timeCode: NumCast(audioanchor._timecodeToShow), - audioId: audioanchor[Id], - textId: textanchor[Id], - }); - Doc.GetProto(textanchor).title = 'dictation:' + audiotag.attrs.timeCode; - const tr = this._editorView.state.tr.insert(this._editorView.state.doc.content.size, audiotag); - const tr2 = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size)); - this._editorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size))); + console.log('BREAK'); + const textanchorFunc = () => { + const tanch = Docs.Create.TextanchorDocument({ title: 'dictation anchor' }); + return this.addDocument(tanch) ? tanch : undefined; + }; + const link = DocUtils.MakeLinkToActiveAudio(textanchorFunc, false).lastElement(); + if (link) { + Doc.GetProto(link).isDictation = true; + const audioanchor = Cast(link.anchor2, Doc, null); + const textanchor = Cast(link.anchor1, Doc, null); + if (audioanchor) { + audioanchor.backgroundColor = 'tan'; + const audiotag = this._editorView.state.schema.nodes.audiotag.create({ + timeCode: NumCast(audioanchor._timecodeToShow), + audioId: audioanchor[Id], + textId: textanchor[Id], + }); + Doc.GetProto(textanchor).title = 'dictation:' + audiotag.attrs.timeCode; + const tr = this._editorView.state.tr.insert(this._editorView.state.doc.content.size, audiotag); + const tr2 = tr.setSelection(TextSelection.create(tr.doc, tr.doc.content.size)); + this._editorView.dispatch(tr.setSelection(TextSelection.create(tr2.doc, tr2.doc.content.size))); + } + } } + console.log('FINALIziNG'); const from = this._editorView.state.selection.from; this._break = false; const tr = this._editorView.state.tr.insertText(value); @@ -1468,7 +1476,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent node that wraps the hyerlink + let target = e.target as any; // hrefs are stored on the dataset of the node that wraps the hyerlink while (target && !target.dataset?.targethrefs) target = target.parentElement; FormattedTextBoxComment.update(this, editor, undefined, target?.dataset?.targethrefs, target?.dataset.linkdoc); } @@ -1725,7 +1733,17 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (this._recording = !this._recording))}> +
+ setupMoveUpEvents( + this, + e, + returnFalse, + emptyFunction, + action(e => (this._recording = !this._recording)) + ) + }>
); -- cgit v1.2.3-70-g09d2 From f9cded6755ee236d88e21b3b2d4c6da357d059b2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 8 Aug 2022 15:47:35 -0400 Subject: moved 5 sec anno recording button to documentButtonsBar. made recording button appear only when there is one & click to play recording. --- src/client/util/CurrentUserUtils.ts | 3 +- src/client/util/DictationManager.ts | 4 -- src/client/views/DocumentButtonBar.tsx | 24 ++++++- src/client/views/PropertiesButtons.tsx | 9 --- src/client/views/nodes/DocumentView.scss | 6 +- src/client/views/nodes/DocumentView.tsx | 82 +++++++++++++--------- src/client/views/nodes/button/FontIconBox.tsx | 9 ++- .../views/nodes/formattedText/FormattedTextBox.tsx | 12 +--- 8 files changed, 90 insertions(+), 59 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 7856c913b..d19874720 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -107,7 +107,7 @@ export class CurrentUserUtils { const reqdClickList = reqdTempOpts.map(opts => { const allOpts = {...reqdClickOpts, ...opts.opts}; const clickDoc = tempClicks ? DocListCast(tempClicks.data).find(doc => doc.title === opts.opts.title): undefined; - return DocUtils.AssignOpts(clickDoc, allOpts) ?? Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script,allOpts)); + return DocUtils.AssignOpts(clickDoc, allOpts) ?? Docs.Create.ScriptingDocument(ScriptField.MakeScript(opts.script, allOpts),allOpts); }); const reqdOpts:DocumentOptions = { title: "child click editors", _height:75, system: true}; @@ -633,6 +633,7 @@ export class CurrentUserUtils { { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", scripts: {onClick:'{ return setAlignment("center", _readOnly_);}'} }, { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", scripts: {onClick:'{ return setAlignment("right", _readOnly_);}'} }, { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", scripts: {onClick:'{ return toggleNoAutoLinkAnchor(_readOnly_);}'}}, + { title: "Dictate",toolTip: "Dictate", btnType: ButtonType.ToggleButton, icon: "microphone", scripts: {onClick:'{ return toggleDictation(_readOnly_);}'}}, ]; } diff --git a/src/client/util/DictationManager.ts b/src/client/util/DictationManager.ts index 13f036838..0a61f3478 100644 --- a/src/client/util/DictationManager.ts +++ b/src/client/util/DictationManager.ts @@ -152,7 +152,6 @@ export namespace DictationManager { return new Promise((resolve, reject) => { recognizer.onerror = (e: any) => { - console.log('SPEECH error:', encodeURIComponent); // e is SpeechRecognitionError but where is that defined? if (!(indefinite && e.error === 'no-speech')) { recognizer.stop(); @@ -162,7 +161,6 @@ export namespace DictationManager { }; recognizer.onresult = (e: SpeechRecognitionEvent) => { - console.log('RESULT: ', e); current = synthesize(e, intra); let matchedTerminator: string | undefined; if (options?.terminators && (matchedTerminator = options.terminators.find(end => (current ? current.trim().toLowerCase().endsWith(end.toLowerCase()) : false)))) { @@ -175,7 +173,6 @@ export namespace DictationManager { }; recognizer.onend = (e: Event) => { - console.log('END: ', e); if (!indefinite || isManuallyStopped) { return complete(); } @@ -188,7 +185,6 @@ export namespace DictationManager { }; const complete = () => { - console.log('COMPLETE:'); if (indefinite) { current && sessionResults.push(current); sessionResults.length && resolve(sessionResults.join(inter || interSession)); diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 1d4056759..265df3abc 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -6,7 +6,7 @@ import { observer } from 'mobx-react'; import { Doc } from '../../fields/Doc'; import { RichTextField } from '../../fields/RichTextField'; import { Cast, NumCast } from '../../fields/Types'; -import { emptyFunction, setupMoveUpEvents, simulateMouseClick } from '../../Utils'; +import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils'; import { Docs } from '../documents/Documents'; @@ -334,6 +334,27 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV ); } + + @observable _isRecording = false; + @computed + get recordButton() { + const targetDoc = this.view0?.props.Document; + return !targetDoc ? null : ( + {'Click to record 5 second annotation'}}> +
{ + this._isRecording = true; + this.props.views().map(view => view?.docView?.recordAudioAnnotation(action(() => (this._isRecording = false)))); + }) + )}> + +
+
+ ); + } @observable _aliasDown = false; onTemplateButton = action((e: React.PointerEvent): void => { this._tooltipOpen = false; @@ -411,6 +432,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV ) : null} +
{this.recordButton}
{ Doc.noviceMode ? null :
{this.templateButton}
/*
diff --git a/src/client/views/PropertiesButtons.tsx b/src/client/views/PropertiesButtons.tsx index 39e7b89c1..80c2c7705 100644 --- a/src/client/views/PropertiesButtons.tsx +++ b/src/client/views/PropertiesButtons.tsx @@ -68,14 +68,6 @@ export class PropertiesButtons extends React.Component<{}, {}> { on => 'thumbtack' ); } - @computed get dictationButton() { - return this.propertyToggleBtn( - 'Dictate', - '_showAudio', - on => `${on ? 'Hide' : 'Show'} dictation/recording controls`, - on => 'microphone' - ); - } @computed get maskButton() { return this.propertyToggleBtn( 'Mask', @@ -365,7 +357,6 @@ export class PropertiesButtons extends React.Component<{}, {}> { {toggle(this.titleButton)} {toggle(this.captionButton)} {toggle(this.lockButton)} - {toggle(this.dictationButton, { display: isNovice ? 'none' : '' })} {toggle(this.onClickButton)} {toggle(this.fitWidthButton)} {toggle(this.freezeThumb)} diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index c02692bfb..6ea697a2f 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -65,11 +65,13 @@ width: 25px; height: 25; position: absolute; - top: 10px; - left: 10px; + top: 0; + left: 50%; border-radius: 25px; background: white; opacity: 0.3; + pointer-events: all; + cursor: default; svg { width: 90% !important; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 2524d66dd..4b2bd07ef 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -10,7 +10,7 @@ import { List } from '../../../fields/List'; import { ObjectField } from '../../../fields/ObjectField'; import { listSpec } from '../../../fields/Schema'; import { ScriptField } from '../../../fields/ScriptField'; -import { BoolCast, Cast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; +import { BoolCast, Cast, DocCast, ImageCast, NumCast, ScriptCast, StrCast } from '../../../fields/Types'; import { AudioField } from '../../../fields/URLField'; import { GetEffectiveAcl, SharingPermissions, TraceMobx } from '../../../fields/util'; import { MobileInterface } from '../../../mobile/MobileInterface'; @@ -52,6 +52,8 @@ 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 { @@ -1005,16 +1007,16 @@ export class DocumentViewInternal extends DocComponent (this.props.NativeDimScaling?.() || 1) * this.props.DocumentView().screenToLocalTransform().Scale; @computed get contents() { TraceMobx(); - const audioView = !this.layoutDoc._showAudio ? null : ( -
- -
- ); + const audioAnnosCount = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null)?.length; + const audioTextAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations-text'], listSpec('string'), null); + const audioView = + (!this.props.isSelected() && !this._isHovering) || this.props.renderDepth === -1 || SnappingManager.GetIsDragging() || (!audioAnnosCount && !this._mediaState) ? null : ( + {audioTextAnnos?.lastElement()}
}> +
+ +
+ + ); return (
{ + playAnnotation = () => { const self = this; - const audioAnnos = DocListCast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations']); - if (audioAnnos && audioAnnos.length && this._mediaState === 0) { - const anno = audioAnnos[Math.floor(Math.random() * audioAnnos.length)]; - anno.data instanceof AudioField && - new Howl({ - src: [anno.data.url.href], - format: ['mp3'], - autoplay: true, - loop: false, - volume: 0.5, - onend: function () { - runInAction(() => (self._mediaState = 0)); - }, - }); + const audioAnnos = Cast(this.dataDoc[this.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null); + const anno = audioAnnos.lastElement(); + if (anno instanceof AudioField && this._mediaState === 0) { + new Howl({ + src: [anno.url.href], + format: ['mp3'], + autoplay: true, + loop: false, + volume: 0.5, + onend: function () { + runInAction(() => { + console.log('PLAYED'); + self._mediaState = 0; + }); + }, + }); this._mediaState = 1; } }; - recordAudioAnnotation = () => { + + recordAudioAnnotation = (onEnd?: () => void) => { let gumStream: any; let recorder: any; const self = this; @@ -1176,18 +1181,30 @@ export class DocumentViewInternal extends DocComponent(['']); + DictationManager.Controls.listen({ + interimHandler: value => (audioTextAnnos[audioTextAnnos.length - 1] = value), + continuous: { indefinite: false }, + }).then(results => { + if (results && [DictationManager.Controls.Infringed].includes(results)) { + DictationManager.Controls.stop(); + } + onEnd?.(); + }); + gumStream = stream; recorder = new MediaRecorder(stream); recorder.ondataavailable = async (e: any) => { const [{ result }] = await Networking.UploadFilesToServer(e.data); if (!(result instanceof Error)) { - const audioDoc = Docs.Create.AudioDocument(result.accessPaths.agnostic.client, { title: 'audio test', _width: 200, _height: 32 }); - audioDoc.treeViewExpandedView = 'layout'; - const audioAnnos = Cast(self.dataDoc[self.LayoutFieldKey + '-audioAnnotations'], listSpec(Doc)); + const audioField = new AudioField(result.accessPaths.agnostic.client); + const audioAnnos = Cast(self.dataDoc[self.LayoutFieldKey + '-audioAnnotations'], listSpec(AudioField), null); if (audioAnnos === undefined) { - self.dataDoc[self.LayoutFieldKey + '-audioAnnotations'] = new List([audioDoc]); + self.dataDoc[self.LayoutFieldKey + '-audioAnnotations'] = new List([audioField]); } else { - audioAnnos.push(audioDoc); + audioAnnos.push(audioField); } } }; @@ -1195,6 +1212,7 @@ export class DocumentViewInternal extends DocComponent { recorder.stop(); + DictationManager.Controls.stop(false); runInAction(() => (self._mediaState = 0)); gumStream.getAudioTracks()[0].stop(); }, 5000); diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index 78ef85ec2..d3b95e25a 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -1,7 +1,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; -import { action, computed, observable } from 'mobx'; +import { action, computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { ColorState, SketchPicker } from 'react-color'; @@ -710,6 +710,13 @@ ScriptingGlobals.add(function toggleNoAutoLinkAnchor(checkResult?: boolean) { } if (editorView) RichTextMenu.Instance?.toggleNoAutoLinkAnchor(); }); +ScriptingGlobals.add(function toggleDictation(checkResult?: boolean) { + const textView = RichTextMenu.Instance?.TextView; + if (checkResult) { + return textView?._recording ? Colors.MEDIUM_BLUE : 'transparent'; + } + if (textView) runInAction(() => (textView._recording = !textView._recording)); +}); ScriptingGlobals.add(function toggleBold(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 849deb04e..929cca1ea 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -771,7 +771,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (FormattedTextBox._canAnnotate = !FormattedTextBox._canAnnotate), icon: 'expand-arrows-alt' }); uicontrols.push({ description: !this.Document._noSidebar ? 'Hide Sidebar Handle' : 'Show Sidebar Handle', event: () => (this.layoutDoc._noSidebar = !this.layoutDoc._noSidebar), icon: 'expand-arrows-alt' }); - uicontrols.push({ description: `${this.layoutDoc._showAudio ? 'Hide' : 'Show'} Dictation Icon`, event: () => (this.layoutDoc._showAudio = !this.layoutDoc._showAudio), icon: 'expand-arrows-alt' }); uicontrols.push({ description: 'Show Highlights...', noexpand: true, subitems: highlighting, icon: 'hand-point-right' }); !Doc.noviceMode && uicontrols.push({ @@ -839,7 +838,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - console.log('RECORD DICTATIN:'); DictationManager.Controls.listen({ interimHandler: this.setDictationContent, continuous: { indefinite: false }, @@ -852,11 +850,8 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent DictationManager.Controls.stop(!abort); setDictationContent = (value: string) => { - console.log('DICTATION CONETNT: ' + value); if (this._editorView && this._recordingStart) { - console.log('STEP 1'); if (this._break) { - console.log('BREAK'); const textanchorFunc = () => { const tanch = Docs.Create.TextanchorDocument({ title: 'dictation anchor' }); return this.addDocument(tanch) ? tanch : undefined; @@ -880,7 +875,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent @@ -1744,7 +1738,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent (this._recording = !this._recording)) ) }> - +
); } @@ -1898,7 +1892,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent {this.noSidebar || this.props.dontSelectOnLoad || !this.SidebarShown || this.sidebarWidthPercent === '0%' ? null : this.sidebarCollection} {this.noSidebar || this.Document._noSidebar || this.props.dontSelectOnLoad || this.Document._singleLine ? null : this.sidebarHandle} - {!this.layoutDoc._showAudio ? null : this.audioHandle} + {this.audioHandle} ); -- cgit v1.2.3-70-g09d2 From 59228638d2ac10a3c670bcf94afc57369d201817 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 8 Aug 2022 17:56:57 -0400 Subject: fixed recent bug where dragging actions wasn't undoable. changed closing an icon to deiconify it first so that following a link to it won't show the icon. --- src/client/util/DragManager.ts | 3 ++- src/client/views/DocumentDecorations.tsx | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 947882958..ccd94c56e 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -457,7 +457,8 @@ export namespace DragManager { document.removeEventListener('pointerup', upHandler, true); SnappingManager.SetIsDragging(false); SnappingManager.clearSnapLines(); - if (undo && batch.end()) UndoManager.Undo(); + const ended = batch.end(); + if (undo && ended) UndoManager.Undo(); docsBeingDragged.length = 0; }); var startWindowDragTimer: any; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 7628beef2..3544f74b4 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -201,7 +201,10 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P const finished = action((force?: boolean) => { if ((force || --iconifyingCount === 0) && this._iconifyBatch) { if (this._deleteAfterIconify) { - views.forEach(iconView => iconView.props.removeDocument?.(iconView.props.Document)); + views.forEach(iconView => { + Doc.setNativeView(iconView.props.Document); + iconView.props.removeDocument?.(iconView.props.Document); + }); SelectionManager.DeselectAll(); } this._iconifyBatch?.end(); -- cgit v1.2.3-70-g09d2 From 1f42bac078fc96d2d63c54f62281941d19abf1be Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 9 Aug 2022 09:33:43 -0400 Subject: fixed presbox pinning from crashing/failing by making it a prefetchproxy. update user cache when switching active page. --- .vscode/launch.json | 188 +++++++++------------ .../views/collections/CollectionDockingView.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 27 +-- src/fields/Doc.ts | 3 +- webpack.config.js | 162 +++++++++--------- 5 files changed, 183 insertions(+), 199 deletions(-) (limited to 'src/client/views') diff --git a/.vscode/launch.json b/.vscode/launch.json index 9692c79d9..ce9f50f67 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,110 +4,86 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { - "type": "chrome", - "request": "launch", - "name": "Launch Chrome against localhost", - "sourceMaps": true, - "breakOnLoad": true, - "url": "http://localhost:1050/login", - "webRoot": "${workspaceFolder}", - "runtimeArgs": [ - "--experimental-modules" - ] - }, - { - "type": "chrome", - "request": "launch", - "name": "Launch Chromium against localhost", - "sourceMaps": true, - "breakOnLoad": true, - "url": "http://localhost:1050/login", - "webRoot": "${workspaceFolder}", - "runtimeExecutable": "/usr/bin/chromium", - "runtimeArgs": [ - "--experimental-modules" - ] - }, - { - "type": "firefox", - "request": "launch", - "name": "Launch Firefox against localhost", - "reAttach": true, - "url": "http://localhost:1050/login", - "webRoot": "${workspaceFolder}", - "pathMappings": [ - { - "url": "webpack://dash/src", - "path": "${workspaceFolder}/src" - } - ] - }, - { - "type": "chrome", - "request": "launch", - "name": "Launch Chrome against Dash server", - "sourceMaps": true, - "breakOnLoad": true, - "url": "https://browndash.com/login", - "webRoot": "${workspaceFolder}" - }, - { - "type": "node", - "request": "attach", - "name": "Typescript Server", - "protocol": "inspector", - "port": 9229, - "localRoot": "${workspaceFolder}", - "remoteRoot": "${workspaceFolder}" - }, - { - "type": "node", - "request": "launch", - "name": "Current TS File", - "runtimeExecutable": "npx", - "runtimeArgs": [ - "ts-node-dev", - "--nolazy", - "--inspect", - "--", - "${relativeFile}" - ], - "port": 9229 - }, - { - "type": "node", - "request": "launch", - "name": "Mocha Tests", - "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", - "args": [ - "-r", - "ts-node/register", - "--timeout", - "999999", - "--colors", - "${workspaceFolder}/test/**/*.ts" - ], - "console": "integratedTerminal", - "internalConsoleOptions": "openOnSessionStart", - "protocol": "inspector" - }, - { - "type": "node", - "request": "launch", - "name": "Mocha Current File", - "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", - "args": [ - "-r", - "ts-node/register", - "--timeout", - "999999", - "--colors", - "${file}" - ], - "console": "integratedTerminal", - "internalConsoleOptions": "openOnSessionStart", - "protocol": "inspector" - } + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome against localhost", + "sourceMaps": true, + "breakOnLoad": true, + "url": "http://localhost:1050/login", + "webRoot": "${workspaceFolder}", + "runtimeArgs": ["--experimental-modules"] + }, + { + "type": "chrome", + "request": "launch", + "name": "Launch Chromium against localhost", + "sourceMaps": true, + "breakOnLoad": true, + "url": "http://localhost:1050/login", + "webRoot": "${workspaceFolder}", + "runtimeExecutable": "/usr/bin/chromium", + "runtimeArgs": ["--experimental-modules"] + }, + { + "type": "firefox", + "request": "launch", + "name": "Launch Firefox against localhost", + "reAttach": true, + "url": "http://localhost:1050/login", + "webRoot": "${workspaceFolder}", + "pathMappings": [ + { + "url": "webpack://dash/src", + "path": "${workspaceFolder}/src" + } + ] + }, + { + "type": "chrome", + "request": "launch", + "name": "Launch Chrome against Dash server", + "sourceMaps": true, + "breakOnLoad": true, + "url": "https://browndash.com/login", + "webRoot": "${workspaceFolder}" + }, + { + "type": "node", + "request": "attach", + "name": "Typescript Server", + "protocol": "inspector", + "port": 9229, + "localRoot": "${workspaceFolder}", + "remoteRoot": "${workspaceFolder}" + }, + { + "type": "node", + "request": "launch", + "name": "Current TS File", + "runtimeExecutable": "npx", + "runtimeArgs": ["ts-node-dev", "--nolazy", "--inspect", "--", "${relativeFile}"], + "port": 9229 + }, + { + "type": "node", + "request": "launch", + "name": "Mocha Tests", + "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "args": ["-r", "ts-node/register", "--timeout", "999999", "--colors", "${workspaceFolder}/test/**/*.ts"], + "console": "integratedTerminal", + "internalConsoleOptions": "openOnSessionStart", + "protocol": "inspector" + }, + { + "type": "node", + "request": "launch", + "name": "Mocha Current File", + "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", + "args": ["-r", "ts-node/register", "--timeout", "999999", "--colors", "${file}"], + "console": "integratedTerminal", + "internalConsoleOptions": "openOnSessionStart", + "protocol": "inspector" + } ] -} \ No newline at end of file +} diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 39e2cc17d..6d70cc0d2 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -175,7 +175,7 @@ export class CollectionDockingView extends CollectionSubView() { @undoBatch @action public static AddSplit(document: Doc, pullSide: string, stack?: any, panelName?: string) { - if (document._viewType === CollectionViewType.Docking) return DashboardView.openDashboard(document); + if (document?._viewType === CollectionViewType.Docking) return DashboardView.openDashboard(document); if (!CollectionDockingView.Instance) return false; const tab = Array.from(CollectionDockingView.Instance.tabMap).find(tab => tab.DashDoc === document); if (tab) { diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index 2d08b1c09..ec291e72c 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -212,11 +212,11 @@ export class TabDocView extends React.Component { @action public static PinDoc(docs: Doc | Doc[], pinProps?: PinProps) { const docList = docs instanceof Doc ? [docs] : docs; - const batch = UndoManager.StartBatch('pinning doc'); // all docs will be added to the ActivePresentation as stored on CurrentUserUtils const curPres = Doc.ActivePresentation; - curPres && + if (curPres) { + const batch = UndoManager.StartBatch('pinning doc'); docList.forEach(doc => { // Edge Case 1: Cannot pin document to itself if (doc === curPres) { @@ -301,18 +301,19 @@ export class TabDocView extends React.Component { PresBox.Instance?._selectedArray.clear(); pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array }); - if ( - CollectionDockingView.Instance && - !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'); - setTimeout(() => DocumentManager.Instance.jumpToDocument(docList.lastElement(), false, undefined, []), 100); // keeps the pinned doc in view since the sidebar shifts things + if ( + CollectionDockingView.Instance && + !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'); + 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 } - setTimeout(batch.end, 500); // need to wait until dockingview (goldenlayout) updates all its structurs } componentDidMount() { diff --git a/src/fields/Doc.ts b/src/fields/Doc.ts index 0c7504913..8d56ebf8c 100644 --- a/src/fields/Doc.ts +++ b/src/fields/Doc.ts @@ -211,6 +211,7 @@ export class Doc extends RefField { } public static set ActivePage(val) { Doc.UserDoc().activePage = val; + DocServer.UPDATE_SERVER_CACHE(); } public static get ActiveDashboard() { return DocCast(Doc.UserDoc().activeDashboard); @@ -228,7 +229,7 @@ export class Doc extends RefField { return DocCast(Doc.UserDoc().activePresentation); } public static set ActivePresentation(val) { - Doc.UserDoc().activePresentation = val; + Doc.UserDoc().activePresentation = new PrefetchProxy(val); } constructor(id?: FieldId, forceSave?: boolean) { super(id); diff --git a/webpack.config.js b/webpack.config.js index 5a954db19..01625988c 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,38 +1,38 @@ -var path = require('path'); -var webpack = require('webpack'); -const CopyWebpackPlugin = require("copy-webpack-plugin"); +const path = require('path'); +const webpack = require('webpack'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); -const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); +const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); const plugins = [ - new CopyWebpackPlugin([{ - from: "deploy", - to: path.join(__dirname, "build") - }]), + new CopyWebpackPlugin([ + { + from: 'deploy', + to: path.join(__dirname, 'build'), + }, + ]), new HtmlWebpackPlugin({ title: 'Caching', }), new ForkTsCheckerWebpackPlugin({ tslint: true, // memoryLimit: 4096, - useTypescriptIncrementalApi: true + useTypescriptIncrementalApi: true, }), - new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'], }), - new webpack.ProvidePlugin({ process: 'process/browser', }), + new webpack.ProvidePlugin({ Buffer: ['buffer', 'Buffer'] }), + new webpack.ProvidePlugin({ process: 'process/browser' }), new webpack.HotModuleReplacementPlugin(), ]; function transferEnvironmentVariables() { - const prefix = "_CLIENT_"; - const { - parsed - } = require('dotenv').config(); + const prefix = '_CLIENT_'; + const { parsed } = require('dotenv').config(); if (!parsed) { return; } const resolvedClientSide = Object.keys(parsed).reduce((mapping, envKey) => { if (envKey.startsWith(prefix)) { - mapping[`process.env.${envKey.replace(prefix, "")}`] = JSON.stringify(parsed[envKey]); + mapping[`process.env.${envKey.replace(prefix, '')}`] = JSON.stringify(parsed[envKey]); } return mapping; }, {}); @@ -44,18 +44,18 @@ transferEnvironmentVariables(); module.exports = { mode: 'development', entry: { - bundle: ["./src/client/views/Main.tsx", 'webpack-hot-middleware/client?reload=true'], - viewer: ["./src/debug/Viewer.tsx", 'webpack-hot-middleware/client?reload=true'], - repl: ["./src/debug/Repl.tsx", 'webpack-hot-middleware/client?reload=true'], - test: ["./src/debug/Test.tsx", 'webpack-hot-middleware/client?reload=true'], - inkControls: ["./src/mobile/InkControls.tsx", 'webpack-hot-middleware/client?reload=true'], - mobileInterface: ["./src/client/views/Main.tsx", 'webpack-hot-middleware/client?reload=true'], + bundle: ['./src/client/views/Main.tsx', 'webpack-hot-middleware/client?reload=true'], + viewer: ['./src/debug/Viewer.tsx', 'webpack-hot-middleware/client?reload=true'], + repl: ['./src/debug/Repl.tsx', 'webpack-hot-middleware/client?reload=true'], + test: ['./src/debug/Test.tsx', 'webpack-hot-middleware/client?reload=true'], + inkControls: ['./src/mobile/InkControls.tsx', 'webpack-hot-middleware/client?reload=true'], + mobileInterface: ['./src/client/views/Main.tsx', 'webpack-hot-middleware/client?reload=true'], }, - devtool: "source-map", + devtool: 'source-map', output: { - filename: "[name].js", - path: path.resolve(__dirname, "build"), - publicPath: "/", + filename: '[name].js', + path: path.resolve(__dirname, 'build'), + publicPath: '/', }, resolve: { extensions: ['.js', '.ts', '.tsx'], @@ -68,67 +68,73 @@ module.exports = { crypto: false, assert: false, os: false, - path: require.resolve("path-browserify"), - http: require.resolve("http-browserify"), - https: require.resolve("https-browserify"), - stream: require.resolve("stream-browserify"), - buffer: require.resolve("buffer") - } + path: require.resolve('path-browserify'), + http: require.resolve('http-browserify'), + https: require.resolve('https-browserify'), + stream: require.resolve('stream-browserify'), + buffer: require.resolve('buffer'), + }, }, module: { - rules: [{ - test: [/\.tsx?$/], - use: [{ - loader: 'ts-loader', - options: { - transpileOnly: true - } - }] - }, - { - test: /\.m?js/, - resolve: { - fullySpecified: false - } - }, - { - test: /\.(woff|woff2|ttf|eot|otf|svg)$/, - use: 'file-loader?name=fonts/[name].[ext]!static' - }, - { - test: /\.scss|css$/, - use: [{ - loader: "style-loader" + rules: [ + { + test: [/\.tsx?$/], + use: [ + { + loader: 'ts-loader', + options: { + transpileOnly: true, + }, + }, + ], }, { - loader: "css-loader" + test: /\.m?js/, + resolve: { + fullySpecified: false, + }, }, { - loader: "sass-loader" + test: /\.(woff|woff2|ttf|eot|otf|svg)$/, + use: 'file-loader?name=fonts/[name].[ext]!static', + }, + { + test: /\.scss|css$/, + use: [ + { + loader: 'style-loader', + }, + { + loader: 'css-loader', + }, + { + loader: 'sass-loader', + }, + ], + }, + { + test: /\.(jpg|png|pdf)$/, + use: [ + { + loader: 'file-loader', + }, + ], + }, + { + test: /\.(png|jpg|gif)$/i, + use: [ + { + loader: 'url-loader', + options: { + limit: 8192, + }, + }, + ], }, - ] - }, - { - test: /\.(jpg|png|pdf)$/, - use: [{ - loader: 'file-loader' - }] - }, - { - test: /\.(png|jpg|gif)$/i, - use: [{ - loader: 'url-loader', - options: { - limit: 8192 - } - }] - } ], noParse: [require.resolve('typescript/lib/typescript.js')], }, plugins, - externals: [ - 'child_process' - ], -}; \ No newline at end of file + externals: ['child_process'], +}; -- cgit v1.2.3-70-g09d2 From af0e9bcc4cea84b3f837847143ea8f0a281856df Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 9 Aug 2022 10:37:44 -0400 Subject: added audio annotation play option for following links --- src/client/util/DocumentManager.ts | 17 +++++++++++++++++ src/client/views/linking/LinkEditor.tsx | 32 +++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 5 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index d3ac2f03f..52b643c04 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -12,6 +12,9 @@ import { CollectionFreeFormView } from '../views/collections/collectionFreeForm' import { CollectionView } from '../views/collections/CollectionView'; import { ScriptingGlobals } from './ScriptingGlobals'; import { SelectionManager } from './SelectionManager'; +import { listSpec } from '../../fields/Schema'; +import { AudioField } from '../../fields/URLField'; +const { Howl } = require('howler'); export class DocumentManager { //global holds all of the nodes (regardless of which collection they're in) @@ -186,6 +189,20 @@ export class DocumentManager { } else { finalTargetDoc.hidden && (finalTargetDoc.hidden = undefined); !noSelect && docView?.select(false); + if (originatingDoc?.followLinkAudio) { + const anno = Cast(finalTargetDoc[Doc.LayoutFieldKey(finalTargetDoc) + '-audioAnnotations'], listSpec(AudioField), null).lastElement(); + if (anno) { + if (anno instanceof AudioField) { + new Howl({ + src: [anno.url.href], + format: ['mp3'], + autoplay: true, + loop: false, + volume: 0.5, + }); + } + } + } } finished?.(); }; diff --git a/src/client/views/linking/LinkEditor.tsx b/src/client/views/linking/LinkEditor.tsx index d23d38854..1697062f4 100644 --- a/src/client/views/linking/LinkEditor.tsx +++ b/src/client/views/linking/LinkEditor.tsx @@ -3,7 +3,7 @@ import { Tooltip } from '@material-ui/core'; import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, NumListCast, StrListCast, Field } from '../../../fields/Doc'; -import { DateCast, StrCast, Cast } from '../../../fields/Types'; +import { DateCast, StrCast, Cast, BoolCast } from '../../../fields/Types'; import { LinkManager } from '../../util/LinkManager'; import { undoBatch } from '../../util/UndoManager'; import './LinkEditor.scss'; @@ -21,7 +21,8 @@ interface LinkEditorProps { export class LinkEditor extends React.Component { @observable description = Field.toString(LinkManager.currentLink?.description as any as Field); @observable relationship = StrCast(LinkManager.currentLink?.linkRelationship); - @observable zoomFollow = StrCast(this.props.sourceDoc.followLinkZoom); + @observable zoomFollow = BoolCast(this.props.sourceDoc.followLinkZoom); + @observable audioFollow = BoolCast(this.props.sourceDoc.followLinkAudio); @observable openDropdown: boolean = false; @observable private buttonColor: string = ''; @observable private relationshipButtonColor: string = ''; @@ -147,8 +148,12 @@ export class LinkEditor extends React.Component { this.relationship = e.target.value; }; @action - handleZoomFollowChange = (e: React.ChangeEvent) => { - this.props.sourceDoc.followLinkZoom = e.target.checked; + handleZoomFollowChange = () => { + this.props.sourceDoc.followLinkZoom = !this.props.sourceDoc.followLinkZoom; + }; + @action + handleAudioFollowChange = () => { + this.props.sourceDoc.followLinkAudio = !this.props.sourceDoc.followLinkAudio; }; @action handleRelationshipSearchChange = (result: string) => { @@ -191,7 +196,23 @@ export class LinkEditor extends React.Component {
Zoom To Link Target:
- + setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.handleZoomFollowChange)} defaultChecked={this.zoomFollow} /> +
+
+ + ); + } + + @computed + get editAudioFollow() { + //NOTE: confusingly, the classnames for the following relationship JSX elements are the same as the for the description elements for shared CSS + console.log('AudioFollow:' + this.audioFollow); + return ( +
+
Play Target Audio:
+
+
+ setupMoveUpEvents(this, e, returnFalse, emptyFunction, this.handleAudioFollowChange)} defaultChecked={this.audioFollow} />
@@ -329,6 +350,7 @@ export class LinkEditor extends React.Component { {this.editDescription} {this.editRelationship} {this.editZoomFollow} + {this.editAudioFollow}
Show Anchor: {this.props.linkDoc.hidden ? 'Show Link Anchor' : 'Hide Link Anchor'}
}> -- cgit v1.2.3-70-g09d2 From bca0026764fe554e1066b2f432e749371450d239 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 9 Aug 2022 11:35:00 -0400 Subject: added an audio annotation button to the anchor menu bar. --- src/client/views/DocumentButtonBar.tsx | 12 +- src/client/views/MarqueeAnnotator.tsx | 1 + src/client/views/SidebarAnnos.tsx | 1 + src/client/views/nodes/DocumentView.tsx | 35 ++-- .../views/nodes/formattedText/FormattedTextBox.tsx | 11 ++ src/client/views/pdf/AnchorMenu.tsx | 14 ++ src/fields/documentSchemas.ts | 182 ++++++++++----------- 7 files changed, 145 insertions(+), 111 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 265df3abc..81e417fca 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -21,7 +21,7 @@ import './DocumentButtonBar.scss'; import { Colors } from './global/globalEnums'; import { MetadataEntryMenu } from './MetadataEntryMenu'; import { DocumentLinksButton } from './nodes/DocumentLinksButton'; -import { DocumentView } from './nodes/DocumentView'; +import { DocumentView, DocumentViewInternal } from './nodes/DocumentView'; import { DashFieldView } from './nodes/formattedText/DashFieldView'; import { GoogleRef } from './nodes/formattedText/FormattedTextBox'; import { TemplateMenu } from './TemplateMenu'; @@ -347,7 +347,15 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV onClick={undoBatch( action(e => { this._isRecording = true; - this.props.views().map(view => view?.docView?.recordAudioAnnotation(action(() => (this._isRecording = false)))); + this.props.views().map( + view => + view && + DocumentViewInternal.recordAudioAnnotation( + view.dataDoc, + view.LayoutFieldKey, + action(() => (this._isRecording = false)) + ) + ); }) )}> diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index b01ee5f42..f90ad8bb5 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -65,6 +65,7 @@ export class MarqueeAnnotator extends React.Component { AnchorMenu.Instance.OnCrop = (e: PointerEvent) => this.props.anchorMenuCrop?.(this.highlight('rgba(173, 216, 230, 0.75)', true), true); AnchorMenu.Instance.OnClick = (e: PointerEvent) => this.props.anchorMenuClick?.()?.(this.highlight('rgba(173, 216, 230, 0.75)', true)); + AnchorMenu.Instance.OnAudio = unimplementedFunction; AnchorMenu.Instance.Highlight = this.highlight; AnchorMenu.Instance.GetAnchor = (savedAnnotations?: ObservableMap) => this.highlight('rgba(173, 216, 230, 0.75)', true, savedAnnotations); AnchorMenu.Instance.onMakeAnchor = AnchorMenu.Instance.GetAnchor; diff --git a/src/client/views/SidebarAnnos.tsx b/src/client/views/SidebarAnnos.tsx index 1c14c7cd5..90d9c3c43 100644 --- a/src/client/views/SidebarAnnos.tsx +++ b/src/client/views/SidebarAnnos.tsx @@ -74,6 +74,7 @@ export class SidebarAnnos extends React.Component { DocUtils.MakeLink({ doc: anchor }, { doc: target }, 'inline comment:comment on'); this.addDocument(target); this._stackRef.current?.focusDocument(target); + return target; }; makeDocUnfiltered = (doc: Doc) => { if (DocListCast(this.props.rootDoc[this.sidebarKey]).includes(doc)) { diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index 4b2bd07ef..f87581875 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -206,7 +206,6 @@ export class DocumentViewInternal extends DocComponent (this.layoutDoc._showAudio = !this.layoutDoc._showAudio)), icon: 'microphone' }); const existingOnClick = cm.findByDescription('OnClick...'); const onClicks: ContextMenuProps[] = existingOnClick && 'subitems' in existingOnClick ? existingOnClick.subitems : []; @@ -1010,10 +1008,15 @@ export class DocumentViewInternal extends DocComponent{audioTextAnnos?.lastElement()}}>
- +
); @@ -1154,7 +1157,7 @@ export class DocumentViewInternal extends DocComponent { - console.log('PLAYED'); - self._mediaState = 0; + self.dataDoc.audioAnnoState = 0; }); }, }); - this._mediaState = 1; + this.dataDoc.audioAnnoState = 1; } }; - recordAudioAnnotation = (onEnd?: () => void) => { + static recordAudioAnnotation(dataDoc: Doc, field: string, onEnd?: () => void) { let gumStream: any; let recorder: any; - const self = this; navigator.mediaDevices .getUserMedia({ audio: true, }) .then(function (stream) { - let audioTextAnnos = Cast(self.dataDoc[self.LayoutFieldKey + '-audioAnnotations-text'], listSpec('string'), null); + let audioTextAnnos = Cast(dataDoc[field + '-audioAnnotations-text'], listSpec('string'), null); if (audioTextAnnos) audioTextAnnos.push(''); - else audioTextAnnos = self.dataDoc[self.LayoutFieldKey + '-audioAnnotations-text'] = new List(['']); + else audioTextAnnos = dataDoc[field + '-audioAnnotations-text'] = new List(['']); DictationManager.Controls.listen({ interimHandler: value => (audioTextAnnos[audioTextAnnos.length - 1] = value), continuous: { indefinite: false }, @@ -1200,24 +1201,24 @@ export class DocumentViewInternal extends DocComponent (self._mediaState = 2)); + runInAction(() => (dataDoc.audioAnnoState = 2)); recorder.start(); setTimeout(() => { recorder.stop(); DictationManager.Controls.stop(false); - runInAction(() => (self._mediaState = 0)); + runInAction(() => (dataDoc.audioAnnoState = 0)); gumStream.getAudioTracks()[0].stop(); }, 5000); }); - }; + } captionStyleProvider = (doc: Opt, props: Opt, property: string) => this.props?.styleProvider?.(doc, props, property + ':caption'); @computed get innards() { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 929cca1ea..223441b3b 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -63,6 +63,7 @@ import applyDevTools = require('prosemirror-dev-tools'); import React = require('react'); import { text } from 'body-parser'; import { CollectionTreeView } from '../../collections/CollectionTreeView'; +import { DocumentViewInternal } from '../DocumentView'; const translateGoogleApi = require('translate-google-api'); export interface FormattedTextBoxProps { @@ -249,6 +250,16 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { + !this.layoutDoc.showSidebar && this.toggleSidebar(); + const anchor = this.getAnchor(); + const target = this._sidebarRef.current?.anchorMenuClick(anchor); + if (target) { + anchor.followLinkAudio = true; + DocumentViewInternal.recordAudioAnnotation(Doc.GetProto(target), Doc.LayoutFieldKey(target)); + target.title = ComputedField.MakeFunction(`self["text-audioAnnotations-text"].lastElement()`); + } + }; AnchorMenu.Instance.Highlight = action((color: string, isLinkButton: boolean) => { this._editorView?.state && RichTextMenu.Instance.setHighlight(color, this._editorView, this._editorView?.dispatch); return undefined; diff --git a/src/client/views/pdf/AnchorMenu.tsx b/src/client/views/pdf/AnchorMenu.tsx index 1a1120b6c..ee2ae10a7 100644 --- a/src/client/views/pdf/AnchorMenu.tsx +++ b/src/client/views/pdf/AnchorMenu.tsx @@ -49,6 +49,7 @@ export class AnchorMenu extends AntimodeMenu { public OnCrop: (e: PointerEvent) => void = unimplementedFunction; public OnClick: (e: PointerEvent) => void = unimplementedFunction; + public OnAudio: (e: PointerEvent) => void = unimplementedFunction; public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction; public Highlight: (color: string, isPushpin: boolean) => Opt = (color: string, isPushpin: boolean) => undefined; @@ -92,6 +93,10 @@ export class AnchorMenu extends AntimodeMenu { ); }; + audioDown = (e: React.PointerEvent) => { + setupMoveUpEvents(this, e, returnFalse, returnFalse, e => this.OnAudio?.(e)); + }; + cropDown = (e: React.PointerEvent) => { setupMoveUpEvents( this, @@ -196,6 +201,15 @@ export class AnchorMenu extends AntimodeMenu { , + AnchorMenu.Instance.OnAudio === unimplementedFunction ? ( + <> + ) : ( + {'Click to Record Annotation'}}> + + + ), //NOTE: link popup is currently in progress {'Find document to link to selected text'}}> diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index 223441b3b..e027ab0bd 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -1884,7 +1884,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent -- cgit v1.2.3-70-g09d2 From a63f017c213563728f45f2caa7415843f50f3559 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 9 Aug 2022 19:50:33 -0400 Subject: fixed click on menu button for text box with hyperlink to not bring up buttonBar. fixed right click on hyperlink to just bring up button bar, but not native context menu. fixe stacking view child pointer events so that sidebar documents in lightbox are editable. --- src/client/views/DocumentButtonBar.tsx | 7 +------ src/client/views/collections/CollectionStackingView.tsx | 5 +++-- src/client/views/nodes/formattedText/FormattedTextBox.tsx | 10 +++++----- 3 files changed, 9 insertions(+), 13 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 81e417fca..1f8550ad6 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -274,12 +274,7 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV get menuButton() { const targetDoc = this.view0?.props.Document; return !targetDoc ? null : ( - -
{`Open Context Menu`}
- - }> + {`Open Context Menu`}}>
this.openContextMenu(e)}>
diff --git a/src/client/views/collections/CollectionStackingView.tsx b/src/client/views/collections/CollectionStackingView.tsx index ef68cadd7..d4efef47a 100644 --- a/src/client/views/collections/CollectionStackingView.tsx +++ b/src/client/views/collections/CollectionStackingView.tsx @@ -300,9 +300,10 @@ export class CollectionStackingView extends CollectionSubView this.props.isSelected() || this.props.isContentActive(); + isContentActive = () => (this.props.isSelected() || this.props.isContentActive() ? true : this.props.isSelected() === false || this.props.isContentActive() === false ? false : undefined); - isChildContentActive = () => this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)); + isChildContentActive = () => + this.props.isDocumentActive?.() && (this.props.childDocumentsActive?.() || BoolCast(this.rootDoc.childDocumentsActive)) ? true : this.props.childDocumentsActive?.() === false || this.rootDoc.childDocumentsActive === false ? false : undefined; // this is what renders the document that you see on the screen // called in Children: this actually adds a document to our children list getDisplayDoc(doc: Doc, width: () => number) { diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index e027ab0bd..f61533619 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -703,9 +703,9 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent node that wraps the hyerlink + let target = e.target as any; // hrefs are stored on the database of the
node that wraps the hyerlink while (target && !target.dataset?.targethrefs) target = target.parentElement; - if (target) { + if (target && !(e.nativeEvent as any).dash) { const hrefs = (target.dataset?.targethrefs as string) ?.trim() .split(' ') @@ -724,6 +724,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent { - if (!this._editorView?.state.selection.empty && FormattedTextBox._canAnnotate) this.setupAnchorMenu(); + if (!this._editorView?.state.selection.empty && FormattedTextBox._canAnnotate && !(e.nativeEvent as any).dash) this.setupAnchorMenu(); if (!this._downEvent) return; this._downEvent = false; - if (this.props.isContentActive(true)) { + if (this.props.isContentActive(true) && !(e.nativeEvent as any).dash) { const editor = this._editorView!; const pcords = editor.posAtCoords({ left: e.clientX, top: e.clientY }); !this.props.isSelected(true) && editor.dispatch(editor.state.tr.setSelection(new TextSelection(editor.state.doc.resolve(pcords?.pos || 0)))); @@ -1795,7 +1796,6 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent ) : (
setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => SelectionManager.SelectView(this.props.DocumentView?.()!, false), true)}> - //@ts-ignore Date: Wed, 10 Aug 2022 14:52:04 -0400 Subject: cleaned up pinning documents with an activeFrame index (and special case of isInkMask). removed text-color from pres boxes which was breaking opacity when docs had an appearFrame > 0. --- src/client/views/DocumentButtonBar.tsx | 18 +- src/client/views/collections/CollectionMenu.tsx | 25 ++- src/client/views/collections/TabDocView.tsx | 15 +- .../views/nodes/CollectionFreeFormDocumentView.tsx | 208 ++++++++++++--------- src/client/views/nodes/DocumentView.tsx | 4 +- src/client/views/nodes/trails/PresBox.tsx | 23 +-- 6 files changed, 163 insertions(+), 130 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/DocumentButtonBar.tsx b/src/client/views/DocumentButtonBar.tsx index 1f8550ad6..40fc8dae6 100644 --- a/src/client/views/DocumentButtonBar.tsx +++ b/src/client/views/DocumentButtonBar.tsx @@ -5,7 +5,7 @@ import { action, computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import { Doc } from '../../fields/Doc'; import { RichTextField } from '../../fields/RichTextField'; -import { Cast, NumCast } from '../../fields/Types'; +import { Cast, DocCast, NumCast } from '../../fields/Types'; import { emptyFunction, returnFalse, setupMoveUpEvents, simulateMouseClick } from '../../Utils'; import { GoogleAuthenticationManager } from '../apis/GoogleAuthenticationManager'; import { Pulls, Pushes } from '../apis/google_docs/GoogleApiClientUtils'; @@ -238,15 +238,13 @@ export class DocumentButtonBar extends React.Component<{ views: () => (DocumentV
- TabDocView.PinDoc( - this.props - .views() - .filter(v => v) - .map(dv => dv!.rootDoc), - { pinDocView: true } - ) - }> + onClick={e => { + const docs = this.props + .views() + .filter(v => v) + .map(dv => dv!.rootDoc); + TabDocView.PinDoc(docs, { pinDocView: true, activeFrame: Cast(docs.lastElement()?.activeFrame, 'number', null) }); + }}>
diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index cfbcec2d6..8432619de 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -769,6 +769,21 @@ export class CollectionFreeFormViewChrome extends React.Component { @@ -1569,13 +1584,5 @@ export class CollectionGridViewChrome extends React.Component { pinDoc.presEndTime = NumCast(doc.clipEnd, duration); } //save position - if (pinProps?.setPosition || pinDoc.isInkMask) { - pinDoc.setPosition = true; - pinDoc.y = doc.y; - pinDoc.x = doc.x; - pinDoc.presHideAfter = true; - pinDoc.presHideBefore = true; + if (pinProps?.activeFrame !== undefined) { + pinDoc.presActiveFrame = pinProps?.activeFrame; pinDoc.title = doc.title + ' (move)'; - pinDoc.presMovement = PresMovement.None; + 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?._selectedArray.clear(); diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 284584a3d..e154e8445 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,26 +1,26 @@ -import { action, computed, observable, trace } from "mobx"; -import { observer } from "mobx-react"; -import { Doc, Opt } from "../../../fields/Doc"; -import { List } from "../../../fields/List"; -import { listSpec } from "../../../fields/Schema"; -import { ComputedField } from "../../../fields/ScriptField"; -import { Cast, NumCast, StrCast } from "../../../fields/Types"; -import { TraceMobx } from "../../../fields/util"; -import { DashColor, numberRange, OmitKeys } from "../../../Utils"; -import { DocumentManager } from "../../util/DocumentManager"; -import { SelectionManager } from "../../util/SelectionManager"; -import { Transform } from "../../util/Transform"; -import { CollectionFreeFormView } from "../collections/collectionFreeForm/CollectionFreeFormView"; -import { DocComponent } from "../DocComponent"; -import { InkingStroke } from "../InkingStroke"; -import { StyleProp } from "../StyleProvider"; -import "./CollectionFreeFormDocumentView.scss"; -import { DocumentView, DocumentViewProps } from "./DocumentView"; -import React = require("react"); +import { action, computed, observable, trace } from 'mobx'; +import { observer } from 'mobx-react'; +import { Doc, Opt } from '../../../fields/Doc'; +import { List } from '../../../fields/List'; +import { listSpec } from '../../../fields/Schema'; +import { ComputedField } from '../../../fields/ScriptField'; +import { Cast, NumCast, StrCast } from '../../../fields/Types'; +import { TraceMobx } from '../../../fields/util'; +import { DashColor, numberRange, OmitKeys } from '../../../Utils'; +import { DocumentManager } from '../../util/DocumentManager'; +import { SelectionManager } from '../../util/SelectionManager'; +import { Transform } from '../../util/Transform'; +import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; +import { DocComponent } from '../DocComponent'; +import { InkingStroke } from '../InkingStroke'; +import { StyleProp } from '../StyleProvider'; +import './CollectionFreeFormDocumentView.scss'; +import { DocumentView, DocumentViewProps } from './DocumentView'; +import React = require('react'); export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { - dataProvider?: (doc: Doc, replica: string) => { x: number, y: number, zIndex?: number, opacity?: number, highlight?: boolean, z: number, transition?: string } | undefined; - sizeProvider?: (doc: Doc, replica: string) => { width: number, height: number } | undefined; + dataProvider?: (doc: Doc, replica: string) => { x: number; y: number; zIndex?: number; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined; + sizeProvider?: (doc: Doc, replica: string) => { width: number; height: number } | undefined; renderCutoffProvider: (doc: Doc) => boolean; zIndex?: number; highlight?: boolean; @@ -32,29 +32,51 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { @observer export class CollectionFreeFormDocumentView extends DocComponent() { - public static animFields = ["_height", "_width", "x", "y", "_scrollTop", "opacity"]; // fields that are configured to be animatable using animation frames + public static animFields = ['_height', '_width', 'x', 'y', '_scrollTop', 'opacity']; // fields that are configured to be animatable using animation frames @observable _animPos: number[] | undefined = undefined; @observable _contentView: DocumentView | undefined | null; - get displayName() { return "CollectionFreeFormDocumentView(" + this.rootDoc.title + ")"; } // this makes mobx trace() statements more descriptive - get maskCentering() { return this.props.Document.isInkMask ? InkingStroke.MaskDim / 2 : 0; } - get transform() { return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${NumCast(this.Document.jitterRotation, this.props.jitterRotation)}deg)`; } - get X() { return this.dataProvider ? this.dataProvider.x : NumCast(this.Document.x); } - get Y() { return this.dataProvider ? this.dataProvider.y : NumCast(this.Document.y); } - get ZInd() { return this.dataProvider ? this.dataProvider.zIndex : NumCast(this.Document.zIndex); } - get Opacity() { return this.dataProvider ? this.dataProvider.opacity : undefined; } - get Highlight() { return this.dataProvider?.highlight; } - @computed get ShowTitle() { return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as (Opt); } - @computed get dataProvider() { return this.props.dataProvider?.(this.props.Document, this.props.replica); } - @computed get sizeProvider() { return this.props.sizeProvider?.(this.props.Document, this.props.replica); } + get displayName() { + return 'CollectionFreeFormDocumentView(' + this.rootDoc.title + ')'; + } // this makes mobx trace() statements more descriptive + get maskCentering() { + return this.props.Document.isInkMask ? InkingStroke.MaskDim / 2 : 0; + } + get transform() { + return `translate(${this.X - this.maskCentering}px, ${this.Y - this.maskCentering}px) rotate(${NumCast(this.Document.jitterRotation, this.props.jitterRotation)}deg)`; + } + get X() { + return this.dataProvider ? this.dataProvider.x : NumCast(this.Document.x); + } + get Y() { + return this.dataProvider ? this.dataProvider.y : NumCast(this.Document.y); + } + get ZInd() { + return this.dataProvider ? this.dataProvider.zIndex : NumCast(this.Document.zIndex); + } + get Opacity() { + return this.dataProvider ? this.dataProvider.opacity : undefined; + } + get Highlight() { + return this.dataProvider?.highlight; + } + @computed get ShowTitle() { + return this.props.styleProvider?.(this.rootDoc, this.props, StyleProp.ShowTitle) as Opt; + } + @computed get dataProvider() { + return this.props.dataProvider?.(this.props.Document, this.props.replica); + } + @computed get sizeProvider() { + return this.props.sizeProvider?.(this.props.Document, this.props.replica); + } styleProvider = (doc: Doc | undefined, props: Opt, property: string) => { if (property === StyleProp.Opacity && doc === this.layoutDoc) return this.Opacity; // only change the opacity for this specific document, not its children return this.props.styleProvider?.(doc, props, property); - } + }; public static getValues(doc: Doc, time: number) { return CollectionFreeFormDocumentView.animFields.reduce((p, val) => { - 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] = 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); return p; }, {} as { [val: string]: Opt }); } @@ -62,35 +84,43 @@ export class CollectionFreeFormDocumentView extends DocComponent }) { const timecode = Math.round(time); Object.keys(vals).forEach(val => { - const findexed = Cast(d[`${val}-indexed`], listSpec("number"), []).slice(); + const findexed = Cast(d[`${val}-indexed`], listSpec('number'), []).slice(); findexed[timecode] = vals[val] as any as number; d[`${val}-indexed`] = new List(findexed); }); - d.appearFrame && (d["text-color"] = - d.appearFrame === timecode + 1 ? "red" : - d.appearFrame < timecode + 1 ? "grey" : "black"); } public static updateKeyframe(docs: Doc[], time: number, targetDoc?: Doc) { const timecode = Math.round(time); - docs.forEach(action(doc => { - doc._viewTransition = doc.dataTransition = "all 1s"; - doc["text-color"] = - !doc.appearFrame || !targetDoc ? "black" : - doc.appearFrame === timecode + 1 ? StrCast(targetDoc["pres-text-color"]) : - doc.appearFrame < timecode + 1 ? StrCast(targetDoc["pres-text-viewed-color"]) : - "black"; - CollectionFreeFormDocumentView.animFields.forEach(val => { - const findexed = Cast(doc[`${val}-indexed`], listSpec("number"), null); - findexed?.length <= timecode + 1 && findexed.push(undefined as any as number); - }); - })); - setTimeout(() => docs.forEach(doc => { doc._viewTransition = undefined; doc.dataTransition = "inherit"; }), 1010); + docs.forEach( + action(doc => { + doc._viewTransition = doc.dataTransition = 'all 1s'; + CollectionFreeFormDocumentView.animFields.forEach(val => { + const findexed = Cast(doc[`${val}-indexed`], listSpec('number'), null); + findexed?.length <= timecode + 1 && findexed.push(undefined as any as number); + }); + }) + ); + setTimeout( + () => + docs.forEach(doc => { + doc._viewTransition = undefined; + doc.dataTransition = 'inherit'; + }), + 1010 + ); } public static gotoKeyframe(docs: Doc[]) { - docs.forEach(doc => doc._viewTransition = doc.dataTransition = "all 1s"); - setTimeout(() => docs.forEach(doc => { doc._viewTransition = undefined; doc.dataTransition = "inherit"; }), 1010); + docs.forEach(doc => (doc._viewTransition = doc.dataTransition = 'all 1s')); + setTimeout( + () => + docs.forEach(doc => { + doc._viewTransition = undefined; + doc.dataTransition = 'inherit'; + }), + 1010 + ); } public static setupZoom(doc: Doc, targDoc: Doc) { @@ -102,21 +132,22 @@ export class CollectionFreeFormDocumentView extends DocComponent { if (doc.appearFrame === undefined) doc.appearFrame = currTimecode; - if (!doc["opacity-indexed"]) { // opacity is unlike other fields because it's value should not be undefined before it appears to enable it to fade-in - doc["opacity-indexed"] = new List(numberRange(currTimecode + 1).map(t => !doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1)); + if (!doc['opacity-indexed']) { + // opacity is unlike other fields because it's value should not be undefined before it appears to enable it to fade-in + doc['opacity-indexed'] = new List(numberRange(currTimecode + 1).map(t => (!doc.z && makeAppear && t < NumCast(doc.appearFrame) ? 0 : 1))); } - CollectionFreeFormDocumentView.animFields.forEach(val => doc[val] = ComputedField.MakeInterpolated(val, "activeFrame", doc, currTimecode)); - doc.activeFrame = ComputedField.MakeFunction("self.context?._currentFrame||0"); - doc.dataTransition = "inherit"; + CollectionFreeFormDocumentView.animFields.forEach(val => (doc[val] = ComputedField.MakeInterpolated(val, 'activeFrame', doc, currTimecode))); + doc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0'); + doc.dataTransition = 'inherit'; }); } @@ -131,7 +162,7 @@ export class CollectionFreeFormDocumentView extends DocComponent SelectionManager.SelectView(DocumentManager.Instance.getDocumentView(topDoc, container)!, false), 0); } - } + }; nudge = (x: number, y: number) => { this.props.Document.x = NumCast(this.props.Document.x) + x; this.props.Document.y = NumCast(this.props.Document.y) + y; - } - panelWidth = () => (this.sizeProvider?.width || this.props.PanelWidth?.()); - panelHeight = () => (this.sizeProvider?.height || this.props.PanelHeight?.()); + }; + panelWidth = () => this.sizeProvider?.width || this.props.PanelWidth?.(); + panelHeight = () => this.sizeProvider?.height || this.props.PanelHeight?.(); screenToLocalTransform = (): Transform => this.props.ScreenToLocalTransform().translate(-this.X, -this.Y); focusDoc = (doc: Doc) => this.props.focus(doc); returnThis = () => this; @@ -163,24 +194,27 @@ export class CollectionFreeFormDocumentView extends DocComponent - {this.props.renderCutoffProvider(this.props.Document) ? -
- : - this._contentView = r)} /> - } -
; + const mixBlendMode = (StrCast(this.layoutDoc.mixBlendMode) as any) || (typeof background === 'string' && background && !background.startsWith('linear') && DashColor(background).alpha() !== 1 ? 'multiply' : undefined); + return ( +
+ {this.props.renderCutoffProvider(this.props.Document) ? ( +
+ ) : ( + (this._contentView = r))} /> + )} +
+ ); } } diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index f87581875..f9ef85595 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -1640,8 +1640,8 @@ export class DocumentView extends React.Component { render() { TraceMobx(); - const xshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined); - const yshift = () => (this.props.Document.isInkMask ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined); + const xshift = () => (this.props.Document.isInkMask && !this.props.LayoutTemplateString && !this.props.LayoutTemplate?.() ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined); + const yshift = () => (this.props.Document.isInkMask && !this.props.LayoutTemplateString && !this.props.LayoutTemplate?.() ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined); const isPresTreeElement: boolean = this.props.treeViewDoc?.type === DocumentType.PRES; const isButton: boolean = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear; return ( diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 6e5eb3300..3bbdce1e4 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -29,10 +29,11 @@ import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentVie import { FieldView, FieldViewProps } from '../FieldView'; import './PresBox.scss'; import { PresEffect, PresMovement, PresStatus } from './PresEnums'; +import { CollectionFreeFormViewChrome } from '../../collections/CollectionMenu'; export interface PinProps { audioRange?: boolean; - setPosition?: boolean; + activeFrame?: number; hidePresBox?: boolean; pinWithView?: PinViewProps; pinDocView?: boolean; // whether the current view specs of the document should be saved the pinned document @@ -509,7 +510,7 @@ export class PresBox extends ViewBoxBaseComponent() { this.childDocs.forEach((doc, index) => { const curDoc = Cast(doc, Doc, null); const tagDoc = Cast(curDoc.presentationTargetDoc, Doc, null); - if (tagDoc) tagDoc.opacity = 1; + //if (tagDoc) tagDoc.opacity = 1; const itemIndexes: number[] = this.getAllIndexes(this.tagDocs, tagDoc); const curInd: number = itemIndexes.indexOf(index); if (tagDoc === this.layoutDoc.presCollection) { @@ -519,7 +520,7 @@ export class PresBox extends ViewBoxBaseComponent() { } else if (curDoc.presHideBefore) { if (index > this.itemIndex) { tagDoc.opacity = 0; - } else if (!curDoc.presHideAfter) { + } else if (index === this.itemIndex || !curDoc.presHideAfter) { tagDoc.opacity = 1; } } @@ -527,7 +528,7 @@ export class PresBox extends ViewBoxBaseComponent() { } else if (curDoc.presHideAfter) { if (index < this.itemIndex) { tagDoc.opacity = 0; - } else if (!curDoc.presHideBefore) { + } else if (index === this.itemIndex || !curDoc.presHideBefore) { tagDoc.opacity = 1; } } @@ -821,17 +822,9 @@ export class PresBox extends ViewBoxBaseComponent() { if (doc.presPinView || doc.presentationTargetDoc === this.layoutDoc.presCollection) setTimeout(() => this.updateCurrentPresentation(context), 0); else this.updateCurrentPresentation(context); - if (this.activeItem.setPosition && this.activeItem.y !== undefined && this.activeItem.x !== undefined && this.targetDoc.x !== undefined && this.targetDoc.y !== undefined) { - const timer = (ms: number) => new Promise(res => (this._presTimer = setTimeout(res, ms))); - const time = 10; - const ydiff = NumCast(this.activeItem.y) - NumCast(this.targetDoc.y); - const xdiff = NumCast(this.activeItem.x) - NumCast(this.targetDoc.x); - - for (let i = 0; i < time; i++) { - this.targetDoc.x = NumCast(this.targetDoc.x) + xdiff / time; - this.targetDoc.y = NumCast(this.targetDoc.y) + ydiff / time; - await timer(0.1); - } + if (this.activeItem.presActiveFrame !== undefined) { + const context = DocCast(DocCast(this.activeItem.presentationTargetDoc).context); + context && CollectionFreeFormViewChrome.gotoKeyFrame(context, NumCast(this.activeItem.presActiveFrame)); } }; -- cgit v1.2.3-70-g09d2 From 3c08d65a63e04d421954193742a49d539b842c20 Mon Sep 17 00:00:00 2001 From: mehekj Date: Wed, 10 Aug 2022 20:02:03 -0400 Subject: stacked timeline zoom overflow and hover timecode position fixed --- src/client/views/collections/CollectionStackedTimeline.scss | 1 + src/client/views/collections/CollectionStackedTimeline.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss index 6611477e5..c296e1172 100644 --- a/src/client/views/collections/CollectionStackedTimeline.scss +++ b/src/client/views/collections/CollectionStackedTimeline.scss @@ -2,6 +2,7 @@ .collectionStackedTimeline-timelineContainer { height: 100%; + position: absolute; overflow-x: auto; overflow-y: hidden; border: none; diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 48e3abbc7..2543624d3 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -668,7 +668,7 @@ export class CollectionStackedTimeline extends CollectionSubView
-
+
{formatTime(this._hoverTime - this.clipStart)}
{this._thumbnail && }
-- cgit v1.2.3-70-g09d2 From 081632757af3c2ec2e4482c1e5fe710c8ee6cad8 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 11 Aug 2022 19:01:37 -0400 Subject: added better ui and backend support for mask ink strokes. added frame #'s ui for seeing current animation frame. --- src/client/documents/Documents.ts | 19 ++++++++-- src/client/util/CurrentUserUtils.ts | 6 ++-- src/client/util/DragManager.ts | 14 ++++---- src/client/views/InkingStroke.tsx | 28 +++++++++++---- src/client/views/MainView.tsx | 1 + src/client/views/collections/CollectionMenu.tsx | 2 +- src/client/views/collections/TabDocView.tsx | 10 +++--- .../CollectionFreeFormLayoutEngines.tsx | 1 + .../collectionFreeForm/CollectionFreeFormView.scss | 23 +++++++++---- .../collectionFreeForm/CollectionFreeFormView.tsx | 9 +++-- src/client/views/global/globalCssVariables.scss | 13 +++---- .../views/global/globalCssVariables.scss.d.ts | 6 ++-- .../views/nodes/CollectionFreeFormDocumentView.tsx | 7 ++-- src/client/views/nodes/DocumentView.tsx | 6 ++-- src/client/views/nodes/button/FontIconBox.tsx | 34 +++++++++++++----- src/client/views/nodes/trails/PresBox.tsx | 36 +++++++++++-------- src/fields/InkField.ts | 40 +++++++++++----------- 17 files changed, 162 insertions(+), 93 deletions(-) (limited to 'src/client/views') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index c7ea04839..cf9ed43e1 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -31,7 +31,7 @@ import { CollectionView } from '../views/collections/CollectionView'; import { ContextMenu } from '../views/ContextMenu'; import { ContextMenuProps } from '../views/ContextMenuItem'; import { DFLT_IMAGE_NATIVE_DIM } from '../views/global/globalCssVariables.scss'; -import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke } from '../views/InkingStroke'; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke } from '../views/InkingStroke'; import { AudioBox } from '../views/nodes/AudioBox'; import { FontIconBox } from '../views/nodes/button/FontIconBox'; import { ColorBox } from '../views/nodes/ColorBox'; @@ -918,9 +918,22 @@ export namespace Docs { return linkDoc; } - export function InkDocument(color: string, tool: string, strokeWidth: number, strokeBezier: string, fillColor: string, arrowStart: string, arrowEnd: string, dash: string, points: PointData[], options: DocumentOptions = {}) { + export function InkDocument( + color: string, + tool: string, + strokeWidth: number, + strokeBezier: string, + fillColor: string, + arrowStart: string, + arrowEnd: string, + dash: string, + points: PointData[], + isInkMask: boolean, + options: DocumentOptions = {} + ) { const I = new Doc(); I[Initializing] = true; + I.isInkMask = isInkMask; I.type = DocumentType.INK; I.layout = InkingStroke.LayoutString('data'); I.color = color; @@ -1424,7 +1437,7 @@ export namespace DocUtils { created = Docs.Create.RecordingDocument(field.url.href, resolved); layout = RecordingBox.LayoutString; } else if (field instanceof InkField) { - created = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, resolved); + created = Docs.Create.InkDocument(ActiveInkColor(), Doc.ActiveTool, ActiveInkWidth(), ActiveInkBezierApprox(), ActiveFillColor(), ActiveArrowStart(), ActiveArrowEnd(), ActiveDash(), field.inkData, ActiveIsInkMask(), resolved); layout = InkingStroke.LayoutString; } else if (field instanceof List && field[0] instanceof Doc) { created = Docs.Create.StackingDocument(DocListCast(field), resolved); diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index d19874720..dcf4a71c8 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -645,8 +645,9 @@ export class CurrentUserUtils { // { title: "Highlighter", toolTip: "Highlighter (Ctrl+H)", btnType: ButtonType.ToggleButton, icon: "highlighter", scripts:{onClick: 'setActiveTool("highlighter")'} }, { title: "Circle", toolTip: "Circle (Ctrl+Shift+C)", btnType: ButtonType.ToggleButton, icon: "circle", scripts: {onClick:'{ return setActiveTool("circle", _readOnly_);}'} }, // { title: "Square", toolTip: "Square (Ctrl+Shift+S)", btnType: ButtonType.ToggleButton, icon: "square", click: 'setActiveTool("square")' }, - { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts: {onClick: '{ return setActiveTool("line", _readOnly_);}' }}, - { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, icon: "fill-drip",ignoreClick: true, scripts: {script: "{ return setFillColor(value, _readOnly_);}"} }, + { title: "Line", toolTip: "Line (Ctrl+Shift+L)", btnType: ButtonType.ToggleButton, icon: "minus", scripts: {onClick:'{ return setActiveTool("line", _readOnly_);}' }}, + { title: "Mask", toolTip: "Mask", btnType: ButtonType.ToggleButton, icon: "user-circle", scripts: {onClick:'{ return setIsInkMask(_readOnly_);}'} }, + { title: "Fill", toolTip: "Fill color", btnType: ButtonType.ColorButton, icon: "fill-drip",ignoreClick: true, scripts: {script: '{ return setFillColor(value, _readOnly_);}'} }, { title: "Width", toolTip: "Stroke width", btnType: ButtonType.NumberButton, ignoreClick: true, scripts: {script: '{ return setStrokeWidth(value, _readOnly_);}'}, numBtnType: NumButtonType.Slider, numBtnMin: 1}, { title: "Color", toolTip: "Stroke color", btnType: ButtonType.ColorButton, icon: "pen", ignoreClick: true, scripts: {script: '{ return setStrokeColor(value, _readOnly_);}'} }, ]; @@ -674,6 +675,7 @@ export class CurrentUserUtils { CollectionViewType.Grid, CollectionViewType.NoteTaking]), title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}}, { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}}, + { title: "Num", icon: "", toolTip: "Frame Number", btnType: ButtonType.TextButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()', buttonText: 'selectedDocs()?.lastElement().currentFrame.toString()'}, width: 20, scripts: {}}, { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}}, { 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_)'}}, diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index ccd94c56e..d781a87ab 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -377,14 +377,14 @@ export namespace DragManager { } const rect = ele.getBoundingClientRect(); const scaleX = rect.width / (ele.offsetWidth || rect.width); - const scaleY = ele.offsetHeight ? rect.height / (ele.offsetHeight || rect.height) : scaleX; + const scaleY = scaleX; //ele.offsetHeight ? rect.height / (ele.offsetHeight || rect.height) : scaleX; elesCont.left = Math.min(rect.left, elesCont.left); elesCont.top = Math.min(rect.top, elesCont.top); elesCont.right = Math.max(rect.right, elesCont.right); elesCont.bottom = Math.max(rect.bottom, elesCont.bottom); - xs.push(rect.left); - ys.push(rect.top); + xs.push(rect.left + (options?.offsetX || 0)); + ys.push(rect.top + (options?.offsetY || 0)); scaleXs.push(scaleX); scaleYs.push(scaleY); Object.assign(dragElement.style, { @@ -401,9 +401,9 @@ export namespace DragManager { transformOrigin: '0 0', width: `${rect.width / scaleX}px`, height: `${rect.height / scaleY}px`, - transform: `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0)}px) scale(${scaleX}, ${scaleY})`, + transform: `translate(${xs[0]}px, ${ys[0]}px) scale(${scaleX}, ${scaleY})`, }); - dragLabel.style.transform = `translate(${rect.left + (options?.offsetX || 0)}px, ${rect.top + (options?.offsetY || 0) - 20}px)`; + dragLabel.style.transform = `translate(${xs[0]}px, ${ys[0] - 20}px)`; if (docsToDrag.length) { const pdfBox = dragElement.getElementsByTagName('canvas'); @@ -543,8 +543,8 @@ export namespace DragManager { const moveVec = { x: x - lastPt.x, y: y - lastPt.y }; lastPt = { x, y }; - dragLabel.style.transform = `translate(${xs[0] + moveVec.x + (options?.offsetX || 0)}px, ${ys[0] + moveVec.y + (options?.offsetY || 0) - 20}px)`; - dragElements.map((dragElement, i) => (dragElement.style.transform = `translate(${(xs[i] += moveVec.x) + (options?.offsetX || 0)}px, ${(ys[i] += moveVec.y) + (options?.offsetY || 0)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`)); + dragElements.map((dragElement, i) => (dragElement.style.transform = `translate(${(xs[i] += moveVec.x)}px, ${(ys[i] += moveVec.y)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`)); + dragLabel.style.transform = `translate(${xs[0]}px, ${ys[0] - 20}px)`; }; const upHandler = (e: PointerEvent) => { clearTimeout(startWindowDragTimer); diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index e5de7a0c5..2671aea56 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -23,9 +23,9 @@ import React = require('react'); import { action, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; -import { Doc, WidthSym } from '../../fields/Doc'; +import { Doc, HeightSym, WidthSym } from '../../fields/Doc'; import { InkData, InkField, InkTool } from '../../fields/InkField'; -import { Cast, NumCast, RTFCast, StrCast } from '../../fields/Types'; +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'; @@ -42,12 +42,13 @@ 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'); @observer export class InkingStroke extends ViewBoxBaseComponent() { - static readonly MaskDim = 50000; // choose a really big number to make sure mask fits over container (which in theory can be arbitrarily big) + static readonly MaskDim = INK_MASK_SIZE; // choose a really big number to make sure mask fits over container (which in theory can be arbitrarily big) public static LayoutString(fieldStr: string) { return FieldView.LayoutString(InkingStroke, fieldStr); } @@ -353,9 +354,18 @@ export class InkingStroke extends ViewBoxBaseComponent() { const endMarker = StrCast(this.layoutDoc.strokeEndMarker); const markerScale = NumCast(this.layoutDoc.strokeMarkerScale, 1); const closed = InkingStroke.IsClosed(inkData); - const fillColor = StrCast(this.layoutDoc.fillColor, 'transparent'); + const isInkMask = BoolCast(this.layoutDoc.isInkMask); + const fillColor = isInkMask ? '#aaaaaa' : StrCast(this.layoutDoc.fillColor, 'transparent'); const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : StrCast(this.layoutDoc.color); + // bcz: Hack!! Not really sure why, but having fractional values for width/height of mask ink strokes causes the dragging clone (see DragManager) to be offset from where it should be. + if (isInkMask && (this.layoutDoc[WidthSym]() !== Math.round(this.layoutDoc[WidthSym]()) || this.layoutDoc[HeightSym]() !== Math.round(this.layoutDoc[HeightSym]()))) { + setTimeout(() => { + this.layoutDoc._width = Math.round(NumCast(this.layoutDoc[WidthSym]())); + this.layoutDoc._height = Math.round(NumCast(this.layoutDoc[HeightSym]())); + }); + } + // Visually renders the polygonal line made by the user. const inkLine = InteractionUtils.CreatePolyline( inkData, @@ -430,7 +440,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { className="inkStroke" style={{ transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined, - mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset', + // mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset', cursor: this.props.isSelected() ? 'default' : undefined, }} {...(!closed ? interactions : {})}> @@ -467,7 +477,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { className="inkStroke" style={{ transform: this.props.Document.isInkMask ? `translate(${InkingStroke.MaskDim / 2}px, ${InkingStroke.MaskDim / 2}px)` : undefined, - mixBlendMode: this.layoutDoc.tool === InkTool.Highlighter ? 'multiply' : 'unset', + mixBlendMode: 'unset', cursor: this.props.isSelected() ? 'default' : undefined, position: 'absolute', }} @@ -489,6 +499,9 @@ export function SetActiveBezierApprox(bezier: string): void { export function SetActiveInkColor(value: string) { ActiveInkPen() && (ActiveInkPen().activeInkColor = value); } +export function SetActiveIsInkMask(value: boolean) { + ActiveInkPen() && (ActiveInkPen().activeIsInkMask = value); +} export function SetActiveFillColor(value: string) { ActiveInkPen() && (ActiveInkPen().activeFillColor = value); } @@ -513,6 +526,9 @@ export function ActiveInkColor(): string { export function ActiveFillColor(): string { return StrCast(ActiveInkPen()?.activeFillColor, ''); } +export function ActiveIsInkMask(): boolean { + return BoolCast(ActiveInkPen()?.activeIsInkMask, false); +} export function ActiveArrowStart(): string { return StrCast(ActiveInkPen()?.activeArrowStart, ''); } diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index e96f65548..c166594e5 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -457,6 +457,7 @@ export class MainView extends React.Component { fa.faVolumeDown, fa.faSquareRootAlt, fa.faVolumeMute, + fa.faUserCircle, ] ); this.initAuthenticationRouters(); diff --git a/src/client/views/collections/CollectionMenu.tsx b/src/client/views/collections/CollectionMenu.tsx index 8432619de..eb55650e4 100644 --- a/src/client/views/collections/CollectionMenu.tsx +++ b/src/client/views/collections/CollectionMenu.tsx @@ -1025,7 +1025,7 @@ export class CollectionFreeFormViewChrome extends React.Component
- Toggle View All
} placement="bottom"> + Frame number} placement="bottom">
{ 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 (pinDoc.isInkMask) { + pinDoc.presHideAfter = true; + pinDoc.presHideBefore = true; + pinDoc.presMovement = PresMovement.None; } if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; PresBox.Instance?._selectedArray.clear(); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index 3d85d32a0..ee01c341b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -45,6 +45,7 @@ export interface PoolData { export interface ViewDefResult { ele: JSX.Element; bounds?: ViewDefBounds; + inkMask?: boolean; } function toLabel(target: FieldResult) { if (typeof target === 'number' || Number(target)) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index 79e063f7f..010132aa5 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -1,4 +1,4 @@ -@import "../../global/globalCssVariables"; +@import '../../global/globalCssVariables'; .collectionfreeformview-none { position: inherit; @@ -20,8 +20,19 @@ pointer-events: none; } +.collectionfreeformview-mask { + mix-blend-mode: multiply; + z-index: 5000; + width: $INK_MASK_SIZE; + height: $INK_MASK_SIZE; + transform: translate($INK_MASK_SIZE_HALF, $INK_MASK_SIZE_HALF); + background-color: rgba(0, 0, 0, 0.7); + pointer-events: none; + position: absolute; +} + .collectionfreeformview-viewdef { - >.collectionFreeFormDocumentView-container { + > .collectionFreeFormDocumentView-container { pointer-events: none; .contentFittingDocumentDocumentView-previewDoc { @@ -210,13 +221,13 @@ } } - .collectionfreeformview>.jsx-parser { + .collectionfreeformview > .jsx-parser { position: inherit; height: 100%; width: 100%; } - >.jsx-parser { + > .jsx-parser { z-index: 0; } @@ -268,6 +279,6 @@ .pullpane-indicator { z-index: 99999; - background-color: rgba($color: #000000, $alpha: .4); + background-color: rgba($color: #000000, $alpha: 0.4); position: absolute; -} \ No newline at end of file +} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 82b377dfa..f3074543b 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -34,7 +34,7 @@ import { COLLECTION_BORDER_WIDTH } from '../../../views/global/globalCssVariable import { Timeline } from '../../animationtimeline/Timeline'; import { ContextMenu } from '../../ContextMenu'; import { GestureOverlay } from '../../GestureOverlay'; -import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke'; +import { ActiveArrowEnd, ActiveArrowStart, ActiveDash, ActiveFillColor, ActiveInkBezierApprox, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, InkingStroke, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke'; import { LightboxView } from '../../LightboxView'; import { CollectionFreeFormDocumentView } from '../../nodes/CollectionFreeFormDocumentView'; import { DocFocusOptions, DocumentView, DocumentViewProps, ViewAdjustment, ViewSpecPrefix } from '../../nodes/DocumentView'; @@ -121,7 +121,10 @@ export class CollectionFreeFormView extends CollectionSubView ele.bounds && !ele.bounds.z).map(ele => ele.ele); + const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask).map(ele => ele.ele); + const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && !ele.inkMask).map(ele => ele.ele); + if (viewsMask.length) renderableEles.push(
{viewsMask}
); + return renderableEles; } @computed get fitToContentVals() { return { @@ -579,6 +582,7 @@ export class CollectionFreeFormView extends CollectionSubView { render() { TraceMobx(); - const xshift = () => (this.props.Document.isInkMask && !this.props.LayoutTemplateString && !this.props.LayoutTemplate?.() ? InkingStroke.MaskDim : Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined); - const yshift = () => (this.props.Document.isInkMask && !this.props.LayoutTemplateString && !this.props.LayoutTemplate?.() ? InkingStroke.MaskDim : Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined); + const xshift = () => (Math.abs(this.Xshift) <= 0.001 ? this.props.PanelWidth() : undefined); + const yshift = () => (Math.abs(this.Yshift) <= 0.001 ? this.props.PanelHeight() : undefined); const isPresTreeElement: boolean = this.props.treeViewDoc?.type === DocumentType.PRES; const isButton: boolean = this.props.Document.type === DocumentType.FONTICON || this.props.Document._viewType === CollectionViewType.Linear; return ( @@ -1652,7 +1652,7 @@ export class DocumentView extends React.Component { ref={this.ContentRef} style={{ transition: this.props.dataTransition, - position: this.props.Document.isInkMask ? 'absolute' : undefined, + //position: this.props.Document.isInkMask ? 'absolute' : undefined, transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`, width: isButton || isPresTreeElement ? '100%' : xshift() ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`, height: diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index d3b95e25a..cb68c1ac3 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -1,6 +1,7 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Tooltip } from '@material-ui/core'; +import Color from 'color'; import { action, computed, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; @@ -21,7 +22,7 @@ import { DocComponent } from '../../DocComponent'; import { EditableView } from '../../EditableView'; import { GestureOverlay } from '../../GestureOverlay'; import { Colors } from '../../global/globalEnums'; -import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth } from '../../InkingStroke'; +import { ActiveFillColor, ActiveInkColor, ActiveInkWidth, ActiveIsInkMask, SetActiveFillColor, SetActiveInkColor, SetActiveInkWidth, SetActiveIsInkMask } from '../../InkingStroke'; import { InkTranscription } from '../../InkTranscription'; import { StyleProp } from '../../StyleProvider'; import { FieldView, FieldViewProps } from '.././FieldView'; @@ -498,8 +499,6 @@ export class FontIconBox extends DocComponent() {
); - const buttonText = StrCast(this.rootDoc.buttonText); - // TODO:glr Add label of button type let button: JSX.Element | null = this.defaultButton; @@ -508,7 +507,7 @@ export class FontIconBox extends DocComponent() { button = (
- {buttonText ?
{buttonText}
: null} + {StrCast(this.rootDoc.buttonText) ?
{StrCast(this.rootDoc.buttonText)}
: null} {label}
); @@ -563,11 +562,13 @@ export class FontIconBox extends DocComponent() { break; } - return !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? ( - button - ) : button !== null ? ( - {StrCast(this.layoutDoc.toolTip)}}>{button} - ) : null; + const retval = + !this.layoutDoc.toolTip || this.type === ButtonType.DropdownList || this.type === ButtonType.ColorButton || this.type === ButtonType.NumberButton || this.type === ButtonType.EditableText ? ( + button + ) : button !== null ? ( + {StrCast(this.layoutDoc.toolTip)}}>{button} + ) : null; + return retval; } } @@ -860,6 +861,21 @@ ScriptingGlobals.add(function setActiveTool(tool: string, checkResult?: boolean) } }); +// toggle: Set overlay status of selected document +ScriptingGlobals.add(function setIsInkMask(checkResult?: boolean) { + const selected = SelectionManager.Docs().lastElement(); + if (checkResult) { + if (selected?.type === DocumentType.INK) { + return BoolCast(selected.isInkMask) ? Colors.MEDIUM_BLUE : 'transparent'; + } + return ActiveIsInkMask() ? Colors.MEDIUM_BLUE : 'transparent'; + } + SetActiveIsInkMask(!ActiveIsInkMask()); + SelectionManager.Docs() + .filter(doc => doc.type === DocumentType.INK) + .map(doc => (doc.isInkMask = !doc.isInkMask)); +}); + // toggle: Set overlay status of selected document ScriptingGlobals.add(function setFillColor(color?: string, checkResult?: boolean) { const selected = SelectionManager.Docs().lastElement(); diff --git a/src/client/views/nodes/trails/PresBox.tsx b/src/client/views/nodes/trails/PresBox.tsx index 3bbdce1e4..627f35e71 100644 --- a/src/client/views/nodes/trails/PresBox.tsx +++ b/src/client/views/nodes/trails/PresBox.tsx @@ -313,10 +313,15 @@ export class PresBox extends ViewBoxBaseComponent() { //it'll also execute the necessary actions if presentation is playing. public gotoDocument = action((index: number, from?: Doc, group?: boolean) => { Doc.UnBrushAllDocs(); + if (index >= 0 && index < this.childDocs.length) { this.rootDoc._itemIndex = index; const activeItem: Doc = this.activeItem; const targetDoc: Doc = this.targetDoc; + if (activeItem.presActiveFrame !== undefined) { + const context = DocCast(DocCast(activeItem.presentationTargetDoc).context); + context && CollectionFreeFormViewChrome.gotoKeyFrame(context, NumCast(activeItem.presActiveFrame)); + } if (from?.mediaStopTriggerList && this.layoutDoc.presStatus !== PresStatus.Edit) { DocListCast(from.mediaStopTriggerList).forEach(this.stopTempMedia); } @@ -516,20 +521,26 @@ export class PresBox extends ViewBoxBaseComponent() { if (tagDoc === this.layoutDoc.presCollection) { tagDoc.opacity = 1; } else { - if (itemIndexes.length > 1 && curDoc.presHideBefore && curInd !== 0) { - } else if (curDoc.presHideBefore) { - if (index > this.itemIndex) { - tagDoc.opacity = 0; - } else if (index === this.itemIndex || !curDoc.presHideAfter) { + if (curDoc.presHideBefore) { + if (itemIndexes.length > 1 && curInd !== 0) { tagDoc.opacity = 1; + } else { + if (index > this.itemIndex) { + tagDoc.opacity = 0; + } else if (index === this.itemIndex || !curDoc.presHideAfter) { + tagDoc.opacity = 1; + } } } - if (itemIndexes.length > 1 && curDoc.presHideAfter && curInd !== itemIndexes.length - 1) { - } else if (curDoc.presHideAfter) { - if (index < this.itemIndex) { - tagDoc.opacity = 0; - } else if (index === this.itemIndex || !curDoc.presHideBefore) { + if (curDoc.presHideAfter) { + if (itemIndexes.length > 1 && curInd !== itemIndexes.length - 1) { tagDoc.opacity = 1; + } else { + if (index < this.itemIndex) { + tagDoc.opacity = 0; + } else if (index === this.itemIndex || !curDoc.presHideBefore) { + tagDoc.opacity = 1; + } } } } @@ -821,11 +832,6 @@ export class PresBox extends ViewBoxBaseComponent() { 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 (this.activeItem.presActiveFrame !== undefined) { - const context = DocCast(DocCast(this.activeItem.presentationTargetDoc).context); - context && CollectionFreeFormViewChrome.gotoKeyFrame(context, NumCast(this.activeItem.presActiveFrame)); - } }; //Command click diff --git a/src/fields/InkField.ts b/src/fields/InkField.ts index 114d5fc2f..a074098c1 100644 --- a/src/fields/InkField.ts +++ b/src/fields/InkField.ts @@ -1,22 +1,21 @@ -import { Bezier } from "bezier-js"; -import { createSimpleSchema, list, object, serializable } from "serializr"; -import { ScriptingGlobals } from "../client/util/ScriptingGlobals"; -import { Deserializable } from "../client/util/SerializationHelper"; -import { Copy, ToScriptString, ToString } from "./FieldSymbols"; -import { ObjectField } from "./ObjectField"; +import { Bezier } from 'bezier-js'; +import { createSimpleSchema, list, object, serializable } from 'serializr'; +import { ScriptingGlobals } from '../client/util/ScriptingGlobals'; +import { Deserializable } from '../client/util/SerializationHelper'; +import { Copy, ToScriptString, ToString } from './FieldSymbols'; +import { ObjectField } from './ObjectField'; // Helps keep track of the current ink tool in use. export enum InkTool { - None = "none", - Pen = "pen", - Highlighter = "highlighter", - Eraser = "eraser", - Stamp = "stamp", - Write = "write", - PresentationPin = 'presentationpin' + None = 'none', + Pen = 'pen', + Highlighter = 'highlighter', + Eraser = 'eraser', + Stamp = 'stamp', + Write = 'write', + PresentationPin = 'presentationpin', } - // Defines a point in an ink as a pair of x- and y-coordinates. export interface PointData { X: number; @@ -54,15 +53,16 @@ export interface HandleLine { } const pointSchema = createSimpleSchema({ - X: true, Y: true + X: true, + Y: true, }); const strokeDataSchema = createSimpleSchema({ pathData: list(object(pointSchema)), - "*": true + '*': true, }); -@Deserializable("ink") +@Deserializable('ink') export class InkField extends ObjectField { @serializable(list(object(strokeDataSchema))) readonly inkData: InkData; @@ -85,11 +85,11 @@ export class InkField extends ObjectField { } [ToScriptString]() { - return "new InkField([" + this.inkData.map(i => `{X: ${i.X}, Y: ${i.Y}`) + "])"; + return 'new InkField([' + this.inkData.map(i => `{X: ${i.X}, Y: ${i.Y}}`) + '])'; } [ToString]() { - return "InkField"; + return 'InkField'; } } -ScriptingGlobals.add("InkField", InkField); \ No newline at end of file +ScriptingGlobals.add('InkField', InkField); -- cgit v1.2.3-70-g09d2 From 8d99f6aa2672aadb8c5c5ae3cf2449f26a9eb74c Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 12 Aug 2022 15:19:53 -0400 Subject: fixed inline text box resizing --- src/client/views/nodes/formattedText/RichTextRules.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/formattedText/RichTextRules.ts b/src/client/views/nodes/formattedText/RichTextRules.ts index 1916b94bf..2097b321f 100644 --- a/src/client/views/nodes/formattedText/RichTextRules.ts +++ b/src/client/views/nodes/formattedText/RichTextRules.ts @@ -80,7 +80,7 @@ export class RichTextRules { textDoc.inlineTextCount = numInlines + 1; const inlineFieldKey = 'inline' + numInlines; // which field on the text document this annotation will write to const inlineLayoutKey = 'layout_' + inlineFieldKey; // the field holding the layout string that will render the inline annotation - const textDocInline = Docs.Create.TextDocument('', { _layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _autoHeight: true, _fontSize: '9px', title: 'inline comment' }); + const textDocInline = Docs.Create.TextDocument('', { _layoutKey: inlineLayoutKey, _width: 75, _height: 35, annotationOn: textDoc, _fitWidth: true, _autoHeight: true, _fontSize: '9px', title: 'inline comment' }); textDocInline.title = inlineFieldKey; // give the annotation its own title textDocInline['title-custom'] = true; // And make sure that it's 'custom' so that editing text doesn't change the title of the containing doc textDocInline.isTemplateForField = inlineFieldKey; // this is needed in case the containing text doc is converted to a template at some point -- cgit v1.2.3-70-g09d2 From 0b43c12bf9394f8561de57299c733734082f9f0a Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 15 Aug 2022 14:08:38 -0400 Subject: from last - fix for setting dash field numbers --- src/client/views/nodes/formattedText/DashFieldView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/client/views') diff --git a/src/client/views/nodes/formattedText/DashFieldView.tsx b/src/client/views/nodes/formattedText/DashFieldView.tsx index d2f7b5677..35d919f38 100644 --- a/src/client/views/nodes/formattedText/DashFieldView.tsx +++ b/src/client/views/nodes/formattedText/DashFieldView.tsx @@ -178,7 +178,7 @@ export class DashFieldViewInternal extends React.Component Date: Wed, 17 Aug 2022 12:31:26 -0400 Subject: fixed dragging docs on web and pdf to work better by temporarily adding transparent docs to the opaque layer so they can get drop events. cleaned eraser and pen interaction code and made erasing strokes work faster and avoid hanging by not intersecting strokes that are already partially deleted. --- src/Utils.ts | 3 +- src/client/documents/Documents.ts | 26 +--- src/client/views/collections/CollectionSubView.tsx | 12 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 140 ++++++--------------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 5 +- src/client/views/nodes/DocumentView.tsx | 6 +- src/client/views/nodes/VideoBox.tsx | 11 +- src/client/views/nodes/WebBox.scss | 17 +-- src/client/views/nodes/WebBox.tsx | 110 ++++++++-------- src/client/views/pdf/PDFViewer.tsx | 75 +++++------ 10 files changed, 153 insertions(+), 252 deletions(-) (limited to 'src/client/views') diff --git a/src/Utils.ts b/src/Utils.ts index 528a429d0..9e002ebd4 100644 --- a/src/Utils.ts +++ b/src/Utils.ts @@ -111,6 +111,7 @@ export namespace Utils { const isTransparentFunctionHack = 'isTransparent(__value__)'; export const noRecursionHack = '__noRecursion'; + export const noDragsDocFilter = 'noDragDocs:any:check'; export function IsRecursiveFilter(val: string) { return !val.includes(noRecursionHack); } @@ -125,7 +126,7 @@ export namespace Utils { // bcz: isTransparent(__value__) is a hack. it would be nice to have acual functions be parsed, but now Doc.matchFieldValue is hardwired to recognize just this one return `backgroundColor:${isTransparentFunctionHack},${noRecursionHack}:x`; // bcz: hack. noRecursion should probably be either another ':' delimited field, or it should be a modifier to the comparision (eg., check, x, etc) field } - export function PropUnsetFilter(prop: string) { + export function IsPropUnsetFilter(prop: string) { return `${prop}:any,${noRecursionHack}:unset`; } diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index cf9ed43e1..e579bfd8a 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1143,30 +1143,6 @@ export namespace Docs { } export namespace DocUtils { - export function Excluded(d: Doc, docFilters: string[]) { - const filterFacets: { [key: string]: { [value: string]: string } } = {}; // maps each filter key to an object with value=>modifier fields - docFilters.forEach(filter => { - const fields = filter.split(':'); - const key = fields[0]; - const value = fields[1]; - const modifiers = fields[2]; - if (!filterFacets[key]) { - filterFacets[key] = {}; - } - filterFacets[key][value] = modifiers; - }); - - if (d.z) return false; - for (const facetKey of Object.keys(filterFacets)) { - const facet = filterFacets[facetKey]; - const xs = Object.keys(facet).filter(value => facet[value] === 'x'); - const failsNotEqualFacets = xs?.some(value => Doc.matchFieldValue(d, facetKey, value)); - if (failsNotEqualFacets) { - return true; - } - } - return false; - } /** * @param docs * @param docFilters @@ -1200,7 +1176,7 @@ export namespace DocUtils { return false; } - for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies')) { + for (const facetKey of Object.keys(filterFacets).filter(fkey => fkey !== 'cookies' && fkey !== Utils.noDragsDocFilter.split(':')[0])) { const facet = filterFacets[facetKey]; // facets that match some value in the field of the document (e.g. some text field) diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 5479929bd..e33bb77de 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -88,11 +88,11 @@ export function CollectionSubView(moreProps?: X) { } collectionFilters = () => this._focusFilters ?? StrListCast(this.props.Document._docFilters); collectionRangeDocFilters = () => this._focusRangeFilters ?? Cast(this.props.Document._docRangeFilters, listSpec('string'), []); + // child filters apply to the descendants of the documents in this collection childDocFilters = () => [...(this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)) || []), ...this.collectionFilters()]; + // unrecursive filters apply to the documents in the collection, but no their children. See Utils.noRecursionHack unrecursiveDocFilters = () => [...(this.props.docFilters?.().filter(f => !Utils.IsRecursiveFilter(f)) || [])]; childDocRangeFilters = () => [...(this.props.docRangeFilters?.() || []), ...this.collectionRangeDocFilters()]; - IsFiltered = () => - this.collectionFilters().length || this.collectionRangeDocFilters().length ? 'hasFilter' : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? 'inheritsFilter' : undefined; searchFilterDocs = () => this.props.searchFilterDocs?.() ?? DocListCast(this.props.Document._searchFilterDocs); @computed.struct get childDocs() { TraceMobx(); @@ -122,13 +122,11 @@ export function CollectionSubView(moreProps?: X) { return childDocs.filter(cd => !cd.cookies); // remove any documents that require a cookie if there are no filters to provide one } - // console.log(Doc.ActiveDashboard._docFilters); - // if (!this.props.Document._docFilters && this.props.Document.currentFilter) { - // (this.props.Document.currentFilter as Doc).filterBoolean = (this.props.ContainingCollectionDoc?.currentFilter as Doc)?.filterBoolean; - // } const docsforFilter: Doc[] = []; childDocs.forEach(d => { - // if (DocUtils.Excluded(d, docFilters)) return; + // dragging facets + const dragged = this.props.docFilters?.().some(f => f.includes(Utils.noDragsDocFilter)); + if (dragged && DragManager.docsBeingDragged.includes(d)) return false; let notFiltered = d.z || Doc.IsSystem(d) || DocUtils.FilterDocs([d], this.unrecursiveDocFilters(), docRangeFilters, viewSpecScript, this.props.Document).length > 0; if (notFiltered) { notFiltered = (!searchDocs.length || searchDocs.includes(d)) && DocUtils.FilterDocs([d], childDocFilters, docRangeFilters, viewSpecScript, this.props.Document).length > 0; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index f3074543b..45a5e30ff 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -109,8 +109,6 @@ export class CollectionFreeFormView extends CollectionSubView, props: Opt, property: string) => { let styleProp = this.props.styleProvider?.(doc, props, property); // bcz: check 'props' used to be renderDepth + 1 - if (property !== StyleProp.BackgroundColor) return styleProp; - const cluster = NumCast(doc?.cluster); - if (this.Document._useClusters) { - if (this._clusterSets.length <= cluster) { - setTimeout(() => doc && this.updateCluster(doc)); - } else { - // choose a cluster color from a palette - const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)']; - styleProp = colors[cluster % colors.length]; - const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor); - // override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document - set?.map(s => (styleProp = StrCast(s.backgroundColor))); - } - } //else if (doc && NumCast(doc.group, -1) !== -1) styleProp = "gray"; + switch (property) { + case StyleProp.BackgroundColor: + const cluster = NumCast(doc?.cluster); + if (this.Document._useClusters) { + if (this._clusterSets.length <= cluster) { + setTimeout(() => doc && this.updateCluster(doc)); + } else { + // choose a cluster color from a palette + const colors = ['#da42429e', '#31ea318c', 'rgba(197, 87, 20, 0.55)', '#4a7ae2c4', 'rgba(216, 9, 255, 0.5)', '#ff7601', '#1dffff', 'yellow', 'rgba(27, 130, 49, 0.55)', 'rgba(0, 0, 0, 0.268)']; + styleProp = colors[cluster % colors.length]; + const set = this._clusterSets[cluster]?.filter(s => s.backgroundColor); + // override the cluster color with an explicitly set color on a non-background document. then override that with an explicitly set color on a background document + set?.map(s => (styleProp = StrCast(s.backgroundColor))); + } + } + break; + } return styleProp; }; @@ -523,17 +524,13 @@ export class CollectionFreeFormView extends CollectionSubView { - if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { - document.removeEventListener('pointermove', this.onEraserMove); - document.removeEventListener('pointerup', this.onEraserUp); - this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc)); - this._deleteList = []; - this._batch?.end(); - } - }; - - @action - onPointerUp = (e: PointerEvent): void => { - if (!InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { - document.removeEventListener('pointermove', this.onPointerMove); - document.removeEventListener('pointerup', this.onPointerUp); - this.removeMoveListeners(); - this.removeEndListeners(); - } + this._deleteList.forEach(ink => ink.props.removeDocument?.(ink.rootDoc)); + this._deleteList = []; + this._batch?.end(); }; onClick = (e: React.MouseEvent) => { @@ -752,46 +735,42 @@ export class CollectionFreeFormView extends CollectionSubView { + onEraserMove = (e: PointerEvent, down: number[], delta: number[]) => { const currPoint = { X: e.clientX, Y: e.clientY }; - this.getEraserIntersections({ X: this._lastX, Y: this._lastY }, currPoint).forEach(intersect => { + this.getEraserIntersections({ X: currPoint.X - delta[0], Y: currPoint.Y - delta[1] }, currPoint).forEach(intersect => { if (!this._deleteList.includes(intersect.inkView)) { this._deleteList.push(intersect.inkView); SetActiveInkWidth(StrCast(intersect.inkView.rootDoc.strokeWidth?.toString()) || '1'); SetActiveInkColor(StrCast(intersect.inkView.rootDoc.color?.toString()) || 'black'); // create a new curve by appending all curves of the current segment together in order to render a single new stroke. - !e.shiftKey && + if (!e.shiftKey) { this.segmentInkStroke(intersect.inkView, intersect.t).forEach(segment => GestureOverlay.Instance.dispatchGesture( GestureUtils.Gestures.Stroke, segment.reduce((data, curve) => [...data, ...curve.points.map(p => intersect.inkView.ComponentView?.ptToScreen?.({ X: p.x, Y: p.y }) ?? { X: 0, Y: 0 })], [] as PointData[]) ) ); + } // Lower ink opacity to give the user a visual indicator of deletion. intersect.inkView.layoutDoc.opacity = 0.5; + intersect.inkView.layoutDoc.dontIntersect = true; } }); - this._lastX = currPoint.X; - this._lastY = currPoint.Y; - - e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers - e.preventDefault(); + return false; }; @action - onPointerMove = (e: PointerEvent): void => { - if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return; + onPointerMove = (e: PointerEvent): boolean => { + if (InteractionUtils.IsType(e, InteractionUtils.PENTYPE)) return false; if (InteractionUtils.IsType(e, InteractionUtils.TOUCHTYPE)) { Doc.ActiveTool = InkTool.None; if (this.props.isContentActive(true)) e.stopPropagation(); } else if (!e.cancelBubble) { if (this.tryDragCluster(e, this._hitCluster)) { - document.removeEventListener('pointermove', this.onPointerMove); - document.removeEventListener('pointerup', this.onPointerUp); + return true; } else this.pan(e); - e.stopPropagation(); // doesn't actually stop propagation since all our listeners are listening to events on 'document' however it does mark the event as cancelBubble=true which we test for in the move event handlers - e.preventDefault(); } + return false; }; /** @@ -801,6 +780,7 @@ export class CollectionFreeFormView extends CollectionSubView { const eraserMin = { X: Math.min(lastPoint.X, currPoint.X), Y: Math.min(lastPoint.Y, currPoint.Y) }; const eraserMax = { X: Math.max(lastPoint.X, currPoint.X), Y: Math.max(lastPoint.Y, currPoint.Y) }; + return this.childDocs .map(doc => DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)) .filter(inkView => inkView?.ComponentView instanceof InkingStroke) @@ -888,7 +868,7 @@ export class CollectionFreeFormView extends CollectionSubView doc.type === DocumentType.INK) + .filter(doc => doc.type === DocumentType.INK && !doc.dontIntersect) .forEach(doc => { const otherInk = DocumentManager.Instance.getDocumentView(doc, this.props.CollectionView)?.ComponentView as InkingStroke; const { inkData: otherInkData } = otherInk?.inkScaledData() ?? { inkData: [] }; @@ -919,7 +899,6 @@ export class CollectionFreeFormView extends CollectionSubView only want to do this when collection is selected' @@ -967,13 +946,6 @@ export class CollectionFreeFormView extends CollectionSubView { - switch (this._pullDirection) { - case 'left': - case 'right': - case 'top': - case 'bottom': - CollectionDockingView.AddSplit(Docs.Create.FreeformDocument([], { title: 'New Collection' }), this._pullDirection); - } - - this._pullDirection = ''; - this._pullCoords = [0, 0]; - - document.removeEventListener('pointermove', this.onPointerMove); - document.removeEventListener('pointerup', this.onPointerUp); this.removeMoveListeners(); this.removeEndListeners(); }; @@ -1380,7 +1321,7 @@ export class CollectionFreeFormView extends CollectionSubView -
+
{this.layoutDoc._backgroundGridShow ? (
} -
{ // uncomment to show snap lines
diff --git a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx index 78f351f4f..e19e2d525 100644 --- a/src/client/views/nodes/CollectionFreeFormDocumentView.tsx +++ b/src/client/views/nodes/CollectionFreeFormDocumentView.tsx @@ -1,4 +1,4 @@ -import { action, computed, observable, trace } from 'mobx'; +import { action, computed, observable } from 'mobx'; import { observer } from 'mobx-react'; import { Doc, Opt } from '../../../fields/Doc'; import { List } from '../../../fields/List'; @@ -6,13 +6,12 @@ import { listSpec } from '../../../fields/Schema'; import { ComputedField } from '../../../fields/ScriptField'; import { Cast, NumCast, StrCast } from '../../../fields/Types'; import { TraceMobx } from '../../../fields/util'; -import { DashColor, numberRange, OmitKeys } from '../../../Utils'; +import { numberRange } from '../../../Utils'; import { DocumentManager } from '../../util/DocumentManager'; import { SelectionManager } from '../../util/SelectionManager'; import { Transform } from '../../util/Transform'; import { CollectionFreeFormView } from '../collections/collectionFreeForm/CollectionFreeFormView'; import { DocComponent } from '../DocComponent'; -import { InkingStroke } from '../InkingStroke'; import { StyleProp } from '../StyleProvider'; import './CollectionFreeFormDocumentView.scss'; import { DocumentView, DocumentViewProps } from './DocumentView'; diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index bcc55eab4..172adcafe 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -970,7 +970,11 @@ export class DocumentViewInternal extends DocComponent StrListCast(this.props.Document._docFilters); collectionRangeDocFilters = () => StrListCast(this.props.Document._docRangeFilters); @computed get showFilterIcon() { - return this.collectionFilters().length || this.collectionRangeDocFilters().length ? 'hasFilter' : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f)).length || this.props.docRangeFilters().length ? 'inheritsFilter' : undefined; + return this.collectionFilters().length || this.collectionRangeDocFilters().length + ? 'hasFilter' + : this.props.docFilters?.().filter(f => Utils.IsRecursiveFilter(f) && f !== Utils.noDragsDocFilter).length || this.props.docRangeFilters().length + ? 'inheritsFilter' + : undefined; } rootSelected = (outsideReaction?: boolean) => this.props.isSelected(outsideReaction) || (this.props.Document.rootDocument && this.props.rootSelected?.(outsideReaction)) || false; panelHeight = () => this.props.PanelHeight() - this.headerMargin; diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index a3d501153..0ff15f93b 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -831,16 +831,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent 1) { - return { height: '100%' }; - } else { - return { width: '100%' }; + //prettier-ignore + return ({ height: '100%' }); } + //prettier-ignore + return ({ width: '100%' }); } // for zoom slider, sets timeline waveform zoom - zoom = (zoom: number) => { - this.timeline?.setZoom(zoom); - }; + zoom = (zoom: number) => this.timeline?.setZoom(zoom); // plays link playLink = (doc: Doc) => { diff --git a/src/client/views/nodes/WebBox.scss b/src/client/views/nodes/WebBox.scss index d8dd074a5..85986ff27 100644 --- a/src/client/views/nodes/WebBox.scss +++ b/src/client/views/nodes/WebBox.scss @@ -1,9 +1,11 @@ -@import "../global/globalCssVariables.scss"; - +@import '../global/globalCssVariables.scss'; .webBox { height: 100%; - position: relative; + width: 100%; + top: 0; + left: 0; + position: absolute; display: flex; .webBox-sideResizer { @@ -84,7 +86,6 @@ background: none; } - .webBox-overlayCont { position: absolute; width: calc(100% - 40px); @@ -95,7 +96,7 @@ justify-content: center; align-items: center; overflow: hidden; - transition: left .5s; + transition: left 0.5s; pointer-events: all; .webBox-searchBar { @@ -158,7 +159,7 @@ left: 0; cursor: text; padding: 15px; - height: 100% + height: 100%; } .webBox-cont { @@ -235,7 +236,7 @@ height: 25px; align-items: center; - >svg { + > svg { margin: auto; } } @@ -257,4 +258,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/client/views/nodes/WebBox.tsx b/src/client/views/nodes/WebBox.tsx index ca9f363c1..6c2e42f86 100644 --- a/src/client/views/nodes/WebBox.tsx +++ b/src/client/views/nodes/WebBox.tsx @@ -1,5 +1,5 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx'; +import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction, trace } from 'mobx'; import { observer } from 'mobx-react'; import * as WebRequest from 'web-request'; import { Doc, DocListCast, HeightSym, Opt, WidthSym } from '../../../fields/Doc'; @@ -11,7 +11,7 @@ import { listSpec } from '../../../fields/Schema'; import { Cast, ImageCast, NumCast, StrCast } from '../../../fields/Types'; import { ImageField, WebField } from '../../../fields/URLField'; import { TraceMobx } from '../../../fields/util'; -import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, Utils } from '../../../Utils'; +import { emptyFunction, getWordAtPoint, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, smoothScroll, StopEvent, Utils } from '../../../Utils'; import { Docs, DocUtils } from '../../documents/Documents'; import { ScriptingGlobals } from '../../util/ScriptingGlobals'; import { SnappingManager } from '../../util/SnappingManager'; @@ -35,6 +35,7 @@ import { LinkDocPreview } from './LinkDocPreview'; import { VideoBox } from './VideoBox'; import './WebBox.scss'; import React = require('react'); +import { DragManager } from '../../util/DragManager'; const { CreateImage } = require('./WebBoxRenderer'); const _global = (window /* browser */ || global) /* node */ as any; const htmlToText = require('html-to-text'); @@ -818,6 +819,56 @@ export class WebBox extends ViewBoxAnnotatableComponent string[]) => ( + + ); + return ( +
this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)} + onPointerDown={this.onMarqueeDown}> +
+ {this.content} + {
{renderAnnotations(this.transparentFilter)}
} + {renderAnnotations(this.opaqueFilter)} + {this.annotationLayer} +
+
+ ); + } + @computed get searchUI() { return (
e.stopPropagation()} style={{ display: this.props.isContentActive() ? 'flex' : 'none' }}> @@ -859,9 +910,8 @@ export class WebBox extends ViewBoxAnnotatableComponent this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._scrollTop)); anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick; - basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')]; transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; - opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()]; + opaqueFilter = () => [...this.props.docFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])]; childStyleProvider = (doc: Doc | undefined, props: Opt, property: string): any => { if (doc instanceof Doc && property === StyleProp.PointerEvents) { if (doc.textInlineAnnotations) return 'none'; @@ -871,37 +921,11 @@ export class WebBox extends ViewBoxAnnotatableComponent (!this._draggingSidebar && this.props.isContentActive() && this.props.pointerEvents?.() !== 'none' && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : SnappingManager.GetIsDragging() ? undefined : 'none'); annotationPointerEvents = () => (this._isAnnotating || SnappingManager.GetIsDragging() ? 'all' : 'none'); render() { - const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any); const previewScale = this._previewNativeWidth ? 1 - this.sidebarWidth() / this._previewNativeWidth : 1; + const pointerEvents = this.layoutDoc._lockedPosition ? 'none' : (this.props.pointerEvents?.() as any); const scale = previewScale * (this.props.NativeDimScaling?.() || 1); - const renderAnnotations = (docFilters?: () => string[]) => ( - - ); return ( -
+
-
{ - e.stopPropagation(); - e.preventDefault(); - }} // block wheel events from propagating since they're handled by the iframe - onScroll={e => this.setDashScrollTop(this._outerRef.current?.scrollTop || 0)} - onPointerDown={this.onMarqueeDown}> -
- {this.content} -
{renderAnnotations(this.transparentFilter)}
- {renderAnnotations(this.opaqueFilter)} - {SnappingManager.GetIsDragging() ? null : renderAnnotations()} - {this.annotationLayer} -
-
+ {this.webpage} {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
{ overlayTransform = () => this.scrollXf().scale(1 / NumCast(this.props.layoutDoc._viewScale, 1)); panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1); // (this.Document.scrollHeight || Doc.NativeHeight(this.Document) || 0); panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1); // () => this._pageSizes.length && this._pageSizes[0] ? this._pageSizes[0].width : Doc.NativeWidth(this.Document); - basicFilter = () => [...this.props.docFilters(), Utils.PropUnsetFilter('textInlineAnnotations')]; transparentFilter = () => [...this.props.docFilters(), Utils.IsTransparentFilter()]; - opaqueFilter = () => [...this.props.docFilters(), Utils.IsOpaqueFilter()]; + opaqueFilter = () => [...this.props.docFilters(), Utils.noDragsDocFilter, ...(DragManager.docsBeingDragged.length ? [] : [Utils.IsOpaqueFilter()])]; childStyleProvider = (doc: Doc | undefined, props: Opt, property: string): any => { if (doc instanceof Doc && property === StyleProp.PointerEvents) { if (doc.textInlineAnnotations) return 'none'; @@ -498,56 +498,43 @@ export class PDFViewer extends React.Component { return this.props.styleProvider?.(doc, props, property); }; - renderAnnotations = (docFilters?: () => string[], dontRender?: boolean) => ( - + renderAnnotations = (docFilters?: () => string[], mixBlendMode?: any, display?: string) => ( +
+ +
); @computed get overlayTransparentAnnotations() { - return this.renderAnnotations(this.transparentFilter, false); + return this.renderAnnotations(this.transparentFilter, 'multiply', DragManager.docsBeingDragged.length ? 'none' : undefined); } @computed get overlayOpaqueAnnotations() { - return this.renderAnnotations(this.opaqueFilter, false); - } - @computed get overlayClickableAnnotations() { - return
{this.renderAnnotations(undefined, true)}
; + return this.renderAnnotations(this.opaqueFilter, this.allAnnotations.some(anno => anno.mixBlendMode) ? 'hard-light' : undefined); } @computed get overlayLayer() { return (
-
- {this.overlayTransparentAnnotations} -
-
anno.mixBlendMode) ? 'hard-light' : undefined, - transform: `scale(${NumCast(this.props.layoutDoc._viewScale, 1)})`, - }}> - {this.overlayOpaqueAnnotations} - {this.overlayClickableAnnotations} -
+ {this.overlayTransparentAnnotations} + {this.overlayOpaqueAnnotations}
); } -- cgit v1.2.3-70-g09d2 From 5a425e5cf18115921ecb4e7cf931e65f45dab8e2 Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 18 Aug 2022 11:18:14 -0400 Subject: fixed up presboxelement to not reference presbox.instance. fixing layout of presboxelement in treeview. fixing "removing" an inkMask without removing it from the collection so that it can be turned on and off. --- src/client/util/CurrentUserUtils.ts | 9 +- src/client/views/DocumentDecorations.tsx | 6 +- src/client/views/PropertiesView.tsx | 5 +- .../views/collections/CollectionTreeView.scss | 11 +- src/client/views/collections/TabDocView.tsx | 4 +- src/client/views/collections/TreeView.scss | 17 +-- .../collectionFreeForm/CollectionFreeFormView.tsx | 1 + src/client/views/nodes/trails/PresBox.tsx | 137 +++++++++------------ src/client/views/nodes/trails/PresElementBox.tsx | 73 ++++++----- 9 files changed, 132 insertions(+), 131 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index dcf4a71c8..f7d072d80 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -289,11 +289,12 @@ export class CurrentUserUtils { { toolTip: "Tap or drag to create a map", title: "Map", icon: "map-marker-alt", dragFactory: doc.emptyMap as Doc, }, { toolTip: "Tap or drag to create a screen grabber", title: "Grab", icon: "photo-video", dragFactory: doc.emptyScreengrab as Doc, scripts: { onClick: 'openInOverlay(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'},funcs: { hidden: 'IsNoviceMode()'} }, { toolTip: "Tap or drag to create a WebCam recorder", title: "WebCam", icon: "photo-video", dragFactory: doc.emptyWebCam as Doc, scripts: { onClick: 'openInOverlay(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'},funcs: { hidden: 'IsNoviceMode()'}}, - { toolTip: "Tap or drag to create a button", title: "Button", icon: "bolt", dragFactory: doc.emptyButton as Doc, funcs: { hidden: 'IsNoviceMode()'} }, - { toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, funcs: { hidden: 'IsNoviceMode()'}}, + { toolTip: "Tap or drag to create a button", title: "Button", icon: "bolt", dragFactory: doc.emptyButton as Doc, funcs: { hidden: 'IsNoviceMode()'} }, + { toolTip: "Tap or drag to create a scripting box", title: "Script", icon: "terminal", dragFactory: doc.emptyScript as Doc, funcs: { hidden: 'IsNoviceMode()'}}, { toolTip: "Tap or drag to create a data viz node", title: "DataViz", icon: "file", dragFactory: doc.emptyDataViz as Doc, }, - { toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize", dragFactory: doc.emptyHeader as Doc, scripts: {onClick: 'openOnRight(delegateDragFactory(this.dragFactory))', onDragStart: '{ return delegateDragFactory(this.dragFactory);}'}, }, - { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", scripts: {onClick: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' } }, + { toolTip: "Tap or drag to create a bullet slide", title: "PPT Slide", icon: "file", dragFactory: doc.emptySlide as Doc, funcs: { hidden: 'IsNoviceMode()'}}, + { toolTip: "Tap or drag to create a data note", title: "DataNote", icon: "window-maximize", dragFactory: doc.emptyHeader as Doc,scripts: { onClick: 'openOnRight(delegateDragFactory(this.dragFactory))', onDragStart: '{ return delegateDragFactory(this.dragFactory);}'}, }, + { toolTip: "Toggle a Calculator REPL", title: "repl", icon: "calculator", scripts: { onClick: 'addOverlayWindow("ScriptingRepl", { x: 300, y: 100, width: 200, height: 200, title: "Scripting REPL" })' } }, ].map(tuple => ({scripts: {onClick: 'openOnRight(copyDragFactory(this.dragFactory))', onDragStart: '{ return copyDragFactory(this.dragFactory);}'}, ...tuple, })) } diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3544f74b4..6d1397395 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -203,7 +203,11 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if (this._deleteAfterIconify) { views.forEach(iconView => { Doc.setNativeView(iconView.props.Document); - iconView.props.removeDocument?.(iconView.props.Document); + if (iconView.props.Document.isInkMask && iconView.props.Document.activeFrame !== undefined) { + iconView.props.Document.opacity = 0; // bcz: hacky ... allows inkMaks to be "turned off" without removing them from the collection which allows them to function properly in a presenation. + } else { + iconView.props.removeDocument?.(iconView.props.Document); + } }); SelectionManager.DeselectAll(); } diff --git a/src/client/views/PropertiesView.tsx b/src/client/views/PropertiesView.tsx index ef0e057dc..33f17047b 100644 --- a/src/client/views/PropertiesView.tsx +++ b/src/client/views/PropertiesView.tsx @@ -15,7 +15,7 @@ import { ComputedField } from '../../fields/ScriptField'; import { Cast, DocCast, NumCast, StrCast } from '../../fields/Types'; import { denormalizeEmail, GetEffectiveAcl, SharingPermissions } from '../../fields/util'; import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnFalse, returnTrue, setupMoveUpEvents } from '../../Utils'; -import { CollectionViewType, DocumentType } from '../documents/DocumentTypes'; +import { DocumentType } from '../documents/DocumentTypes'; import { DocumentManager } from '../util/DocumentManager'; import { LinkManager } from '../util/LinkManager'; import { SelectionManager } from '../util/SelectionManager'; @@ -1614,9 +1614,6 @@ export class PropertiesView extends React.Component { if (this.isPres) { const selectedItem: boolean = PresBox.Instance?._selectedArray.size > 0; const type = PresBox.Instance.activeItem?.type; - const viewType = PresBox.Instance.activeItem?._viewType; - const pannable: boolean = (type === DocumentType.COL && viewType === CollectionViewType.Freeform) || type === DocumentType.IMG; - const scrollable: boolean = type === DocumentType.PDF || type === DocumentType.WEB || type === DocumentType.RTF || viewType === CollectionViewType.Stacking || viewType === CollectionViewType.NoteTaking; return (
diff --git a/src/client/views/collections/CollectionTreeView.scss b/src/client/views/collections/CollectionTreeView.scss index 93523a6cf..c0561e42c 100644 --- a/src/client/views/collections/CollectionTreeView.scss +++ b/src/client/views/collections/CollectionTreeView.scss @@ -1,6 +1,5 @@ -@import "../global/globalCssVariables"; +@import '../global/globalCssVariables'; - .collectionTreeView-container { transform-origin: top left; height: 100%; @@ -28,7 +27,7 @@ list-style: none; padding-left: $TREE_BULLET_WIDTH; margin-bottom: 1px; // otherwise vertical scrollbars may pop up for no apparent reason.... - > .contentFittingDocumentView { + > .contentFittingDocumentView { width: unset; height: unset; } @@ -39,7 +38,7 @@ .no-indent { padding-left: 0; - width: max-content; + //width: max-content; } .no-indent-outline { @@ -85,7 +84,7 @@ width: 100%; height: max-content; .contentFittingDocumentView { - display: block; // makes titleBar take up full width of the treeView (flex doesn't for some reason) + display: block; // makes titleBar take up full width of the treeView (flex doesn't for some reason) } } @@ -114,4 +113,4 @@ padding-left: 3px; padding-right: 3px; padding-bottom: 2px; -} \ No newline at end of file +} diff --git a/src/client/views/collections/TabDocView.tsx b/src/client/views/collections/TabDocView.tsx index ff4b1e2ce..e147f34d2 100644 --- a/src/client/views/collections/TabDocView.tsx +++ b/src/client/views/collections/TabDocView.tsx @@ -299,8 +299,8 @@ export class TabDocView extends React.Component { pinDoc.presMovement = PresMovement.None; } if (curPres.expandBoolean) pinDoc.presExpandInlineButton = true; - PresBox.Instance?._selectedArray.clear(); - pinDoc && PresBox.Instance?._selectedArray.set(pinDoc, undefined); //Update selected array + PresBox.Instance?.clearSelectedArray(); + pinDoc && PresBox.Instance?.addToSelectedArray(pinDoc); //Update selected array }); if ( CollectionDockingView.Instance && diff --git a/src/client/views/collections/TreeView.scss b/src/client/views/collections/TreeView.scss index f587dbbf6..ce87e6f89 100644 --- a/src/client/views/collections/TreeView.scss +++ b/src/client/views/collections/TreeView.scss @@ -1,4 +1,4 @@ -@import "../global/globalCssVariables"; +@import '../global/globalCssVariables'; .treeView-label { max-height: 1.5em; @@ -21,7 +21,7 @@ } .treeView-bulletIcons { - // width: $TREE_BULLET_WIDTH; + // width: $TREE_BULLET_WIDTH; width: 100%; height: 100%; @@ -101,6 +101,9 @@ .treeView-border { display: flex; overflow: hidden; + > ul { + width: 100%; + } } .treeView-border { @@ -118,7 +121,6 @@ } .formattedTextBox-cont { - .formattedTextbox-sidebar, .formattedTextbox-sidebar-inking { overflow: visible !important; @@ -144,12 +146,12 @@ pointer-events: all; cursor: pointer; - >svg { + > svg { margin-left: 0.25rem; margin-right: 0.25rem; } - >svg { + > svg { //display: none; opacity: 0; pointer-events: none; @@ -176,8 +178,7 @@ } .treeView-rightButtons { - - >svg, + > svg, .styleProvider-treeView-icon { display: inherit; opacity: unset; @@ -196,4 +197,4 @@ .treeView-header-inside { border: black 1px solid; -} \ No newline at end of file +} diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 45a5e30ff..0fd326091 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -251,6 +251,7 @@ export class CollectionFreeFormView extends CollectionSubView() { delay: presDoc.presTransition, // when: this.layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc, }; + //prettier-ignore switch (presDoc.presEffect) { - case PresEffect.Zoom: - return {renderDoc}; - case PresEffect.Fade: - return {renderDoc}; - case PresEffect.Flip: - return {renderDoc}; - case PresEffect.Rotate: - return {renderDoc}; - case PresEffect.Bounce: - return {renderDoc}; - case PresEffect.Roll: - return {renderDoc}; - case PresEffect.Lightspeed: - return {renderDoc}; - case PresEffect.None: - default: - return renderDoc; + default: + case PresEffect.None: return renderDoc; + case PresEffect.Zoom: return {renderDoc}; + case PresEffect.Fade: return {renderDoc}; + case PresEffect.Flip: return {renderDoc}; + case PresEffect.Rotate: return {renderDoc}; + case PresEffect.Bounce: return {renderDoc}; + case PresEffect.Roll: return {renderDoc}; + case PresEffect.Lightspeed: return {renderDoc}; } } public static EffectsProvider(layoutDoc: Doc, renderDoc: any) { return PresBox.Instance && layoutDoc === PresBox.Instance.childDocs[PresBox.Instance.itemIndex]?.presentationTargetDoc ? PresBox.renderEffectsDoc(renderDoc, layoutDoc, PresBox.Instance.childDocs[PresBox.Instance.itemIndex]) : renderDoc; } + private _disposers: { [name: string]: IReactionDisposer } = {}; + + 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; @observable _isChildActive = false; @@ -98,14 +99,11 @@ export class PresBox extends ViewBoxBaseComponent() { @observable _presTimer!: NodeJS.Timeout; @observable _presKeyEventsActive: boolean = false; - @observable _selectedArray: ObservableMap = new ObservableMap(); @observable _eleArray: HTMLElement[] = []; @observable _dragArray: HTMLElement[] = []; @observable _pathBoolean: boolean = false; @observable _expandBoolean: boolean = false; - private _disposers: { [name: string]: IReactionDisposer } = {}; - @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; @@ -152,11 +150,6 @@ export class PresBox extends ViewBoxBaseComponent() { if ((this.targetDoc.type === DocumentType.COL && this.targetDoc._viewType === CollectionViewType.Freeform) || this.targetDoc.type === DocumentType.IMG) return true; else return false; } - 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 - } @computed get selectedDocumentView() { if (SelectionManager.Views().length) return SelectionManager.Views()[0]; if (this._selectedArray.size) return DocumentManager.Instance.getDocumentView(this.rootDoc); @@ -173,6 +166,10 @@ 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); _unmounting = false; @action @@ -188,6 +185,7 @@ export class PresBox extends ViewBoxBaseComponent() { @action componentDidMount() { + this.props.setContentView?.(this); this._unmounting = false; this.rootDoc._forceRenderEngine = 'timeline'; this.layoutDoc.presStatus = PresStatus.Edit; @@ -344,8 +342,8 @@ export class PresBox extends ViewBoxBaseComponent() { if (targetDoc?.lastFrame !== undefined) { targetDoc._currentFrame = 0; } - if (!group) this._selectedArray.clear(); - this.childDocs[index] && this._selectedArray.set(this.childDocs[index], undefined); //Update selected array + if (!group) this.clearSelectedArray(); + this.childDocs[index] && this.addToSelectedArray(this.childDocs[index]); //Update selected array this.navigateToElement(this.childDocs[index]); //Handles movement to element only when presTrail is list this.onHideDocument(); //Handles hide after/before } @@ -415,7 +413,7 @@ export class PresBox extends ViewBoxBaseComponent() { this.layoutDoc.presCollection = srcContext; } const presStatus = this.rootDoc.presStatus; - const selViewCache = Array.from(this._selectedArray.keys()); + const selViewCache = Array.from(this._selectedArray); const dragViewCache = Array.from(this._dragArray); const eleViewCache = Array.from(this._eleArray); const self = this; @@ -423,8 +421,8 @@ export class PresBox extends ViewBoxBaseComponent() { const presDocView = DocumentManager.Instance.getDocumentView(self.rootDoc); if (presDocView) SelectionManager.SelectView(presDocView, false); self.rootDoc.presStatus = presStatus; - self._selectedArray.clear(); - selViewCache.forEach(doc => self._selectedArray.set(doc, undefined)); + self.clearSelectedArray(); + selViewCache.forEach(doc => self.addToSelectedArray(doc)); self._dragArray.splice(0, self._dragArray.length, ...dragViewCache); self._eleArray.splice(0, self._eleArray.length, ...eleViewCache); }); @@ -792,7 +790,7 @@ export class PresBox extends ViewBoxBaseComponent() { * Method to get the list of selected items in the order in which they have been selected */ @computed get listOfSelected() { - const list = Array.from(this._selectedArray.keys()).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) @@ -816,7 +814,6 @@ export class PresBox extends ViewBoxBaseComponent() {
); }); - return list; } @action @@ -838,11 +835,11 @@ export class PresBox extends ViewBoxBaseComponent() { @action multiSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => { if (!this._selectedArray.has(doc)) { - this._selectedArray.set(doc, undefined); + this.addToSelectedArray(doc); this._eleArray.push(ref); this._dragArray.push(drag); } else { - this._selectedArray.delete(doc); + this.removeFromSelectedArray(doc); this.removeFromArray(this._eleArray, doc); this.removeFromArray(this._dragArray, doc); } @@ -858,11 +855,11 @@ export class PresBox extends ViewBoxBaseComponent() { //Shift click @action shiftSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement) => { - this._selectedArray.clear(); + this.clearSelectedArray(); // const activeItem = Cast(this.childDocs[this.itemIndex], Doc, null); if (this.activeItem) { for (let i = Math.min(this.itemIndex, this.childDocs.indexOf(doc)); i <= Math.max(this.itemIndex, this.childDocs.indexOf(doc)); i++) { - this._selectedArray.set(this.childDocs[i], undefined); + this.addToSelectedArray(this.childDocs[i]); this._eleArray.push(ref); this._dragArray.push(drag); } @@ -873,8 +870,8 @@ export class PresBox extends ViewBoxBaseComponent() { //regular click @action regularSelect = (doc: Doc, ref: HTMLElement, drag: HTMLElement, focus: boolean, selectPres = true) => { - this._selectedArray.clear(); - this._selectedArray.set(doc, undefined); + this.clearSelectedArray(); + this.addToSelectedArray(doc); this._eleArray.splice(0, this._eleArray.length, ref); this._dragArray.splice(0, this._dragArray.length, drag); focus && this.selectElement(doc); @@ -903,10 +900,10 @@ export class PresBox extends ViewBoxBaseComponent() { if (this.layoutDoc.presStatus === 'edit') { undoBatch( action(() => { - for (const doc of Array.from(this._selectedArray.keys())) { + for (const doc of this._selectedArray) { this.removeDocument(doc); } - this._selectedArray.clear(); + this.clearSelectedArray(); this._eleArray.length = 0; this._dragArray.length = 0; }) @@ -918,7 +915,7 @@ export class PresBox extends ViewBoxBaseComponent() { if (DocListCast(Doc.MyOverlayDocs?.data).includes(this.layoutDoc)) { this.updateMinimize(); } else if (this.layoutDoc.presStatus === 'edit') { - this._selectedArray.clear(); + this.clearSelectedArray(); this._eleArray.length = this._dragArray.length = 0; } else this.layoutDoc.presStatus = 'edit'; if (this._presTimer) clearTimeout(this._presTimer); @@ -931,7 +928,7 @@ export class PresBox extends ViewBoxBaseComponent() { if (e.shiftKey && this.itemIndex < this.childDocs.length - 1) { // TODO: update to work properly this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) + 1; - this._selectedArray.set(this.childDocs[this.rootDoc._itemIndex], undefined); + this.addToSelectedArray(this.childDocs[this.rootDoc._itemIndex]); } else { this.next(); if (this._presTimer) { @@ -948,7 +945,7 @@ export class PresBox extends ViewBoxBaseComponent() { if (e.shiftKey && this.itemIndex !== 0) { // TODO: update to work properly this.rootDoc._itemIndex = NumCast(this.rootDoc._itemIndex) - 1; - this._selectedArray.set(this.childDocs[this.rootDoc._itemIndex], undefined); + this.addToSelectedArray(this.childDocs[this.rootDoc._itemIndex]); } else { this.back(); if (this._presTimer) { @@ -966,8 +963,8 @@ export class PresBox extends ViewBoxBaseComponent() { break; case 'a': if ((e.metaKey || e.altKey) && this.layoutDoc.presStatus === 'edit') { - this._selectedArray.clear(); - this.childDocs.forEach(doc => this._selectedArray.set(doc, undefined)); + this.clearSelectedArray(); + this.childDocs.forEach(doc => this.addToSelectedArray(doc)); handled = true; } default: @@ -1115,7 +1112,7 @@ export class PresBox extends ViewBoxBaseComponent() { if (change) timeInMS += change; if (timeInMS < 100) timeInMS = 100; if (timeInMS > 10000) timeInMS = 10000; - Array.from(this._selectedArray.keys()).forEach(doc => (doc.presTransition = timeInMS)); + this._selectedArray.forEach(doc => (doc.presTransition = timeInMS)); }; // Converts seconds to ms and updates presTransition @@ -1124,7 +1121,7 @@ export class PresBox extends ViewBoxBaseComponent() { if (change) scale += change; if (scale < 0.01) scale = 0.01; if (scale > 1.5) scale = 1.5; - Array.from(this._selectedArray.keys()).forEach(doc => (doc.presZoom = scale)); + this._selectedArray.forEach(doc => (doc.presZoom = scale)); }; // Converts seconds to ms and updates presDuration @@ -1133,7 +1130,7 @@ export class PresBox extends ViewBoxBaseComponent() { if (change) timeInMS += change; if (timeInMS < 100) timeInMS = 100; if (timeInMS > 20000) timeInMS = 20000; - Array.from(this._selectedArray.keys()).forEach(doc => (doc.presDuration = timeInMS)); + this._selectedArray.forEach(doc => (doc.presDuration = timeInMS)); }; /** @@ -1141,8 +1138,7 @@ export class PresBox extends ViewBoxBaseComponent() { */ @undoBatch updateMovement = action((movement: any, all?: boolean) => { - const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys()); - array.forEach(doc => { + (all ? this.childDocs : this._selectedArray).forEach(doc => { switch (movement) { case PresMovement.Zoom: //Pan and zoom doc.presMovement = PresMovement.Zoom; @@ -1166,21 +1162,21 @@ export class PresBox extends ViewBoxBaseComponent() { @action updateHideBefore = (activeItem: Doc) => { activeItem.presHideBefore = !activeItem.presHideBefore; - Array.from(this._selectedArray.keys()).forEach(doc => (doc.presHideBefore = activeItem.presHideBefore)); + this._selectedArray.forEach(doc => (doc.presHideBefore = activeItem.presHideBefore)); }; @undoBatch @action updateHideAfter = (activeItem: Doc) => { activeItem.presHideAfter = !activeItem.presHideAfter; - Array.from(this._selectedArray.keys()).forEach(doc => (doc.presHideAfter = activeItem.presHideAfter)); + this._selectedArray.forEach(doc => (doc.presHideAfter = activeItem.presHideAfter)); }; @undoBatch @action updateOpenDoc = (activeItem: Doc) => { activeItem.openDocument = !activeItem.openDocument; - Array.from(this._selectedArray.keys()).forEach(doc => { + this._selectedArray.forEach(doc => { doc.openDocument = activeItem.openDocument; }); }; @@ -1188,8 +1184,7 @@ export class PresBox extends ViewBoxBaseComponent() { @undoBatch @action updateEffectDirection = (effect: any, all?: boolean) => { - const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys()); - array.forEach(doc => { + (all ? this.childDocs : this._selectedArray).forEach(doc => { const tagDoc = doc; // Cast(doc.presentationTargetDoc, Doc, null); switch (effect) { case PresEffect.Left: @@ -1215,29 +1210,17 @@ export class PresBox extends ViewBoxBaseComponent() { @undoBatch @action updateEffect = (effect: any, all?: boolean) => { - const array: any[] = all ? this.childDocs : Array.from(this._selectedArray.keys()); - array.forEach(doc => { + (all ? this.childDocs : this._selectedArray).forEach(doc => { const tagDoc = doc; //Cast(doc.presentationTargetDoc, Doc, null); + //prettier-ignore switch (effect) { - 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; - case PresEffect.None: default: - tagDoc.presEffect = PresEffect.None; - break; + 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; } }); }; diff --git a/src/client/views/nodes/trails/PresElementBox.tsx b/src/client/views/nodes/trails/PresElementBox.tsx index 0cf15d297..3af8cad9a 100644 --- a/src/client/views/nodes/trails/PresElementBox.tsx +++ b/src/client/views/nodes/trails/PresElementBox.tsx @@ -47,6 +47,13 @@ export class PresElementBox extends ViewBoxBaseComponent() { @computed get presStatus() { return this.presBox.presStatus; } + @computed get selectedArray() { + return this.presBoxView?._selectedArray; + } + @computed get presBoxView() { + const vpath = this.props.docViewPath(); + return vpath.length > 1 ? (vpath[vpath.length - 2].ComponentView as PresBox) : undefined; + } @computed get presBox() { return (this.props.DocumentView?.().props.treeViewDoc ?? this.props.ContainingCollectionDoc)!; } @@ -129,10 +136,9 @@ export class PresElementBox extends ViewBoxBaseComponent() {
{ - console.log('Clicked on slide with index: ', ind); e.stopPropagation(); e.preventDefault(); - PresBox.Instance.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); + this.presBoxView?.modifierSelect(doc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); this.presExpandDocumentClick(); }}>
{`${ind + 1}.`}
@@ -181,15 +187,15 @@ export class PresElementBox extends ViewBoxBaseComponent() { e.stopPropagation(); e.preventDefault(); if (element && !(e.ctrlKey || e.metaKey)) { - if (PresBox.Instance._selectedArray.has(this.rootDoc)) { - PresBox.Instance._selectedArray.size === 1 && PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false); + if (this.selectedArray?.has(this.rootDoc)) { + this.selectedArray.size === 1 && this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false); setupMoveUpEvents(this, e, this.startDrag, emptyFunction, emptyFunction); } else { setupMoveUpEvents( this, e, (e: PointerEvent) => { - PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false); + this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, false, false); return this.startDrag(e); }, emptyFunction, @@ -205,8 +211,8 @@ export class PresElementBox extends ViewBoxBaseComponent() { startDrag = (e: PointerEvent) => { const miniView: boolean = this.toolbarWidth <= 100; const activeItem = this.rootDoc; - const dragArray = PresBox.Instance._dragArray; - const dragData = new DragManager.DocumentDragData(PresBox.Instance.sortArray()); + const dragArray = this.presBoxView?._dragArray ?? []; + const dragData = new DragManager.DocumentDragData(this.presBoxView?.sortArray() ?? []); if (!dragData.draggedDocuments.length) dragData.draggedDocuments.push(this.rootDoc); dragData.dropAction = 'move'; dragData.treeViewDoc = this.props.docViewPath().lastElement()?.props.treeViewDoc; @@ -221,7 +227,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { } else if (dragArray.length >= 1) { const doc = document.createElement('div'); doc.className = 'presItem-multiDrag'; - doc.innerText = 'Move ' + PresBox.Instance._selectedArray.size + ' slides'; + doc.innerText = 'Move ' + this.selectedArray?.size + ' slides'; doc.style.position = 'absolute'; doc.style.top = e.clientY + 'px'; doc.style.left = e.clientX - 50 + 'px'; @@ -286,10 +292,11 @@ export class PresElementBox extends ViewBoxBaseComponent() { @undoBatch removeItem = action((e: React.MouseEvent) => { e.stopPropagation(); - this.props.removeDocument?.(this.rootDoc); - if (PresBox.Instance._selectedArray.has(this.rootDoc)) { - PresBox.Instance._selectedArray.delete(this.rootDoc); + if (this.indexInPres < (this.presBoxView?.itemIndex || 0)) { + this.presBox.itemIndex = (this.presBoxView?.itemIndex || 0) - 1; } + this.props.removeDocument?.(this.rootDoc); + this.presBoxView?.removeFromSelectedArray(this.rootDoc); this.removeAllRecordingInOverlay(); }); @@ -441,7 +448,7 @@ export class PresElementBox extends ViewBoxBaseComponent() { } @computed get mainItem() { - const isSelected: boolean = PresBox.Instance?._selectedArray.has(this.rootDoc); + const isSelected: boolean = this.selectedArray?.has(this.rootDoc) ? true : false; const toolbarWidth: number = this.toolbarWidth; const showMore: boolean = this.toolbarWidth >= 300; const miniView: boolean = this.toolbarWidth <= 110; @@ -463,12 +470,12 @@ export class PresElementBox extends ViewBoxBaseComponent() { onClick={e => { e.stopPropagation(); e.preventDefault(); - PresBox.Instance.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); + this.presBoxView?.modifierSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, !e.shiftKey && !e.ctrlKey && !e.metaKey, e.ctrlKey || e.metaKey, e.shiftKey); this.showRecording(activeItem); }} onDoubleClick={action(e => { this.toggleProperties(); - PresBox.Instance.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true); + this.presBoxView?.regularSelect(this.rootDoc, this._itemRef.current!, this._dragRef.current!, true); })} onPointerOver={this.onPointerOver} onPointerLeave={this.onPointerLeave} @@ -542,21 +549,29 @@ export class PresElementBox extends ViewBoxBaseComponent() { )} - {/* {this.indexInPres === 0 ? (null) :
{activeItem.groupWithUp ? "Ungroup" : "Group with up"}
}> -
activeItem.groupWithUp = !activeItem.groupWithUp} - style={{ - zIndex: 1000 - this.indexInPres, - fontWeight: 700, - backgroundColor: activeItem.groupWithUp ? presColorBool ? presBoxColor : Colors.MEDIUM_BLUE : undefined, - height: activeItem.groupWithUp ? 53 : 18, - transform: activeItem.groupWithUp ? "translate(0, -17px)" : undefined - }}> -
- e.stopPropagation()} /> -
-
-
} */} + {this.indexInPres === 0 ? null : ( + +
{activeItem.groupWithUp ? 'Ungroup' : 'Group with up'}
+ + }> +
(activeItem.groupWithUp = !activeItem.groupWithUp)} + style={{ + zIndex: 1000 - this.indexInPres, + fontWeight: 700, + backgroundColor: activeItem.groupWithUp ? (presColorBool ? presBoxColor : Colors.MEDIUM_BLUE) : undefined, + height: activeItem.groupWithUp ? 53 : 18, + transform: activeItem.groupWithUp ? 'translate(0, -17px)' : undefined, + }}> +
+ e.stopPropagation()} /> +
+
+
+ )} -- cgit v1.2.3-70-g09d2 From 96a15ed27e9b17a77e6cab72d4087ddfe6066d2a Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 22 Aug 2022 11:02:17 -0400 Subject: made ink strokes animatable. added currentFrame to playgroundFields --- package-lock.json | 866 +++++++++++++++---------------- src/client/util/InteractionUtils.tsx | 240 +++++---- src/client/views/InkStroke.scss | 25 +- src/client/views/InkStrokeProperties.ts | 12 +- src/client/views/InkingStroke.tsx | 6 +- src/client/views/MainView.tsx | 1 + src/client/views/nodes/DocumentView.scss | 1 + 7 files changed, 587 insertions(+), 564 deletions(-) (limited to 'src/client/views') diff --git a/package-lock.json b/package-lock.json index f90b0b5f0..35b7683e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13288,12 +13288,12 @@ }, "abbrev": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, "agent-base": { "version": "4.3.0", - "resolved": "", + "resolved": false, "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", "requires": { "es6-promisify": "^5.0.0" @@ -13301,7 +13301,7 @@ }, "agentkeepalive": { "version": "3.5.2", - "resolved": "", + "resolved": false, "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", "requires": { "humanize-ms": "^1.2.1" @@ -13309,7 +13309,7 @@ }, "ansi-align": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", "requires": { "string-width": "^2.0.0" @@ -13317,12 +13317,12 @@ }, "ansi-regex": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { "version": "3.2.1", - "resolved": "", + "resolved": false, "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "requires": { "color-convert": "^1.9.0" @@ -13330,27 +13330,27 @@ }, "ansicolors": { "version": "0.3.2", - "resolved": "", + "resolved": false, "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=" }, "ansistyles": { "version": "0.1.3", - "resolved": "", + "resolved": false, "integrity": "sha1-XeYEFb2gcbs3EnhUyGT0GyMlRTk=" }, "aproba": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" }, "archy": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=" }, "are-we-there-yet": { "version": "1.1.4", - "resolved": "", + "resolved": false, "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", "requires": { "delegates": "^1.0.0", @@ -13359,7 +13359,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -13373,7 +13373,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -13383,12 +13383,12 @@ }, "asap": { "version": "2.0.6", - "resolved": "", + "resolved": false, "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, "asn1": { "version": "0.2.4", - "resolved": "", + "resolved": false, "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", "requires": { "safer-buffer": "~2.1.0" @@ -13396,32 +13396,32 @@ }, "assert-plus": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "asynckit": { "version": "0.4.0", - "resolved": "", + "resolved": false, "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "aws-sign2": { "version": "0.7.0", - "resolved": "", + "resolved": false, "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.8.0", - "resolved": "", + "resolved": false, "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "balanced-match": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "bcrypt-pbkdf": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", "optional": true, "requires": { @@ -13430,7 +13430,7 @@ }, "bin-links": { "version": "1.1.8", - "resolved": "", + "resolved": false, "integrity": "sha512-KgmVfx+QqggqP9dA3iIc5pA4T1qEEEL+hOhOhNPaUm77OTrJoOXE/C05SJLNJe6m/2wUK7F1tDSou7n5TfCDzQ==", "requires": { "bluebird": "^3.5.3", @@ -13443,12 +13443,12 @@ }, "bluebird": { "version": "3.5.5", - "resolved": "", + "resolved": false, "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==" }, "boxen": { "version": "1.3.0", - "resolved": "", + "resolved": false, "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", "requires": { "ansi-align": "^2.0.0", @@ -13462,7 +13462,7 @@ }, "brace-expansion": { "version": "1.1.11", - "resolved": "", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "requires": { "balanced-match": "^1.0.0", @@ -13471,27 +13471,27 @@ }, "buffer-from": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==" }, "builtins": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=" }, "byline": { "version": "5.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=" }, "byte-size": { "version": "5.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-/XuKeqWocKsYa/cBY1YbSJSWWqTi4cFgr9S6OyM7PBaPbr9zvNGwWP33vt0uqGhwDdN+y3yhbXVILEUpnwEWGw==" }, "cacache": { "version": "12.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", "requires": { "bluebird": "^3.5.5", @@ -13513,27 +13513,27 @@ }, "call-limit": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-5twvci5b9eRBw2wCfPtN0GmlR2/gadZqyFpPhOK6CvMFoFgA+USnZ6Jpu1lhG9h85pQ3Ouil3PfXWRD4EUaRiQ==" }, "camelcase": { "version": "4.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" }, "capture-stack-trace": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=" }, "caseless": { "version": "0.12.0", - "resolved": "", + "resolved": false, "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chalk": { "version": "2.4.1", - "resolved": "", + "resolved": false, "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "requires": { "ansi-styles": "^3.2.1", @@ -13543,17 +13543,17 @@ }, "chownr": { "version": "1.1.4", - "resolved": "", + "resolved": false, "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, "ci-info": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" }, "cidr-regex": { "version": "2.0.10", - "resolved": "", + "resolved": false, "integrity": "sha512-sB3ogMQXWvreNPbJUZMRApxuRYd+KoIo4RGQ81VatjmMW6WJPo+IJZ2846FGItr9VzKo5w7DXzijPLGtSd0N3Q==", "requires": { "ip-regex": "^2.1.0" @@ -13561,12 +13561,12 @@ }, "cli-boxes": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=" }, "cli-columns": { "version": "3.1.2", - "resolved": "", + "resolved": false, "integrity": "sha1-ZzLZcpee/CrkRKHwjgj6E5yWoY4=", "requires": { "string-width": "^2.0.0", @@ -13575,7 +13575,7 @@ }, "cli-table3": { "version": "0.5.1", - "resolved": "", + "resolved": false, "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", "requires": { "colors": "^1.1.2", @@ -13585,7 +13585,7 @@ }, "cliui": { "version": "5.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", "requires": { "string-width": "^3.1.0", @@ -13595,17 +13595,17 @@ "dependencies": { "ansi-regex": { "version": "4.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" }, "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "string-width": { "version": "3.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "requires": { "emoji-regex": "^7.0.1", @@ -13615,7 +13615,7 @@ }, "strip-ansi": { "version": "5.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "requires": { "ansi-regex": "^4.1.0" @@ -13625,12 +13625,12 @@ }, "clone": { "version": "1.0.4", - "resolved": "", + "resolved": false, "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" }, "cmd-shim": { "version": "3.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-DtGg+0xiFhQIntSBRzL2fRQBnmtAVwXIDo4Qq46HPpObYquxMaZS4sb82U9nH91qJrlosC1wa9gwr0QyL/HypA==", "requires": { "graceful-fs": "^4.1.2", @@ -13639,12 +13639,12 @@ }, "code-point-at": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "color-convert": { "version": "1.9.1", - "resolved": "", + "resolved": false, "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "requires": { "color-name": "^1.1.1" @@ -13652,18 +13652,18 @@ }, "color-name": { "version": "1.1.3", - "resolved": "", + "resolved": false, "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "colors": { "version": "1.3.3", - "resolved": "", + "resolved": false, "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", "optional": true }, "columnify": { "version": "1.5.4", - "resolved": "", + "resolved": false, "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=", "requires": { "strip-ansi": "^3.0.0", @@ -13672,7 +13672,7 @@ }, "combined-stream": { "version": "1.0.6", - "resolved": "", + "resolved": false, "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "requires": { "delayed-stream": "~1.0.0" @@ -13680,12 +13680,12 @@ }, "concat-map": { "version": "0.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "concat-stream": { "version": "1.6.2", - "resolved": "", + "resolved": false, "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "requires": { "buffer-from": "^1.0.0", @@ -13696,7 +13696,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -13710,7 +13710,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -13720,7 +13720,7 @@ }, "config-chain": { "version": "1.1.12", - "resolved": "", + "resolved": false, "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", "requires": { "ini": "^1.3.4", @@ -13729,7 +13729,7 @@ }, "configstore": { "version": "3.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", "requires": { "dot-prop": "^4.2.1", @@ -13742,12 +13742,12 @@ }, "console-control-strings": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "copy-concurrently": { "version": "1.0.5", - "resolved": "", + "resolved": false, "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", "requires": { "aproba": "^1.1.1", @@ -13760,24 +13760,24 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, "iferr": { "version": "0.1.5", - "resolved": "", + "resolved": false, "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" } } }, "core-util-is": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, "create-error-class": { "version": "3.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", "requires": { "capture-stack-trace": "^1.0.0" @@ -13785,7 +13785,7 @@ }, "cross-spawn": { "version": "5.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "requires": { "lru-cache": "^4.0.1", @@ -13795,7 +13795,7 @@ "dependencies": { "lru-cache": { "version": "4.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", "requires": { "pseudomap": "^1.0.2", @@ -13804,24 +13804,24 @@ }, "yallist": { "version": "2.1.2", - "resolved": "", + "resolved": false, "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" } } }, "crypto-random-string": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=" }, "cyclist": { "version": "0.2.2", - "resolved": "", + "resolved": false, "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=" }, "dashdash": { "version": "1.14.1", - "resolved": "", + "resolved": false, "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "requires": { "assert-plus": "^1.0.0" @@ -13829,7 +13829,7 @@ }, "debug": { "version": "3.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "requires": { "ms": "2.0.0" @@ -13837,34 +13837,34 @@ "dependencies": { "ms": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" } } }, "debuglog": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=" }, "decamelize": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "decode-uri-component": { "version": "0.2.0", - "resolved": "", + "resolved": false, "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, "deep-extend": { "version": "0.6.0", - "resolved": "", + "resolved": false, "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, "defaults": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", "requires": { "clone": "^1.0.2" @@ -13872,7 +13872,7 @@ }, "define-properties": { "version": "1.1.3", - "resolved": "", + "resolved": false, "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "requires": { "object-keys": "^1.0.12" @@ -13880,27 +13880,27 @@ }, "delayed-stream": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "delegates": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, "detect-indent": { "version": "5.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50=" }, "detect-newline": { "version": "2.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=" }, "dezalgo": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=", "requires": { "asap": "^2.0.0", @@ -13909,7 +13909,7 @@ }, "dot-prop": { "version": "4.2.1", - "resolved": "", + "resolved": false, "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", "requires": { "is-obj": "^1.0.0" @@ -13917,17 +13917,17 @@ }, "dotenv": { "version": "5.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-4As8uPrjfwb7VXC+WnLCbXK7y+Ueb2B3zgNCePYfhxS1PYeaO1YTeplffTEcbfLhvFNGLAz90VvJs9yomG7bow==" }, "duplexer3": { "version": "0.1.4", - "resolved": "", + "resolved": false, "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" }, "duplexify": { "version": "3.6.0", - "resolved": "", + "resolved": false, "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", "requires": { "end-of-stream": "^1.0.0", @@ -13938,7 +13938,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -13952,7 +13952,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -13962,7 +13962,7 @@ }, "ecc-jsbn": { "version": "0.1.2", - "resolved": "", + "resolved": false, "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", "optional": true, "requires": { @@ -13972,17 +13972,17 @@ }, "editor": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-YMf4e9YrzGqJT6jM1q+3gjok90I=" }, "emoji-regex": { "version": "7.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" }, "encoding": { "version": "0.1.12", - "resolved": "", + "resolved": false, "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", "requires": { "iconv-lite": "~0.4.13" @@ -13990,7 +13990,7 @@ }, "end-of-stream": { "version": "1.4.1", - "resolved": "", + "resolved": false, "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "requires": { "once": "^1.4.0" @@ -13998,17 +13998,17 @@ }, "env-paths": { "version": "2.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA==" }, "err-code": { "version": "1.1.2", - "resolved": "", + "resolved": false, "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=" }, "errno": { "version": "0.1.7", - "resolved": "", + "resolved": false, "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "requires": { "prr": "~1.0.1" @@ -14016,7 +14016,7 @@ }, "es-abstract": { "version": "1.12.0", - "resolved": "", + "resolved": false, "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", "requires": { "es-to-primitive": "^1.1.1", @@ -14028,7 +14028,7 @@ }, "es-to-primitive": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", "requires": { "is-callable": "^1.1.4", @@ -14038,12 +14038,12 @@ }, "es6-promise": { "version": "4.2.8", - "resolved": "", + "resolved": false, "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" }, "es6-promisify": { "version": "5.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "requires": { "es6-promise": "^4.0.3" @@ -14051,12 +14051,12 @@ }, "escape-string-regexp": { "version": "1.0.5", - "resolved": "", + "resolved": false, "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "execa": { "version": "0.7.0", - "resolved": "", + "resolved": false, "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "requires": { "cross-spawn": "^5.0.1", @@ -14070,39 +14070,39 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" } } }, "extend": { "version": "3.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, "extsprintf": { "version": "1.3.0", - "resolved": "", + "resolved": false, "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-json-stable-stringify": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, "figgy-pudding": { "version": "3.5.1", - "resolved": "", + "resolved": false, "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" }, "find-npm-prefix": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-KEftzJ+H90x6pcKtdXZEPsQse8/y/UnvzRKrOSQFprnrGaFuJ62fVkP34Iu2IYuMvyauCyoLTNkJZgrrGA2wkA==" }, "flush-write-stream": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", "requires": { "inherits": "^2.0.1", @@ -14111,7 +14111,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -14125,7 +14125,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -14135,12 +14135,12 @@ }, "forever-agent": { "version": "0.6.1", - "resolved": "", + "resolved": false, "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { "version": "2.3.2", - "resolved": "", + "resolved": false, "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "requires": { "asynckit": "^0.4.0", @@ -14150,7 +14150,7 @@ }, "from2": { "version": "2.3.0", - "resolved": "", + "resolved": false, "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "requires": { "inherits": "^2.0.1", @@ -14159,7 +14159,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -14173,7 +14173,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -14183,7 +14183,7 @@ }, "fs-minipass": { "version": "1.2.7", - "resolved": "", + "resolved": false, "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", "requires": { "minipass": "^2.6.0" @@ -14191,7 +14191,7 @@ "dependencies": { "minipass": { "version": "2.9.0", - "resolved": "", + "resolved": false, "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "requires": { "safe-buffer": "^5.1.2", @@ -14202,7 +14202,7 @@ }, "fs-vacuum": { "version": "1.2.10", - "resolved": "", + "resolved": false, "integrity": "sha1-t2Kb7AekAxolSP35n17PHMizHjY=", "requires": { "graceful-fs": "^4.1.2", @@ -14212,7 +14212,7 @@ }, "fs-write-stream-atomic": { "version": "1.0.10", - "resolved": "", + "resolved": false, "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", "requires": { "graceful-fs": "^4.1.2", @@ -14223,12 +14223,12 @@ "dependencies": { "iferr": { "version": "0.1.5", - "resolved": "", + "resolved": false, "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" }, "readable-stream": { "version": "2.3.6", - "resolved": "", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -14242,7 +14242,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -14252,17 +14252,17 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "function-bind": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "gauge": { "version": "2.7.4", - "resolved": "", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "requires": { "aproba": "^1.0.3", @@ -14277,12 +14277,12 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, "string-width": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { "code-point-at": "^1.0.0", @@ -14294,12 +14294,12 @@ }, "genfun": { "version": "5.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==" }, "gentle-fs": { "version": "2.3.1", - "resolved": "", + "resolved": false, "integrity": "sha512-OlwBBwqCFPcjm33rF2BjW+Pr6/ll2741l+xooiwTCeaX2CA1ZuclavyMBe0/KlR21/XGsgY6hzEQZ15BdNa13Q==", "requires": { "aproba": "^1.1.2", @@ -14317,24 +14317,24 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" }, "iferr": { "version": "0.1.5", - "resolved": "", + "resolved": false, "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" } } }, "get-caller-file": { "version": "2.0.5", - "resolved": "", + "resolved": false, "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-stream": { "version": "4.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "requires": { "pump": "^3.0.0" @@ -14342,7 +14342,7 @@ }, "getpass": { "version": "0.1.7", - "resolved": "", + "resolved": false, "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "requires": { "assert-plus": "^1.0.0" @@ -14350,7 +14350,7 @@ }, "glob": { "version": "7.1.6", - "resolved": "", + "resolved": false, "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "requires": { "fs.realpath": "^1.0.0", @@ -14363,7 +14363,7 @@ }, "global-dirs": { "version": "0.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", "requires": { "ini": "^1.3.4" @@ -14371,7 +14371,7 @@ }, "got": { "version": "6.7.1", - "resolved": "", + "resolved": false, "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", "requires": { "create-error-class": "^3.0.0", @@ -14389,24 +14389,24 @@ "dependencies": { "get-stream": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" } } }, "graceful-fs": { "version": "4.2.4", - "resolved": "", + "resolved": false, "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "har-schema": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { "version": "5.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "requires": { "ajv": "^6.12.3", @@ -14415,7 +14415,7 @@ "dependencies": { "ajv": { "version": "6.12.6", - "resolved": "", + "resolved": false, "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "requires": { "fast-deep-equal": "^3.1.1", @@ -14426,19 +14426,19 @@ }, "fast-deep-equal": { "version": "3.1.3", - "resolved": "", + "resolved": false, "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "json-schema-traverse": { "version": "0.4.1", - "resolved": "", + "resolved": false, "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" } } }, "has": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "requires": { "function-bind": "^1.1.1" @@ -14446,32 +14446,32 @@ }, "has-flag": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" }, "has-unicode": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, "hosted-git-info": { "version": "2.8.9", - "resolved": "", + "resolved": false, "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, "http-cache-semantics": { "version": "3.8.1", - "resolved": "", + "resolved": false, "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==" }, "http-proxy-agent": { "version": "2.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", "requires": { "agent-base": "4", @@ -14480,7 +14480,7 @@ }, "http-signature": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "requires": { "assert-plus": "^1.0.0", @@ -14490,7 +14490,7 @@ }, "https-proxy-agent": { "version": "2.2.4", - "resolved": "", + "resolved": false, "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", "requires": { "agent-base": "^4.3.0", @@ -14499,7 +14499,7 @@ }, "humanize-ms": { "version": "1.2.1", - "resolved": "", + "resolved": false, "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", "requires": { "ms": "^2.0.0" @@ -14507,7 +14507,7 @@ }, "iconv-lite": { "version": "0.4.23", - "resolved": "", + "resolved": false, "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", "requires": { "safer-buffer": ">= 2.1.2 < 3" @@ -14515,12 +14515,12 @@ }, "iferr": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-9AfeLfji44r5TKInjhz3W9DyZI1zR1JAf2hVBMGhddAKPqBsupb89jGfbCTHIGZd6fGZl9WlHdn4AObygyMKwg==" }, "ignore-walk": { "version": "3.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==", "requires": { "minimatch": "^3.0.4" @@ -14528,22 +14528,22 @@ }, "import-lazy": { "version": "2.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" }, "imurmurhash": { "version": "0.1.4", - "resolved": "", + "resolved": false, "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" }, "infer-owner": { "version": "1.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" }, "inflight": { "version": "1.0.6", - "resolved": "", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "requires": { "once": "^1.3.0", @@ -14552,17 +14552,17 @@ }, "inherits": { "version": "2.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { "version": "1.3.8", - "resolved": "", + "resolved": false, "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, "init-package-json": { "version": "1.10.3", - "resolved": "", + "resolved": false, "integrity": "sha512-zKSiXKhQveNteyhcj1CoOP8tqp1QuxPIPBl8Bid99DGLFqA1p87M6lNgfjJHSBoWJJlidGOv5rWjyYKEB3g2Jw==", "requires": { "glob": "^7.1.1", @@ -14577,22 +14577,22 @@ }, "ip": { "version": "1.1.5", - "resolved": "", + "resolved": false, "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" }, "ip-regex": { "version": "2.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" }, "is-callable": { "version": "1.1.4", - "resolved": "", + "resolved": false, "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" }, "is-ci": { "version": "1.2.1", - "resolved": "", + "resolved": false, "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", "requires": { "ci-info": "^1.5.0" @@ -14600,14 +14600,14 @@ "dependencies": { "ci-info": { "version": "1.6.0", - "resolved": "", + "resolved": false, "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==" } } }, "is-cidr": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-8Xnnbjsb0x462VoYiGlhEi+drY8SFwrHiSYuzc/CEwco55vkehTaxAyIjEdpi3EMvLPPJAJi9FlzP+h+03gp0Q==", "requires": { "cidr-regex": "^2.0.10" @@ -14615,12 +14615,12 @@ }, "is-date-object": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "requires": { "number-is-nan": "^1.0.0" @@ -14628,7 +14628,7 @@ }, "is-installed-globally": { "version": "0.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", "requires": { "global-dirs": "^0.1.0", @@ -14637,17 +14637,17 @@ }, "is-npm": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=" }, "is-obj": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=" }, "is-path-inside": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "requires": { "path-is-inside": "^1.0.1" @@ -14655,12 +14655,12 @@ }, "is-redirect": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=" }, "is-regex": { "version": "1.0.4", - "resolved": "", + "resolved": false, "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "requires": { "has": "^1.0.1" @@ -14668,17 +14668,17 @@ }, "is-retry-allowed": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" }, "is-stream": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, "is-symbol": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", "requires": { "has-symbols": "^1.0.0" @@ -14686,53 +14686,53 @@ }, "is-typedarray": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "isarray": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isstream": { "version": "0.1.2", - "resolved": "", + "resolved": false, "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "jsbn": { "version": "0.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "optional": true }, "json-parse-better-errors": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, "json-schema": { "version": "0.4.0", - "resolved": "", + "resolved": false, "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" }, "json-stringify-safe": { "version": "5.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "jsonparse": { "version": "1.3.1", - "resolved": "", + "resolved": false, "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" }, "jsprim": { "version": "1.4.2", - "resolved": "", + "resolved": false, "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", "requires": { "assert-plus": "1.0.0", @@ -14743,7 +14743,7 @@ }, "latest-version": { "version": "3.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", "requires": { "package-json": "^4.0.0" @@ -14751,12 +14751,12 @@ }, "lazy-property": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-hN3Es3Bnm6i9TNz6TAa0PVcREUc=" }, "libcipm": { "version": "4.0.8", - "resolved": "", + "resolved": false, "integrity": "sha512-IN3hh2yDJQtZZ5paSV4fbvJg4aHxCCg5tcZID/dSVlTuUiWktsgaldVljJv6Z5OUlYspx6xQkbR0efNodnIrOA==", "requires": { "bin-links": "^1.1.2", @@ -14778,7 +14778,7 @@ }, "libnpm": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-d7jU5ZcMiTfBqTUJVZ3xid44fE5ERBm9vBnmhp2ECD2Ls+FNXWxHSkO7gtvrnbLO78gwPdNPz1HpsF3W4rjkBQ==", "requires": { "bin-links": "^1.1.2", @@ -14805,7 +14805,7 @@ }, "libnpmaccess": { "version": "3.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-01512AK7MqByrI2mfC7h5j8N9V4I7MHJuk9buo8Gv+5QgThpOgpjB7sQBDDkeZqRteFb1QM/6YNdHfG7cDvfAQ==", "requires": { "aproba": "^2.0.0", @@ -14816,7 +14816,7 @@ }, "libnpmconfig": { "version": "1.2.1", - "resolved": "", + "resolved": false, "integrity": "sha512-9esX8rTQAHqarx6qeZqmGQKBNZR5OIbl/Ayr0qQDy3oXja2iFVQQI81R6GZ2a02bSNZ9p3YOGX1O6HHCb1X7kA==", "requires": { "figgy-pudding": "^3.5.1", @@ -14826,7 +14826,7 @@ "dependencies": { "find-up": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "requires": { "locate-path": "^3.0.0" @@ -14834,7 +14834,7 @@ }, "locate-path": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "requires": { "p-locate": "^3.0.0", @@ -14843,7 +14843,7 @@ }, "p-limit": { "version": "2.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", "requires": { "p-try": "^2.0.0" @@ -14851,7 +14851,7 @@ }, "p-locate": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "requires": { "p-limit": "^2.0.0" @@ -14859,14 +14859,14 @@ }, "p-try": { "version": "2.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" } } }, "libnpmhook": { "version": "5.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-UdNLMuefVZra/wbnBXECZPefHMGsVDTq5zaM/LgKNE9Keyl5YXQTnGAzEo+nFOpdRqTWI9LYi4ApqF9uVCCtuA==", "requires": { "aproba": "^2.0.0", @@ -14877,7 +14877,7 @@ }, "libnpmorg": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-0sRUXLh+PLBgZmARvthhYXQAWn0fOsa6T5l3JSe2n9vKG/lCVK4nuG7pDsa7uMq+uTt2epdPK+a2g6btcY11Ww==", "requires": { "aproba": "^2.0.0", @@ -14888,7 +14888,7 @@ }, "libnpmpublish": { "version": "1.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-2yIwaXrhTTcF7bkJKIKmaCV9wZOALf/gsTDxVSu/Gu/6wiG3fA8ce8YKstiWKTxSFNC0R7isPUb6tXTVFZHt2g==", "requires": { "aproba": "^2.0.0", @@ -14904,7 +14904,7 @@ }, "libnpmsearch": { "version": "2.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-VTBbV55Q6fRzTdzziYCr64+f8AopQ1YZ+BdPOv16UegIEaE8C0Kch01wo4s3kRTFV64P121WZJwgmBwrq68zYg==", "requires": { "figgy-pudding": "^3.5.1", @@ -14914,7 +14914,7 @@ }, "libnpmteam": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-p420vM28Us04NAcg1rzgGW63LMM6rwe+6rtZpfDxCcXxM0zUTLl7nPFEnRF3JfFBF5skF/yuZDUthTsHgde8QA==", "requires": { "aproba": "^2.0.0", @@ -14925,7 +14925,7 @@ }, "libnpx": { "version": "10.2.4", - "resolved": "", + "resolved": false, "integrity": "sha512-BPc0D1cOjBeS8VIBKUu5F80s6njm0wbVt7CsGMrIcJ+SI7pi7V0uVPGpEMH9H5L8csOcclTxAXFE2VAsJXUhfA==", "requires": { "dotenv": "^5.0.1", @@ -14940,7 +14940,7 @@ }, "lock-verify": { "version": "2.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-vcLpxnGvrqisKvLQ2C2v0/u7LVly17ak2YSgoK4PrdsYBXQIax19vhKiLfvKNFx7FRrpTnitrpzF/uuCMuorIg==", "requires": { "npm-package-arg": "^6.1.0", @@ -14949,7 +14949,7 @@ }, "lockfile": { "version": "1.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", "requires": { "signal-exit": "^3.0.2" @@ -14957,12 +14957,12 @@ }, "lodash._baseindexof": { "version": "3.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=" }, "lodash._baseuniq": { "version": "4.6.0", - "resolved": "", + "resolved": false, "integrity": "sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=", "requires": { "lodash._createset": "~4.0.0", @@ -14971,17 +14971,17 @@ }, "lodash._bindcallback": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=" }, "lodash._cacheindexof": { "version": "3.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=" }, "lodash._createcache": { "version": "3.1.2", - "resolved": "", + "resolved": false, "integrity": "sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=", "requires": { "lodash._getnative": "^3.0.0" @@ -14989,52 +14989,52 @@ }, "lodash._createset": { "version": "4.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=" }, "lodash._getnative": { "version": "3.9.1", - "resolved": "", + "resolved": false, "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=" }, "lodash._root": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=" }, "lodash.clonedeep": { "version": "4.5.0", - "resolved": "", + "resolved": false, "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=" }, "lodash.restparam": { "version": "3.6.1", - "resolved": "", + "resolved": false, "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=" }, "lodash.union": { "version": "4.6.0", - "resolved": "", + "resolved": false, "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" }, "lodash.uniq": { "version": "4.5.0", - "resolved": "", + "resolved": false, "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" }, "lodash.without": { "version": "4.4.0", - "resolved": "", + "resolved": false, "integrity": "sha1-PNRXSgC2e643OpS3SHcmQFB7eqw=" }, "lowercase-keys": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" }, "lru-cache": { "version": "5.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "requires": { "yallist": "^3.0.2" @@ -15042,7 +15042,7 @@ }, "make-dir": { "version": "1.3.0", - "resolved": "", + "resolved": false, "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "requires": { "pify": "^3.0.0" @@ -15050,7 +15050,7 @@ }, "make-fetch-happen": { "version": "5.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==", "requires": { "agentkeepalive": "^3.4.1", @@ -15068,17 +15068,17 @@ }, "meant": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-KN+1uowN/NK+sT/Lzx7WSGIj2u+3xe5n2LbwObfjOhPZiA+cCfCm6idVl0RkEfjThkw5XJ96CyRcanq6GmKtUg==" }, "mime-db": { "version": "1.35.0", - "resolved": "", + "resolved": false, "integrity": "sha512-JWT/IcCTsB0Io3AhWUMjRqucrHSPsSf2xKLaRldJVULioggvkJvggZ3VXNNSRkCddE6D+BUI4HEIZIA2OjwIvg==" }, "mime-types": { "version": "2.1.19", - "resolved": "", + "resolved": false, "integrity": "sha512-P1tKYHVSZ6uFo26mtnve4HQFE3koh1UWVkp8YUC+ESBHe945xWSoXuHHiGarDqcEZ+whpCDnlNw5LON0kLo+sw==", "requires": { "mime-db": "~1.35.0" @@ -15086,7 +15086,7 @@ }, "minimatch": { "version": "3.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "requires": { "brace-expansion": "^1.1.7" @@ -15094,12 +15094,12 @@ }, "minimist": { "version": "1.2.6", - "resolved": "", + "resolved": false, "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "minizlib": { "version": "1.3.3", - "resolved": "", + "resolved": false, "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", "requires": { "minipass": "^2.9.0" @@ -15107,7 +15107,7 @@ "dependencies": { "minipass": { "version": "2.9.0", - "resolved": "", + "resolved": false, "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "requires": { "safe-buffer": "^5.1.2", @@ -15118,7 +15118,7 @@ }, "mississippi": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", "requires": { "concat-stream": "^1.5.0", @@ -15135,7 +15135,7 @@ }, "mkdirp": { "version": "0.5.5", - "resolved": "", + "resolved": false, "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "requires": { "minimist": "^1.2.5" @@ -15143,14 +15143,14 @@ "dependencies": { "minimist": { "version": "1.2.6", - "resolved": "", + "resolved": false, "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" } } }, "move-concurrently": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", "requires": { "aproba": "^1.1.1", @@ -15163,24 +15163,24 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" } } }, "ms": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" }, "mute-stream": { "version": "0.0.7", - "resolved": "", + "resolved": false, "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" }, "node-fetch-npm": { "version": "2.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-nJIxm1QmAj4v3nfCvEeCrYSoVwXyxLnaPBK5W1W5DGEJwjlKuC2VEUycGw5oxk+4zZahRrB84PUJJgEmhFTDFw==", "requires": { "encoding": "^0.1.11", @@ -15190,7 +15190,7 @@ }, "node-gyp": { "version": "5.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-OUTryc5bt/P8zVgNUmC6xdXiDJxLMAW8cF5tLQOT9E5sOQj+UeQxnnPy74K3CLCa/SOjjBlbuzDLR8ANwA+wmw==", "requires": { "env-paths": "^2.2.0", @@ -15208,7 +15208,7 @@ }, "nopt": { "version": "4.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", "requires": { "abbrev": "1", @@ -15217,7 +15217,7 @@ }, "normalize-package-data": { "version": "2.5.0", - "resolved": "", + "resolved": false, "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "requires": { "hosted-git-info": "^2.1.4", @@ -15228,7 +15228,7 @@ "dependencies": { "resolve": { "version": "1.10.0", - "resolved": "", + "resolved": false, "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", "requires": { "path-parse": "^1.0.6" @@ -15238,7 +15238,7 @@ }, "npm-audit-report": { "version": "1.3.3", - "resolved": "", + "resolved": false, "integrity": "sha512-8nH/JjsFfAWMvn474HB9mpmMjrnKb1Hx/oTAdjv4PT9iZBvBxiZ+wtDUapHCJwLqYGQVPaAfs+vL5+5k9QndXw==", "requires": { "cli-table3": "^0.5.0", @@ -15247,7 +15247,7 @@ }, "npm-bundled": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==", "requires": { "npm-normalize-package-bin": "^1.0.1" @@ -15255,12 +15255,12 @@ }, "npm-cache-filename": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-3tMGxbC/yHCp6fr4I7xfKD4FrhE=" }, "npm-install-checks": { "version": "3.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-E4kzkyZDIWoin6uT5howP8VDvkM+E8IQDcHAycaAxMbwkqhIg5eEYALnXOl3Hq9MrkdQB/2/g1xwBINXdKSRkg==", "requires": { "semver": "^2.3.0 || 3.x || 4 || 5" @@ -15268,7 +15268,7 @@ }, "npm-lifecycle": { "version": "3.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-lDLVkjfZmvmfvpvBzA4vzee9cn+Me4orq0QF8glbswJVEbIcSNWib7qGOffolysc3teCqbbPZZkzbr3GQZTL1g==", "requires": { "byline": "^5.0.0", @@ -15283,17 +15283,17 @@ }, "npm-logical-tree": { "version": "1.2.1", - "resolved": "", + "resolved": false, "integrity": "sha512-AJI/qxDB2PWI4LG1CYN579AY1vCiNyWfkiquCsJWqntRu/WwimVrC8yXeILBFHDwxfOejxewlmnvW9XXjMlYIg==" }, "npm-normalize-package-bin": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==" }, "npm-package-arg": { "version": "6.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==", "requires": { "hosted-git-info": "^2.7.1", @@ -15304,7 +15304,7 @@ }, "npm-packlist": { "version": "1.4.8", - "resolved": "", + "resolved": false, "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==", "requires": { "ignore-walk": "^3.0.1", @@ -15314,7 +15314,7 @@ }, "npm-pick-manifest": { "version": "3.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==", "requires": { "figgy-pudding": "^3.5.1", @@ -15324,7 +15324,7 @@ }, "npm-profile": { "version": "4.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-Ta8xq8TLMpqssF0H60BXS1A90iMoM6GeKwsmravJ6wYjWwSzcYBTdyWa3DZCYqPutacBMEm7cxiOkiIeCUAHDQ==", "requires": { "aproba": "^1.1.2 || 2", @@ -15334,7 +15334,7 @@ }, "npm-registry-fetch": { "version": "4.0.7", - "resolved": "", + "resolved": false, "integrity": "sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ==", "requires": { "JSONStream": "^1.3.4", @@ -15348,14 +15348,14 @@ "dependencies": { "safe-buffer": { "version": "5.2.1", - "resolved": "", + "resolved": false, "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } }, "npm-run-path": { "version": "2.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "requires": { "path-key": "^2.0.0" @@ -15363,12 +15363,12 @@ }, "npm-user-validate": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw==" }, "npmlog": { "version": "4.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "requires": { "are-we-there-yet": "~1.1.2", @@ -15379,27 +15379,27 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "oauth-sign": { "version": "0.9.0", - "resolved": "", + "resolved": false, "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "object-assign": { "version": "4.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-keys": { "version": "1.0.12", - "resolved": "", + "resolved": false, "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==" }, "object.getownpropertydescriptors": { "version": "2.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", "requires": { "define-properties": "^1.1.2", @@ -15408,7 +15408,7 @@ }, "once": { "version": "1.4.0", - "resolved": "", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "requires": { "wrappy": "1" @@ -15416,22 +15416,22 @@ }, "opener": { "version": "1.5.2", - "resolved": "", + "resolved": false, "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==" }, "os-homedir": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" }, "os-tmpdir": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, "osenv": { "version": "0.1.5", - "resolved": "", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "requires": { "os-homedir": "^1.0.0", @@ -15440,12 +15440,12 @@ }, "p-finally": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "package-json": { "version": "4.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", "requires": { "got": "^6.7.1", @@ -15456,7 +15456,7 @@ }, "pacote": { "version": "9.5.12", - "resolved": "", + "resolved": false, "integrity": "sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ==", "requires": { "bluebird": "^3.5.3", @@ -15493,7 +15493,7 @@ "dependencies": { "minipass": { "version": "2.9.0", - "resolved": "", + "resolved": false, "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "requires": { "safe-buffer": "^5.1.2", @@ -15504,7 +15504,7 @@ }, "parallel-transform": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", "requires": { "cyclist": "~0.2.2", @@ -15514,7 +15514,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -15528,7 +15528,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -15538,57 +15538,57 @@ }, "path-exists": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" }, "path-is-absolute": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, "path-is-inside": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" }, "path-key": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" }, "path-parse": { "version": "1.0.7", - "resolved": "", + "resolved": false, "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "performance-now": { "version": "2.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "pify": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" }, "prepend-http": { "version": "1.0.4", - "resolved": "", + "resolved": false, "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" }, "process-nextick-args": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "promise-inflight": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" }, "promise-retry": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=", "requires": { "err-code": "^1.0.0", @@ -15597,14 +15597,14 @@ "dependencies": { "retry": { "version": "0.10.1", - "resolved": "", + "resolved": false, "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=" } } }, "promzard": { "version": "0.3.0", - "resolved": "", + "resolved": false, "integrity": "sha1-JqXW7ox97kyxIggwWs+5O6OCqe4=", "requires": { "read": "1" @@ -15612,12 +15612,12 @@ }, "proto-list": { "version": "1.2.4", - "resolved": "", + "resolved": false, "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=" }, "protoduck": { "version": "5.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==", "requires": { "genfun": "^5.0.0" @@ -15625,22 +15625,22 @@ }, "prr": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" }, "pseudomap": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, "psl": { "version": "1.1.29", - "resolved": "", + "resolved": false, "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" }, "pump": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "requires": { "end-of-stream": "^1.1.0", @@ -15649,7 +15649,7 @@ }, "pumpify": { "version": "1.5.1", - "resolved": "", + "resolved": false, "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "requires": { "duplexify": "^3.6.0", @@ -15659,7 +15659,7 @@ "dependencies": { "pump": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "requires": { "end-of-stream": "^1.1.0", @@ -15670,22 +15670,22 @@ }, "punycode": { "version": "1.4.1", - "resolved": "", + "resolved": false, "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" }, "qrcode-terminal": { "version": "0.12.0", - "resolved": "", + "resolved": false, "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==" }, "qs": { "version": "6.5.2", - "resolved": "", + "resolved": false, "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, "query-string": { "version": "6.8.2", - "resolved": "", + "resolved": false, "integrity": "sha512-J3Qi8XZJXh93t2FiKyd/7Ec6GNifsjKXUsVFkSBj/kjLsDylWhnCz4NT1bkPcKotttPW+QbKGqqPH8OoI2pdqw==", "requires": { "decode-uri-component": "^0.2.0", @@ -15695,12 +15695,12 @@ }, "qw": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-77/cdA+a0FQwRCassYNBLMi5ltQ=" }, "rc": { "version": "1.2.8", - "resolved": "", + "resolved": false, "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "requires": { "deep-extend": "^0.6.0", @@ -15711,7 +15711,7 @@ }, "read": { "version": "1.0.7", - "resolved": "", + "resolved": false, "integrity": "sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ=", "requires": { "mute-stream": "~0.0.4" @@ -15719,7 +15719,7 @@ }, "read-cmd-shim": { "version": "1.0.5", - "resolved": "", + "resolved": false, "integrity": "sha512-v5yCqQ/7okKoZZkBQUAfTsQ3sVJtXdNfbPnI5cceppoxEVLYA3k+VtV2omkeo8MS94JCy4fSiUwlRBAwCVRPUA==", "requires": { "graceful-fs": "^4.1.2" @@ -15727,7 +15727,7 @@ }, "read-installed": { "version": "4.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-/5uLZ/GH0eTCm5/rMfayI6zRkGc=", "requires": { "debuglog": "^1.0.1", @@ -15741,7 +15741,7 @@ }, "read-package-json": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-dAiqGtVc/q5doFz6096CcnXhpYk0ZN8dEKVkGLU0CsASt8SrgF6SF7OTKAYubfvFhWaqofl+Y8HK19GR8jwW+A==", "requires": { "glob": "^7.1.1", @@ -15753,7 +15753,7 @@ }, "read-package-tree": { "version": "5.3.1", - "resolved": "", + "resolved": false, "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==", "requires": { "read-package-json": "^2.0.0", @@ -15763,7 +15763,7 @@ }, "readable-stream": { "version": "3.6.0", - "resolved": "", + "resolved": false, "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", "requires": { "inherits": "^2.0.3", @@ -15773,7 +15773,7 @@ }, "readdir-scoped-modules": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", "requires": { "debuglog": "^1.0.1", @@ -15784,7 +15784,7 @@ }, "registry-auth-token": { "version": "3.4.0", - "resolved": "", + "resolved": false, "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", "requires": { "rc": "^1.1.6", @@ -15793,7 +15793,7 @@ }, "registry-url": { "version": "3.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "requires": { "rc": "^1.0.1" @@ -15801,7 +15801,7 @@ }, "request": { "version": "2.88.0", - "resolved": "", + "resolved": false, "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "requires": { "aws-sign2": "~0.7.0", @@ -15828,27 +15828,27 @@ }, "require-directory": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "require-main-filename": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, "resolve-from": { "version": "4.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" }, "retry": { "version": "0.12.0", - "resolved": "", + "resolved": false, "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" }, "rimraf": { "version": "2.7.1", - "resolved": "", + "resolved": false, "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "requires": { "glob": "^7.1.3" @@ -15856,7 +15856,7 @@ }, "run-queue": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", "requires": { "aproba": "^1.1.1" @@ -15864,29 +15864,29 @@ "dependencies": { "aproba": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" } } }, "safe-buffer": { "version": "5.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safer-buffer": { "version": "2.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { "version": "5.7.1", - "resolved": "", + "resolved": false, "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" }, "semver-diff": { "version": "2.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "requires": { "semver": "^5.0.3" @@ -15894,12 +15894,12 @@ }, "set-blocking": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "sha": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-DOYnM37cNsLNSGIG/zZWch5CKIRNoLdYUQTQlcgkRkoYIUwDYjqDyye16YcDZg/OPdcbUgTKMjc4SY6TB7ZAPw==", "requires": { "graceful-fs": "^4.1.2" @@ -15907,7 +15907,7 @@ }, "shebang-command": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "requires": { "shebang-regex": "^1.0.0" @@ -15915,27 +15915,27 @@ }, "shebang-regex": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, "signal-exit": { "version": "3.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "slide": { "version": "1.1.6", - "resolved": "", + "resolved": false, "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=" }, "smart-buffer": { "version": "4.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==" }, "socks": { "version": "2.3.3", - "resolved": "", + "resolved": false, "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==", "requires": { "ip": "1.1.5", @@ -15944,7 +15944,7 @@ }, "socks-proxy-agent": { "version": "4.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==", "requires": { "agent-base": "~4.2.1", @@ -15953,7 +15953,7 @@ "dependencies": { "agent-base": { "version": "4.2.1", - "resolved": "", + "resolved": false, "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==", "requires": { "es6-promisify": "^5.0.0" @@ -15963,12 +15963,12 @@ }, "sorted-object": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-fWMfS9OnmKJK8d/8+/6DM3pd9fw=" }, "sorted-union-stream": { "version": "2.1.3", - "resolved": "", + "resolved": false, "integrity": "sha1-x3lMfgd4gAUv9xqNSi27Sppjisc=", "requires": { "from2": "^1.3.0", @@ -15977,7 +15977,7 @@ "dependencies": { "from2": { "version": "1.3.0", - "resolved": "", + "resolved": false, "integrity": "sha1-iEE7qqX5pZfP3pIh2GmGzTwGHf0=", "requires": { "inherits": "~2.0.1", @@ -15986,12 +15986,12 @@ }, "isarray": { "version": "0.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" }, "readable-stream": { "version": "1.1.14", - "resolved": "", + "resolved": false, "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "requires": { "core-util-is": "~1.0.0", @@ -16002,14 +16002,14 @@ }, "string_decoder": { "version": "0.10.31", - "resolved": "", + "resolved": false, "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" } } }, "spdx-correct": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "requires": { "spdx-expression-parse": "^3.0.0", @@ -16018,12 +16018,12 @@ }, "spdx-exceptions": { "version": "2.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-4K1NsmrlCU1JJgUrtgEeTVyfx8VaYea9J9LvARxhbHtVtohPs/gFGG5yy49beySjlIMhhXZ4QqujIZEfS4l6Cg==" }, "spdx-expression-parse": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "requires": { "spdx-exceptions": "^2.1.0", @@ -16032,17 +16032,17 @@ }, "spdx-license-ids": { "version": "3.0.5", - "resolved": "", + "resolved": false, "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==" }, "split-on-first": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==" }, "sshpk": { "version": "1.14.2", - "resolved": "", + "resolved": false, "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", "requires": { "asn1": "~0.2.3", @@ -16058,7 +16058,7 @@ }, "ssri": { "version": "6.0.2", - "resolved": "", + "resolved": false, "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "requires": { "figgy-pudding": "^3.5.1" @@ -16066,7 +16066,7 @@ }, "stream-each": { "version": "1.2.2", - "resolved": "", + "resolved": false, "integrity": "sha512-mc1dbFhGBxvTM3bIWmAAINbqiuAk9TATcfIQC8P+/+HJefgaiTlMn2dHvkX8qlI12KeYKSQ1Ua9RrIqrn1VPoA==", "requires": { "end-of-stream": "^1.1.0", @@ -16075,7 +16075,7 @@ }, "stream-iterate": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha1-K9fHcpbBcCpGSIuK1B95hl7s1OE=", "requires": { "readable-stream": "^2.1.5", @@ -16084,7 +16084,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -16098,7 +16098,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -16108,17 +16108,17 @@ }, "stream-shift": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=" }, "strict-uri-encode": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY=" }, "string-width": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "requires": { "is-fullwidth-code-point": "^2.0.0", @@ -16127,17 +16127,17 @@ "dependencies": { "ansi-regex": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" }, "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "strip-ansi": { "version": "4.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "requires": { "ansi-regex": "^3.0.0" @@ -16160,12 +16160,12 @@ }, "stringify-package": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==" }, "strip-ansi": { "version": "3.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "requires": { "ansi-regex": "^2.0.0" @@ -16173,17 +16173,17 @@ }, "strip-eof": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" }, "strip-json-comments": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" }, "supports-color": { "version": "5.4.0", - "resolved": "", + "resolved": false, "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "requires": { "has-flag": "^3.0.0" @@ -16191,7 +16191,7 @@ }, "tar": { "version": "4.4.19", - "resolved": "", + "resolved": false, "integrity": "sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==", "requires": { "chownr": "^1.1.4", @@ -16205,7 +16205,7 @@ "dependencies": { "minipass": { "version": "2.9.0", - "resolved": "", + "resolved": false, "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", "requires": { "safe-buffer": "^5.1.2", @@ -16214,19 +16214,19 @@ }, "safe-buffer": { "version": "5.2.1", - "resolved": "", + "resolved": false, "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "yallist": { "version": "3.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" } } }, "term-size": { "version": "1.2.0", - "resolved": "", + "resolved": false, "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", "requires": { "execa": "^0.7.0" @@ -16234,17 +16234,17 @@ }, "text-table": { "version": "0.2.0", - "resolved": "", + "resolved": false, "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" }, "through": { "version": "2.3.8", - "resolved": "", + "resolved": false, "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { "version": "2.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "requires": { "readable-stream": "^2.1.5", @@ -16253,7 +16253,7 @@ "dependencies": { "readable-stream": { "version": "2.3.6", - "resolved": "", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "requires": { "core-util-is": "~1.0.0", @@ -16267,7 +16267,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "requires": { "safe-buffer": "~5.1.0" @@ -16277,17 +16277,17 @@ }, "timed-out": { "version": "4.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" }, "tiny-relative-date": { "version": "1.3.0", - "resolved": "", + "resolved": false, "integrity": "sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==" }, "tough-cookie": { "version": "2.4.3", - "resolved": "", + "resolved": false, "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", "requires": { "psl": "^1.1.24", @@ -16296,7 +16296,7 @@ }, "tunnel-agent": { "version": "0.6.0", - "resolved": "", + "resolved": false, "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "requires": { "safe-buffer": "^5.0.1" @@ -16304,28 +16304,28 @@ }, "tweetnacl": { "version": "0.14.5", - "resolved": "", + "resolved": false, "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", "optional": true }, "typedarray": { "version": "0.0.6", - "resolved": "", + "resolved": false, "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, "uid-number": { "version": "0.0.6", - "resolved": "", + "resolved": false, "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=" }, "umask": { "version": "1.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-8pzr8B31F5ErtY/5xOUP3o4zMg0=" }, "unique-filename": { "version": "1.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", "requires": { "unique-slug": "^2.0.0" @@ -16333,7 +16333,7 @@ }, "unique-slug": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", "requires": { "imurmurhash": "^0.1.4" @@ -16341,7 +16341,7 @@ }, "unique-string": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", "requires": { "crypto-random-string": "^1.0.0" @@ -16349,17 +16349,17 @@ }, "unpipe": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, "unzip-response": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=" }, "update-notifier": { "version": "2.5.0", - "resolved": "", + "resolved": false, "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", "requires": { "boxen": "^1.2.1", @@ -16376,7 +16376,7 @@ }, "uri-js": { "version": "4.4.0", - "resolved": "", + "resolved": false, "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", "requires": { "punycode": "^2.1.0" @@ -16384,14 +16384,14 @@ "dependencies": { "punycode": { "version": "2.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" } } }, "url-parse-lax": { "version": "1.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", "requires": { "prepend-http": "^1.0.1" @@ -16399,17 +16399,17 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "util-extend": { "version": "1.0.3", - "resolved": "", + "resolved": false, "integrity": "sha1-p8IW0mdUUWljeztu3GypEZ4v+T8=" }, "util-promisify": { "version": "2.1.0", - "resolved": "", + "resolved": false, "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=", "requires": { "object.getownpropertydescriptors": "^2.0.3" @@ -16417,12 +16417,12 @@ }, "uuid": { "version": "3.3.3", - "resolved": "", + "resolved": false, "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" }, "validate-npm-package-license": { "version": "3.0.4", - "resolved": "", + "resolved": false, "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "requires": { "spdx-correct": "^3.0.0", @@ -16431,7 +16431,7 @@ }, "validate-npm-package-name": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", "requires": { "builtins": "^1.0.3" @@ -16439,7 +16439,7 @@ }, "verror": { "version": "1.10.0", - "resolved": "", + "resolved": false, "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { "assert-plus": "^1.0.0", @@ -16449,7 +16449,7 @@ }, "wcwidth": { "version": "1.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", "requires": { "defaults": "^1.0.3" @@ -16457,7 +16457,7 @@ }, "which": { "version": "1.3.1", - "resolved": "", + "resolved": false, "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "requires": { "isexe": "^2.0.0" @@ -16465,12 +16465,12 @@ }, "which-module": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, "wide-align": { "version": "1.1.2", - "resolved": "", + "resolved": false, "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", "requires": { "string-width": "^1.0.2" @@ -16478,7 +16478,7 @@ "dependencies": { "string-width": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "requires": { "code-point-at": "^1.0.0", @@ -16490,7 +16490,7 @@ }, "widest-line": { "version": "2.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", "requires": { "string-width": "^2.1.1" @@ -16498,7 +16498,7 @@ }, "worker-farm": { "version": "1.7.0", - "resolved": "", + "resolved": false, "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", "requires": { "errno": "~0.1.7" @@ -16506,7 +16506,7 @@ }, "wrap-ansi": { "version": "5.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", "requires": { "ansi-styles": "^3.2.0", @@ -16516,17 +16516,17 @@ "dependencies": { "ansi-regex": { "version": "4.1.1", - "resolved": "", + "resolved": false, "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" }, "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "string-width": { "version": "3.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "requires": { "emoji-regex": "^7.0.1", @@ -16536,7 +16536,7 @@ }, "strip-ansi": { "version": "5.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "requires": { "ansi-regex": "^4.1.0" @@ -16546,12 +16546,12 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "write-file-atomic": { "version": "2.4.3", - "resolved": "", + "resolved": false, "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", "requires": { "graceful-fs": "^4.1.11", @@ -16561,27 +16561,27 @@ }, "xdg-basedir": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=" }, "xtend": { "version": "4.0.1", - "resolved": "", + "resolved": false, "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" }, "y18n": { "version": "4.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==" }, "yallist": { "version": "3.0.3", - "resolved": "", + "resolved": false, "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" }, "yargs": { "version": "14.2.3", - "resolved": "", + "resolved": false, "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", "requires": { "cliui": "^5.0.0", @@ -16599,12 +16599,12 @@ "dependencies": { "ansi-regex": { "version": "4.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" }, "find-up": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "requires": { "locate-path": "^3.0.0" @@ -16612,12 +16612,12 @@ }, "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "", + "resolved": false, "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" }, "locate-path": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "requires": { "p-locate": "^3.0.0", @@ -16626,7 +16626,7 @@ }, "p-limit": { "version": "2.3.0", - "resolved": "", + "resolved": false, "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "requires": { "p-try": "^2.0.0" @@ -16634,7 +16634,7 @@ }, "p-locate": { "version": "3.0.0", - "resolved": "", + "resolved": false, "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "requires": { "p-limit": "^2.0.0" @@ -16642,12 +16642,12 @@ }, "p-try": { "version": "2.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "string-width": { "version": "3.1.0", - "resolved": "", + "resolved": false, "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", "requires": { "emoji-regex": "^7.0.1", @@ -16657,7 +16657,7 @@ }, "strip-ansi": { "version": "5.2.0", - "resolved": "", + "resolved": false, "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", "requires": { "ansi-regex": "^4.1.0" @@ -16667,7 +16667,7 @@ }, "yargs-parser": { "version": "15.0.1", - "resolved": "", + "resolved": false, "integrity": "sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==", "requires": { "camelcase": "^5.0.0", @@ -16676,7 +16676,7 @@ "dependencies": { "camelcase": { "version": "5.3.1", - "resolved": "", + "resolved": false, "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" } } diff --git a/src/client/util/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 289c5bc51..4af51b9a0 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -1,12 +1,12 @@ -import React = require("react"); -import { Utils } from "../../Utils"; -import "./InteractionUtils.scss"; +import React = require('react'); +import { Utils } from '../../Utils'; +import './InteractionUtils.scss'; export namespace InteractionUtils { - export const MOUSETYPE = "mouse"; - export const TOUCHTYPE = "touch"; - export const PENTYPE = "pen"; - export const ERASERTYPE = "eraser"; + export const MOUSETYPE = 'mouse'; + export const TOUCHTYPE = 'touch'; + export const PENTYPE = 'pen'; + export const ERASERTYPE = 'eraser'; const POINTER_PEN_BUTTON = -1; const REACT_POINTER_PEN_BUTTON = 0; @@ -19,24 +19,23 @@ export namespace InteractionUtils { readonly touches: T extends React.TouchEvent ? React.Touch[] : Touch[], readonly changedTouches: T extends React.TouchEvent ? React.Touch[] : Touch[], readonly touchEvent: T extends React.TouchEvent ? React.TouchEvent : TouchEvent - ) { } + ) {} } - export interface MultiTouchEventDisposer { (): void; } + export interface MultiTouchEventDisposer { + (): void; + } /** * * @param element - element to turn into a touch target * @param startFunc - event handler, typically Touchable.onTouchStart (classes that inherit touchable can pass in this.onTouchStart) */ - export function MakeMultiTouchTarget( - element: HTMLElement, - startFunc: (e: Event, me: MultiTouchEvent) => void - ): MultiTouchEventDisposer { + export function MakeMultiTouchTarget(element: HTMLElement, startFunc: (e: Event, me: MultiTouchEvent) => void): MultiTouchEventDisposer { const onMultiTouchStartHandler = (e: Event) => startFunc(e, (e as CustomEvent>).detail); // const onMultiTouchMoveHandler = moveFunc ? (e: Event) => moveFunc(e, (e as CustomEvent>).detail) : undefined; // const onMultiTouchEndHandler = endFunc ? (e: Event) => endFunc(e, (e as CustomEvent>).detail) : undefined; - element.addEventListener("dashOnTouchStart", onMultiTouchStartHandler); + element.addEventListener('dashOnTouchStart', onMultiTouchStartHandler); // if (onMultiTouchMoveHandler) { // element.addEventListener("dashOnTouchMove", onMultiTouchMoveHandler); // } @@ -44,7 +43,7 @@ export namespace InteractionUtils { // element.addEventListener("dashOnTouchEnd", onMultiTouchEndHandler); // } return () => { - element.removeEventListener("dashOnTouchStart", onMultiTouchStartHandler); + element.removeEventListener('dashOnTouchStart', onMultiTouchStartHandler); // if (onMultiTouchMoveHandler) { // element.removeEventListener("dashOnTouchMove", onMultiTouchMoveHandler); // } @@ -59,14 +58,11 @@ export namespace InteractionUtils { * @param element - element to add events to * @param func - function to add to the event */ - export function MakeHoldTouchTarget( - element: HTMLElement, - func: (e: Event, me: MultiTouchEvent) => void - ): MultiTouchEventDisposer { + export function MakeHoldTouchTarget(element: HTMLElement, func: (e: Event, me: MultiTouchEvent) => void): MultiTouchEventDisposer { const handler = (e: Event) => func(e, (e as CustomEvent>).detail); - element.addEventListener("dashOnTouchHoldStart", handler); + element.addEventListener('dashOnTouchHoldStart', handler); return () => { - element.removeEventListener("dashOnTouchHoldStart", handler); + element.removeEventListener('dashOnTouchHoldStart', handler); }; } @@ -89,71 +85,108 @@ export namespace InteractionUtils { return myTouches; } - export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, - color: string, width: number, strokeWidth: number, lineJoin: string, lineCap: string, bezier: string, fill: string, arrowStart: string, arrowEnd: string, - markerScale: number, dash: string | undefined, scalex: number, scaley: number, shape: string, pevents: string, opacity: number, nodefs: boolean, - downHdlr?: ((e: React.PointerEvent) => void)) { + export function CreatePolyline( + points: { X: number; Y: number }[], + left: number, + top: number, + color: string, + width: number, + strokeWidth: number, + lineJoin: string, + lineCap: string, + bezier: string, + fill: string, + arrowStart: string, + arrowEnd: string, + markerScale: number, + dash: string | undefined, + scalex: number, + scaley: number, + shape: string, + pevents: string, + opacity: number, + nodefs: boolean, + downHdlr?: (e: React.PointerEvent) => void + ) { const pts = shape ? makePolygon(shape, points) : points; if (isNaN(scalex)) scalex = 1; if (isNaN(scaley)) scaley = 1; - const toScr = (p: { X: number, Y: number }) => ` ${!p ? 0 : (p.X - left - width / 2) * scalex + width / 2}, ${!p ? 0 : (p.Y - top - width / 2) * scaley + width / 2} `; - const strpts = bezier ? - pts.reduce((acc: string, pt, i) => acc + (i % 4 !== 0 ? "" : (i === 0 ? "M" + toScr(pt) : "") + "C" + toScr(pts[i + 1]) + toScr(pts[i + 2]) + toScr(pts[i + 3])), "") : - pts.reduce((acc: string, pt) => acc + `${toScr(pt)} `, ""); + const toScr = (p: { X: number; Y: number }) => ` ${!p ? 0 : (p.X - left - width / 2) * scalex + width / 2}, ${!p ? 0 : (p.Y - top - width / 2) * scaley + width / 2} `; + const strpts = bezier + ? pts.reduce((acc: string, pt, i) => acc + (i % 4 !== 0 ? '' : (i === 0 ? 'M' + toScr(pt) : '') + 'C' + toScr(pts[i + 1]) + toScr(pts[i + 2]) + toScr(pts[i + 3])), '') + : pts.reduce((acc: string, pt) => acc + `${toScr(pt)} `, ''); const dashArray = dash && Number(dash) ? String(Number(width) * Number(dash)) : undefined; const defGuid = Utils.GenerateGuid(); - const Tag = (bezier ? "path" : "polyline") as keyof JSX.IntrinsicElements; + const Tag = (bezier ? 'path' : 'polyline') as keyof JSX.IntrinsicElements; const markerStrokeWidth = strokeWidth / 2; - const arrowWidthFactor = 3 * (markerScale || 0.5);// used to be 1.5 + const arrowWidthFactor = 3 * (markerScale || 0.5); // used to be 1.5 const arrowLengthFactor = 5 * (markerScale || 0.5); const arrowNotchFactor = 2 * (markerScale || 0.5); - return ( {/* setting the svg fill sets the arrowStart fill */} - {nodefs ? (null) : - {arrowStart !== "dot" && arrowEnd !== "dot" ? (null) : - - - } - {arrowStart !== "arrow" ? (null) : - - - } - {arrowEnd !== "arrow" ? (null) : - - - } - } - - - - ); + return ( + + {' '} + {/* setting the svg fill sets the arrowStart fill */} + {nodefs ? null : ( + + {arrowStart !== 'dot' && arrowEnd !== 'dot' ? null : ( + + + + )} + {arrowStart !== 'arrow' ? null : ( + + + + )} + {arrowEnd !== 'arrow' ? null : ( + + + + )} + + )} + + + ); } - export function makePolygon(shape: string, points: { X: number, Y: number }[]) { + export function makePolygon(shape: string, points: { X: number; Y: number }[]) { if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y + 1 === points[0].Y) { //pointer is up (first and last points are the same) - if (shape === "arrow" || shape === "line" || shape === "circle") { + if (shape === 'arrow' || shape === 'line' || shape === 'circle') { //if arrow or line, the two end points should be the starting and the ending point var left = points[0].X; var top = points[0].Y; @@ -175,7 +208,7 @@ export namespace InteractionUtils { left = points[0].X; bottom = points[points.length - 1].Y; top = points[0].Y; - if (shape !== "arrow" && shape !== "line" && shape !== "circle") { + if (shape !== 'arrow' && shape !== 'line' && shape !== 'circle') { //switch left/right and top/bottom if needed if (left > right) { const temp = right; @@ -191,14 +224,13 @@ export namespace InteractionUtils { } points = []; switch (shape) { - case "rectangle": + case 'rectangle': points.push({ X: left, Y: top }); points.push({ X: right, Y: top }); points.push({ X: right, Y: bottom }); points.push({ X: left, Y: bottom }); points.push({ X: left, Y: top }); - return points; - case "triangle": + case 'triangle': // points.push({ X: left, Y: bottom }); // points.push({ X: right, Y: bottom }); // points.push({ X: (right + left) / 2, Y: top }); @@ -219,62 +251,39 @@ export namespace InteractionUtils { points.push({ X: left, Y: bottom }); points.push({ X: left, Y: bottom }); - - - return points; - case "circle": + 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; + 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 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) }); + 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; + 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 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 }); + points.push({ X: Math.min(left, right), Y: Math.sqrt(Math.pow(radius, 2) - Math.pow(Math.min(left, right) - centerX, 2)) + centerY }); } - return points; - // case "arrow": - // const x1 = left; - // const y1 = top; - // const x2 = right; - // const y2 = bottom; - // const L1 = Math.sqrt(Math.pow(Math.abs(x1 - x2), 2) + (Math.pow(Math.abs(y1 - y2), 2))); - // const L2 = L1 / 5; - // const angle = 0.785398; - // const x3 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) + (y1 - y2) * Math.sin(angle)); - // const y3 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) - (x1 - x2) * Math.sin(angle)); - // const x4 = x2 + (L2 / L1) * ((x1 - x2) * Math.cos(angle) - (y1 - y2) * Math.sin(angle)); - // const y4 = y2 + (L2 / L1) * ((y1 - y2) * Math.cos(angle) + (x1 - x2) * Math.sin(angle)); - // points.push({ X: x1, Y: y1 }); - // points.push({ X: x2, Y: y2 }); - // points.push({ X: x3, Y: y3 }); - // points.push({ X: x4, Y: y4 }); - // points.push({ X: x2, Y: y2 }); - // return points; - case "line": + case 'line': points.push({ X: left, Y: top }); points.push({ X: right, Y: bottom }); return points; - default: - return points; } + return points; } /** * Returns whether or not the pointer event passed in is of the type passed in @@ -284,11 +293,14 @@ export namespace InteractionUtils { export function IsType(e: PointerEvent | React.PointerEvent, type: string): boolean { switch (type) { // pen and eraser are both pointer type 'pen', but pen is button 0 and eraser is button 5. -syip2 - case PENTYPE: return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0); - case ERASERTYPE: return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON); + case PENTYPE: + return e.pointerType === PENTYPE && (e.button === -1 || e.button === 0); + case ERASERTYPE: + return e.pointerType === PENTYPE && e.button === (e instanceof PointerEvent ? ERASER_BUTTON : ERASER_BUTTON); case TOUCHTYPE: return e.pointerType === TOUCHTYPE; - default: return e.pointerType === type; + default: + return e.pointerType === type; } } @@ -305,7 +317,7 @@ export namespace InteractionUtils { * Returns the centroid of an n-arbitrary long list of points (takes the average the x and y components of each point) * @param pts - n-arbitrary long list of points */ - export function CenterPoint(pts: React.Touch[]): { X: number, Y: number } { + export function CenterPoint(pts: React.Touch[]): { X: number; Y: number } { const centerX = pts.map(pt => pt.clientX).reduce((a, b) => a + b, 0) / pts.length; const centerY = pts.map(pt => pt.clientY).reduce((a, b) => a + b, 0) / pts.length; return { X: centerX, Y: centerY }; @@ -324,9 +336,9 @@ export namespace InteractionUtils { const newDist = TwoPointEuclidist(pt1, pt2); /** if they have the same sign, then we are either pinching in or out. - * threshold it by 10 (it has to be pinching by at least threshold to be a valid pinch) - * so that it can still pan without freaking out - */ + * threshold it by 10 (it has to be pinching by at least threshold to be a valid pinch) + * so that it can still pan without freaking out + */ if (Math.sign(oldDist) === Math.sign(newDist) && Math.abs(oldDist - newDist) > threshold) { return Math.sign(oldDist - newDist); } @@ -372,8 +384,6 @@ export namespace InteractionUtils { // These might not be very useful anymore, but I'll leave them here for now -syip2 { - - /** * Returns the type of Touch Interaction from a list of points. * Also returns any data that is associated with a Touch Interaction diff --git a/src/client/views/InkStroke.scss b/src/client/views/InkStroke.scss index 664f2448b..bed7caf7f 100644 --- a/src/client/views/InkStroke.scss +++ b/src/client/views/InkStroke.scss @@ -1,22 +1,23 @@ .inkstroke-UI { - // transform-origin: top left; - position: absolute; - overflow: visible; - pointer-events: none; - z-index: 2001; // 1 higher than documentdecorations - - svg:not(:root) { - overflow: visible !important; + // transform-origin: top left; position: absolute; - left:0; - top:0; - } + overflow: visible; + pointer-events: none; + z-index: 2001; // 1 higher than documentdecorations + + svg:not(:root) { + overflow: visible !important; + position: absolute; + left: 0; + top: 0; + } } .inkStroke-wrapper { display: flex; align-items: center; height: 100%; + transition: inherit; .inkStroke { mix-blend-mode: multiply; stroke-linejoin: round; @@ -26,8 +27,10 @@ width: 100%; height: 100%; pointer-events: none; + transition: inherit; svg:not(:root) { overflow: visible !important; + transition: inherit; } } diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index 821e2f739..b32c9d54c 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -4,6 +4,7 @@ import { Doc, NumListCast, Opt } from '../../fields/Doc'; import { InkData, InkField, InkTool, PointData } from '../../fields/InkField'; import { List } from '../../fields/List'; import { listSpec } from '../../fields/Schema'; +import { ComputedField } from '../../fields/ScriptField'; import { Cast, NumCast } from '../../fields/Types'; import { Point } from '../../pen-gestures/ndollar'; import { DocumentType } from '../documents/DocumentTypes'; @@ -11,6 +12,7 @@ import { FitOneCurve } from '../util/bezierFit'; import { DocumentManager } from '../util/DocumentManager'; import { undoBatch } from '../util/UndoManager'; import { InkingStroke } from './InkingStroke'; +import { CollectionFreeFormDocumentView } from './nodes/CollectionFreeFormDocumentView'; import { DocumentView } from './nodes/DocumentView'; export class InkStrokeProperties { @@ -65,7 +67,14 @@ export class InkStrokeProperties { doc._height = (newYrange.max - newYrange.min) * ptsYscale + NumCast(doc.strokeWidth); doc.x = oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale; doc.y = oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale; - Doc.GetProto(doc).data = new InkField(newPoints); + if (doc.activeFrame !== undefined) { + doc.data = ComputedField.MakeInterpolated('data', 'activeFrame', doc, NumCast(doc.activeFrame)); + const findexed = Cast(doc[`data-indexed`], listSpec(InkField), []).slice(); + findexed[NumCast(doc.activeFrame)] = new InkField(newPoints); + doc[`data-indexed`] = new List(findexed); + } else { + Doc.GetProto(doc).data = new InkField(newPoints); + } appliedFunc = true; } } @@ -347,7 +356,6 @@ export class InkStrokeProperties { const deltaX = snapData.nearestPt.X - ink[controlIndex].X; const deltaY = snapData.nearestPt.Y - ink[controlIndex].Y; const res = this.moveControlPtHandle(inkView, deltaX, deltaY, controlIndex, ink.slice()); - console.log('X = ' + snapData.nearestPt.X + ' ' + snapData.nearestPt.Y); return res; } } diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 2671aea56..52efbdfd7 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -401,13 +401,13 @@ export class InkingStroke extends ViewBoxBaseComponent() { inkTop, highlightColor, inkStrokeWidth, - fillColor && closed && highlightIndex ? highlightIndex / 2 : inkStrokeWidth + (fillColor ? (closed ? 0 : highlightIndex + 2) : 0), + Math.max(5, fillColor && closed && highlightIndex ? highlightIndex / 2 : inkStrokeWidth + (fillColor ? (closed ? 0 : highlightIndex + 2) : 0)), StrCast(this.layoutDoc.strokeLineJoin), StrCast(this.layoutDoc.strokeLineCap), StrCast(this.layoutDoc.strokeBezier), !closed ? 'none' : fillColor === 'transparent' || suppressFill ? 'none' : fillColor, - startMarker, - endMarker, + '', + '', markerScale, undefined, inkScaleX, diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index 490e9e025..d2b0e10c1 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -172,6 +172,7 @@ export class MainView extends React.Component { 'curPage', 'viewType', 'chromeHidden', + 'currentFrame', 'width', 'nativeWidth', ]); // can play with these fields on someone else's diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index 6ea697a2f..9aaaf1e68 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -99,6 +99,7 @@ border-radius: inherit; width: 100%; height: 100%; + transition: inherit; .sharingIndicator { height: 30px; -- cgit v1.2.3-70-g09d2 From d8f2a45a95a14151d3484c3c14f72d217a818786 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 22 Aug 2022 12:50:52 -0400 Subject: made inkMasks animate when they are turned on /off. fixed inkStrokes to be interpolated when they have an activeFrame set. --- src/client/util/CurrentUserUtils.ts | 4 ++-- src/client/views/DocumentDecorations.tsx | 4 ++-- src/client/views/InkStrokeProperties.ts | 1 - src/client/views/InkingStroke.tsx | 22 +++++++++++++++---- .../CollectionFreeFormLayoutEngines.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.scss | 9 ++++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 9 ++++---- src/client/views/nodes/DocumentView.tsx | 1 - src/client/views/nodes/button/FontIconBox.tsx | 25 +++++++++++----------- 9 files changed, 47 insertions(+), 30 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index f7d072d80..20c57c617 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -633,7 +633,7 @@ export class CurrentUserUtils { { title: "Left", toolTip: "Left align", btnType: ButtonType.ToggleButton, icon: "align-left", scripts: {onClick:'{ return setAlignment("left", _readOnly_);}' }}, { title: "Center", toolTip: "Center align", btnType: ButtonType.ToggleButton, icon: "align-center", scripts: {onClick:'{ return setAlignment("center", _readOnly_);}'} }, { title: "Right", toolTip: "Right align", btnType: ButtonType.ToggleButton, icon: "align-right", scripts: {onClick:'{ return setAlignment("right", _readOnly_);}'} }, - { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", scripts: {onClick:'{ return toggleNoAutoLinkAnchor(_readOnly_);}'}}, + { title: "NoLink", toolTip: "Auto Link", btnType: ButtonType.ToggleButton, icon: "link", scripts: {onClick:'{ return toggleNoAutoLinkAnchor(_readOnly_);}'}, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}}, { title: "Dictate",toolTip: "Dictate", btnType: ButtonType.ToggleButton, icon: "microphone", scripts: {onClick:'{ return toggleDictation(_readOnly_);}'}}, ]; } @@ -676,7 +676,7 @@ export class CurrentUserUtils { CollectionViewType.Grid, CollectionViewType.NoteTaking]), title: "Perspective", toolTip: "View", btnType: ButtonType.DropdownList, ignoreClick: true, width: 100, scripts: { script: 'setView(value, _readOnly_)'}}, { title: "Back", icon: "chevron-left", toolTip: "Prev Animation Frame", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20, scripts: { onClick: 'prevKeyFrame(_readOnly_)'}}, - { title: "Num", icon: "", toolTip: "Frame Number", btnType: ButtonType.TextButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()', buttonText: 'selectedDocs()?.lastElement().currentFrame.toString()'}, width: 20, scripts: {}}, + { title: "Num", icon: "", toolTip: "Frame Number", btnType: ButtonType.TextButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()', buttonText: 'selectedDocs()?.lastElement().currentFrame.toString()'}, width: 20, scripts: {}}, { title: "Fwd", icon: "chevron-right", toolTip: "Next Animation Frame", btnType: ButtonType.ClickButton, funcs: {hidden: '!SelectionManager_selectedDocType(undefined, "freeform") || IsNoviceMode()'}, width: 20, scripts: { onClick: 'nextKeyFrame(_readOnly_)'}}, { 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_)'}}, diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 6d1397395..ab77af0f4 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -203,8 +203,8 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if (this._deleteAfterIconify) { views.forEach(iconView => { Doc.setNativeView(iconView.props.Document); - if (iconView.props.Document.isInkMask && iconView.props.Document.activeFrame !== undefined) { - iconView.props.Document.opacity = 0; // bcz: hacky ... allows inkMaks to be "turned off" without removing them from the collection which allows them to function properly in a presenation. + if (iconView.props.Document.activeFrame) { + iconView.props.Document.opacity = 0; // bcz: hacky ... allows inkMasks and other documents to be "turned off" without removing them from the animated collection which allows them to function properly in a presenation. } else { iconView.props.removeDocument?.(iconView.props.Document); } diff --git a/src/client/views/InkStrokeProperties.ts b/src/client/views/InkStrokeProperties.ts index b32c9d54c..1f5f16592 100644 --- a/src/client/views/InkStrokeProperties.ts +++ b/src/client/views/InkStrokeProperties.ts @@ -68,7 +68,6 @@ export class InkStrokeProperties { doc.x = oldXrange.coord + (newXrange.min - oldXrange.min) * ptsXscale; doc.y = oldYrange.coord + (newYrange.min - oldYrange.min) * ptsYscale; if (doc.activeFrame !== undefined) { - doc.data = ComputedField.MakeInterpolated('data', 'activeFrame', doc, NumCast(doc.activeFrame)); const findexed = Cast(doc[`data-indexed`], listSpec(InkField), []).slice(); findexed[NumCast(doc.activeFrame)] = new InkField(newPoints); doc[`data-indexed`] = new List(findexed); diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 52efbdfd7..520d40abf 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -25,7 +25,7 @@ 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, NumCast, RTFCast, StrCast } from '../../fields/Types'; +import { BoolCast, Cast, FieldValue, NumCast, RTFCast, StrCast } from '../../fields/Types'; import { TraceMobx } from '../../fields/util'; import { OmitKeys, returnFalse, setupMoveUpEvents } from '../../Utils'; import { CognitiveServices } from '../cognitive_services/CognitiveServices'; @@ -45,6 +45,9 @@ 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'; @observer export class InkingStroke extends ViewBoxBaseComponent() { @@ -56,7 +59,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { return inkData && inkData.lastElement().X === inkData[0].X && inkData.lastElement().Y === inkData[0].Y; } private _handledClick = false; // flag denoting whether ink stroke has handled a psuedo-click onPointerUp so that the real onClick event can be stopPropagated - private _selDisposer?: IReactionDisposer; + private _disposers: { [key: string]: IReactionDisposer } = {}; @observable _nearestSeg?: number; // nearest Bezier segment along the ink stroke to the cursor (used for displaying the Add Point highlight) @observable _nearestT?: number; // nearest t value within the nearest Bezier segment " @@ -64,13 +67,24 @@ export class InkingStroke extends ViewBoxBaseComponent() { componentDidMount() { this.props.setContentView?.(this); - this._selDisposer = reaction( + this._disposers.activeFrame = reaction( + () => this.rootDoc.activeFrame !== undefined && !(ComputedField.WithoutComputed(() => FieldValue(this.rootDoc[this.fieldKey])) instanceof ComputedField), + () => { + const newPoints = Cast(this.rootDoc[this.fieldKey], InkField, null).inkData; + this.rootDoc[this.fieldKey] = ComputedField.MakeInterpolated(this.fieldKey, 'activeFrame', this.rootDoc, NumCast(this.rootDoc.activeFrame)); + const findexed = Cast(this.rootDoc[`data-indexed`], listSpec(InkField), []).slice(); + findexed[NumCast(this.rootDoc.activeFrame)] = new InkField(newPoints); + this.rootDoc[this.fieldKey + '-indexed'] = new List(findexed); + }, + { fireImmediately: true } + ); + this._disposers.selfDisper = reaction( () => this.props.isSelected(), // react to stroke being deselected by turning off ink handles selected => !selected && (InkStrokeProperties.Instance._controlButton = false) ); } componentWillUnmount() { - this._selDisposer?.(); + Object.keys(this._disposers).forEach(key => this._disposers[key]()); } // transform is the inherited screentolocal xf plus any scaling that was done to make the stroke diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index ee01c341b..b210e7d9a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -45,7 +45,7 @@ export interface PoolData { export interface ViewDefResult { ele: JSX.Element; bounds?: ViewDefBounds; - inkMask?: boolean; + inkMask?: number; //sort elements into either the mask layer (which has a mixedBlendMode appropriate for transparent masks), or the regular documents layer; -1 = no mask, 0 = mask layer but stroke is transprent (hidden), >0 = mask layer and not hidden } function toLabel(target: FieldResult) { if (typeof target === 'number' || Number(target)) { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss index 010132aa5..d80fcdfc3 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.scss @@ -20,15 +20,20 @@ pointer-events: none; } +.collectionfreeformview-mask-empty, .collectionfreeformview-mask { - mix-blend-mode: multiply; z-index: 5000; width: $INK_MASK_SIZE; height: $INK_MASK_SIZE; transform: translate($INK_MASK_SIZE_HALF, $INK_MASK_SIZE_HALF); - background-color: rgba(0, 0, 0, 0.7); pointer-events: none; position: absolute; + background-color: transparent; + transition: background-color 1s ease 0s; +} +.collectionfreeformview-mask { + mix-blend-mode: multiply; + background-color: rgba(0, 0, 0, 0.7); } .collectionfreeformview-viewdef { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0fd326091..1d518076a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -119,9 +119,9 @@ export class CollectionFreeFormView extends CollectionSubView ele.bounds && !ele.bounds.z && ele.inkMask).map(ele => ele.ele); - const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && !ele.inkMask).map(ele => ele.ele); - if (viewsMask.length) renderableEles.push(
{viewsMask}
); + const viewsMask = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask !== -1).map(ele => ele.ele); + const renderableEles = this._layoutElements.filter(ele => ele.bounds && !ele.bounds.z && ele.inkMask === -1).map(ele => ele.ele); + if (viewsMask.length) renderableEles.push(
(ele.inkMask ?? 0) > 0) ? '' : '-empty'}`}>{viewsMask}
); return renderableEles; } @computed get fitToContentVals() { @@ -251,7 +251,6 @@ export class CollectionFreeFormView extends CollectionSubView { ref={this.ContentRef} style={{ transition: this.props.dataTransition, - //position: this.props.Document.isInkMask ? 'absolute' : undefined, transform: isButton ? undefined : `translate(${this.centeringX}px, ${this.centeringY}px)`, width: isButton || isPresTreeElement ? '100%' : xshift() ?? `${(100 * (this.props.PanelWidth() - this.Xshift * 2)) / this.props.PanelWidth()}%`, height: diff --git a/src/client/views/nodes/button/FontIconBox.tsx b/src/client/views/nodes/button/FontIconBox.tsx index cb68c1ac3..fc5bf86f4 100644 --- a/src/client/views/nodes/button/FontIconBox.tsx +++ b/src/client/views/nodes/button/FontIconBox.tsx @@ -90,9 +90,11 @@ export class FontIconBox extends DocComponent() { @computed get label() { return StrCast(this.rootDoc.label, StrCast(this.rootDoc.title)); } - @computed get icon() { - return StrCast(this.dataDoc.icon, 'user') as any; - } + Icon = (color: string) => { + const icon = StrCast(this.dataDoc.icon, 'user') as any; + const trailsIcon = () => ; + return !icon ? null : icon === 'pres-trail' ? trailsIcon() : ; + }; @computed get dropdown() { return BoolCast(this.rootDoc.dropDownOpen); } @@ -228,7 +230,7 @@ export class FontIconBox extends DocComponent() { className={`menuButton ${this.type} ${active}`} style={{ color: color, backgroundColor: backgroundColor, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} onClick={action(() => (this.rootDoc.dropDownOpen = !this.rootDoc.dropDownOpen))}> - + {this.Icon(color)} {!this.label || !FontIconBox.GetShowLabels() ? null : (
{' '} @@ -384,7 +386,7 @@ export class FontIconBox extends DocComponent() { style={{ color: color, borderBottomLeftRadius: this.dropdown ? 0 : undefined }} onClick={action(() => (this.colorPickerClosed = !this.colorPickerClosed))} onPointerDown={e => e.stopPropagation()}> - + {this.Icon(color)}
{label} {/* {dropdownCaret} */} @@ -436,7 +438,7 @@ export class FontIconBox extends DocComponent() { } else { return (
- + {this.Icon(color)} {label}
); @@ -453,7 +455,7 @@ export class FontIconBox extends DocComponent() { return (
- + {this.Icon(color)} {!this.label || !FontIconBox.GetShowLabels() ? null : (
{' '} @@ -506,7 +508,7 @@ export class FontIconBox extends DocComponent() { case ButtonType.TextButton: button = (
- + {this.Icon(color)} {StrCast(this.rootDoc.buttonText) ?
{StrCast(this.rootDoc.buttonText)}
: null} {label}
@@ -531,7 +533,7 @@ export class FontIconBox extends DocComponent() { case ButtonType.ToolButton: button = (
- + {this.Icon(color)} {label}
); @@ -543,16 +545,15 @@ export class FontIconBox extends DocComponent() { case ButtonType.ClickButton: button = (
- + {this.Icon(color)} {label}
); break; case ButtonType.MenuButton: - const trailsIcon = ; button = (
- {this.icon === 'pres-trail' ? trailsIcon : } + {this.Icon(color)} {menuLabel}
-- cgit v1.2.3-70-g09d2 From 440f5c296a599d5c3f9ed781b58c5b00da6edb5a Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 22 Aug 2022 17:18:10 -0400 Subject: added animation of background color, and streamlined/extended a variety of props-related things. --- src/client/util/CurrentUserUtils.ts | 6 +-- src/client/views/DocComponent.tsx | 14 ------- src/client/views/StyleProvider.tsx | 9 +++-- .../CollectionFreeFormLayoutEngines.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 33 ++++++++-------- .../views/nodes/CollectionFreeFormDocumentView.tsx | 30 ++++++++++++--- src/client/views/nodes/FieldView.tsx | 11 ++++-- .../views/nodes/formattedText/FormattedTextBox.tsx | 45 ++++++++++------------ src/fields/Doc.ts | 14 +++++++ src/fields/ScriptField.ts | 12 +++++- 10 files changed, 102 insertions(+), 74 deletions(-) (limited to 'src/client/views') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 20c57c617..17d58595c 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -235,9 +235,9 @@ export class CurrentUserUtils { const header = Docs.Create.RTFDocument(new RichTextField(JSON.stringify(json), ""), { ...opts, title: "text", layout: "" + - ` ` + - " " + - ` Metadata` + + ` ` + + " " + + ` Metadata` + "" }, "header"); diff --git a/src/client/views/DocComponent.tsx b/src/client/views/DocComponent.tsx index 280ca8a8c..886dd974b 100644 --- a/src/client/views/DocComponent.tsx +++ b/src/client/views/DocComponent.tsx @@ -132,20 +132,6 @@ export function ViewBoxAnnotatableComponent

() lookupField = (field: string) => ScriptCast((this.layoutDoc as any).lookupField)?.script.run({ self: this.layoutDoc, data: this.rootDoc, field: field }).result; - styleFromLayoutString = (scale: number) => { - const style: { [key: string]: any } = {}; - const divKeys = ['width', 'height', 'fontSize', 'transform', 'left', 'background', 'left', 'right', 'top', 'bottom', 'pointerEvents', 'position']; - const replacer = (match: any, expr: string, offset: any, string: any) => { - // bcz: this executes a script to convert a property expression string: { script } into a value - return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ self: this.rootDoc, this: this.layoutDoc, scale }).result?.toString() ?? ''; - }; - divKeys.map((prop: string) => { - const p = (this.props as any)[prop]; - typeof p === 'string' && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer)); - }); - return style; - }; - protected _multiTouchDisposer?: InteractionUtils.MultiTouchEventDisposer; @computed public get annotationKey() { diff --git a/src/client/views/StyleProvider.tsx b/src/client/views/StyleProvider.tsx index 3bd4f5152..c0ba170c6 100644 --- a/src/client/views/StyleProvider.tsx +++ b/src/client/views/StyleProvider.tsx @@ -40,7 +40,8 @@ export enum StyleProp { JitterRotation = 'jitterRotation', // whether documents should be randomly rotated BorderPath = 'customBorder', // border path for document view FontSize = 'fontSize', // size of text font - FontFamily = 'fontFamily', // size of text font + FontFamily = 'fontFamily', // font family of text + FontWeight = 'fontWeight', // font weight of text } function darkScheme() { @@ -117,9 +118,11 @@ export function DefaultStyleProvider(doc: Opt, props: Opt(); @observable _marqueeRef = React.createRef(); @observable _marqueeViewRef = React.createRef(); - @observable _keyframeEditing = false; @observable ChildDrag: DocumentView | undefined; // child document view being dragged. needed to update drop areas of groups when a group item is dragged. @computed get views() { @@ -185,8 +184,6 @@ export class CollectionFreeFormView extends CollectionSubView (this._keyframeEditing = set); - getKeyFrameEditing = () => this._keyframeEditing; onBrowseClickHandler = () => this.props.onBrowseClick?.() || ScriptCast(this.layoutDoc.onBrowseClick); onChildClickHandler = () => this.props.childClickScript || ScriptCast(this.Document.onChildClick); onChildDoubleClickHandler = () => this.props.childDoubleClickScript || ScriptCast(this.Document.onChildDoubleClick); @@ -272,15 +269,15 @@ export class CollectionFreeFormView extends CollectionSubView NumCast(doc1.zIndex) - NumCast(doc2.zIndex)); zsorted.forEach((doc, index) => (doc.zIndex = doc.isInkMask ? 5000 : index + 1)); const dvals = CollectionFreeFormDocumentView.getValues(refDoc, NumCast(refDoc.activeFrame, 1000)); - const dropPos = this.Document._currentFrame !== undefined ? [dvals.x || 0, dvals.y || 0] : [NumCast(refDoc.x), NumCast(refDoc.y)]; + const dropPos = this.Document._currentFrame !== undefined ? [NumCast(dvals.x), NumCast(dvals.y)] : [NumCast(refDoc.x), NumCast(refDoc.y)]; for (let i = 0; i < docDragData.droppedDocuments.length; i++) { const d = docDragData.droppedDocuments[i]; const layoutDoc = Doc.Layout(d); if (this.Document._currentFrame !== undefined) { CollectionFreeFormDocumentView.setupKeyframes([d], NumCast(this.Document._currentFrame), false); const vals = CollectionFreeFormDocumentView.getValues(d, NumCast(d.activeFrame, 1000)); - vals.x = x + (vals.x || 0) - dropPos[0]; - vals.y = y + (vals.y || 0) - dropPos[1]; + vals.x = x + NumCast(vals.x) - dropPos[0]; + vals.y = y + NumCast(vals.y) - dropPos[1]; vals._scrollTop = this.Document.editScrollProgressivize ? vals._scrollTop : undefined; CollectionFreeFormDocumentView.setValues(NumCast(this.Document._currentFrame), d, vals); } else { @@ -1317,22 +1314,22 @@ export class CollectionFreeFormView extends CollectionSubView { x: number; y: number; zIndex?: number; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined; + dataProvider?: (doc: Doc, replica: string) => { x: number; y: number; zIndex?: number; backgroundColor?: string; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined; sizeProvider?: (doc: Doc, replica: string) => { width: number; height: number } | undefined; renderCutoffProvider: (doc: Doc) => boolean; zIndex?: number; @@ -32,6 +32,7 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { @observer export class CollectionFreeFormDocumentView extends DocComponent() { public static animFields = ['_height', '_width', 'x', 'y', '_scrollTop', 'opacity']; // fields that are configured to be animatable using animation frames + public static animStringFields = ['backgroundColor']; // fields that are configured to be animatable using animation frames @observable _animPos: number[] | undefined = undefined; @observable _contentView: DocumentView | undefined | null; get displayName() { @@ -41,16 +42,19 @@ export class CollectionFreeFormDocumentView extends DocComponent, property: string) => { if (property === StyleProp.Opacity && doc === this.layoutDoc) return this.Opacity; // only change the opacity for this specific document, not its children + if (property === StyleProp.BackgroundColor) { + return this.BackgroundColor; // only change the opacity for this specific document, not its children + } return this.props.styleProvider?.(doc, props, property); }; @@ -77,6 +84,13 @@ export class CollectionFreeFormDocumentView extends DocComponent }); } + 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); + return p; + }, {} as { [val: string]: Opt }); + } + public static setValues(time: number, d: Doc, vals: { [val: string]: Opt }) { const timecode = Math.round(time); Object.keys(vals).forEach(val => { @@ -95,6 +109,10 @@ export class CollectionFreeFormDocumentView extends DocComponent { + const findexed = Cast(doc[`${val}-indexed`], listSpec('string'), null); + findexed?.length <= timecode + 1 && findexed.push(undefined as any as string); + }); }) ); setTimeout( @@ -142,6 +160,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.MakeInterpolated(val, 'activeFrame', doc, currTimecode))); + CollectionFreeFormDocumentView.animStringFields.forEach(val => (doc[val] = ComputedField.MakeInterpolatedString(val, 'activeFrame', doc, currTimecode))); doc.activeFrame = ComputedField.MakeFunction('self.context?._currentFrame||0'); doc.dataTransition = 'inherit'; }); @@ -189,7 +208,6 @@ export class CollectionFreeFormDocumentView extends DocComponent number; // scaling the DocumentView does to transform its contents into its panel & needed by ScreenToLocal NOTE: Must also be added to DocumentViewInternalsProps onBrowseClick?: () => ScriptField | undefined; onKey?: (e: React.KeyboardEvent, fieldProps: FieldViewProps) => boolean | undefined; + pointerEvents?: () => Opt; // properties intended to be used from within layout strings (otherwise use the function equivalents that work more efficiently with React) - pointerEvents?: () => Opt; + // See currentUserUtils headerTemplate for examples of creating text boxes from html which set some of these fields + // Also, see InkingStroke for examples of creating text boxes from render() methods which set some of these fields + backgroundColor?: string; + color?: string; fontSize?: number; height?: number; width?: number; - background?: string; - color?: string; xPadding?: number; yPadding?: number; + noSidebar?: boolean; + dontScale?: boolean; + dontSelectOnLoad?: boolean; // suppress selecting (e.g.,. text box) when loaded (and mark as not being associated with scrollTop document field) } @observer diff --git a/src/client/views/nodes/formattedText/FormattedTextBox.tsx b/src/client/views/nodes/formattedText/FormattedTextBox.tsx index f61533619..81ac45521 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBox.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBox.tsx @@ -11,7 +11,7 @@ import { Fragment, Mark, Node, Slice } from 'prosemirror-model'; import { EditorState, NodeSelection, Plugin, TextSelection, Transaction } from 'prosemirror-state'; import { EditorView } from 'prosemirror-view'; import { DateField } from '../../../../fields/DateField'; -import { AclAdmin, AclAugment, AclEdit, AclReadonly, AclSelfEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from '../../../../fields/Doc'; +import { AclAdmin, AclAugment, AclEdit, AclSelfEdit, DataSym, Doc, DocListCast, DocListCastAsync, Field, ForceServerWrite, HeightSym, Opt, UpdatingFromServer, WidthSym } from '../../../../fields/Doc'; import { Id } from '../../../../fields/FieldSymbols'; import { InkTool } from '../../../../fields/InkField'; import { PrefetchProxy } from '../../../../fields/Proxy'; @@ -35,6 +35,7 @@ import { SnappingManager } from '../../../util/SnappingManager'; import { undoBatch, UndoManager } from '../../../util/UndoManager'; import { CollectionFreeFormView } from '../../collections/collectionFreeForm/CollectionFreeFormView'; import { CollectionStackingView } from '../../collections/CollectionStackingView'; +import { CollectionTreeView } from '../../collections/CollectionTreeView'; import { ContextMenu } from '../../ContextMenu'; import { ContextMenuProps } from '../../ContextMenuItem'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; @@ -44,6 +45,7 @@ import { LightboxView } from '../../LightboxView'; import { AnchorMenu } from '../../pdf/AnchorMenu'; import { SidebarAnnos } from '../../SidebarAnnos'; import { StyleProp } from '../../StyleProvider'; +import { DocumentViewInternal } from '../DocumentView'; import { FieldView, FieldViewProps } from '../FieldView'; import { LinkDocPreview } from '../LinkDocPreview'; import { DashDocCommentView } from './DashDocCommentView'; @@ -61,25 +63,14 @@ import { schema } from './schema_rts'; import { SummaryView } from './SummaryView'; import applyDevTools = require('prosemirror-dev-tools'); import React = require('react'); -import { text } from 'body-parser'; -import { CollectionTreeView } from '../../collections/CollectionTreeView'; -import { DocumentViewInternal } from '../DocumentView'; const translateGoogleApi = require('translate-google-api'); -export interface FormattedTextBoxProps { - makeLink?: () => Opt; // bcz: hack: notifies the text document when the container has made a link. allows the text doc to react and setup a hyeprlink for any selected text - xPadding?: number; // used to override document's settings for xMargin --- see CollectionCarouselView - yPadding?: number; - noSidebar?: boolean; - dontScale?: boolean; - dontSelectOnLoad?: boolean; // suppress selecting the text box when loaded (and mark as not being associated with scrollTop document field) -} export const GoogleRef = 'googleDocId'; type PullHandler = (exportState: Opt, dataDoc: Doc) => void; @observer -export class FormattedTextBox extends ViewBoxAnnotatableComponent() { +export class FormattedTextBox extends ViewBoxAnnotatableComponent() { public static LayoutString(fieldStr: string) { return FieldView.LayoutString(FormattedTextBox, fieldStr); } @@ -1843,18 +1834,27 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent= 10 ? '-selected' : ''; - const styleFromString = this.styleFromLayoutString(scale); // this converts any expressions in the format string to style props. e.g., - return styleFromString?.height === '0px' ? null : ( + const styleFromLayoutString = Doc.styleFromLayoutString(this.rootDoc, this.layoutDoc, this.props, scale); // this converts any expressions in the format string to style props. e.g., + return styleFromLayoutString?.height === '0px' ? null : (

this.props.isContentActive() && e.stopPropagation()} style={{ - transform: this.props.dontScale ? undefined : `scale(${scale})`, - transformOrigin: this.props.dontScale ? undefined : 'top left', - width: this.props.dontScale ? undefined : `${100 / scale}%`, - height: this.props.dontScale ? undefined : `${100 / scale}%`, + ...(this.props.dontScale + ? {} + : { + transform: `scale(${scale})`, + transformOrigin: 'top left', + width: `${100 / scale}%`, + height: `${100 / scale}%`, + }), + transition: 'inherit', // overflowY: this.layoutDoc._autoHeight ? "hidden" : undefined, - ...styleFromString, + color: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color), + fontSize: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontSize), + fontFamily: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontFamily), + fontWeight: this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.FontWeight), + ...styleFromLayoutString, }}>
Doc.AreProtosEqual(val, doc)) !== -1; } + export function styleFromLayoutString(rootDoc: Doc, layoutDoc: Doc, props: any, scale: number) { + const style: { [key: string]: any } = {}; + const divKeys = ['width', 'height', 'fontSize', 'transform', 'left', 'backgroundColor', 'left', 'right', 'top', 'bottom', 'pointerEvents', 'position']; + const replacer = (match: any, expr: string, offset: any, string: any) => { + // bcz: this executes a script to convert a property expression string: { script } into a value + return ScriptField.MakeFunction(expr, { self: Doc.name, this: Doc.name, scale: 'number' })?.script.run({ self: rootDoc, this: layoutDoc, scale }).result?.toString() ?? ''; + }; + divKeys.map((prop: string) => { + const p = props[prop]; + typeof p === 'string' && (style[prop] = p?.replace(/{([^.'][^}']+)}/g, replacer)); + }); + return style; + } + // prettier-ignore export function toIcon(doc?: Doc, isOpen?: boolean) { switch (StrCast(doc?.type)) { diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index 68fb45987..0fd992d3b 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -9,7 +9,7 @@ import { Doc, Field, Opt } from './Doc'; import { Copy, Id, ToScriptString, ToString } from './FieldSymbols'; import { List } from './List'; import { ObjectField } from './ObjectField'; -import { Cast, NumCast } from './Types'; +import { Cast, NumCast, StrCast } from './Types'; import { Plugins } from './util'; function optional(propSchema: PropSchema) { @@ -199,6 +199,16 @@ export class ComputedField extends ScriptField { 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; } + public static MakeInterpolatedString(fieldKey: string, interpolatorKey: string, doc: Doc, curTimecode: number) { + if (!doc[`${fieldKey}-indexed`]) { + const flist = new List(numberRange(curTimecode + 1).map(i => undefined) as any as string[]); + flist[curTimecode] = StrCast(doc[fieldKey]); + doc[`${fieldKey}-indexed`] = flist; + } + const getField = ScriptField.CompileScript(`getIndexVal(self['${fieldKey}-indexed'], self.${interpolatorKey})`, {}, 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; + } } export namespace ComputedField { let useComputed = true; -- cgit v1.2.3-70-g09d2 From 653afba6635d676ec4fcdfa649360ca26c91cb88 Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 22 Aug 2022 17:23:21 -0400 Subject: from last --- .../collectionFreeForm/CollectionFreeFormView.tsx | 4 ++-- src/client/views/nodes/DocumentContentsView.tsx | 17 ++++++++--------- 2 files changed, 10 insertions(+), 11 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 a807ba4ea..b34dad226 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1314,7 +1314,7 @@ export class CollectionFreeFormView extends CollectionSubView { @observer export class DocumentContentsView extends React.Component< - DocumentViewProps & - FormattedTextBoxProps & { - isSelected: (outsideReaction: boolean) => boolean; - select: (ctrl: boolean) => void; - NativeDimScaling?: () => number; - setHeight?: (height: number) => void; - layoutKey: string; - } + DocumentViewProps & { + isSelected: (outsideReaction: boolean) => boolean; + select: (ctrl: boolean) => void; + NativeDimScaling?: () => number; + setHeight?: (height: number) => void; + layoutKey: string; + } > { @computed get layout(): string { TraceMobx(); -- cgit v1.2.3-70-g09d2 From cfcff13b6a5acfd1299102716a3c9747b32a7e7a Mon Sep 17 00:00:00 2001 From: bobzel Date: Mon, 22 Aug 2022 17:28:45 -0400 Subject: from last --- .../views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 2 +- 1 file changed, 1 insertion(+), 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 b34dad226..0234d303f 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1324,7 +1324,7 @@ export class CollectionFreeFormView extends CollectionSubView Date: Tue, 23 Aug 2022 11:57:58 -0400 Subject: added color to animated properties. changed doc decorations to stop before menu bar. changed color of tab bar so that doc decorations is visible when overlapping. --- src/client/views/InkingStroke.tsx | 3 ++- src/client/views/MainView.tsx | 4 ++-- src/client/views/_nodeModuleOverrides.scss | 3 ++- .../CollectionFreeFormLayoutEngines.tsx | 1 + .../collectionFreeForm/CollectionFreeFormView.tsx | 14 ++++++++++-- .../views/nodes/CollectionFreeFormDocumentView.tsx | 25 +++++++++++++--------- 6 files changed, 34 insertions(+), 16 deletions(-) (limited to 'src/client/views') diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 520d40abf..ceaabd0e1 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -48,6 +48,7 @@ import Color = require('color'); import { ComputedField } from '../../fields/ScriptField'; import { listSpec } from '../../fields/Schema'; import { List } from '../../fields/List'; +import { StyleProp } from './StyleProvider'; @observer export class InkingStroke extends ViewBoxBaseComponent() { @@ -370,7 +371,7 @@ export class InkingStroke extends ViewBoxBaseComponent() { const closed = InkingStroke.IsClosed(inkData); const isInkMask = BoolCast(this.layoutDoc.isInkMask); const fillColor = isInkMask ? '#aaaaaa' : StrCast(this.layoutDoc.fillColor, 'transparent'); - const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : StrCast(this.layoutDoc.color); + const strokeColor = !closed && fillColor && fillColor !== 'transparent' ? fillColor : this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.Color) ?? StrCast(this.layoutDoc.color); // bcz: Hack!! Not really sure why, but having fractional values for width/height of mask ink strokes causes the dragging clone (see DragManager) to be offset from where it should be. if (isInkMask && (this.layoutDoc[WidthSym]() !== Math.round(this.layoutDoc[WidthSym]()) || this.layoutDoc[HeightSym]() !== Math.round(this.layoutDoc[HeightSym]()))) { diff --git a/src/client/views/MainView.tsx b/src/client/views/MainView.tsx index d2b0e10c1..06be4d194 100644 --- a/src/client/views/MainView.tsx +++ b/src/client/views/MainView.tsx @@ -981,7 +981,7 @@ export class MainView extends React.Component { - + {this._hideUI ? null : } {LinkDescriptionPopup.descriptionPopup ? : null} @@ -994,7 +994,7 @@ export class MainView extends React.Component { default: return ( <> -
+
{this.mainDashboardArea} diff --git a/src/client/views/_nodeModuleOverrides.scss b/src/client/views/_nodeModuleOverrides.scss index 17eff022f..b1cce8705 100644 --- a/src/client/views/_nodeModuleOverrides.scss +++ b/src/client/views/_nodeModuleOverrides.scss @@ -44,7 +44,8 @@ div .lm_header { position: absolute; width: calc(100% - 60px); overflow: scroll; - background: $dark-gray; + background: #6b6b6b6b; //$dark-gray; + border-radius: 5px; } .lm_tab { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx index d84717b95..89cc22d07 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormLayoutEngines.tsx @@ -35,6 +35,7 @@ export interface PoolData { width?: number; height?: number; backgroundColor?: string; + color?: string; opacity?: number; transition?: string; highlight?: boolean; diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0234d303f..03beaf65e 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -1318,12 +1318,13 @@ export class CollectionFreeFormView extends CollectionSubView { x: number; y: number; zIndex?: number; backgroundColor?: string; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined; + dataProvider?: (doc: Doc, replica: string) => { x: number; y: number; zIndex?: number; color?: string; backgroundColor?: string; opacity?: number; highlight?: boolean; z: number; transition?: string } | undefined; sizeProvider?: (doc: Doc, replica: string) => { width: number; height: number } | undefined; renderCutoffProvider: (doc: Doc) => boolean; zIndex?: number; @@ -32,12 +32,14 @@ export interface CollectionFreeFormDocumentViewProps extends DocumentViewProps { @observer export class CollectionFreeFormDocumentView extends DocComponent() { public static animFields = ['_height', '_width', 'x', 'y', '_scrollTop', 'opacity']; // fields that are configured to be animatable using animation frames - public static animStringFields = ['backgroundColor']; // 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 @observable _animPos: number[] | undefined = undefined; @observable _contentView: DocumentView | undefined | null; get displayName() { + // this makes mobx trace() statements more descriptive return 'CollectionFreeFormDocumentView(' + this.rootDoc.title + ')'; - } // this makes mobx trace() statements more descriptive + } + get transform() { return `translate(${this.X}px, ${this.Y}px) rotate(${NumCast(this.Document.jitterRotation, this.props.jitterRotation)}deg)`; } @@ -56,12 +58,12 @@ export class CollectionFreeFormDocumentView extends DocComponent; - } @computed get dataProvider() { return this.props.dataProvider?.(this.props.Document, this.props.replica); } @@ -70,10 +72,13 @@ export class CollectionFreeFormDocumentView extends DocComponent, property: string) => { - if (property === StyleProp.Opacity && doc === this.layoutDoc) return this.Opacity; // only change the opacity for this specific document, not its children - if (property === StyleProp.BackgroundColor) { - return this.BackgroundColor; // only change the opacity for this specific document, not its children - } + if (doc === this.layoutDoc) + // prettier-ignore + switch (property) { + case StyleProp.Opacity: return this.Opacity; // only change the opacity for this specific document, not its children + case StyleProp.BackgroundColor: return this.BackgroundColor; + case StyleProp.Color: return this.Color; + } return this.props.styleProvider?.(doc, props, property); }; -- cgit v1.2.3-70-g09d2