From 0f178f1d74e17b15cec0fc98a12ccb2acaec937a Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Sat, 30 May 2020 12:52:40 -0500 Subject: generating suggestions for methods --- src/client/util/CurrentUserUtils.ts | 2 +- src/client/util/Scripting.ts | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 496099557..1fdf50dd4 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -736,6 +736,6 @@ export class CurrentUserUtils { } } -Scripting.addGlobal(function setupMobileInkingDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileInkingDoc(userDoc); }); +Scripting.addGlobal("setupMobileInkingDoc", function setupMobileInkingDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileInkingDoc(userDoc); }); Scripting.addGlobal(function setupMobileUploadDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileUploadDoc(userDoc); }); Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); }); \ No newline at end of file diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index ab577315c..817e6b29d 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -49,12 +49,25 @@ export function isCompileError(toBeDetermined: CompileResult): toBeDetermined is export namespace Scripting { export function addGlobal(global: { name: string }): void; export function addGlobal(name: string, global: any): void; - export function addGlobal(nameOrGlobal: any, global?: any) { - let n: string; + + export function addGlobal(global: { name: string }, decription?: string, params?: any): void; + + export function addGlobal(nameOrGlobal: any, global?: any, params?: any) { + let n: any; let obj: any; - if (global !== undefined && typeof nameOrGlobal === "string") { - n = nameOrGlobal; - obj = global; + + if (global !== undefined) { + if (typeof nameOrGlobal === "string") { + n = nameOrGlobal; + obj = global; + } else { + n = nameOrGlobal.name; + obj = [nameOrGlobal]; + obj.push(global); + if (params) { + obj.push(params); + } + } } else if (nameOrGlobal && typeof nameOrGlobal.name === "string") { n = nameOrGlobal.name; obj = nameOrGlobal; @@ -87,6 +100,10 @@ export namespace Scripting { export function getGlobals() { return Object.keys(scriptingGlobals); } + + export function getGlobalObj() { + return _scriptingGlobals; + } } export function scriptingGlobal(constructor: { new(...args: any[]): any }) { -- cgit v1.2.3-70-g09d2 From 823b8728b5506697b7d305a8c07979984b8351c8 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Mon, 1 Jun 2020 15:27:57 -0500 Subject: adding descriptions and params to methods --- src/client/util/CurrentUserUtils.ts | 3 +- src/client/util/Scripting.ts | 1 - src/client/views/nodes/ScriptingBox.scss | 76 +++++++------- src/client/views/nodes/ScriptingBox.tsx | 168 ++++++++++++++++++++++++++----- 4 files changed, 180 insertions(+), 68 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 1fdf50dd4..632187b49 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -737,5 +737,6 @@ export class CurrentUserUtils { } Scripting.addGlobal("setupMobileInkingDoc", function setupMobileInkingDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileInkingDoc(userDoc); }); -Scripting.addGlobal(function setupMobileUploadDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileUploadDoc(userDoc); }); +Scripting.addGlobal(function setupMobileUploadDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileUploadDoc(userDoc); }, + "initializes the Mobile upload document", "{ userDoc: the document to initialize }"); Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); }); \ No newline at end of file diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 817e6b29d..00b01a75a 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -49,7 +49,6 @@ export function isCompileError(toBeDetermined: CompileResult): toBeDetermined is export namespace Scripting { export function addGlobal(global: { name: string }): void; export function addGlobal(name: string, global: any): void; - export function addGlobal(global: { name: string }, decription?: string, params?: any): void; export function addGlobal(nameOrGlobal: any, global?: any, params?: any) { diff --git a/src/client/views/nodes/ScriptingBox.scss b/src/client/views/nodes/ScriptingBox.scss index 256119809..6113efbb5 100644 --- a/src/client/views/nodes/ScriptingBox.scss +++ b/src/client/views/nodes/ScriptingBox.scss @@ -22,7 +22,7 @@ display: flex; flex-direction: row; justify-content: center; - overflow-y: scroll; + overflow-y: hidden; .scriptingBox-textArea { flex: 70; @@ -32,53 +32,43 @@ resize: none; padding: 7px; overflow-y: scroll; + overflow-x: hidden; body { font-family: Arial, Helvetica, sans-serif; border: 1px solid red; - } - + } + .rta { position: relative; - font-size: 18px; + font-size: 12px; width: 100%; height: 100%; margin-bottom: 60px !important; - } - .rta__loader.rta__loader--empty-suggestion-data { - border-radius: 3px; - box-shadow: 0 0 5px rgba(27, 31, 35, 0.1); - padding: 5px; - } - .rta--loading .rta__loader.rta__loader--suggestion-data { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(255, 255, 255, 0.8); - } - .rta--loading .rta__loader.rta__loader--suggestion-data > * { - position: relative; - top: 50%; - } - .rta__textarea { + overflow-y: scroll; + font-size: 9px; + } + + .rta__textarea { width: 100%; height: 100%; - font-size: 1em; - } - .rta__autocomplete { + font-size: 10px; + } + + .rta__autocomplete { position: absolute; display: block; margin-top: 1em; - } - .rta__autocomplete--top { + } + + .rta__autocomplete--top { margin-top: 0; margin-bottom: 1em; max-height: 100px; overflow-y: auto; - } - .rta__list { + } + + .rta__list { margin: 0; padding: 0; background: #fff; @@ -86,28 +76,30 @@ border-radius: 3px; box-shadow: 0 0 5px rgba(27, 31, 35, 0.1); list-style: none; - } - .rta__entity { + overflow-y: scroll; + } + + .rta__entity { background: white; width: 100%; text-align: left; outline: none; - } - .rta__entity:hover { + } + + .rta__entity:hover { cursor: pointer; - } - .rta__item:not(:last-child) { - border-bottom: 1px solid #dfe2e5; - } - .rta__entity > * { + } + + .rta__entity>* { padding-left: 4px; padding-right: 4px; - } - .rta__entity--selected { + } + + .rta__entity--selected { color: #fff; text-decoration: none; background: #0366d6; - } + } } .scriptingBox-plist { diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index b0f7b3993..72c92f00b 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -38,18 +38,62 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent p.split(":")[0].trim()); } - @computed get paramsTypes() { return this.compileParams.map(p => p.split(":")[1].trim()); } - @computed get rawScript() { return StrCast(this.dataDoc[this.props.fieldKey + "-rawScript"], ""); } - @computed get compileParams() { return Cast(this.dataDoc[this.props.fieldKey + "-params"], listSpec("string"), []); } + @computed({ keepAlive: true }) get paramsNames() { return this.compileParams.map(p => p.split(":")[0].trim()); } + @computed({ keepAlive: true }) get paramsTypes() { return this.compileParams.map(p => p.split(":")[1].trim()); } + @computed({ keepAlive: true }) get rawScript() { return StrCast(this.dataDoc[this.props.fieldKey + "-rawScript"], ""); } + @computed({ keepAlive: true }) get compileParams() { return Cast(this.dataDoc[this.props.fieldKey + "-params"], listSpec("string"), []); } set rawScript(value) { this.dataDoc[this.props.fieldKey + "-rawScript"] = value; } set compileParams(value) { this.dataDoc[this.props.fieldKey + "-params"] = new List(value); } + // WORK ON THIS + // in: global, description, params + @computed get _descriptions() { + const descrip: string[] = []; + let value = ""; + this._scriptKeys.forEach((element: any) => { + const result = this._scriptGlobals[element]; + if (typeof result === "object") { + const d = result[1]; + if (d !== undefined) { + value = d; + } else { + value = ""; + } + } else { + value = ""; + } + descrip.push(value); + }); + return descrip; + } + + @computed get _scriptParams() { + const params: string[] = []; + let value = ""; + this._scriptKeys.forEach((element: any) => { + const result = this._scriptGlobals[element]; + if (typeof result === "object") { + const p = result[2]; + if (p !== undefined) { + value = StrCast(p); + } else { + value = ""; + } + } else { + value = ""; + } + params.push(value); + }); + return params; + } + @action componentDidMount() { this.rawScript = ScriptCast(this.dataDoc[this.props.fieldKey])?.script?.originalScript ?? this.rawScript; @@ -307,16 +351,32 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent { + // if (element.indexOf(this._currWord) >= 0) { + // this._suggestions.push(StrCast(element)); + // } + // }); + + // console.log(this._suggestions); + // return (this._suggestions); + // } + + @action + handleToken(str: string) { + + this._currWord = str; this._suggestions = []; this._scriptKeys.forEach((element: string) => { - if (element.indexOf(this._currWord) >= 0) { + if (element.toLowerCase().indexOf(this._currWord.toLowerCase()) >= 0) { this._suggestions.push(StrCast(element)); } }); @@ -326,23 +386,67 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent { - if (element.indexOf(this._currWord) >= 0) { - this._suggestions.push(StrCast(element)); - } - }); + const index = this._scriptKeys.indexOf(this._currWord); + const params = StrCast(this._scriptParams[index]); + + this._suggestions.push(params); console.log(this._suggestions); + return (this._suggestions); } + + getDescription(value: string) { + const index = this._scriptKeys.indexOf(value); + const descrip = this._descriptions[index]; + let display = ""; + if (descrip !== undefined) { + if (descrip.length > 0) { + display = descrip; + } + } + return display; + } + + getParams(value: string) { + const index = this._scriptKeys.indexOf(value); + const descrip = this._scriptParams[index]; + let display = ""; + if (descrip !== undefined) { + if (descrip.length > 0) { + display = descrip; + } + } + return display; + } + + setHovered(bool: boolean) { + this._hovered = bool; + } + + returnParam(item: string) { + const params = item.split(","); + let value = ""; + let first = true; + params.forEach((element) => { + if (first) { + value = element.split(":")[0].trim(); + first = false; + } else { + value = value + ", " + element.split(":")[0].trim(); + } + }); + return value; + } + textarea: any; - @computed get renderScriptingBox() { + @computed({ keepAlive: true }) get renderScriptingBox() { return 0 ? "70%" : "100%", resize: "none", height: "100%" }} movePopupAsYouType={true} loadingComponent={() => Loading} - ref={(rta: any) => { this.rta = rta; }} + + ref={(rta) => { this.rta = rta; }} //innerRef={textarea => { this.rawScript = textarea.value; }} minChar={0} - // ISSUE IS HEERE, doesn't display entity in a menu, prints hello for reach item in the list trigger={{ " ": { dataProvider: (token: any) => this.handleToken(token), + component: ({ entity: value }) => +
this.setHovered(true)} + onMouseLeave={() => this.setHovered(false)}> + {value} +
+ {this._hovered ?
{this.getDescription(value)}
: (null)} + {this._hovered ?
{this.getParams(value)}
: (null)} +
+ , + output: (item: any, trigger) => trigger + item.trim(), + }, + + "(": { + dataProvider: (token: any) => this.handleFunc(this.rta.getCaretPosition()), component: ({ entity: value }) =>
{value}
, - //afterWhitespace: true, - output: (item: any) => item, + output: (item: any) => "(" + this.returnParam(item) + ")", } + }} onCaretPositionChange={(number: any) => null} //this.handleKeyPress(number)} @@ -374,7 +494,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent @@ -467,7 +587,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent
this.props.isSelected(true) && e.stopPropagation()}> - {!this._applied ? this.renderScriptingInputs() : this.renderParamsInputs()} + {!this._applied ? this.renderScriptingInputs : this.renderParamsInputs()} {!this._applied ? this.renderScriptingTools() : this.renderParamsTools()}
-- cgit v1.2.3-70-g09d2 From db747fcb82c44a63cc3905c031ab2ae34403f5ae Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Wed, 3 Jun 2020 13:04:47 -0500 Subject: fixed uneven parantheses showing params suggestion --- src/client/util/CurrentUserUtils.ts | 2 +- src/client/views/nodes/ScriptingBox.scss | 1 + src/client/views/nodes/ScriptingBox.tsx | 43 +++++++++++++++++++++----------- 3 files changed, 31 insertions(+), 15 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 632187b49..4c30b4c03 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -738,5 +738,5 @@ export class CurrentUserUtils { Scripting.addGlobal("setupMobileInkingDoc", function setupMobileInkingDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileInkingDoc(userDoc); }); Scripting.addGlobal(function setupMobileUploadDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileUploadDoc(userDoc); }, - "initializes the Mobile upload document", "{ userDoc: the document to initialize }"); + "initializes the Mobile upload document", "{ userDoc: Doc }"); Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); }); \ No newline at end of file diff --git a/src/client/views/nodes/ScriptingBox.scss b/src/client/views/nodes/ScriptingBox.scss index edf79b38e..ae2569704 100644 --- a/src/client/views/nodes/ScriptingBox.scss +++ b/src/client/views/nodes/ScriptingBox.scss @@ -94,6 +94,7 @@ width: 100%; text-align: left; outline: none; + overflow-y: scroll; } .rta__entity:hover { diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index f1105e614..f6e7e375b 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -491,10 +491,25 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent 0) { - this._paramSuggestion = true; + if (this.rawScript[pos - 2] !== "(") { + console.log("suggestion params"); + this._paramSuggestion = true; + } } } else if (e.key === ")") { this._paramSuggestion = false; + } else { + console.log(this.rawScript.split("(").length - 1); + console.log(this.rawScript.split(")").length - 1); + if (this.rawScript.split("(").length - 1 <= this.rawScript.split(")").length - 1) { + console.log("removed params"); + this._paramSuggestion = false; + } else { + if (e.key === "Backspace") { + console.log("removed params"); + this._paramSuggestion = false; + } + } } } @@ -542,12 +557,12 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent this.setHovered(false)}> {value} - {!this._hovered ? (null) : - <> -
{this.getDescription(value)}
-
{this.getParams(value)}
- - } + {/* {!this._hovered ? (null) : + <> */} +
{this.getDescription(value)}
+
{this.getParams(value)}
+ {/* + } */} , output: (item: any, trigger) => { this._spaced = true; @@ -563,12 +578,12 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent this.setHovered(false)}> {value} - {!this._hovered ? (null) : - <> -
{this.getDescription(value)}
-
{this.getParams(value)}
- - } + {/* {!this._hovered ? (null) : + <> */} +
{this.getDescription(value)}
+
{this.getParams(value)}
+ {/* + } */} , output: (item: any, trigger) => { this._spaced = true; @@ -577,7 +592,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent this.keyHandler(e, this.caretPos)} + onKeyDown={(e) => this.keyHandler(e, this.caretPos)} onCaretPositionChange={(number: any) => this.handlePosChange(number)} /> -- cgit v1.2.3-70-g09d2 From 65ff6d515cd8102c3a4284244f8a84d356ba0c05 Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Wed, 3 Jun 2020 15:58:12 -0500 Subject: fixed wrapped params except onResize --- src/client/util/CurrentUserUtils.ts | 6 ++-- src/client/util/DropConverter.ts | 3 +- src/client/util/LinkManager.ts | 3 +- src/client/views/nodes/ScriptingBox.scss | 7 ++-- src/client/views/nodes/ScriptingBox.tsx | 56 ++++++++++++++++++++++---------- 5 files changed, 50 insertions(+), 25 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 4c30b4c03..3dcb484be 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -736,7 +736,9 @@ export class CurrentUserUtils { } } -Scripting.addGlobal("setupMobileInkingDoc", function setupMobileInkingDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileInkingDoc(userDoc); }); +Scripting.addGlobal(function setupMobileInkingDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileInkingDoc(userDoc); }, + "initializes the Mobile inking document", "{ userDoc: Doc }"); Scripting.addGlobal(function setupMobileUploadDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileUploadDoc(userDoc); }, "initializes the Mobile upload document", "{ userDoc: Doc }"); -Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); }); \ No newline at end of file +Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); }, + "creates a new workspace when called"); \ No newline at end of file diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 752c1cfc5..785e087f9 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -76,4 +76,5 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { data.droppedDocuments[i] = dbox; }); } -Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); }); \ No newline at end of file +Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); }, + "converts the dropped data to buttons", "{ dragData: any }"); \ No newline at end of file diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 8e6ccf098..9b20af4cb 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -211,4 +211,5 @@ export class LinkManager { } } -Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }); \ No newline at end of file +Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, + "creates a link to inputted document", "{ doc: any }"); \ No newline at end of file diff --git a/src/client/views/nodes/ScriptingBox.scss b/src/client/views/nodes/ScriptingBox.scss index ae2569704..32021e637 100644 --- a/src/client/views/nodes/ScriptingBox.scss +++ b/src/client/views/nodes/ScriptingBox.scss @@ -15,6 +15,8 @@ position: absolute; z-index: 100; padding: 5px; + white-space: nowrap; + overflow: hidden; } .scriptingBox-inputDiv { @@ -51,12 +53,11 @@ .rta { position: relative; - font-size: 12px; width: 100%; height: 100%; margin-bottom: 60px !important; overflow-y: scroll; - font-size: 9px; + overflow-x: hidden; } .rta__textarea { @@ -75,7 +76,6 @@ margin-top: 0; margin-bottom: 1em; max-height: 100px; - overflow-y: auto; } .rta__list { @@ -87,6 +87,7 @@ box-shadow: 0 0 5px rgba(27, 31, 35, 0.1); list-style: none; overflow-y: scroll; + overflow-x: hidden; } .rta__entity { diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index f6e7e375b..3c268b5cd 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -44,11 +44,14 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent p.split(":")[0].trim()); } @computed({ keepAlive: true }) get paramsTypes() { return this.compileParams.map(p => p.split(":")[1].trim()); } @@ -469,27 +472,42 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent (x + scriptWidth)) { + const diff = (left + suggestionWidth) - (x + scriptWidth); + left = left - diff; + } + + runInAction(() => { + This._suggestionBoxX = left; + This._suggestionBoxY = top; + }); + }); + } + @action keyHandler(e: any, pos: number) { console.log(e.key); if (e.key === "(") { console.log("hello"); - const getCaretCoordinates = require('textarea-caret'); - - const This = this; - document.querySelector('textarea')?.addEventListener('input', function () { - const caret = getCaretCoordinates(this, this.selectionEnd); - console.log('(top, left, height) = (%s, %s, %s)', caret.top, caret.left, caret.height); - let top = caret.top; - let left = caret.left; - runInAction(() => { - This._suggestionBoxX = left; - This._suggestionBoxY = top; - }); - }); + this.suggestionPos(); this._scriptSuggestedParams = this.getSuggestedParams(pos); + //this._scriptSuggestedParams =
; + if (this._scriptSuggestedParams !== undefined && this._scriptSuggestedParams.length > 0) { if (this.rawScript[pos - 2] !== "(") { console.log("suggestion params"); @@ -536,7 +554,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent 0 ? "70%" : "100%" }}> + return
0 ? "70%" : "100%" }} ref={this._scriptTextRef}> -
this.props.isSelected(true) && e.stopPropagation()}> - {this._paramSuggestion ?
{this._scriptSuggestedParams}
: null} +
+
this.props.isSelected(true) && e.stopPropagation()}> + {this._paramSuggestion ?
{this._scriptSuggestedParams}
: null} {!this._applied ? this.renderScriptingInputs : this.renderParamsInputs()} {!this._applied ? this.renderScriptingTools() : this.renderParamsTools()}
-- cgit v1.2.3-70-g09d2 From cdc9c7e293a656573d49c4265b5867e34e8db7c8 Mon Sep 17 00:00:00 2001 From: Bob Zeleznik Date: Fri, 5 Jun 2020 18:30:56 -0400 Subject: changed all scroll behaviors to be _scrollTop to go to a scroll and _scrollY to animate to it. --- src/client/util/DocumentManager.ts | 2 +- src/client/views/animationtimeline/Timeline.tsx | 2 -- src/client/views/animationtimeline/Track.tsx | 5 +++-- src/client/views/collections/CollectionSubView.tsx | 2 +- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- src/client/views/nodes/PDFBox.tsx | 2 +- src/client/views/nodes/WebBox.tsx | 14 +++++++------- .../views/nodes/formattedText/FormattedTextBox.tsx | 4 ++-- .../formattedText/FormattedTextBoxComment.tsx | 2 +- src/client/views/pdf/PDFViewer.tsx | 22 +++++++++------------- src/fields/documentSchemas.ts | 8 ++++---- 11 files changed, 30 insertions(+), 35 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 67f2f244c..78c05f572 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -168,7 +168,7 @@ export class DocumentManager { highlight(); } else { // otherwise try to get a view of the context of the target const targetDocContextView = getFirstDocView(targetDocContext); - targetDocContext.scrollY = 0; // this will force PDFs to activate and load their annotations / allow scrolling + targetDocContext._scrollY = 0; // this will force PDFs to activate and load their annotations / allow scrolling if (targetDocContextView) { // we found a context view and aren't forced to create a new one ... focus on the context first.. targetDocContext.panTransformType = "Ease"; targetDocContextView.props.focus(targetDocContextView.props.Document, willZoom); diff --git a/src/client/views/animationtimeline/Timeline.tsx b/src/client/views/animationtimeline/Timeline.tsx index 017623120..43f15a33f 100644 --- a/src/client/views/animationtimeline/Timeline.tsx +++ b/src/client/views/animationtimeline/Timeline.tsx @@ -11,8 +11,6 @@ import { KeyframeFunc } from "./Keyframe"; import "./Timeline.scss"; import { TimelineOverview } from "./TimelineOverview"; import { Track } from "./Track"; -import { utils } from "mocha"; -import { Util } from "../../../../deploy/assets/pdf.worker"; import clamp from "../../util/clamp"; /** diff --git a/src/client/views/animationtimeline/Track.tsx b/src/client/views/animationtimeline/Track.tsx index fc96c320a..4987006e7 100644 --- a/src/client/views/animationtimeline/Track.tsx +++ b/src/client/views/animationtimeline/Track.tsx @@ -31,13 +31,14 @@ export class Track extends React.Component { @observable private _autoKfReaction: any; @observable private _newKeyframe: boolean = false; private readonly MAX_TITLE_HEIGHT = 75; - private _trackHeight = 0; + @observable private _trackHeight = 0; private primitiveWhitelist = [ "x", "y", "_width", "_height", "opacity", + "_scrollTop" ]; private objectWhitelist = [ "data" @@ -51,7 +52,7 @@ export class Track extends React.Component { if (!regions) this.props.node.regions = new List(); //if there is no region, then create new doc to store stuff //these two lines are exactly same from timeline.tsx const relativeHeight = window.innerHeight / 20; - this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT; //for responsiveness + runInAction(() => this._trackHeight = relativeHeight < this.MAX_TITLE_HEIGHT ? relativeHeight : this.MAX_TITLE_HEIGHT); //for responsiveness this._timelineVisibleReaction = this.timelineVisibleReaction(); this._currentBarXReaction = this.currentBarXReaction(); if (DocListCast(this.props.node.regions).length === 0) this.createRegion(this.time); diff --git a/src/client/views/collections/CollectionSubView.tsx b/src/client/views/collections/CollectionSubView.tsx index 423eb1d90..56c6ce457 100644 --- a/src/client/views/collections/CollectionSubView.tsx +++ b/src/client/views/collections/CollectionSubView.tsx @@ -343,7 +343,7 @@ export function CollectionSubView(schemaCtor: (doc: Doc) => T, moreProps?: if (focusNode) { const rect = "getBoundingClientRect" in focusNode ? focusNode.getBoundingClientRect() : focusNode?.parentElement.getBoundingClientRect(); const x = (rect?.x || 0); - const y = NumCast(srcWeb.scrollTop) + (rect?.y || 0); + const y = NumCast(srcWeb._scrollTop) + (rect?.y || 0); const anchor = Docs.Create.FreeformDocument([], { _LODdisable: true, _backgroundColor: "transparent", _width: 25, _height: 25, x, y, annotationOn: srcWeb }); anchor.context = srcWeb; const key = Doc.LayoutFieldKey(srcWeb); diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index fc5e61026..80ee2a65d 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -886,7 +886,7 @@ export class CollectionFreeFormView extends CollectionSubView ({ y: this.layoutDoc.scrollY, x: this.layoutDoc.scrollX }), + this._reactionDisposer = reaction(() => ({ y: this.layoutDoc._scrollY, x: this.layoutDoc._scrollX }), ({ x, y }) => { if (y !== undefined) { this._outerRef.current!.scrollTop = y; - this.layoutDoc.scrollY = undefined; + this.layoutDoc._scrollY = undefined; } if (x !== undefined) { this._outerRef.current!.scrollLeft = x; @@ -83,8 +83,8 @@ export class WebBox extends ViewBoxAnnotatableComponent { const scrollTop = e.target?.children?.[0].scrollTop; const scrollLeft = e.target?.children?.[0].scrollLeft; - this.layoutDoc.scrollTop = this._outerRef.current!.scrollTop = scrollTop; - this.layoutDoc.scrollLeft = this._outerRef.current!.scrollLeft = scrollLeft; + this.layoutDoc._scrollTop = this._outerRef.current!.scrollTop = scrollTop; + this.layoutDoc._scrollLeft = this._outerRef.current!.scrollLeft = scrollLeft; } async componentDidMount() { const urlField = Cast(this.dataDoc[this.props.fieldKey], WebField); @@ -440,7 +440,7 @@ export class WebBox extends ViewBoxAnnotatableComponent); } - scrollXf = () => this.props.ScreenToLocalTransform().translate(NumCast(this.layoutDoc.scrollLeft), NumCast(this.layoutDoc.scrollTop)); + scrollXf = () => this.props.ScreenToLocalTransform().translate(NumCast(this.layoutDoc._scrollLeft), NumCast(this.layoutDoc._scrollTop)); render() { return (
NumCast(this.layoutDoc.scrollPos), + this._disposers.scroll = reaction(() => NumCast(this.layoutDoc._scrollTop), pos => this._scrollRef.current && this._scrollRef.current.scrollTo({ top: pos }), { fireImmediately: true } ); @@ -1174,7 +1174,7 @@ export class FormattedTextBox extends ViewBoxAnnotatableComponent<(FieldViewProp } onscrolled = (ev: React.UIEvent) => { - this.layoutDoc.scrollPos = this._scrollRef.current!.scrollTop; + this.layoutDoc._scrollTop = this._scrollRef.current!.scrollTop; } @action tryUpdateHeight(limitHeight?: number) { diff --git a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx index d47ae63af..59a6045ab 100644 --- a/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx +++ b/src/client/views/nodes/formattedText/FormattedTextBoxComment.tsx @@ -183,7 +183,7 @@ export class FormattedTextBoxComment { const anchor = FieldValue(Doc.AreProtosEqual(FieldValue(Cast(linkDoc.anchor1, Doc)), textBox.dataDoc) ? Cast(linkDoc.anchor2, Doc) : (Cast(linkDoc.anchor1, Doc)) || linkDoc); const target = anchor?.annotationOn ? await DocCastAsync(anchor.annotationOn) : anchor; if (anchor !== target && anchor && target) { - target.scrollY = NumCast(anchor?.y); + target._scrollY = NumCast(anchor?.y); } if (target) { ReactDOM.render( this._showWaiting = this._showCover = true); this.props.startupLive && this.setupPdfJsViewer(); + this._mainCont.current!.scrollTop = this.layoutDoc._scrollTop || 0; this._searchReactionDisposer = reaction(() => this.Document.searchMatch, search => { if (search) { this.search(Doc.SearchQuery(), true); @@ -158,14 +155,12 @@ export class PDFViewer extends ViewBoxAnnotatableComponent (SelectionManager.SelectedDocuments().length === 1) && this.setupPdfJsViewer(), { fireImmediately: true }); this._reactionDisposer = reaction( - () => this.Document.scrollY, + () => this.Document._scrollY, (scrollY) => { if (scrollY !== undefined) { - if (this._showCover || this._showWaiting) { - this.setupPdfJsViewer(); - } - this._mainCont.current && smoothScroll(1000, this._mainCont.current, (this.Document.scrollY || 0)); - this.Document.scrollY = undefined; + (this._showCover || this._showWaiting) && this.setupPdfJsViewer(); + this._mainCont.current && smoothScroll(1000, this._mainCont.current, (this.Document._scrollY || 0)); + setTimeout(() => this.Document._scrollY = undefined, 1000); } }, { fireImmediately: true } @@ -220,7 +215,8 @@ export class PDFViewer extends ViewBoxAnnotatableComponent Cast(this.layoutDoc._scrollTop, "number", null), - (stop) => (stop !== undefined) && this._mainCont.current && smoothScroll(500, this._mainCont.current, stop), { fireImmediately: true }); + (stop) => (stop !== undefined && this.layoutDoc._scrollY === undefined) && (this._mainCont.current!.scrollTop = stop), { fireImmediately: true }); + this._annotationReactionDisposer = reaction( () => DocListCast(this.dataDoc[this.props.fieldKey + "-annotations"]), annotations => annotations?.length && (this._annotations = annotations), @@ -352,7 +348,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent) => { - this._scrollTop = this._mainCont.current!.scrollTop; + this.Document._scrollY === undefined && (this.layoutDoc._scrollTop = this._mainCont.current!.scrollTop); this._pdfViewer && (this.Document.curPage = this._pdfViewer.currentPageNumber); } @@ -604,7 +600,7 @@ export class PDFViewer extends ViewBoxAnnotatableComponent { - return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this._scrollTop) : this.props.ScreenToLocalTransform(); + return this._mainCont.current ? this.props.ScreenToLocalTransform().translate(0, this.layoutDoc._scrollTop || 0) : this.props.ScreenToLocalTransform(); } onClick = (e: React.MouseEvent) => { this._setPreviewCursor && diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index e7031cc39..e0a02d93c 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -22,10 +22,10 @@ export const documentSchema = createSchema({ y: "number", // y coordinate when in a freeform view z: "number", // z "coordinate" - non-zero specifies the overlay layer of a freeformview zIndex: "number", // zIndex of a document in a freeform view - scrollY: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that ) - scrollX: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that ) - scrollTop: "number", // scroll position of a scrollable document (pdf, text, web) - scrollLeft: "number", // scroll position of a scrollable document (pdf, text, web) + _scrollY: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that ) + _scrollX: "number", // "command" to scroll a document to a position on load (the value will be reset to 0 after that ) + _scrollTop: "number", // scroll position of a scrollable document (pdf, text, web) + _scrollLeft: "number", // scroll position of a scrollable document (pdf, text, web) // appearance properties on the layout _autoHeight: "boolean", // whether the height of the document should be computed automatically based on its contents -- cgit v1.2.3-70-g09d2 From 4ac63cedfdfd33b47c97e7cc8be60ec9b5490a3e Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Sat, 6 Jun 2020 12:48:25 -0500 Subject: changing { to ( and adding , --- src/client/util/CurrentUserUtils.ts | 4 ++-- src/client/util/DropConverter.ts | 2 +- src/client/util/LinkManager.ts | 2 +- src/client/util/Scripting.ts | 1 + src/client/views/GestureOverlay.tsx | 2 +- src/client/views/collections/CollectionDockingView.tsx | 2 +- src/client/views/nodes/ScriptingBox.tsx | 5 +++++ src/fields/ScriptField.ts | 2 +- 8 files changed, 13 insertions(+), 7 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/CurrentUserUtils.ts b/src/client/util/CurrentUserUtils.ts index 3dcb484be..8509a1e64 100644 --- a/src/client/util/CurrentUserUtils.ts +++ b/src/client/util/CurrentUserUtils.ts @@ -737,8 +737,8 @@ export class CurrentUserUtils { } Scripting.addGlobal(function setupMobileInkingDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileInkingDoc(userDoc); }, - "initializes the Mobile inking document", "{ userDoc: Doc }"); + "initializes the Mobile inking document", "(userDoc: Doc)"); Scripting.addGlobal(function setupMobileUploadDoc(userDoc: Doc) { return CurrentUserUtils.setupMobileUploadDoc(userDoc); }, - "initializes the Mobile upload document", "{ userDoc: Doc }"); + "initializes the Mobile upload document", "(userDoc: Doc)"); Scripting.addGlobal(function createNewWorkspace() { return MainView.Instance.createNewWorkspace(); }, "creates a new workspace when called"); \ No newline at end of file diff --git a/src/client/util/DropConverter.ts b/src/client/util/DropConverter.ts index 785e087f9..ea1769d85 100644 --- a/src/client/util/DropConverter.ts +++ b/src/client/util/DropConverter.ts @@ -77,4 +77,4 @@ export function convertDropDataToButtons(data: DragManager.DocumentDragData) { }); } Scripting.addGlobal(function convertToButtons(dragData: any) { convertDropDataToButtons(dragData as DragManager.DocumentDragData); }, - "converts the dropped data to buttons", "{ dragData: any }"); \ No newline at end of file + "converts the dropped data to buttons", "(dragData: any)"); \ No newline at end of file diff --git a/src/client/util/LinkManager.ts b/src/client/util/LinkManager.ts index 9b20af4cb..133f40d5a 100644 --- a/src/client/util/LinkManager.ts +++ b/src/client/util/LinkManager.ts @@ -212,4 +212,4 @@ export class LinkManager { } Scripting.addGlobal(function links(doc: any) { return new List(LinkManager.Instance.getAllRelatedLinks(doc)); }, - "creates a link to inputted document", "{ doc: any }"); \ No newline at end of file + "creates a link to inputted document", "(doc: any)"); \ No newline at end of file diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 00b01a75a..817e6b29d 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -49,6 +49,7 @@ export function isCompileError(toBeDetermined: CompileResult): toBeDetermined is export namespace Scripting { export function addGlobal(global: { name: string }): void; export function addGlobal(name: string, global: any): void; + export function addGlobal(global: { name: string }, decription?: string, params?: any): void; export function addGlobal(nameOrGlobal: any, global?: any, params?: any) { diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index facfd1ab5..e9a848a5a 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -818,4 +818,4 @@ Scripting.addGlobal(function resetPen() { }, "resets the pen tool"); Scripting.addGlobal(function createText(text: any, x: any, y: any) { GestureOverlay.Instance.dispatchGesture("text", [{ X: x, Y: y }], text); -}, "creates a text document with inputted text and coordinates", "{ text: any, x: any, y: any }"); \ No newline at end of file +}, "creates a text document with inputted text and coordinates", "(text: any, x: any, y: any)"); \ No newline at end of file diff --git a/src/client/views/collections/CollectionDockingView.tsx b/src/client/views/collections/CollectionDockingView.tsx index 6e27169e2..8c0b0a1c8 100644 --- a/src/client/views/collections/CollectionDockingView.tsx +++ b/src/client/views/collections/CollectionDockingView.tsx @@ -860,5 +860,5 @@ export class DockedFrameRenderer extends React.Component { } } Scripting.addGlobal(function openOnRight(doc: any) { CollectionDockingView.AddRightSplit(doc); }, - "opens up the inputted document on the right side of the screen", "{ doc: any }"); + "opens up the inputted document on the right side of the screen", "(doc: any)"); Scripting.addGlobal(function useRightSplit(doc: any, shiftKey?: boolean) { CollectionDockingView.UseRightSplit(doc, undefined, shiftKey); }); diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index 071a990a3..f44f4fdad 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -524,6 +524,11 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent { + if (i !== parameters.length - 1) { + parameters[i] = element + ","; + } + }); console.log("numEntered: " + numEntered); diff --git a/src/fields/ScriptField.ts b/src/fields/ScriptField.ts index d1a1c46cf..11b3b0524 100644 --- a/src/fields/ScriptField.ts +++ b/src/fields/ScriptField.ts @@ -161,7 +161,7 @@ export class ComputedField extends ScriptField { Scripting.addGlobal(function getIndexVal(list: any[], index: number) { return list.reduce((p, x, i) => (i <= index && x !== undefined) || p === undefined ? x : p, undefined as any); -}, "returns the value at a given index of a list", "{ list: any[], index: number }"); +}, "returns the value at a given index of a list", "(list: any[], index: number)"); export namespace ComputedField { let useComputed = true; -- cgit v1.2.3-70-g09d2 From 632dec95b9fccccef13b50cb41fc598613a9df1e Mon Sep 17 00:00:00 2001 From: yunahi <60233430+yunahi@users.noreply.github.com> Date: Mon, 8 Jun 2020 10:56:36 +0900 Subject: added ink options menu --- package-lock.json | 56 ++++- package.json | 2 + src/client/documents/Documents.ts | 7 +- src/client/util/InteractionUtils.tsx | 139 ++++++++++- src/client/views/GestureOverlay.tsx | 123 ++++++++- src/client/views/InkingControl.tsx | 46 +++- src/client/views/InkingStroke.tsx | 3 +- src/client/views/MainView.tsx | 2 + .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../collectionFreeForm/InkOptionsMenu.scss | 36 +++ .../collectionFreeForm/InkOptionsMenu.tsx | 274 +++++++++++++++++++++ .../collections/collectionFreeForm/MarqueeView.tsx | 126 +++++++++- src/client/views/nodes/ColorBox.tsx | 6 +- src/fields/documentSchemas.ts | 1 + src/pen-gestures/GestureUtils.ts | 5 +- src/pen-gestures/ndollar.ts | 17 +- src/typings/index.d.ts | 2 + 17 files changed, 808 insertions(+), 39 deletions(-) create mode 100644 src/client/views/collections/collectionFreeForm/InkOptionsMenu.scss create mode 100644 src/client/views/collections/collectionFreeForm/InkOptionsMenu.tsx (limited to 'src/client/util') diff --git a/package-lock.json b/package-lock.json index 842553659..b3fb00ab6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2200,6 +2200,11 @@ "callsite": "1.0.0" } }, + "bezier-curve": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/bezier-curve/-/bezier-curve-1.0.0.tgz", + "integrity": "sha1-o9+v6rEqlMRicw1QeYxSqEBdc3k=" + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -2827,7 +2832,8 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true + "bundled": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2845,11 +2851,13 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true + "bundled": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2862,15 +2870,18 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "concat-map": { "version": "0.0.1", - "bundled": true + "bundled": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true + "bundled": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2973,7 +2984,8 @@ }, "inherits": { "version": "2.0.4", - "bundled": true + "bundled": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2983,6 +2995,7 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2995,17 +3008,20 @@ "minimatch": { "version": "3.0.4", "bundled": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "1.2.5", - "bundled": true + "bundled": true, + "optional": true }, "minipass": { "version": "2.9.0", "bundled": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -3022,6 +3038,7 @@ "mkdirp": { "version": "0.5.3", "bundled": true, + "optional": true, "requires": { "minimist": "^1.2.5" } @@ -3077,7 +3094,8 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "npm-packlist": { "version": "1.4.8", @@ -3102,7 +3120,8 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true + "bundled": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3112,6 +3131,7 @@ "once": { "version": "1.4.0", "bundled": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3180,7 +3200,8 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true + "bundled": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3210,6 +3231,7 @@ "string-width": { "version": "1.0.2", "bundled": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3227,6 +3249,7 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3265,11 +3288,13 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true + "bundled": true, + "optional": true }, "yallist": { "version": "3.1.1", - "bundled": true + "bundled": true, + "optional": true } } } @@ -5879,6 +5904,11 @@ "resolve-dir": "^1.0.1" } }, + "fit-curve": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/fit-curve/-/fit-curve-0.1.7.tgz", + "integrity": "sha512-Md3b3ReA/qJwwYvKXeHpOV1fhPqwhJ9/29zc9lfi8ijyg00J0S9C7+5XMZDFhlDAKbwOvVgBxVIG1EHoMSC3vw==" + }, "flat": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", @@ -18044,4 +18074,4 @@ } } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index d1d7935af..20614f5eb 100644 --- a/package.json +++ b/package.json @@ -124,6 +124,7 @@ "async": "^2.6.2", "babel-runtime": "^6.26.0", "bcrypt-nodejs": "0.0.3", + "bezier-curve": "^1.0.0", "bluebird": "^3.7.2", "body-parser": "^1.18.3", "bootstrap": "^4.4.1", @@ -146,6 +147,7 @@ "express-validator": "^5.3.1", "expressjs": "^1.0.1", "find-in-files": "^0.5.0", + "fit-curve": "^0.1.7", "flexlayout-react": "^0.3.3", "formidable": "^1.2.1", "golden-layout": "^1.5.9", diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index f7e19eecd..71bf8a516 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -630,12 +630,13 @@ export namespace Docs { return doc; } - export function InkDocument(color: string, tool: number, strokeWidth: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) { + export function InkDocument(color: string, tool: number, strokeWidth: string, strokeBezier: string, points: { X: number, Y: number }[], options: DocumentOptions = {}) { const I = new Doc(); I.type = DocumentType.INK; I.layout = InkingStroke.LayoutString("data"); I.color = color; I.strokeWidth = strokeWidth; + I.strokeBezier = strokeBezier; I.tool = tool; I.title = "ink"; I.x = options.x; @@ -936,8 +937,8 @@ export namespace Docs { created = Docs.Create.AudioDocument((field).url.href, resolved); layout = AudioBox.LayoutString; } else if (field instanceof InkField) { - const { selectedColor, selectedWidth, selectedTool } = InkingControl.Instance; - created = Docs.Create.InkDocument(selectedColor, selectedTool, selectedWidth, (field).inkData, resolved); + const { selectedColor, selectedWidth, selectedTool, selectedBezier } = InkingControl.Instance; + created = Docs.Create.InkDocument(selectedColor, selectedTool, selectedWidth, selectedBezier, (field).inkData, 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/InteractionUtils.tsx b/src/client/util/InteractionUtils.tsx index 3a5345c80..ab1ccb25a 100644 --- a/src/client/util/InteractionUtils.tsx +++ b/src/client/util/InteractionUtils.tsx @@ -1,4 +1,7 @@ import React = require("react"); +import * as beziercurve from 'bezier-curve'; +import * as fitCurve from 'fit-curve'; +import InkOptionsMenu from "../views/collections/collectionFreeForm/InkOptionsMenu"; export namespace InteractionUtils { export const MOUSETYPE = "mouse"; @@ -87,8 +90,45 @@ export namespace InteractionUtils { return myTouches; } - export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: string) { - const pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, ""); + export function CreatePolyline(points: { X: number, Y: number }[], left: number, top: number, color: string, width: string, bezier: string) { + var pts = ""; + var shape = ""; + if (InkOptionsMenu.Instance._circle) { + shape = "circle"; + } else if (InkOptionsMenu.Instance._rectangle) { + shape = "rectangle"; + } else if (InkOptionsMenu.Instance._triangle) { + shape = "triangle"; + } else if (InkOptionsMenu.Instance._arrow) { + shape = "arrow"; + } else if (InkOptionsMenu.Instance._line) { + shape = "line"; + } + if (shape !== "") { + //if any of the shape are true + const shapePts = makePolygon(shape, points); + pts = shapePts.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, ""); + } + else if (points.length > 1 && points[points.length - 1].X === points[0].X && points[points.length - 1].Y === points[0].Y) { + //pointer is up (first and last points are the same) + const newPoints: number[][] = []; + const newPts: { X: number; Y: number; }[] = []; + //convert to [][] for fitcurve module + for (var i = 0; i < points.length - 1; i++) { + newPoints.push([points[i].X, points[i].Y]); + } + const bezierCurves = fitCurve(newPoints, parseInt(bezier)); + for (var i = 0; i < bezierCurves.length; i++) { + for (var t = 0; t < 1; t += 0.01) { + const point = beziercurve(t, bezierCurves[i]); + newPts.push({ X: point[0], Y: point[1] }); + } + } + pts = newPts.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, ""); + } else { + //in the middle of drawing + pts = points.reduce((acc: string, pt: { X: number, Y: number }) => acc + `${pt.X - left},${pt.Y - top} `, ""); + } return ( 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") { + //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; + var right = points[1].X; + var bottom = points[1].Y; + } else { + //otherwise take max and min + const xs = points.map(p => p.X); + const ys = points.map(p => p.Y); + right = Math.max(...xs); + left = Math.min(...xs); + bottom = Math.max(...ys); + top = Math.min(...ys); + } + } else { + //if in the middle of drawing + //take first and last points + right = points[points.length - 1].X; + left = points[0].X; + bottom = points[points.length - 1].Y; + top = points[0].Y; + if (shape !== "arrow" && shape !== "line") { + //switch left/right and top/bottom if needed + if (left > right) { + const temp = right; + right = left; + left = temp; + } + if (top > bottom) { + const temp = top; + top = bottom; + bottom = temp; + } + } + } + points = []; + switch (shape) { + 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": + points.push({ X: left, Y: bottom }); + points.push({ X: right, Y: bottom }); + points.push({ X: (right + left) / 2, Y: top }); + points.push({ X: left, Y: bottom }); + return points; + case "circle": + const centerX = (right + left) / 2; + const centerY = (bottom + top) / 2; + const radius = bottom - centerY; + for (var y = top; y < bottom; y++) { + const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX; + points.push({ X: x, Y: y }); + } + for (var y = bottom; y > top; y--) { + const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX; + const newX = centerX - (x - centerX); + points.push({ X: newX, Y: y }); + } + points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top }); + 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": + points.push({ X: left, Y: top }); + points.push({ X: right, Y: bottom }); + return points; + default: + return points; + } + } /** * Returns whether or not the pointer event passed in is of the type passed in * @param e - pointer event. this event could be from a mouse, a pen, or a finger diff --git a/src/client/views/GestureOverlay.tsx b/src/client/views/GestureOverlay.tsx index 4352ac52c..5714970c1 100644 --- a/src/client/views/GestureOverlay.tsx +++ b/src/client/views/GestureOverlay.tsx @@ -32,6 +32,7 @@ import { MobileInkOverlayContent } from "../../server/Message"; import MobileInkOverlay from "../../mobile/MobileInkOverlay"; import { RadialMenu } from "./nodes/RadialMenu"; import { SelectionManager } from "../util/SelectionManager"; +import InkOptionsMenu from "./collections/collectionFreeForm/InkOptionsMenu"; @observer @@ -581,7 +582,8 @@ export default class GestureOverlay extends Touchable { if (this._points.length > 1) { const B = this.svgBounds; const points = this._points.map(p => ({ X: p.X - B.left, Y: p.Y - B.top })); - + //push first points to so interactionUtil knows pointer is up + this._points.push({ X: this._points[0].X, Y: this._points[0].Y }); if (MobileInterface.Instance && MobileInterface.Instance.drawingInk) { const { selectedColor, selectedWidth } = InkingControl.Instance; DocServer.Mobile.dispatchGesturePoints({ @@ -630,6 +632,23 @@ export default class GestureOverlay extends Touchable { break; } } + //if any of the shape is activated in the InkOptionsMenu + else if (InkOptionsMenu.Instance._circle || InkOptionsMenu.Instance._triangle || InkOptionsMenu.Instance._rectangle || InkOptionsMenu.Instance._line || InkOptionsMenu.Instance._arrow) { + if (InkOptionsMenu.Instance._circle) { + this.makePolygon("circle", false); + } else if (InkOptionsMenu.Instance._triangle) { + this.makePolygon("triangle", false); + } else if (InkOptionsMenu.Instance._rectangle) { + this.makePolygon("rectangle", false); + } else if (InkOptionsMenu.Instance._line) { + this.makePolygon("line", false); + } else if (InkOptionsMenu.Instance._arrow) { + this.makePolygon("arrow", false); + } + this.dispatchGesture(GestureUtils.Gestures.Stroke); + this._points = []; + InkOptionsMenu.Instance.allFalse(); + } // if we're not drawing in a toolglass try to recognize as gesture else { const result = points.length > 2 && GestureUtils.GestureRecognizer.Recognize(new Array(points)); @@ -651,6 +670,15 @@ export default class GestureOverlay extends Touchable { case GestureUtils.Gestures.Line: actionPerformed = this.handleLineGesture(); break; + case GestureUtils.Gestures.Triangle: + this.makePolygon("triangle", true); + break; + case GestureUtils.Gestures.Circle: + this.makePolygon("circle", true); + break; + case GestureUtils.Gestures.Rectangle: + this.makePolygon("rectangle", true); + break; case GestureUtils.Gestures.Scribble: console.log("scribble"); break; @@ -671,6 +699,95 @@ export default class GestureOverlay extends Touchable { document.removeEventListener("pointerup", this.onPointerUp); } + makePolygon = (shape: string, gesture: boolean) => { + const xs = this._points.map(p => p.X); + const ys = this._points.map(p => p.Y); + var right = Math.max(...xs); + var left = Math.min(...xs); + var bottom = Math.max(...ys); + var top = Math.min(...ys); + + if (!gesture) { + //if shape options is activated in inkOptionMenu + //take second to last point because _point[length-1] is _points[0] + right = this._points[this._points.length - 2].X; + left = this._points[0].X; + bottom = this._points[this._points.length - 2].Y; + top = this._points[0].Y; + if (shape !== "arrow" && shape !== "line") { + if (left > right) { + const temp = right; + right = left; + left = temp; + } + if (top > bottom) { + const temp = top; + top = bottom; + bottom = temp; + } + } + } + this._points = []; + switch (shape) { + //must push an extra point in the end so InteractionUtils knows pointer is up. + //must be (points[0].X,points[0]-1) + case "rectangle": + this._points.push({ X: left, Y: top }); + this._points.push({ X: right, Y: top }); + this._points.push({ X: right, Y: bottom }); + this._points.push({ X: left, Y: bottom }); + this._points.push({ X: left, Y: top }); + this._points.push({ X: left, Y: top - 1 }); + break; + case "triangle": + this._points.push({ X: left, Y: bottom }); + this._points.push({ X: right, Y: bottom }); + this._points.push({ X: (right + left) / 2, Y: top }); + this._points.push({ X: left, Y: bottom }); + this._points.push({ X: left, Y: bottom - 1 }); + break; + case "circle": + const centerX = (right + left) / 2; + const centerY = (bottom + top) / 2; + const radius = bottom - centerY; + for (var y = top; y < bottom; y++) { + const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX; + this._points.push({ X: x, Y: y }); + } + for (var y = bottom; y > top; y--) { + const x = Math.sqrt(Math.pow(radius, 2) - (Math.pow((y - centerY), 2))) + centerX; + const newX = centerX - (x - centerX); + this._points.push({ X: newX, Y: y }); + } + this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top }); + this._points.push({ X: Math.sqrt(Math.pow(radius, 2) - (Math.pow((top - centerY), 2))) + centerX, Y: top - 1 }); + break; + case "line": + this._points.push({ X: left, Y: top }); + this._points.push({ X: right, Y: bottom }); + this._points.push({ X: right, Y: bottom - 1 }); + break; + 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)); + this._points.push({ X: x1, Y: y1 }); + this._points.push({ X: x2, Y: y2 }); + this._points.push({ X: x3, Y: y3 }); + this._points.push({ X: x4, Y: y4 }); + this._points.push({ X: x2, Y: y2 }); + this._points.push({ X: x1, Y: y1 - 1 }); + } + } + dispatchGesture = (gesture: "box" | "line" | "startbracket" | "endbracket" | "stroke" | "scribble" | "text", stroke?: InkData, data?: any) => { const target = document.elementFromPoint((stroke ?? this._points)[0].X, (stroke ?? this._points)[0].Y); target?.dispatchEvent( @@ -710,11 +827,11 @@ export default class GestureOverlay extends Touchable { [this._strokes.map(l => { const b = this.getBounds(l); return - {InteractionUtils.CreatePolyline(l, b.left, b.top, InkingControl.Instance.selectedColor, InkingControl.Instance.selectedWidth)} + {InteractionUtils.CreatePolyline(l, b.left, b.top, InkingControl.Instance.selectedColor, InkingControl.Instance.selectedWidth, InkingControl.Instance.selectedBezier)} ; }), this._points.length <= 1 ? (null) : - {InteractionUtils.CreatePolyline(this._points, B.left, B.top, InkingControl.Instance.selectedColor, InkingControl.Instance.selectedWidth)} + {InteractionUtils.CreatePolyline(this._points, B.left, B.top, InkingControl.Instance.selectedColor, InkingControl.Instance.selectedWidth, InkingControl.Instance.selectedBezier)} ] ]; } diff --git a/src/client/views/InkingControl.tsx b/src/client/views/InkingControl.tsx index 41ee36d05..83109db1c 100644 --- a/src/client/views/InkingControl.tsx +++ b/src/client/views/InkingControl.tsx @@ -9,14 +9,15 @@ import { SelectionManager } from "../util/SelectionManager"; import { undoBatch } from "../util/UndoManager"; import GestureOverlay from "./GestureOverlay"; import { FormattedTextBox } from "./nodes/formattedText/FormattedTextBox"; +import InkOptionsMenu from "./collections/collectionFreeForm/InkOptionsMenu"; export class InkingControl { @observable static Instance: InkingControl; @computed private get _selectedTool(): InkTool { return FieldValue(NumCast(Doc.UserDoc().inkTool)) ?? InkTool.None; } @computed private get _selectedColor(): string { return CurrentUserUtils.ActivePen ? FieldValue(StrCast(CurrentUserUtils.ActivePen.backgroundColor)) ?? "rgb(0, 0, 0)" : "rgb(0, 0, 0)"; } @computed private get _selectedWidth(): string { return FieldValue(StrCast(Doc.UserDoc().inkWidth)) ?? "2"; } + @computed private get _selectedBezier(): string { return FieldValue(StrCast(Doc.UserDoc().inkBezier)) ?? "2"; } @observable public _open: boolean = false; - constructor() { InkingControl.Instance = this; } @@ -32,10 +33,21 @@ export class InkingControl { return (number < 16 ? "0" : "") + number.toString(16).toUpperCase(); } + @action + inkOptionsMenuChangeColor = (color: string) => { + const col: ColorState = { + hex: color, hsl: { a: 0, h: 0, s: 0, l: 0, source: "" }, hsv: { a: 0, h: 0, s: 0, v: 0, source: "" }, + rgb: { a: 0, r: 0, b: 0, g: 0, source: "" }, oldHue: 0, source: "", + }; + this.switchColor(col); + InkOptionsMenu.Instance._colorBt = false; + } + @undoBatch switchColor = action((color: ColorState): void => { Doc.UserDoc().backgroundColor = color.hex.startsWith("#") ? color.hex + (color.rgb.a ? this.decimalToHexString(Math.round(color.rgb.a * 255)) : "ff") : color.hex; + InkOptionsMenu.Instance._color = StrCast(Doc.UserDoc().backgroundColor); CurrentUserUtils.ActivePen && (CurrentUserUtils.ActivePen.backgroundColor = color.hex); if (InkingControl.Instance.selectedTool === InkTool.None) { @@ -60,6 +72,23 @@ export class InkingControl { if (!isNaN(parseInt(width))) { Doc.UserDoc().inkWidth = width; } + InkOptionsMenu.Instance._widthBt = false; + } + + @action + switchBezier = (bezier: string): void => { + if (!isNaN(parseInt(bezier))) { + Doc.UserDoc().inkBezier = bezier; + } + } + + @action + inkOptionsMenuChangeBezier = (e: React.PointerEvent): void => { + if (InkOptionsMenu.Instance._bezierBt === true) { + Doc.UserDoc().inkBezier = "300"; + } else { + Doc.UserDoc().inkBezier = "0"; + } } @computed @@ -83,8 +112,21 @@ export class InkingControl { return this._selectedWidth; } + @computed + get selectedBezier() { + return this._selectedBezier; + } } -Scripting.addGlobal(function activatePen(pen: any, width: any, color: any) { InkingControl.Instance.switchTool(pen ? InkTool.Pen : InkTool.None); InkingControl.Instance.switchWidth(width); InkingControl.Instance.updateSelectedColor(color); }); +Scripting.addGlobal(function activatePen(pen: any, width: any, color: any) { + InkingControl.Instance.switchTool(pen ? InkTool.Pen : InkTool.None); InkingControl.Instance.switchWidth(width); InkingControl.Instance.updateSelectedColor(color); + //setup InkOptionsMenu(change jumpto value if necessary.Currenlty hardcoded to 300,300) + pen ? InkOptionsMenu.Instance.jumpTo(300, 300) : InkOptionsMenu.Instance.fadeOut(true); + InkOptionsMenu.Instance.changeColor = InkingControl.Instance.inkOptionsMenuChangeColor; + InkOptionsMenu.Instance.changeBezier = InkingControl.Instance.inkOptionsMenuChangeBezier; + InkOptionsMenu.Instance.changeWidth = InkingControl.Instance.switchWidth; + InkOptionsMenu.Instance._widthSelected = width; + InkOptionsMenu.Instance._color = color; +}); Scripting.addGlobal(function activateBrush(pen: any, width: any, color: any) { InkingControl.Instance.switchTool(pen ? InkTool.Highlighter : InkTool.None); InkingControl.Instance.switchWidth(width); InkingControl.Instance.updateSelectedColor(color); }); Scripting.addGlobal(function activateEraser(pen: any) { return InkingControl.Instance.switchTool(pen ? InkTool.Eraser : InkTool.None); }); Scripting.addGlobal(function activateStamp(pen: any) { return InkingControl.Instance.switchTool(pen ? InkTool.Stamp : InkTool.None); }); diff --git a/src/client/views/InkingStroke.tsx b/src/client/views/InkingStroke.tsx index 8938e8b6c..3dc0a5b20 100644 --- a/src/client/views/InkingStroke.tsx +++ b/src/client/views/InkingStroke.tsx @@ -40,7 +40,8 @@ export class InkingStroke extends ViewBoxBaseComponent + diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index c753a703d..fb7784b58 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -458,7 +458,7 @@ export class CollectionFreeFormView extends CollectionSubView void = unimplementedFunction; + public changeBezier: (e: React.PointerEvent) => void = unimplementedFunction; + public changeWidth: (color: string) => void = unimplementedFunction; + + private _palette: (string)[]; + private _width: (string)[]; + + + public _circle: boolean; + public _triangle: boolean; + public _rectangle: boolean; + public _arrow: boolean; + public _line: boolean; + public _widthSelected: string; + + @observable public _circleBt: boolean; + @observable public _triangleBt: boolean; + @observable public _rectangleBt: boolean; + @observable public _arrowBt: boolean; + @observable public _lineBt: boolean; + @observable public _colorBt: boolean; + @observable public _color: string; + @observable public _bezierBt: boolean; + @observable public _widthBt: boolean; + + + + constructor(props: Readonly<{}>) { + super(props); + InkOptionsMenu.Instance = this; + this._canFade = false; + + this._circle = false; + this._triangle = false; + this._rectangle = false; + this._arrow = false; + this._line = false; + this._circleBt = false; + this._triangleBt = false; + this._rectangleBt = false; + this._arrowBt = false; + this._lineBt = false; + this._colorBt = false; + this._bezierBt = false; + this._widthBt = false; + + this._color = ""; + this._widthSelected = ""; + + + this._palette = [ + "D0021B", "F5A623", "F8E71C", "8B572A", "7ED321", "417505", "9013FE", "4A90E2", "50E3C2", "B8E986", "000000", "4A4A4A", "9B9B9B", "FFFFFF", + ]; + + this._width = [ + "1", "5", "10", "100", "200", "300" + ]; + + } + + + + drag = (e: React.PointerEvent) => { + this.dragStart(e); + } + + + + + + @action + toggleCircle = (e: React.PointerEvent) => { + const curr = this._circle; + this.allFalse(); + curr ? this._circle = false : this._circle = true; + this._circleBt = this._circle; + } + @action + toggleTriangle = (e: React.PointerEvent) => { + const curr = this._triangle; + this.allFalse(); + curr ? this._triangle = false : this._triangle = true; + this._triangleBt = this._triangle; + } + @action + toggleRectangle = (e: React.PointerEvent) => { + const curr = this._rectangle; + this.allFalse(); + curr ? this._rectangle = false : this._rectangle = true; + this._rectangleBt = this._rectangle; + } + @action + toggleArrow = (e: React.PointerEvent) => { + const curr = this._arrow; + this.allFalse(); + curr ? this._arrow = false : this._arrow = true; + this._arrowBt = this._arrow; + } + @action + toggleLine = (e: React.PointerEvent) => { + const curr = this._line; + this.allFalse(); + curr ? this._line = false : this._line = true; + this._lineBt = this._line; + } + + @action + changeBezierClick = (e: React.PointerEvent) => { + const curr = this._bezierBt; + this.allFalse(); + curr ? this._bezierBt = false : this._bezierBt = true; + this.changeBezier(e); + } + + @action + changeWidthClick = (e: React.PointerEvent) => { + this._widthBt ? this._widthBt = false : this._widthBt = true; + } + @action + changeColorClick = (e: React.PointerEvent) => { + this._colorBt ? this._colorBt = false : this._colorBt = true; + } + + allFalse = () => { + this._circle = false; + this._triangle = false; + this._rectangle = false; + this._arrow = false; + this._line = false; + this._circleBt = false; + this._triangleBt = false; + this._rectangleBt = false; + this._arrowBt = false; + this._lineBt = false; + this._bezierBt = false; + } + + render() { + var widthPicker; + if (this._widthBt) { + widthPicker =
+ + {this._width.map(wid => { + return ; + + })} +
; + } else { + widthPicker = ; + } + + var colorPicker; + if (this._colorBt) { + colorPicker =
+ + {this._palette.map(color => { + return ; + })} +
; + } else { + colorPicker = ; + } + + + const buttons = [ + , + , + , + , + , + , + , + widthPicker, + colorPicker, + ]; + return this.getElement(buttons); + } +} \ No newline at end of file diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index cdfeeaa6b..97244ed06 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -42,6 +42,9 @@ export class MarqueeView extends React.Component { this._lastX = e.pageX; this._lastY = e.pageY; + this._pointsX.push(e.clientX); + this._pointsY.push(e.clientY); if (!e.cancelBubble) { if (Math.abs(this._lastX - this._downX) > Utils.DRAG_THRESHOLD || Math.abs(this._lastY - this._downY) > Utils.DRAG_THRESHOLD) { @@ -519,6 +527,17 @@ export class MarqueeView extends React.Component { + this._freeHand = x; } // @action // marqueeInkSelect(ink: Map) { @@ -559,7 +578,51 @@ export class MarqueeView extends React.Component r1.left && + topLeft[0] < r1.left + r1.width && + topLeft[1] > r1.top && + topLeft[1] < r1.top + r1.height) { + return true; + } + } + return false; + } + boundingShape(r1: { left: number, top: number, width: number, height: number }) { + const trueLeft = this.props.getTransform().transformPoint(Math.min(...this._pointsX), Math.min(...this._pointsY))[0]; + const trueTop = this.props.getTransform().transformPoint(Math.min(...this._pointsX), Math.min(...this._pointsY))[1]; + const trueRight = this.props.getTransform().transformPoint(Math.max(...this._pointsX), Math.max(...this._pointsY))[0]; + const trueBottom = this.props.getTransform().transformPoint(Math.max(...this._pointsX), Math.max(...this._pointsY))[1]; + + if (r1.left > trueLeft && r1.top > trueTop && r1.left + r1.width < trueRight && r1.top + r1.height < trueBottom) { + var hasTop = false; + var hasLeft = false; + var hasBottom = false; + var hasRight = false; + for (var i = 0; i < this._pointsX.length; i++) { + const truePoint = this.props.getTransform().transformPoint(this._pointsX[i], this._pointsY[i]); + if (!hasLeft && (truePoint[0] > trueLeft && truePoint[0] < r1.left) && (truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height)) { + hasLeft = true; + } + if (!hasTop && (truePoint[1] > trueTop && truePoint[1] < r1.top) && (truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width)) { + hasTop = true; + } + if (!hasRight && (truePoint[0] < trueRight && truePoint[0] > r1.left + r1.width) && (truePoint[1] > r1.top && truePoint[1] < r1.top + r1.height)) { + hasRight = true; + } + if (!hasBottom && (truePoint[1] < trueBottom && truePoint[1] > r1.top + r1.height) && (truePoint[0] > r1.left && truePoint[0] < r1.left + r1.width)) { + hasBottom = true; + } + if (hasTop && hasLeft && hasBottom && hasRight) { + return true; + } + } + } + return false; + } marqueeSelect(selectBackgrounds: boolean = true) { const selRect = this.Bounds; const selection: Doc[] = []; @@ -569,8 +632,15 @@ export class MarqueeView extends React.Component - {/* */} -
; + if (!this._freeHand) { + return
+ {/* */} +
; + + } else { + //subtracted 250 for offset + var str: string = ""; + for (var i = 0; i < this._pointsX.length; i++) { + var x = 0; + x = this._pointsX[i] - 250; + str += x.toString(); + str += ","; + str += this._pointsY[i].toString(); + str += (" "); + } + + //hardcoded height and width. + return
+ + + +
; + } } render() { diff --git a/src/client/views/nodes/ColorBox.tsx b/src/client/views/nodes/ColorBox.tsx index 6d53915ea..2ddf2c74a 100644 --- a/src/client/views/nodes/ColorBox.tsx +++ b/src/client/views/nodes/ColorBox.tsx @@ -28,8 +28,12 @@ export class ColorBox extends ViewBoxBaseComponent
-
{InkingControl.Instance.selectedWidth ?? 2}
+
{InkingControl.Instance.selectedWidth ?? 2}
) => InkingControl.Instance.switchWidth(e.target.value)} /> +
{InkingControl.Instance.selectedBezier ?? 2}
+ ) => InkingControl.Instance.switchBezier(e.target.value)} /> +
+
; } diff --git a/src/fields/documentSchemas.ts b/src/fields/documentSchemas.ts index e7031cc39..31f589ec0 100644 --- a/src/fields/documentSchemas.ts +++ b/src/fields/documentSchemas.ts @@ -63,6 +63,7 @@ export const documentSchema = createSchema({ letterSpacing: "string", opacity: "number", // opacity of document strokeWidth: "number", + strokeBezier: "number", textTransform: "string", treeViewOpen: "boolean", // flag denoting whether the documents sub-tree (contents) is visible or hidden treeViewExpandedView: "string", // name of field whose contents are being displayed as the document's subtree diff --git a/src/pen-gestures/GestureUtils.ts b/src/pen-gestures/GestureUtils.ts index 3b6170f68..a50cca2b0 100644 --- a/src/pen-gestures/GestureUtils.ts +++ b/src/pen-gestures/GestureUtils.ts @@ -39,7 +39,10 @@ export namespace GestureUtils { EndBracket = "endbracket", Stroke = "stroke", Scribble = "scribble", - Text = "text" + Text = "text", + Triangle = "triangle", + Circle = "circle", + Rectangle = "rectangle", } export const GestureRecognizer = new NDollarRecognizer(false); diff --git a/src/pen-gestures/ndollar.ts b/src/pen-gestures/ndollar.ts index e5740d105..9d42035d1 100644 --- a/src/pen-gestures/ndollar.ts +++ b/src/pen-gestures/ndollar.ts @@ -142,7 +142,7 @@ export class Result { // // NDollarRecognizer constants // -const NumMultistrokes = 4; +const NumMultistrokes = 7; const NumPoints = 96; const SquareSize = 250.0; const OneDThreshold = 0.25; // customize to desired gesture set (usually 0.20 - 0.35) @@ -190,6 +190,21 @@ export class NDollarRecognizer { // new Array(new Point(150, 150), new Point(150, 0), new Point(150, 150), new Point(0, 150)) new Array(new Point(10, 100), new Point(100, 100), new Point(150, 12), new Point(200, 103), new Point(300, 100)) )); + this.Multistrokes[4] = new Multistroke(GestureUtils.Gestures.Triangle, useBoundedRotationInvariance, new Array( + new Array(new Point(40, 100), new Point(100, 200), new Point(140, 102), new Point(42, 100)) + )); + this.Multistrokes[5] = new Multistroke(GestureUtils.Gestures.Circle, useBoundedRotationInvariance, new Array( + new Array(new Point(200, 250), new Point(240, 230), new Point(248, 210), new Point(248, 190), new Point(240, 170), new Point(200, 150), new Point(160, 170), new Point(151, 190), new Point(151, 210), new Point(160, 230), new Point(201, 250)) + )); + this.Multistrokes[6] = new Multistroke(GestureUtils.Gestures.Rectangle, useBoundedRotationInvariance, new Array( + new Array( + new Point(30, 146), //new Point(29, 160), new Point(30, 180), new Point(31, 200), + new Point(30, 222), //new Point(50, 219), new Point(70, 225), new Point(90, 230), + new Point(106, 225), //new Point(100, 200), new Point(106, 180), new Point(110, 160), + new Point(106, 146), //new Point(80, 150), new Point(50, 146), + new Point(30, 143), + new Point(29, 220)) + )); // // PREDEFINED STROKES diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts index 850c533fc..452882e09 100644 --- a/src/typings/index.d.ts +++ b/src/typings/index.d.ts @@ -5,6 +5,8 @@ declare module 'react-image-lightbox-with-rotate'; declare module 'cors'; declare module 'webrtc-adapter'; +declare module 'bezier-curve'; +declare module 'fit-curve' declare module '@react-pdf/renderer' { -- cgit v1.2.3-70-g09d2 From b21aeae569c3ebcf2538918a13cf64b9d4c8bade Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Mon, 8 Jun 2020 10:57:59 -0500 Subject: adding ScriptManager and saving --- src/client/documents/DocumentTypes.ts | 1 + src/client/documents/Documents.ts | 12 +++++++ src/client/util/ScriptManager.ts | 63 +++++++++++++++++++++++++++++++++ src/client/util/Scripting.ts | 9 +++-- src/client/views/nodes/ScriptingBox.tsx | 39 +++++++++++++++----- 5 files changed, 113 insertions(+), 11 deletions(-) create mode 100644 src/client/util/ScriptManager.ts (limited to 'src/client/util') diff --git a/src/client/documents/DocumentTypes.ts b/src/client/documents/DocumentTypes.ts index 06d35038a..7ba21b2f6 100644 --- a/src/client/documents/DocumentTypes.ts +++ b/src/client/documents/DocumentTypes.ts @@ -34,5 +34,6 @@ export enum DocumentType { COMPARISON = "comparison", // before/after view with slider (view of 2 images) LINKDB = "linkdb", // database of links ??? why do we have this + SCRIPTDB = "scriptdb", // database of scripts RECOMMENDATION = "recommendation", // view of a recommendation } \ No newline at end of file diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 38fe04534..27fbbc433 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -262,6 +262,11 @@ export namespace Docs { layout: { view: EmptyBox, dataField: defaultDataKey }, options: { childDropAction: "alias", title: "Global Link Database" } }], + [DocumentType.SCRIPTDB, { + data: new List(), + layout: { view: EmptyBox, dataField: defaultDataKey }, + options: { childDropAction: "alias", title: "Global Script Database" } + }], [DocumentType.SCRIPTING, { layout: { view: ScriptingBox, dataField: defaultDataKey } }], @@ -360,6 +365,13 @@ export namespace Docs { return Prototypes.get(DocumentType.LINKDB); } + /** + * A collection of all scripts in the database + */ + export function MainScriptDocument() { + return Prototypes.get(DocumentType.SCRIPTDB); + } + /** * This is a convenience method that is used to initialize * prototype documents for the first time. diff --git a/src/client/util/ScriptManager.ts b/src/client/util/ScriptManager.ts new file mode 100644 index 000000000..5bddb44ca --- /dev/null +++ b/src/client/util/ScriptManager.ts @@ -0,0 +1,63 @@ +import { Doc, DocListCast } from "../../fields/Doc"; +import { List } from "../../fields/List"; +import { Docs } from "../documents/Documents"; +import { Scripting, ScriptParam } from "./Scripting"; +import { StrCast } from "../../fields/Types"; + + +export class ScriptManager { + + private static _instance: ScriptManager; + public static get Instance(): ScriptManager { + return this._instance || (this._instance = new this()); + } + private constructor() { + } + + public get ScriptManagerDoc(): Doc | undefined { + return Docs.Prototypes.MainScriptDocument(); + } + + public getAllScripts(): Doc[] { + const sdoc = ScriptManager.Instance.ScriptManagerDoc; + if (sdoc) { + const docs = DocListCast(sdoc.data); + return docs; + } + return []; + } + + public addScript(scriptDoc: Doc): boolean { + const scriptList = ScriptManager.Instance.getAllScripts(); + scriptList.push(scriptDoc); + if (ScriptManager.Instance.ScriptManagerDoc) { + ScriptManager.Instance.ScriptManagerDoc.data = new List(scriptList); + return true; + } + return false; + } + + public deleteScript(scriptDoc: Doc): boolean { + const scriptList = ScriptManager.Instance.getAllScripts(); + const index = ScriptManager.Instance.getAllScripts().indexOf(scriptDoc); + if (index > -1) { + scriptList.splice(index, 1); + if (ScriptManager.Instance.ScriptManagerDoc) { + ScriptManager.Instance.ScriptManagerDoc.data = new List(scriptList); + return true; + } + } + return false; + } +} + +const scriptList = ScriptManager.Instance.getAllScripts(); + +scriptList.forEach((scriptDoc: Doc) => { + + const p = scriptDoc.compileParams?.reduce((o: ScriptParam, p: string) => { o[p] = "any"; return o; }, {} as ScriptParam); + const f = new Function(...Array.from(Object.keys(p)), StrCast(scriptDoc.rawScript)); + + Scripting.addGlobal(f, StrCast(scriptDoc.description), StrCast(p), StrCast(scriptDoc.name)); + +}); \ No newline at end of file diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 817e6b29d..5619b22b0 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -51,8 +51,9 @@ export namespace Scripting { export function addGlobal(name: string, global: any): void; export function addGlobal(global: { name: string }, decription?: string, params?: any): void; + export function addGlobal(global: { name: string }, decription?: string, params?: any, name?: any): void; - export function addGlobal(nameOrGlobal: any, global?: any, params?: any) { + export function addGlobal(nameOrGlobal: any, global?: any, params?: any, name?: any) { let n: any; let obj: any; @@ -61,12 +62,16 @@ export namespace Scripting { n = nameOrGlobal; obj = global; } else { - n = nameOrGlobal.name; obj = [nameOrGlobal]; obj.push(global); if (params) { obj.push(params); } + if (name) { + n = name; + } else { + n = nameOrGlobal.name; + } } } else if (nameOrGlobal && typeof nameOrGlobal.name === "string") { n = nameOrGlobal.name; diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index 149640c07..6f94ae8f9 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -22,6 +22,7 @@ const _global = (window /* browser */ || global /* node */) as any; import ReactTextareaAutocomplete from "@webscopeio/react-textarea-autocomplete"; import "@webscopeio/react-textarea-autocomplete/style.css"; +import { ScriptManager } from "../../util/ScriptManager"; const ScriptingSchema = createSchema({}); @@ -184,23 +185,30 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent { - this.onCompile(); - const bindings: { [name: string]: any } = {}; - this.paramsNames.forEach(key => bindings[key] = this.dataDoc[key]); - // binds vars so user doesnt have to refer to everything as self. - ScriptCast(this.dataDoc.data, null)?.script.run({ self: this.rootDoc, this: this.layoutDoc, ...bindings }, this.onError); + if (this.onCompile()) { + const bindings: { [name: string]: any } = {}; + this.paramsNames.forEach(key => bindings[key] = this.dataDoc[key]); + // binds vars so user doesnt have to refer to everything as self. + ScriptCast(this.dataDoc.data, null)?.script.run({ self: this.rootDoc, this: this.layoutDoc, ...bindings }, this.onError); + } } // checks if the script compiles and switches to applied UI @action onApply = () => { - this.onCompile(); - this._applied = true; + if (this.onCompile()) { + this._applied = true; + } } @action @@ -208,6 +216,17 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent { + if (this.onCompile()) { + this.dataDoc.funcName = "testingTitle"; + this.dataDoc.descripition = "description test"; + ScriptManager.Instance.addScript(this.dataDoc); + } else { + this._errorMessage = "Can not save script, does not compile"; + } + } + // overlays document numbers (ex. d32) over all documents when clicked on onFocus = () => { this._overlayDisposer?.(); @@ -653,8 +672,10 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent - - + + + + {this.rootDoc.layoutKey !== "layout_onClick" ? (null) : }
; -- cgit v1.2.3-70-g09d2 From c232bbfd28937c139ae18727310c91d9c6c2dbec Mon Sep 17 00:00:00 2001 From: anika-ahluwalia Date: Mon, 8 Jun 2020 13:07:11 -0500 Subject: added function UI --- src/client/util/ScriptManager.ts | 25 ++++++++- src/client/util/Scripting.ts | 14 ++++- src/client/views/nodes/ScriptingBox.scss | 2 + src/client/views/nodes/ScriptingBox.tsx | 96 +++++++++++++++++++++++++++++--- 4 files changed, 126 insertions(+), 11 deletions(-) (limited to 'src/client/util') diff --git a/src/client/util/ScriptManager.ts b/src/client/util/ScriptManager.ts index 5bddb44ca..c87fdf5fd 100644 --- a/src/client/util/ScriptManager.ts +++ b/src/client/util/ScriptManager.ts @@ -2,7 +2,8 @@ import { Doc, DocListCast } from "../../fields/Doc"; import { List } from "../../fields/List"; import { Docs } from "../documents/Documents"; import { Scripting, ScriptParam } from "./Scripting"; -import { StrCast } from "../../fields/Types"; +import { StrCast, Cast } from "../../fields/Types"; +import { listSpec } from "../../fields/Schema"; export class ScriptManager { @@ -38,6 +39,10 @@ export class ScriptManager { } public deleteScript(scriptDoc: Doc): boolean { + + if (scriptDoc.funcName) { + Scripting.removeGlobal(StrCast(scriptDoc.funcName)); + } const scriptList = ScriptManager.Instance.getAllScripts(); const index = ScriptManager.Instance.getAllScripts().indexOf(scriptDoc); if (index > -1) { @@ -55,9 +60,23 @@ const scriptList = ScriptManager.Instance.getAllScripts(); scriptList.forEach((scriptDoc: Doc) => { - const p = scriptDoc.compileParams?.reduce((o: ScriptParam, p: string) => { o[p] = "any"; return o; }, {} as ScriptParam); + const params = Cast(scriptDoc.compileParams, listSpec("string"), []); + const p = params.reduce((o: ScriptParam, p: string) => { o[p] = "any"; return o; }, {} as ScriptParam); const f = new Function(...Array.from(Object.keys(p)), StrCast(scriptDoc.rawScript)); - Scripting.addGlobal(f, StrCast(scriptDoc.description), StrCast(p), StrCast(scriptDoc.name)); + let parameters = "("; + params.forEach((element: string, i: number) => { + if (i === params.length - 1) { + parameters = parameters + element + ")"; + } else { + parameters = parameters + element + ", "; + } + }); + + if (parameters === "(") { + Scripting.addGlobal(f, StrCast(scriptDoc.description), StrCast(scriptDoc.funcName)); + } else { + Scripting.addGlobal(f, StrCast(scriptDoc.description), parameters, StrCast(scriptDoc.funcName)); + } }); \ No newline at end of file diff --git a/src/client/util/Scripting.ts b/src/client/util/Scripting.ts index 5619b22b0..16012eb5a 100644 --- a/src/client/util/Scripting.ts +++ b/src/client/util/Scripting.ts @@ -65,7 +65,11 @@ export namespace Scripting { obj = [nameOrGlobal]; obj.push(global); if (params) { - obj.push(params); + if (params.indexOf("(") > 0) { + obj.push(params); + } else { + n = params; + } } if (name) { n = name; @@ -93,6 +97,14 @@ export namespace Scripting { scriptingGlobals = globals; } + export function removeGlobal(name: string) { + if (_scriptingGlobals.hasKey(name)) { + delete _scriptingGlobals.container[name]; + return true; + } + return false; + } + export function resetScriptingGlobals() { scriptingGlobals = _scriptingGlobals; } diff --git a/src/client/views/nodes/ScriptingBox.scss b/src/client/views/nodes/ScriptingBox.scss index f28a2fcef..1e4dc4868 100644 --- a/src/client/views/nodes/ScriptingBox.scss +++ b/src/client/views/nodes/ScriptingBox.scss @@ -35,6 +35,8 @@ flex-direction: row; justify-content: center; + + .scriptingBox-textArea { flex: 70; height: 100%; diff --git a/src/client/views/nodes/ScriptingBox.tsx b/src/client/views/nodes/ScriptingBox.tsx index 6f94ae8f9..2e7b544d2 100644 --- a/src/client/views/nodes/ScriptingBox.tsx +++ b/src/client/views/nodes/ScriptingBox.tsx @@ -39,6 +39,7 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent p.split(":")[0].trim()); } @computed({ keepAlive: true }) get paramsTypes() { return this.compileParams.map(p => p.split(":")[1].trim()); } @@ -213,20 +217,46 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent { + this._errorMessage = ""; this._applied = false; + this._function = false; } @action onSave = () => { if (this.onCompile()) { - this.dataDoc.funcName = "testingTitle"; - this.dataDoc.descripition = "description test"; - ScriptManager.Instance.addScript(this.dataDoc); + this._function = true; } else { this._errorMessage = "Can not save script, does not compile"; } } + @action + onCreate = () => { + + if (this._functionName.length === 0) { + this._errorMessage = "Must enter a function name"; + return false; + } + + if (this._functionName.indexOf(" ") > 0) { + this._errorMessage = "Name can not include spaces"; + return false; + } + + this.dataDoc.funcName = this._functionName; + this.dataDoc.descripition = this._functionDescription; + + ScriptManager.Instance.deleteScript(this.dataDoc); + + this.dataDoc.funcName = "testingTitle"; + this.dataDoc.descripition = "description test"; + + ScriptManager.Instance.addScript(this.dataDoc); + + console.log("created"); + } + // overlays document numbers (ex. d32) over all documents when clicked on onFocus = () => { this._overlayDisposer?.(); @@ -270,6 +300,40 @@ export class ScriptingBox extends ViewBoxAnnotatableComponent this._functionDescription = e.target.value} + placeholder="enter description here" + value={this._functionDescription} + style={{ height: "40%", width: "100%" }} + />; + + const nameInput = +